@@ -27,12 +27,10 @@ class MorphInitBlockHelper
27
27
return " MorphInitBlock" ;
28
28
}
29
29
30
- static GenTree* MorphBlock (Compiler* comp, GenTree* tree, bool isDest);
31
- static GenTree* MorphCommaBlock (Compiler* comp, GenTreeOp* firstComma);
32
-
33
30
private:
34
- void TryInitFieldByField ();
35
- void TryPrimitiveInit ();
31
+ void TryInitFieldByField ();
32
+ void TryPrimitiveInit ();
33
+ GenTree* EliminateCommas (GenTree** commaPool);
36
34
37
35
protected:
38
36
Compiler* m_comp;
@@ -127,6 +125,9 @@ GenTree* MorphInitBlockHelper::Morph()
127
125
{
128
126
JITDUMP (" %s:\n " , GetHelperName ());
129
127
128
+ GenTree* commaPool;
129
+ GenTree* sideEffects = EliminateCommas (&commaPool);
130
+
130
131
PrepareDst ();
131
132
PrepareSrc ();
132
133
PropagateBlockAssertions ();
@@ -147,12 +148,34 @@ GenTree* MorphInitBlockHelper::Morph()
147
148
{
148
149
m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
149
150
}
150
- if (m_comp->verbose )
151
+ #endif
152
+
153
+ while (sideEffects != nullptr )
151
154
{
152
- printf (" %s (after):\n " , GetHelperName ());
153
- m_comp->gtDispTree (m_result);
155
+ if (commaPool != nullptr )
156
+ {
157
+ GenTree* comma = commaPool;
158
+ commaPool = commaPool->gtNext ;
159
+
160
+ assert (comma->OperIs (GT_COMMA));
161
+ comma->AsOp ()->gtOp1 = sideEffects;
162
+ comma->AsOp ()->gtOp2 = m_result;
163
+
164
+ comma->gtFlags &= ~GTF_ALL_EFFECT;
165
+ comma->gtFlags |= (sideEffects->gtFlags | m_result->gtFlags ) & GTF_ALL_EFFECT;
166
+ m_result = comma;
167
+ }
168
+ else
169
+ {
170
+ m_result = m_comp->gtNewOperNode (GT_COMMA, m_result->TypeGet (), sideEffects, m_result);
171
+ }
172
+ INDEBUG (m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
173
+
174
+ sideEffects = sideEffects->gtNext ;
154
175
}
155
- #endif // DEBUG
176
+
177
+ JITDUMP (" %s (after):\n " , GetHelperName ());
178
+ DISPTREE (m_result);
156
179
157
180
return m_result;
158
181
}
@@ -317,125 +340,6 @@ void MorphInitBlockHelper::MorphStructCases()
317
340
}
318
341
}
319
342
320
- // ------------------------------------------------------------------------
321
- // MorphBlock: Morph a block node preparatory to morphing a block assignment.
322
- //
323
- // Arguments:
324
- // comp - a compiler instance;
325
- // tree - a struct type node;
326
- // isDest - true if this is the destination of an assignment;
327
- //
328
- // Return Value:
329
- // Returns the possibly-morphed node. The caller is responsible for updating
330
- // the parent of this node.
331
- //
332
- // static
333
- GenTree* MorphInitBlockHelper::MorphBlock (Compiler* comp, GenTree* tree, bool isDest)
334
- {
335
- JITDUMP (" MorphBlock for %s tree, before:\n " , (isDest ? " dst" : " src" ));
336
- DISPTREE (tree);
337
-
338
- assert (varTypeIsStruct (tree));
339
-
340
- if (tree->OperIs (GT_COMMA))
341
- {
342
- // TODO-Cleanup: this block is not needed for not struct nodes, but
343
- // TryPrimitiveCopy works wrong without this transformation.
344
- tree = MorphCommaBlock (comp, tree->AsOp ());
345
- if (isDest)
346
- {
347
- tree->SetDoNotCSE ();
348
- }
349
- }
350
-
351
- assert (!tree->OperIsIndir () || varTypeIsI (genActualType (tree->AsIndir ()->Addr ())));
352
-
353
- JITDUMP (" MorphBlock after:\n " );
354
- DISPTREE (tree);
355
- return tree;
356
- }
357
-
358
- // ------------------------------------------------------------------------
359
- // MorphCommaBlock: transform COMMA<struct>(X) as OBJ<STRUCT>(COMMA byref(ADDR(X)).
360
- //
361
- // Notes:
362
- // In order to CSE and value number array index expressions and bounds checks,
363
- // the commas in which they are contained need to match.
364
- // The pattern is that the COMMA should be the address expression.
365
- // Therefore, we insert a GT_ADDR just above the node, and wrap it in an obj or ind.
366
- // TODO-1stClassStructs: Consider whether this can be improved.
367
- // Example:
368
- // before: [3] comma struct <- [2] comma struct <- [1] LCL_VAR struct
369
- // after: [5] obj <- [3] comma byref <- [2] comma byref <- [4] addr byref <- [1] LCL_VAR struct
370
- //
371
- // static
372
- GenTree* MorphInitBlockHelper::MorphCommaBlock (Compiler* comp, GenTreeOp* firstComma)
373
- {
374
- assert (firstComma->OperIs (GT_COMMA));
375
-
376
- ArrayStack<GenTree*> commas (comp->getAllocator (CMK_ArrayStack));
377
- for (GenTree* currComma = firstComma; currComma != nullptr && currComma->OperIs (GT_COMMA);
378
- currComma = currComma->gtGetOp2 ())
379
- {
380
- commas.Push (currComma);
381
- }
382
-
383
- GenTree* lastComma = commas.Top ();
384
-
385
- GenTree* effectiveVal = lastComma->gtGetOp2 ();
386
-
387
- if (!effectiveVal->OperIsIndir () && !effectiveVal->IsLocal ())
388
- {
389
- return firstComma;
390
- }
391
-
392
- assert (effectiveVal == firstComma->gtEffectiveVal ());
393
-
394
- GenTree* effectiveValAddr = comp->gtNewOperNode (GT_ADDR, TYP_BYREF, effectiveVal);
395
-
396
- INDEBUG (effectiveValAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
397
-
398
- lastComma->AsOp ()->gtOp2 = effectiveValAddr;
399
-
400
- while (!commas.Empty ())
401
- {
402
- GenTree* comma = commas.Pop ();
403
- comma->gtType = TYP_BYREF;
404
-
405
- // The "IND(COMMA)" => "COMMA(IND)" transform may have set NO_CSEs on these COMMAs, clear them.
406
- comma->ClearDoNotCSE ();
407
- comp->gtUpdateNodeSideEffects (comma);
408
- }
409
-
410
- const var_types blockType = effectiveVal->TypeGet ();
411
- GenTree* addr = firstComma;
412
-
413
- GenTree* res;
414
-
415
- if (blockType == TYP_STRUCT)
416
- {
417
- CORINFO_CLASS_HANDLE structHnd = comp->gtGetStructHandleIfPresent (effectiveVal);
418
- if (structHnd == NO_CLASS_HANDLE)
419
- {
420
- // TODO-1stClassStructs: get rid of all such cases.
421
- res = comp->gtNewIndir (blockType, addr);
422
- }
423
- else
424
- {
425
- res = comp->gtNewObjNode (structHnd, addr);
426
- comp->gtSetObjGcInfo (res->AsObj ());
427
- }
428
- }
429
- else
430
- {
431
- res = comp->gtNewIndir (blockType, addr);
432
- }
433
-
434
- comp->gtUpdateNodeSideEffects (res);
435
- INDEBUG (res->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
436
- return res;
437
- }
438
-
439
343
// ------------------------------------------------------------------------
440
344
// InitFieldByField: Attempts to promote a local block init tree to a tree
441
345
// of promoted field initialization assignments.
@@ -663,6 +567,119 @@ void MorphInitBlockHelper::TryPrimitiveInit()
663
567
}
664
568
}
665
569
570
+ // ------------------------------------------------------------------------
571
+ // EliminateCommas: Prepare for block morphing by removing commas from the
572
+ // source operand of the assignment.
573
+ //
574
+ // Parameters:
575
+ // commaPool - [out] Pool of GT_COMMA nodes linked by their gtNext nodes that
576
+ // can be used by the caller to avoid unnecessarily creating
577
+ // new commas.
578
+ //
579
+ // Returns:
580
+ // Extracted side effects, in reverse order, linked via the gtNext fields of
581
+ // the nodes.
582
+ //
583
+ // Notes:
584
+ // We have a tree like the following (note that location-valued commas are
585
+ // illegal, so there cannot be a comma on the left):
586
+ //
587
+ // ASG
588
+ // / \.
589
+ // IND COMMA
590
+ // | / \.
591
+ // B C D
592
+ //
593
+ // We'd like downstream code to just see and be expand ASG(IND(B), D).
594
+ // We will produce:
595
+ //
596
+ // COMMA
597
+ // / \.
598
+ // ASG COMMA
599
+ // / \ / \.
600
+ // tmp B C ASG
601
+ // / \.
602
+ // IND D
603
+ // |
604
+ // tmp
605
+ //
606
+ // If the ASG has GTF_REVERSE_OPS then we will produce:
607
+ //
608
+ // COMMA
609
+ // / \.
610
+ // C ASG
611
+ // / \.
612
+ // IND D
613
+ // |
614
+ // B
615
+ //
616
+ // While keeping the GTF_REVERSE_OPS.
617
+ //
618
+ // Note that the final resulting tree is created in the caller since it also
619
+ // needs to propagate side effect flags from the decomposed assignment to all
620
+ // the created commas. Therefore this function just returns a linked list of
621
+ // the side effects to be used for that purpose.
622
+ //
623
+ GenTree* MorphInitBlockHelper::EliminateCommas (GenTree** commaPool)
624
+ {
625
+ *commaPool = nullptr ;
626
+
627
+ GenTree* sideEffects = nullptr ;
628
+ auto addSideEffect = [&sideEffects](GenTree* sideEff) {
629
+ sideEff->gtNext = sideEffects;
630
+ sideEffects = sideEff;
631
+ };
632
+
633
+ auto addComma = [commaPool, &addSideEffect](GenTree* comma) {
634
+ addSideEffect (comma->gtGetOp1 ());
635
+ comma->gtNext = *commaPool;
636
+ *commaPool = comma;
637
+ };
638
+
639
+ GenTree* lhs = m_asg->gtGetOp1 ();
640
+ assert (lhs->OperIsIndir () || lhs->OperIsLocal ());
641
+
642
+ GenTree* rhs = m_asg->gtGetOp2 ();
643
+
644
+ if (m_asg->IsReverseOp ())
645
+ {
646
+ while (rhs->OperIs (GT_COMMA))
647
+ {
648
+ addComma (rhs);
649
+ rhs = rhs->gtGetOp2 ();
650
+ }
651
+ }
652
+ else
653
+ {
654
+ if (lhs->OperIsIndir () && rhs->OperIs (GT_COMMA))
655
+ {
656
+ GenTree* addr = lhs->gtGetOp1 ();
657
+ if (((addr->gtFlags & GTF_ALL_EFFECT) != 0 ) || (((rhs->gtFlags & GTF_ASG) != 0 ) && !addr->IsInvariant ()))
658
+ {
659
+ unsigned lhsAddrLclNum = m_comp->lvaGrabTemp (true DEBUGARG (" Block morph LHS addr" ));
660
+
661
+ addSideEffect (m_comp->gtNewTempAssign (lhsAddrLclNum, addr));
662
+ lhs->AsUnOp ()->gtOp1 = m_comp->gtNewLclvNode (lhsAddrLclNum, genActualType (addr));
663
+ m_comp->gtUpdateNodeSideEffects (lhs);
664
+ }
665
+ }
666
+
667
+ while (rhs->OperIs (GT_COMMA))
668
+ {
669
+ addComma (rhs);
670
+ rhs = rhs->gtGetOp2 ();
671
+ }
672
+ }
673
+
674
+ if (sideEffects != nullptr )
675
+ {
676
+ m_asg->gtOp2 = rhs;
677
+ m_comp->gtUpdateNodeSideEffects (m_asg);
678
+ }
679
+
680
+ return sideEffects;
681
+ }
682
+
666
683
class MorphCopyBlockHelper : public MorphInitBlockHelper
667
684
{
668
685
public:
@@ -733,12 +750,7 @@ MorphCopyBlockHelper::MorphCopyBlockHelper(Compiler* comp, GenTree* asg) : Morph
733
750
//
734
751
void MorphCopyBlockHelper::PrepareSrc ()
735
752
{
736
- GenTree* origSrc = m_asg->gtGetOp2 ();
737
- m_src = MorphBlock (m_comp, origSrc, false );
738
- if (m_src != origSrc)
739
- {
740
- m_asg->gtOp2 = m_src;
741
- }
753
+ m_src = m_asg->gtGetOp2 ();
742
754
743
755
if (m_src->IsLocal ())
744
756
{
0 commit comments