Skip to content

Question/issue about GT_COMMA on rhs of GT_ASG. #1699

@sandreenko

Description

@sandreenko

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:

  1. LocalAddressVisitor visits [02], but doesn't mark it as address taken or do-not-enregister (because it is not);
  2. fgMorphBlkNode tranforms 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

// 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

  1. Now V01 is address taken, but if it was promoted, then it could be promoted independently (because V01 did not and does not have lvaDoNotEnreg or lvaAddressTaken);
  2. ADDR(LCL_VAR V01) is replaced by LCL_VAR_ADDR, ASG(OBJ) is replaced by STORE_OBJ;
  3. lvaComputeRefCounts visits LCL_VAR_ADDR and marks only promoted fields as referenced,
    not the variable itself, because the promotion is PROMOTION_TYPE_INDEPENDENT:
    https://github.com/dotnet/runtime/blob/master/src/coreclr/src/jit/compiler.hpp#L1754-L1756
  4. genCodeForStoreBlk fails 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 is assert((varDsc->lvIsParam && !varDsc->lvIsRegArg) || isPrespilledArg); in lvaFrameAddress called from emitIns_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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions