-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
If somehow import generates a tree like this:
[01] * ASG struct (copy)
[02] +--* LCL_VAR struct<System.Threading.Tasks.ValueTask, 16>(P) V05 tmp3
[03] \--* COMMA struct
[04] +--* NOP
[05] \--* LCL_VAR struct<System.Threading.Tasks.ValueTask, 16> V01 loc0
then when we come to fgMorph next events happen:
LocalAddressVisitorvisits [02], but doesn't mark it as address taken or do-not-enregister (because it is not);fgMorphBlkNodetranforms it into:
[01] * ASG struct (copy)
[02] +--* LCL_VAR struct<System.Threading.Tasks.ValueTask, 16>(P) V05 tmp3
[06] \--* OBJ struct<System.Threading.Tasks.ValueTask, 16>
[03] \--* COMMA byref
[04] +--* NOP
[07] \--* ADDR byref
[05] \--* LCL_VAR struct<System.Threading.Tasks.ValueTask, 16> V01 loc0
creating address node after LocalAddressVisitor is finished;
Details
runtime/src/coreclr/src/jit/morph.cpp
Lines 9442 to 9450 in 0812d49
| // In order to CSE and value number array index expressions and bounds checks, | |
| // the commas in which they are contained need to match. | |
| // The pattern is that the COMMA should be the address expression. | |
| // Therefore, we insert a GT_ADDR just above the node, and wrap it in an obj or ind. | |
| // TODO-1stClassStructs: Consider whether this can be improved. | |
| // Example: | |
| // before: [3] comma struct <- [2] comma struct <- [1] LCL_VAR struct | |
| // after: [3] comma byref <- [2] comma byref <- [4] addr byref <- [1] LCL_VAR struct | |
- Now V01 is address taken, but if it was promoted, then it could be promoted independently (because V01 did not and does not have
lvaDoNotEnregorlvaAddressTaken); ADDR(LCL_VAR V01)is replaced byLCL_VAR_ADDR,ASG(OBJ)is replaced bySTORE_OBJ;lvaComputeRefCountsvisitsLCL_VAR_ADDRand marks only promoted fields as referenced,
not the variable itself, because the promotion isPROMOTION_TYPE_INDEPENDENT:
https://github.com/dotnet/runtime/blob/master/src/coreclr/src/jit/compiler.hpp#L1754-L1756genCodeForStoreBlkfails because V01 doesn't have a stack location because we think it is unreferenced:
;* V05 tmp3 [V05 ] ( 0, 0 ) struct (16) zero-ref "fgInsertCommaFormTemp is creating a new local variable"
the actual assert that is firing isassert((varDsc->lvIsParam && !varDsc->lvIsRegArg) || isPrespilledArg);inlvaFrameAddresscalled fromemitIns_R_R_S_S.
Details
The final tree will look like:
N029 ( 3, 2) [000034] -c-----N---- t34 = LCL_VAR_ADDR byref V05 tmp3
* ref V05._obj (offs=0x00) -> V07 tmp5 (last use)
* short V05._token (offs=0x08) -> V08 tmp6 (last use)
* bool V05._continueOnCapturedContext (offs=0x0a) -> V09 tmp7 (last use)
/--* t34 byref
N033 ( 54, 37) [000082] -c-XGO------ t82 = * IND struct <l:$541, c:$540>
N034 ( 3, 2) [000078] Dc-----N---- t78 = LCL_VAR_ADDR byref V06 tmp4 d:1
/--* t78 byref
+--* t82 struct
[000098] nA-XGO------ * STORE_BLK struct<System.Threading.Tasks.ValueTask, 16> (copy) (Unroll)
So, in short, our IR doesn't accept COMMA as RHS on ASG (as LHS as well, but it is another story) and we have a few workarounds for that, for example:
dotnet/coreclr#23141
or
runtime/src/coreclr/src/jit/optcse.cpp
Lines 2633 to 2638 in 0812d49
| GenTree* asg = m_pCompiler->gtNewTempAssign(cseLclVarNum, val); | |
| GenTree* origAsg = asg; | |
| if (!asg->OperIs(GT_ASG)) | |
| { | |
| // This can only be the case for a struct in which the 'val' was a COMMA, so |
If we don't want to fix it, then we could add a few checks that prevent it earlier and more obviously.
Also, the fact that ADDR node can appear after LocalAddressVisitor looks dangerous to me, does anybody have an idea how can it be made safer?
Note: I do not have an example where we create trees like the described in the master branch; that is from one of my struct changes. However, I thought it would be useful to open an issue and make sure that we are aware of such behavior.
category:design
theme:ir
skill-level:intermediate
cost:small