@@ -62,29 +62,27 @@ static bool blockNeedsGCPoll(BasicBlock* block)
62
62
63
63
PhaseStatus Compiler::fgInsertClsInitChecks ()
64
64
{
65
-
66
- if (!strcmp (info.compMethodName , " Test" ))
67
- {
68
- fgDispBasicBlocks (true );
69
- }
70
-
71
65
if (!opts.OptimizationEnabled ())
72
66
{
73
67
return PhaseStatus::MODIFIED_NOTHING;
74
68
}
75
69
76
70
bool modified = false ;
77
71
BasicBlock* block;
72
+
73
+ BasicBlock* prevBb = nullptr ;
78
74
for (block = fgFirstBB; block; block = block->bbNext )
79
75
{
80
- if (!block->isRunRarely () && block-> bbFlags )
76
+ if (!block->isRunRarely ())
81
77
{
82
78
for (Statement* stmt : block->Statements ())
83
79
{
84
80
for (GenTree* tree = stmt->GetTreeList (); tree != nullptr ; tree = tree->gtNext )
85
81
{
86
- if (!tree->IsCall ())
82
+ // we only need GT_CALL nodes with helper funcs
83
+ if (!tree->IsCall () || (tree->gtFlags & GTF_CALL_HOISTABLE))
87
84
{
85
+ // TODO: remove that GTF_CALL_HOISTABLE check
88
86
continue ;
89
87
}
90
88
@@ -95,20 +93,46 @@ PhaseStatus Compiler::fgInsertClsInitChecks()
95
93
}
96
94
97
95
CorInfoHelpFunc helpFunc = eeGetHelperNum (call->gtCallMethHnd );
98
- if (helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE)
96
+ if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) ||
97
+ (call->fgArgInfo ->ArgCount () != 2 ))
99
98
{
100
99
continue ;
101
100
}
102
101
103
- GenTree* moduleIdArg = call->LateArgs (). begin ()-> GetNode ( );
104
- GenTree* clsIdArg = call->LateArgs (). begin ()-> GetNext ()-> GetNode ( );
102
+ GenTree* moduleIdArg = call->fgArgInfo -> GetArgNode ( 0 );
103
+ GenTree* clsIdArg = call->fgArgInfo -> GetArgNode ( 1 );
105
104
106
105
if (!moduleIdArg->IsCnsIntOrI () || !clsIdArg->IsCnsIntOrI ())
107
106
{
108
107
// Looks like moduleId or/and clsId were passed as indirect loads
109
108
continue ;
110
109
}
111
110
111
+ if (clsIdArg->AsIntCon ()->IconValue () < 0 )
112
+ {
113
+ // Unknown clsId
114
+ continue ;
115
+ }
116
+
117
+ if (prevBb == nullptr )
118
+ {
119
+ // We're going to emit a BB in front of fgFirstBB
120
+ fgEnsureFirstBBisScratch ();
121
+ prevBb = fgFirstBB;
122
+ if (prevBb == block)
123
+ {
124
+ continue ;
125
+ }
126
+ }
127
+
128
+ // So, we found a helper call inside the "block" - let's extract it to a
129
+ // separate block "callInitBb" and guard it with a fast "isInitedBb" bb.
130
+ // The final layout should look like this:
131
+
132
+ // BB0 "prevBb":
133
+ // ...
134
+ //
135
+ // BB1 "isInitedBb": (preds: BB0 + %current preds of BB3%)
112
136
//
113
137
// * JTRUE void
114
138
// \--* NE int
@@ -118,62 +142,119 @@ PhaseStatus Compiler::fgInsertClsInitChecks()
118
142
// | \--* CNS_INT int isInitMask
119
143
// \--* CNS_INT int 0
120
144
//
145
+ //
146
+ // BB2 "callInitBb": (preds: BB1)
147
+ //
121
148
// * CALL help long HELPER.CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE
122
149
// +--* CNS_INT long moduleIdArg
123
150
// \--* CNS_INT int clsIdArg
124
151
//
125
-
152
+ // BB3 "block" (preds: BB1, BB2)
153
+ // ...
154
+ //
155
+
156
+
157
+ // Let's start from emitting that BB2 "callInitBb"
126
158
BasicBlock* callInitBb = fgNewBBbefore (BBJ_NONE, block, true );
127
- fgAddRefPred (block, callInitBb);
159
+ // it's executed only once so can be marked as cold
128
160
callInitBb->bbSetRunRarely ();
129
- callInitBb->bbFlags |= block->bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL);
130
-
161
+ callInitBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_CALL | BBF_HAS_LABEL);
131
162
GenTree* clonedHelperCall = gtCloneExprCallHelper (call);
132
163
clonedHelperCall->gtFlags |= call->gtFlags ;
133
- fgInsertStmtAtEnd (callInitBb, fgNewStmtFromTree (clonedHelperCall));
134
164
165
+ Statement* callStmt = fgNewStmtFromTree (clonedHelperCall);
166
+ if (fgStmtListThreaded)
167
+ {
168
+ gtSetStmtInfo (callStmt);
169
+ fgSetStmtSeq (callStmt);
170
+ }
171
+ fgInsertStmtAtEnd (callInitBb, callStmt);
172
+ gtUpdateStmtSideEffects (callStmt);
173
+
174
+ // BB1 "isInitedBb"
135
175
BasicBlock* isInitedBb = fgNewBBbefore (BBJ_COND, callInitBb, true );
136
- fgAddRefPred (callInitBb, isInitedBb);
137
- fgAddRefPred (block, isInitedBb);
138
176
isInitedBb->inheritWeight (block);
139
- isInitedBb->bbFlags |= block-> bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL );
177
+ isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP );
140
178
141
179
// TODO: ask VM for these constants:
142
- const int dataBlobOffset = 48 ;
143
- const int isInitMask = 1 ;
180
+ const int dataBlobOffset = 48 ; // DomainLocalModule::GetOffsetOfDataBlob()
181
+ const int isInitMask = 1 ; // ClassInitFlags::INITIALIZED_FLAG;
144
182
145
183
size_t address = moduleIdArg->AsIntCon ()->IconValue () + dataBlobOffset + clsIdArg->AsIntCon ()->IconValue ();
146
184
GenTree* indir = gtNewIndir (TYP_UBYTE, gtNewIconNode (address, TYP_I_IMPL));
147
185
indir->gtFlags = (GTF_IND_NONFAULTING | GTF_IND_INVARIANT);
148
186
149
187
GenTree* isInitedMask = gtNewOperNode (GT_AND, TYP_INT, indir, gtNewIconNode (isInitMask));
150
- GenTree* isInitedCmp = gtNewOperNode (GT_NE, TYP_INT, isInitedMask, gtNewIconNode (0 ));
188
+ GenTree* isInitedCmp = gtNewOperNode (GT_GT, TYP_INT, isInitedMask, gtNewIconNode (0 ));
189
+ isInitedCmp->gtFlags |= (GTF_UNSIGNED | GTF_RELOP_JMP_USED);
151
190
152
- fgInsertStmtAtEnd (isInitedBb, fgNewStmtFromTree (gtNewOperNode (GT_JTRUE, TYP_VOID, isInitedCmp)));
191
+ Statement* isInitedStmt = fgNewStmtFromTree (gtNewOperNode (GT_JTRUE, TYP_VOID, isInitedCmp));
192
+ if (fgStmtListThreaded)
193
+ {
194
+ gtSetStmtInfo (isInitedStmt);
195
+ fgSetStmtSeq (isInitedStmt);
196
+ }
197
+
198
+ fgInsertStmtAtEnd (isInitedBb, isInitedStmt);
153
199
isInitedBb->bbJumpDest = block;
154
- gtReplaceTree (stmt, call, gtNewNothingNode ());
200
+ block->bbFlags |= BBF_JMP_TARGET;
201
+
202
+ // Now we can remove the call from the current block
203
+ // We're going to replace the call with just "moduleId" node (it's what it was supposed to return)
204
+ gtReplaceTree (stmt, call, gtNewIconNode (moduleIdArg->AsIntCon ()->IconValue (), call->TypeGet ()));
205
+
206
+ // Now we need to fix all the preds:
207
+
208
+ // isInitedBb is a pred of callInitBb
209
+ fgAddRefPred (callInitBb, isInitedBb);
210
+ for (flowList* pred = block->bbPreds ; pred != nullptr ; pred = pred->flNext )
211
+ {
212
+ // Redirect all the preds from the current block to isInitedBb
213
+
214
+ // TODO: should I check EH region here?
215
+ // TODO: should I update some loop info if I'm inside a loop?
216
+
217
+ BasicBlock* predBlock = pred->getBlock ();
218
+ if (predBlock->bbJumpDest == block)
219
+ {
220
+ predBlock->bbJumpDest = isInitedBb;
221
+ isInitedBb->bbFlags |= BBF_JMP_TARGET;
222
+ }
223
+ fgRemoveRefPred (block, predBlock);
224
+ fgAddRefPred (isInitedBb, predBlock);
225
+ }
226
+ // Both callInitBb and isInitedBb are preds of block now
227
+ fgAddRefPred (block, callInitBb);
228
+ fgAddRefPred (block, isInitedBb);
155
229
230
+ // Make sure all three basic blocks are in the same EH region:
231
+ BasicBlock::sameEHRegion (callInitBb, block);
232
+ BasicBlock::sameEHRegion (isInitedBb, block);
156
233
157
234
modified = true ;
158
235
}
159
236
if (modified)
160
237
{
161
- // clear GTF_CALL and GTF_EXC flags
238
+ // clear GTF_CALL and GTF_EXC flags (we've just removed a call)
162
239
gtUpdateStmtSideEffects (stmt);
163
240
}
164
241
}
165
242
}
166
- }
167
-
168
- if (!strcmp (info.compMethodName , " Test" ))
169
- {
170
- fgDispBasicBlocks (true );
243
+ prevBb = block;
171
244
}
172
245
173
246
if (modified)
174
247
{
248
+ #ifdef DEBUG
249
+ if (verbose)
250
+ {
251
+ printf (" \n After fgInsertClsInitChecks:" );
252
+ fgDispBasicBlocks (true );
253
+ }
254
+ #endif // DEBUG
175
255
fgReorderBlocks ();
176
- fgUpdateChangedFlowGraph (false );
256
+ constexpr bool computeDoms = false ;
257
+ fgUpdateChangedFlowGraph (computeDoms);
177
258
return PhaseStatus::MODIFIED_EVERYTHING;
178
259
}
179
260
return PhaseStatus::MODIFIED_NOTHING;
0 commit comments