Skip to content

Commit e6cb09e

Browse files
committed
Bug 2035741 - Part 6: Fold to comparison against zero if possible. r=jandem
To avoid repeating the same optimisations across all architectures, handle these comparisons at the MIR level. 1. `i >= 1` Before: x86 ```asm cmpl $0x1, %eax jl ``` arm64 ```asm cmp w0, #0x1 b.lt ``` riscv64 ```asm li t4, 1 bge t0, t4, 0 nop auipc t4, 0x0 jr t4 ``` Now: x86 ```asm testl %eax, %eax jle ``` arm64 (no optimisation!): ```asm cmp w0, #0x0 b.le ``` riscv64: ```asm blt zero, t0, 0 nop auipc t4, 0x0 jr t4 ``` 2. Changes for `i < 1` are similar to `i >= 1`. 3. `i <= -1` Before: x86 ```asm cmpl $0xffffffff, %eax jg ``` arm64 ```asm cmn w0, #0x1 b.gt ``` riscv64 ```asm li t4, -1 bge t4, t0, 0 nop auipc t4, 0x0 jr t4 ``` Now: x86 ```asm testl %eax, %eax jge ``` arm64 ```asm tbz w0, #31 ``` riscv64 ```asm blt t0, zero, 0 nop auipc t4, 0x0 jr t4 ``` 4. Changes for `i > -1` are similar to `i <= -1`. Differential Revision: https://phabricator.services.mozilla.com/D297325
1 parent 68146c0 commit e6cb09e

2 files changed

Lines changed: 179 additions & 38 deletions

File tree

js/src/jit/MIR.cpp

Lines changed: 171 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "jit/MIR.h"
66

7+
#include "mozilla/Casting.h"
78
#include "mozilla/FloatingPoint.h"
89
#include "mozilla/MathAlgorithms.h"
910
#include "mozilla/Maybe.h"
@@ -5700,6 +5701,80 @@ MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) {
57005701
return MNot::New(alloc, startsWith);
57015702
}
57025703

5704+
/**
5705+
* Most architectures can generate smaller code for comparison against zero, so
5706+
* the macro-assemblers special-case a zero immediate when emitting
5707+
* compare-and-branch instructions.
5708+
*
5709+
* Some comparisons against one resp. negative one can instead be written as a
5710+
* comparison against zero. Handle these cases here to avoid duplicating the
5711+
* same code across all architectures.
5712+
*/
5713+
static bool CanCompareAgainstZero(int64_t value, JSOp op, bool isSigned) {
5714+
switch (op) {
5715+
case JSOp::Lt:
5716+
case JSOp::Ge:
5717+
// Can rewrite |operand < 1| as |operand <= 0|.
5718+
// Can rewrite |operand >= 1| as |operand > 0|.
5719+
return value == 1;
5720+
5721+
case JSOp::Le:
5722+
case JSOp::Gt:
5723+
// Can rewrite |operand <= -1| as |operand < 0|.
5724+
// Can rewrite |operand > -1| as |operand >= 0|.
5725+
return isSigned && value == -1;
5726+
5727+
default:
5728+
return false;
5729+
}
5730+
}
5731+
5732+
MCompare* MCompare::newCompareInt(TempAllocator& alloc, MDefinition* operand,
5733+
int64_t value, JSOp op, bool isSigned) {
5734+
MOZ_ASSERT(IsIntType(operand->type()) || operand->type() == MIRType::BigInt);
5735+
MOZ_ASSERT_IF(operand->type() == MIRType::BigInt, isSigned);
5736+
5737+
// Prefer comparison against zero if possible.
5738+
if (CanCompareAgainstZero(value, op, isSigned)) {
5739+
value = 0;
5740+
5741+
// Update operator: (Lt -> Le), (Le -> Lt), (Gt -> Ge), (Ge -> Gt).
5742+
op = ReverseCompareOp(NegateCompareOp(op));
5743+
}
5744+
5745+
MConstant* cst;
5746+
CompareType compareType;
5747+
switch (operand->type()) {
5748+
case MIRType::Int32:
5749+
cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5750+
compareType = isSigned ? Compare_Int32 : Compare_UInt32;
5751+
break;
5752+
5753+
case MIRType::Int64:
5754+
cst = MConstant::NewInt64(alloc, value);
5755+
compareType = isSigned ? Compare_Int64 : Compare_UInt64;
5756+
break;
5757+
5758+
case MIRType::IntPtr:
5759+
cst = MConstant::NewIntPtr(alloc, mozilla::AssertedCast<intptr_t>(value));
5760+
compareType = isSigned ? Compare_IntPtr : Compare_UIntPtr;
5761+
break;
5762+
5763+
case MIRType::BigInt:
5764+
cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5765+
compareType = Compare_BigInt_Int32;
5766+
break;
5767+
5768+
default:
5769+
MOZ_CRASH("unexpected operand type");
5770+
}
5771+
block()->insertBefore(this, cst);
5772+
5773+
auto* ins = MCompare::New(alloc, operand, cst, op, compareType);
5774+
ins->setResultType(type());
5775+
return ins;
5776+
}
5777+
57035778
MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) {
57045779
if (compareType() == Compare_BigInt) {
57055780
auto* left = lhs();
@@ -5797,17 +5872,11 @@ MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) {
57975872
return MConstant::NewBoolean(alloc, result);
57985873
}
57995874

5800-
auto* cst = MConstant::NewInt64(alloc, *value);
5801-
block()->insertBefore(this, cst);
5802-
5803-
auto compareType =
5804-
isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64;
5805-
if (left == int64ToBigInt) {
5806-
return MCompare::New(alloc, int64ToBigInt->input(), cst, jsop_,
5807-
compareType);
5875+
JSOp op = jsop();
5876+
if (right == int64ToBigInt) {
5877+
op = ReverseCompareOp(op);
58085878
}
5809-
return MCompare::New(alloc, cst, int64ToBigInt->input(), jsop_,
5810-
compareType);
5879+
return newCompareInt(alloc, int64ToBigInt->input(), *value, op, isSigned);
58115880
}
58125881

58135882
if (compareType() == Compare_BigInt_Int32) {
@@ -5833,13 +5902,8 @@ MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) {
58335902
return MConstant::NewBoolean(alloc, result);
58345903
}
58355904

5836-
auto* cst = MConstant::NewInt64(alloc, int64_t(constInt32));
5837-
block()->insertBefore(this, cst);
5838-
5839-
auto compareType =
5840-
isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64;
5841-
return MCompare::New(alloc, int64ToBigInt->input(), cst, jsop_,
5842-
compareType);
5905+
return newCompareInt(alloc, int64ToBigInt->input(), constInt32, jsop(),
5906+
isSigned);
58435907
}
58445908

