@@ -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,33 @@ 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
+ comma->gtFlags = (sideEffects->gtFlags | m_result->gtFlags ) & GTF_ALL_EFFECT;
164
+
165
+ m_result = comma;
166
+ }
167
+ else
168
+ {
169
+ m_result = m_comp->gtNewOperNode (GT_COMMA, m_result->TypeGet (), sideEffects, m_result);
170
+ }
171
+ INDEBUG (m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
172
+
173
+ sideEffects = sideEffects->gtNext ;
154
174
}
155
- #endif // DEBUG
175
+
176
+ JITDUMP (" %s (after):\n " , GetHelperName ());
177
+ DISPTREE (m_result);
156
178
157
179
return m_result;
158
180
}
@@ -317,125 +339,6 @@ void MorphInitBlockHelper::MorphStructCases()
317
339
}
318
340
}
319
341
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
342
// ------------------------------------------------------------------------
440
343
// InitFieldByField: Attempts to promote a local block init tree to a tree
441
344
// of promoted field initialization assignments.
@@ -663,6 +566,119 @@ void MorphInitBlockHelper::TryPrimitiveInit()
663
566
}
664
567
}
665
568
569
+ // ------------------------------------------------------------------------
570
+ // EliminateCommas: Prepare for block morphing by removing commas from the
571
+ // source operand of the assignment.
572
+ //
573
+ // Parameters:
574
+ // commaPool - [out] Pool of GT_COMMA nodes linked by their gtNext nodes that
575
+ // can be used by the caller to avoid unnecessarily creating
576
+ // new commas.
577
+ //
578
+ // Returns:
579
+ // Extracted side effects, in reverse order, linked via the gtNext fields of
580
+ // the nodes.
581
+ //
582
+ // Notes:
583
+ // We have a tree like the following (note that location-valued commas are
584
+ // illegal, so there cannot be a comma on the left):
585
+ //
586
+ // ASG
587
+ // / \.
588
+ // IND COMMA
589
+ // | / \.
590
+ // B C D
591
+ //
592
+ // We'd like downstream code to just see and be expand ASG(IND(B), D).
593
+ // We will produce:
594
+ //
595
+ // COMMA
596
+ // / \.
597
+ // ASG COMMA
598
+ // / \ / \.
599
+ // tmp B C ASG
600
+ // / \.
601
+ // IND D
602
+ // |
603
+ // tmp
604
+ //
605
+ // If the ASG has GTF_REVERSE_OPS then we will produce:
606
+ //
607
+ // COMMA
608
+ // / \.
609
+ // C ASG
610
+ // / \.
611
+ // IND D
612
+ // |
613
+ // B
614
+ //
615
+ // While keeping the GTF_REVERSE_OPS.
616
+ //
617
+ // Note that the final resulting tree is created in the caller since it also
618
+ // needs to propagate side effect flags from the decomposed assignment to all
619
+ // the created commas. Therefore this function just returns a linked list of
620
+ // the side effects to be used for that purpose.
621
+ //
622
+ GenTree* MorphInitBlockHelper::EliminateCommas (GenTree** commaPool)
623
+ {
624
+ *commaPool = nullptr ;
625
+
626
+ GenTree* sideEffects = nullptr ;
627
+ auto addSideEffect = [&sideEffects](GenTree* sideEff) {
628
+ sideEff->gtNext = sideEffects;
629
+ sideEffects = sideEff;
630
+ };
631
+
632
+ auto addComma = [commaPool, &addSideEffect](GenTree* comma) {
633
+ addSideEffect (comma->gtGetOp1 ());
634
+ comma->gtNext = *commaPool;
635
+ *commaPool = comma;
636
+ };
637
+
638
+ GenTree* lhs = m_asg->gtGetOp1 ();
639
+ assert (lhs->OperIsIndir () || lhs->OperIsLocal ());
640
+
641
+ GenTree* rhs = m_asg->gtGetOp2 ();
642
+
643
+ if (m_asg->IsReverseOp ())
644
+ {
645
+ while (rhs->OperIs (GT_COMMA))
646
+ {
647
+ addComma (rhs);
648
+ rhs = rhs->gtGetOp2 ();
649
+ }
650
+ }
651
+ else
652
+ {
653
+ if (lhs->OperIsIndir () && rhs->OperIs (GT_COMMA))
654
+ {
655
+ GenTree* addr = lhs->gtGetOp1 ();
656
+ if (((addr->gtFlags & GTF_ALL_EFFECT) != 0 ) || (((rhs->gtFlags & GTF_ASG) != 0 ) && !addr->IsInvariant ()))
657
+ {
658
+ unsigned lhsAddrLclNum = m_comp->lvaGrabTemp (true DEBUGARG (" Block morph LHS addr" ));
659
+
660
+ addSideEffect (m_comp->gtNewTempAssign (lhsAddrLclNum, addr));
661
+ lhs->AsUnOp ()->gtOp1 = m_comp->gtNewLclvNode (lhsAddrLclNum, genActualType (addr));
662
+ m_comp->gtUpdateNodeSideEffects (lhs);
663
+ }
664
+ }
665
+
666
+ while (rhs->OperIs (GT_COMMA))
667
+ {
668
+ addComma (rhs);
669
+ rhs = rhs->gtGetOp2 ();
670
+ }
671
+ }
672
+
673
+ if (sideEffects != nullptr )
674
+ {
675
+ m_asg->gtOp2 = rhs;
676
+ m_comp->gtUpdateNodeSideEffects (m_asg);
677
+ }
678
+
679
+ return sideEffects;
680
+ }
681
+
666
682
class MorphCopyBlockHelper : public MorphInitBlockHelper
667
683
{
668
684
public:
@@ -733,12 +749,7 @@ MorphCopyBlockHelper::MorphCopyBlockHelper(Compiler* comp, GenTree* asg) : Morph
733
749
//
734
750
void MorphCopyBlockHelper::PrepareSrc ()
735
751
{
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
- }
752
+ m_src = m_asg->gtGetOp2 ();
742
753
743
754
if (m_src->IsLocal ())
744
755
{
0 commit comments