Skip to content

Commit a5323e2

Browse files
committed
Ensure side effects have been accounted for before swapping operands
1 parent e58abbb commit a5323e2

File tree

2 files changed

+134
-19
lines changed

2 files changed

+134
-19
lines changed

src/coreclr/jit/gentree.cpp

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19875,7 +19875,11 @@ GenTree* Compiler::gtNewSimdBinOpNode(
1987519875
}
1987619876

1987719877
// GT_AND_NOT expects `op1 & ~op2`, but xarch does `~op1 & op2`
19878+
// We expect op1 to have already been spilled
19879+
19880+
assert((op1->gtFlags & GTF_SIDE_EFFECT) == 0);
1987819881
std::swap(op1, op2);
19882+
1987919883
break;
1988019884
}
1988119885

@@ -24234,58 +24238,76 @@ GenTree* Compiler::gtNewSimdTernaryLogicNode(var_types type,
2423424238
{
2423524239
case TernaryLogicUseFlags::A:
2423624240
{
24237-
// One operand. We swap it with C so it can be contained where possible
24238-
value1 = op1;
24239-
value2 = nullptr;
24241+
// We'll swap from 'A, B, C' to 'B, C, A'
24242+
// We expect A to have been spilled
2424024243

24241-
std::swap(op1, op3);
24244+
assert((op1->gtFlags & GTF_SIDE_EFFECT) == 0);
24245+
24246+
std::swap(op1, op2); // B, A, C
24247+
std::swap(op2, op3); // B, C, A
24248+
24249+
value1 = op3;
24250+
value2 = nullptr;
2424224251
break;
2424324252
}
2424424253

2424524254
case TernaryLogicUseFlags::B:
2424624255
{
24247-
// One operand. We swap it with C so it can be contained where possible
24248-
value1 = op2;
24249-
value2 = nullptr;
24256+
// We'll swap from 'A, B, C' to 'A, C, B'
24257+
// We expect A and B to have been spilled
24258+
24259+
assert((op1->gtFlags & GTF_SIDE_EFFECT) == 0);
24260+
assert((op2->gtFlags & GTF_SIDE_EFFECT) == 0);
24261+
24262+
std::swap(op2, op3); // A, C, B
2425024263

24251-
std::swap(op2, op3);
24264+
value1 = op3;
24265+
value2 = nullptr;
2425224266
break;
2425324267
}
2425424268

2425524269
case TernaryLogicUseFlags::C:
2425624270
{
24257-
// One operand. It is already in the ideal position
24271+
// We don't require any operands to have been spilled
24272+
2425824273
value1 = op3;
2425924274
value2 = nullptr;
2426024275
break;
2426124276
}
2426224277

2426324278
case TernaryLogicUseFlags::AB:
2426424279
{
24265-
// Two operands. We swap A with C so it can be contained where possible
24266-
// and so we aren't RMW
24280+
// We'll swap from 'A, B, C' to 'C, A, B'
24281+
// We expect A and B to have been spilled
2426724282

24268-
value1 = op1;
24269-
value2 = op2;
24283+
assert((op1->gtFlags & GTF_SIDE_EFFECT) == 0);
24284+
assert((op2->gtFlags & GTF_SIDE_EFFECT) == 0);
2427024285

24271-
std::swap(op1, op3);
24286+
std::swap(op1, op3); // C, B, A
24287+
std::swap(op2, op3); // C, A, B
24288+
24289+
value1 = op2;
24290+
value2 = op3;
2427224291
break;
2427324292
}
2427424293

2427524294
case TernaryLogicUseFlags::AC:
2427624295
{
24277-
// Two operands. We swap A with B so we aren't RMW
24296+
// We'll swap from 'A, B, C' to 'B, C, A'
24297+
// We expect A to have been spilled
2427824298

24279-
value1 = op1;
24280-
value2 = op3;
24299+
assert((op1->gtFlags & GTF_SIDE_EFFECT) == 0);
24300+
24301+
std::swap(op1, op2); // B, A, C
2428124302

24282-
std::swap(op1, op2);
24303+
value1 = op2;
24304+
value2 = op3;
2428324305
break;
2428424306
}
2428524307

2428624308
case TernaryLogicUseFlags::BC:
2428724309
{
24288-
// Two operands. They are already in the ideal positions
24310+
// We don't require any operands to have been spilled
2428924311

2429024312
value1 = op2;
2429124313
value2 = op3;
@@ -24469,6 +24491,9 @@ GenTree* Compiler::gtNewSimdTernaryLogicNode(var_types type,
2446924491
value1 = gtNewOperNode(GT_COMMA, type, op1, value1);
2447024492
}
2447124493

24494+
// GT_AND_NOT takes them as `op1 & ~op2` and x86 reorders them back to `~op1 & op2`
24495+
// since the underlying andnps/andnpd/pandn instructions take them as such
24496+
2447224497
return gtNewSimdBinOpNode(GT_AND_NOT, type, value2, value1, simdBaseJitType, simdSize);
2447324498
}
2447424499
else

src/coreclr/jit/hwintrinsicxarch.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3002,6 +3002,96 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
30023002
assert(sig->numArgs == 4);
30033003

30043004
op4 = impPopStack().val;
3005+
3006+
bool spillOp1 = false;
3007+
bool spillOp2 = false;
3008+
3009+
if (op4->IsIntegralConst())
3010+
{
3011+
uint8_t control = static_cast<uint8_t>(op4->AsIntCon()->gtIconVal);
3012+
const TernaryLogicInfo& info = TernaryLogicInfo::lookup(control);
3013+
TernaryLogicUseFlags useFlags = info.GetAllUseFlags();
3014+
3015+
if (useFlags != TernaryLogicUseFlags::ABC)
3016+
{
3017+
switch (useFlags)
3018+
{
3019+
case TernaryLogicUseFlags::A:
3020+
{
3021+
// We'll swap from 'A, B, C' to 'B, C, A'
3022+
// so just spill A
3023+
3024+
spillOp1 = true;
3025+
break;
3026+
}
3027+
3028+
case TernaryLogicUseFlags::B:
3029+
{
3030+
// We'll swap from 'A, B, C' to 'A, C, B'
3031+
// so spill A and B
3032+
3033+
spillOp1 = true;
3034+
spillOp2 = true;
3035+
break;
3036+
}
3037+
3038+
case TernaryLogicUseFlags::C:
3039+
{
3040+
// No operands will be swapped
3041+
break;
3042+
}
3043+
3044+
case TernaryLogicUseFlags::AB:
3045+
{
3046+
// We'll swap from 'A, B, C' to 'C, A, B'
3047+
// so spill A and B
3048+
3049+
spillOp1 = true;
3050+
spillOp2 = true;
3051+
break;
3052+
}
3053+
3054+
case TernaryLogicUseFlags::AC:
3055+
{
3056+
// We'll swap from 'A, B, C' to 'B, C, A'
3057+
// so just spill A
3058+
3059+
spillOp1 = true;
3060+
break;
3061+
}
3062+
3063+
case TernaryLogicUseFlags::BC:
3064+
{
3065+
// No operands will be swapped
3066+
break;
3067+
}
3068+
3069+
case TernaryLogicUseFlags::None:
3070+
{
3071+
// No operands.
3072+
break;
3073+
}
3074+
3075+
default:
3076+
{
3077+
unreached();
3078+
}
3079+
}
3080+
}
3081+
}
3082+
3083+
if (spillOp1)
3084+
{
3085+
impSpillSideEffect(true, verCurrentState.esStackDepth -
3086+
3 DEBUGARG("Spilling op1 side effects for HWIntrinsic"));
3087+
}
3088+
3089+
if (spillOp2)
3090+
{
3091+
impSpillSideEffect(true, verCurrentState.esStackDepth -
3092+
2 DEBUGARG("Spilling op2 side effects for HWIntrinsic"));
3093+
}
3094+
30053095
op3 = impSIMDPopStack();
30063096
op2 = impSIMDPopStack();
30073097
op1 = impSIMDPopStack();

0 commit comments

Comments
 (0)