58455909
return this;
@@ -5894,17 +5958,11 @@ MDefinition* MCompare::tryFoldBigIntPtr(TempAllocator& alloc) {
58945958
return MConstant::NewBoolean(alloc, result);
58955959
}
58965960

5897-
auto* cst = MConstant::NewIntPtr(alloc, value);
5898-
block()->insertBefore(this, cst);
5899-
5900-
if (left == intPtrToBigInt) {
5901-
left = intPtrToBigInt->input();
5902-
right = cst;
5903-
} else {
5904-
left = cst;
5905-
right = intPtrToBigInt->input();
5961+
JSOp op = jsop();
5962+
if (right == intPtrToBigInt) {
5963+
op = ReverseCompareOp(op);
59065964
}
5907-
return MCompare::New(alloc, left, right, jsop_, MCompare::Compare_IntPtr);
5965+
return newCompareInt(alloc, intPtrToBigInt->input(), value, op);
59085966
}
59095967

59105968
if (compareType() == Compare_BigInt_Int32) {
@@ -5919,12 +5977,8 @@ MDefinition* MCompare::tryFoldBigIntPtr(TempAllocator& alloc) {
59195977
return this;
59205978
}
59215979

5922-
auto* cst =
5923-
MConstant::NewIntPtr(alloc, intptr_t(right->toConstant()->toInt32()));
5924-
block()->insertBefore(this, cst);
5925-
5926-
return MCompare::New(alloc, left->toIntPtrToBigInt()->input(), cst, jsop_,
5927-
MCompare::Compare_IntPtr);
5980+
return newCompareInt(alloc, left->toIntPtrToBigInt()->input(),
5981+
right->toConstant()->toInt32(), jsop());
59285982
}
59295983

59305984
return this;
@@ -5956,9 +6010,6 @@ MDefinition* MCompare::tryFoldBigInt(TempAllocator& alloc) {
59566010
return this;
59576011
}
59586012

