@@ -14327,15 +14327,12 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14327
14327
14328
14328
val = cons->AsIntConCommon()->IconValue();
14329
14329
14330
- // Transforms that would drop op cannot be performed if op has side effects
14331
- bool opHasSideEffects = (op->gtFlags & GTF_SIDE_EFFECT) != 0;
14332
-
14333
14330
// Helper function that creates a new IntCon node and morphs it, if required
14334
14331
auto NewMorphedIntConNode = [&](int value) -> GenTreeIntCon* {
14335
14332
GenTreeIntCon* icon = gtNewIconNode(value);
14336
14333
if (fgGlobalMorph)
14337
14334
{
14338
- fgMorphTreeDone (icon);
14335
+ INDEBUG (icon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED );
14339
14336
}
14340
14337
return icon;
14341
14338
};
@@ -14370,43 +14367,52 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14370
14367
switch (oper)
14371
14368
{
14372
14369
case GT_LE:
14373
- if (tree->IsUnsigned() && (val == 0) && (op1 == cons) && !opHasSideEffects)
14370
+ {
14371
+ if (tree->IsUnsigned() && (val == 0) && (op1 == cons))
14374
14372
{
14375
14373
// unsigned (0 <= x) is always true
14376
- op = NewMorphedIntConNode(1);
14374
+ op = gtWrapWithSideEffects( NewMorphedIntConNode(1), op, GTF_ALL_EFFECT );
14377
14375
goto DONE_FOLD;
14378
14376
}
14379
14377
break;
14378
+ }
14380
14379
14381
14380
case GT_GE:
14382
- if (tree->IsUnsigned() && (val == 0) && (op2 == cons) && !opHasSideEffects)
14381
+ {
14382
+ if (tree->IsUnsigned() && (val == 0) && (op2 == cons))
14383
14383
{
14384
14384
// unsigned (x >= 0) is always true
14385
- op = NewMorphedIntConNode(1);
14385
+ op = gtWrapWithSideEffects( NewMorphedIntConNode(1), op, GTF_ALL_EFFECT );
14386
14386
goto DONE_FOLD;
14387
14387
}
14388
14388
break;
14389
+ }
14389
14390
14390
14391
case GT_LT:
14391
- if (tree->IsUnsigned() && (val == 0) && (op2 == cons) && !opHasSideEffects)
14392
+ {
14393
+ if (tree->IsUnsigned() && (val == 0) && (op2 == cons))
14392
14394
{
14393
14395
// unsigned (x < 0) is always false
14394
- op = NewMorphedIntConNode(0);
14396
+ op = gtWrapWithSideEffects( NewMorphedIntConNode(0), op, GTF_ALL_EFFECT );
14395
14397
goto DONE_FOLD;
14396
14398
}
14397
14399
break;
14400
+ }
14398
14401
14399
14402
case GT_GT:
14400
- if (tree->IsUnsigned() && (val == 0) && (op1 == cons) && !opHasSideEffects)
14403
+ {
14404
+ if (tree->IsUnsigned() && (val == 0) && (op1 == cons))
14401
14405
{
14402
14406
// unsigned (0 > x) is always false
14403
- op = NewMorphedIntConNode(0);
14407
+ op = gtWrapWithSideEffects( NewMorphedIntConNode(0), op, GTF_ALL_EFFECT );
14404
14408
goto DONE_FOLD;
14405
14409
}
14410
+ }
14406
14411
FALLTHROUGH;
14412
+
14407
14413
case GT_EQ:
14408
14414
case GT_NE:
14409
-
14415
+ {
14410
14416
// Optimize boxed value classes; these are always false. This IL is
14411
14417
// generated when a generic value is tested against null:
14412
14418
// <T> ... foo(T x) { ... if ((object)x == null) ...
@@ -14468,57 +14474,59 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14468
14474
{
14469
14475
return gtFoldBoxNullable(tree);
14470
14476
}
14471
-
14472
14477
break;
14478
+ }
14473
14479
14474
14480
case GT_ADD:
14481
+ {
14475
14482
if (val == 0)
14476
14483
{
14477
14484
goto DONE_FOLD;
14478
14485
}
14479
14486
break;
14487
+ }
14480
14488
14481
14489
case GT_MUL:
14490
+ {
14482
14491
if (val == 1)
14483
14492
{
14484
14493
goto DONE_FOLD;
14485
14494
}
14486
14495
else if (val == 0)
14487
14496
{
14488
- /* Multiply by zero - return the 'zero' node, but not if side effects */
14489
- if (!opHasSideEffects)
14490
- {
14491
- op = cons;
14492
- goto DONE_FOLD;
14493
- }
14497
+ // Multiply by zero - return the 'zero' node
14498
+ op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14499
+ goto DONE_FOLD;
14494
14500
}
14495
14501
break;
14502
+ }
14496
14503
14497
14504
case GT_DIV:
14498
14505
case GT_UDIV:
14506
+ {
14499
14507
if ((op2 == cons) && (val == 1) && !op1->OperIsConst())
14500
14508
{
14501
14509
goto DONE_FOLD;
14502
14510
}
14503
14511
break;
14512
+ }
14504
14513
14505
14514
case GT_SUB:
14515
+ {
14506
14516
if ((op2 == cons) && (val == 0) && !op1->OperIsConst())
14507
14517
{
14508
14518
goto DONE_FOLD;
14509
14519
}
14510
14520
break;
14521
+ }
14511
14522
14512
14523
case GT_AND:
14524
+ {
14513
14525
if (val == 0)
14514
14526
{
14515
- /* AND with zero - return the 'zero' node, but not if side effects */
14516
-
14517
- if (!opHasSideEffects)
14518
- {
14519
- op = cons;
14520
- goto DONE_FOLD;
14521
- }
14527
+ // AND with zero - return the 'zero' node
14528
+ op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14529
+ goto DONE_FOLD;
14522
14530
}
14523
14531
else if (val == 0xFF)
14524
14532
{
@@ -14551,8 +14559,10 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14551
14559
}
14552
14560
}
14553
14561
break;
14562
+ }
14554
14563
14555
14564
case GT_OR:
14565
+ {
14556
14566
if (val == 0)
14557
14567
{
14558
14568
goto DONE_FOLD;
@@ -14563,34 +14573,33 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14563
14573
14564
14574
assert(val == 1);
14565
14575
14566
- /* OR with one - return the 'one' node, but not if side effects */
14567
-
14568
- if (!opHasSideEffects)
14569
- {
14570
- op = cons;
14571
- goto DONE_FOLD;
14572
- }
14576
+ // OR with one - return the 'one' node
14577
+ op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14578
+ goto DONE_FOLD;
14573
14579
}
14574
14580
break;
14581
+ }
14575
14582
14576
14583
case GT_LSH:
14577
14584
case GT_RSH:
14578
14585
case GT_RSZ:
14579
14586
case GT_ROL:
14580
14587
case GT_ROR:
14588
+ {
14581
14589
if (val == 0)
14582
14590
{
14583
14591
if (op2 == cons)
14584
14592
{
14585
14593
goto DONE_FOLD;
14586
14594
}
14587
- else if (!opHasSideEffects)
14595
+ else
14588
14596
{
14589
- op = cons;
14597
+ op = gtWrapWithSideEffects( cons, op, GTF_ALL_EFFECT) ;
14590
14598
goto DONE_FOLD;
14591
14599
}
14592
14600
}
14593
14601
break;
14602
+ }
14594
14603
14595
14604
case GT_QMARK:
14596
14605
{
@@ -14613,9 +14622,8 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14613
14622
{
14614
14623
fgWalkTreePre(&op, gtClearColonCond);
14615
14624
}
14616
- }
14617
-
14618
14625
goto DONE_FOLD;
14626
+ }
14619
14627
14620
14628
default:
14621
14629
break;
@@ -14632,6 +14640,13 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14632
14640
JITDUMP("Transformed into:\n");
14633
14641
DISPTREE(op);
14634
14642
14643
+ if (fgGlobalMorph)
14644
+ {
14645
+ // We can sometimes produce a comma over the constant if the original op
14646
+ // had a side effect, so just ensure we set the flag (which will be already
14647
+ // set for the operands otherwise).
14648
+ INDEBUG(op->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
14649
+ }
14635
14650
return op;
14636
14651
}
14637
14652
@@ -16903,9 +16918,19 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags)
16903
16918
// Are there only GTF_CALL side effects remaining? (and no other side effect kinds)
16904
16919
if (flags & GTF_CALL)
16905
16920
{
16906
- if (tree->OperGet() == GT_CALL)
16921
+ GenTree* potentialCall = tree;
16922
+
16923
+ if (potentialCall->OperIs(GT_RET_EXPR))
16924
+ {
16925
+ // We need to preserve return expressions where the underlying call
16926
+ // has side effects. Otherwise early folding can result in us dropping
16927
+ // the call.
16928
+ potentialCall = potentialCall->AsRetExpr()->gtInlineCandidate;
16929
+ }
16930
+
16931
+ if (potentialCall->OperIs(GT_CALL))
16907
16932
{
16908
- GenTreeCall* const call = tree ->AsCall();
16933
+ GenTreeCall* const call = potentialCall ->AsCall();
16909
16934
const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0;
16910
16935
const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors.
16911
16936
if (!call->HasSideEffects(this, ignoreExceptions, ignoreCctors))
0 commit comments