5959-
MConstant* int32Const = MConstant::NewInt32(alloc, x);
5960-
block()->insertBefore(this, int32Const);
5961-
59626013
auto op = jsop();
59636014
if (IsStrictEqualityOp(op)) {
59646015
// Compare_BigInt_Int32 is only valid for loose comparison.
@@ -5967,9 +6018,87 @@ MDefinition* MCompare::tryFoldBigInt(TempAllocator& alloc) {
59676018
// Reverse the comparison operator if the operands were reordered.
59686019
op = ReverseCompareOp(op);
59696020
}
6021+
return newCompareInt(alloc, operand, x, op);
6022+
}
6023+
6024+
MDefinition* MCompare::tryFoldIntZero(TempAllocator& alloc) {
6025+
// Expect signed or unsigned integer relational comparison.
6026+
if (!IsRelationalOp(jsop())) {
6027+
return this;
6028+
}
59706029

5971-
return MCompare::New(alloc, operand, int32Const, op,
5972-
MCompare::Compare_BigInt_Int32);
6030+
bool isSigned;
6031+
switch (compareType()) {
6032+
case Compare_Int32:
6033+
case Compare_Int64:
6034+
case Compare_IntPtr:
6035+
isSigned = true;
6036+
break;
6037+
6038+
case Compare_UInt32:
6039+
case Compare_UInt64:
6040+
case Compare_UIntPtr:
6041+
isSigned = false;
6042+
break;
6043+
6044+
case Compare_Undefined:
6045+
case Compare_Null:
6046+
case Compare_Double:
6047+
case Compare_Float32:
6048+
case Compare_String:
6049+
case Compare_Symbol:
6050+
case Compare_Object:
6051+
case Compare_BigInt:
6052+
case Compare_BigInt_Int32:
6053+
case Compare_BigInt_Double:
6054+
case Compare_BigInt_String:
6055+
case Compare_WasmAnyRef:
6056+
return this;
6057+
}
6058+
6059+
auto* left = lhs();
6060+
auto* right = rhs();
6061+
6062+
// Both operands have the same Int type.
6063+
MOZ_ASSERT(left->type() == right->type());
6064+
MOZ_ASSERT(IsIntType(left->type()));
6065+
6066+
// One operand must be a constant.
6067+
if (!left->isConstant() && !right->isConstant()) {
6068+
return this;
6069+
}
6070+
6071+
auto* constant =
6072+
left->isConstant() ? left->toConstant() : right->toConstant();
6073+
auto* operand = left->isConstant() ? right : left;
6074+
6075+
int64_t value;
6076+
switch (constant->type()) {
6077+
case MIRType::Int32:
6078+
value = constant->toInt32();
6079+
break;
6080+
6081+
case MIRType::Int64:
6082+
value = constant->toInt64();
6083+
break;
6084+
6085+
case MIRType::IntPtr:
6086+
value = constant->toIntPtr();
6087+
break;
6088+
6089+
default:
6090+
MOZ_CRASH("unexpected int type");
6091+
}
6092+
6093+
auto op = jsop();
6094+
if (operand == right) {
6095+
op = ReverseCompareOp(op);
6096+
}
6097+
6098+
if (!CanCompareAgainstZero(value, op, isSigned)) {
6099+
return this;
6100+
}
6101+
return newCompareInt(alloc, operand, value, op, isSigned);
59736102
}
59746103

59756104
MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
@@ -6016,6 +6145,10 @@ MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
60166145
return folded;
60176146
}
60186147

6148+
if (MDefinition* folded = tryFoldIntZero(alloc); folded != this) {
6149+
return folded;
6150+
}
6151+
60196152
return this;
60206153
}
60216154

js/src/jit/MIR.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,6 +2930,14 @@ class MCompare : public MBinaryInstruction, public ComparePolicy::Data {
29302930
[[nodiscard]] MDefinition* tryFoldBigInt64(TempAllocator& alloc);
29312931
[[nodiscard]] MDefinition* tryFoldBigIntPtr(TempAllocator& alloc);
29322932
[[nodiscard]] MDefinition* tryFoldBigInt(TempAllocator& alloc);
2933+
[[nodiscard]] MDefinition* tryFoldIntZero(TempAllocator& alloc);
2934+
2935+
// Create a new comparison with |operand| as the left-hand side operand and
2936+
// |value| as the right-hand side operand. |operand| must be an integer or
2937+
// BigInt.
2938+
[[nodiscard]] MCompare* newCompareInt(TempAllocator& alloc,
2939+
MDefinition* operand, int64_t value,
2940+
JSOp op, bool isSigned = true);
29332941

29342942
public:
29352943
bool congruentTo(const MDefinition* ins) const override {

0 commit comments

Comments
 (0)