- Revision
- 279378
- Author
- [email protected]
- Date
- 2021-06-29 11:41:28 -0700 (Tue, 29 Jun 2021)
Log Message
Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
https://bugs.webkit.org/show_bug.cgi?id=227203
Patch by Yijia Huang <[email protected]> on 2021-06-29
Reviewed by Filip Pizlo.
This patch includes two modifications:
1. Introduce a strength reduction rule for sign extending bitfield.
2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
in Zero (SBFIZ) to Air opcode to serve instruction selector.
-------------------------------------------------------
### Part A Sign extending from a variable bit-width ###
-------------------------------------------------------
According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
(https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
int bf; // sign extend this width-bit number to bfsx
int width; // number of bits representing the number in bf
int bfsx; // resulting sign-extended number
Approach 1
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1)
bf = bf & mask1 // zero bits in bf above position width
bfsx = (bf ^ mask2) - mask2
Approach 2
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (bf << amount) >> amount
Then, a new strength reduction rule is introduced:
Turn this: ((bf & mask1) ^ mask2) - mask2
Into this: (bf << amount) >> amount
-------------------
### Part B SBFX ###
-------------------
Given instruction:
sbfx Rd, Rn, lsb, width
Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
from a register Rn, sign-extends them to the size of the register, and writes the
result to the destination register Rd.
The equivalent patterns of this instruction are:
Pattern 1:
bf = src >> lsb
dst = ((bf & mask1) ^ mask2) - mask2
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
bf = src >> lsb
amount = CHAR_BIT * sizeof(bf) - width
dst = (bf << amount) >> amount
Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = ZShr(@0, @1)
Int @4 = Shl(@3, @2)
Int @5 = SShr(@4, @2)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Urshift %x0, lsb, %x0, @3
Lshift %x0, amount, %x0, @4
Rshift %x0, amount, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
ExtractSignedBitfield %x0, lsb, width, %x0, @5
Ret %x0, @6
--------------------
### Part B SBFIZ ###
--------------------
Given instruction:
sbfiz Rd, Rn, lsb, width
Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
width number of contiguous bits from a source register Rn into lsb position in the
destination register, sign-extending the most significant bit of the transferred value.
The equivalent patterns of this instruction are:
Pattern 1:
bfsx = ((src & mask1) ^ mask2) - mask2
dst = bfsx << lsb
mask1 = (1 << width) - 1
mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
Pattern 2:
amount = CHAR_BIT * sizeof(bf) - width
bfsx = (src << amount) >> amount
dst = bfsx << lsb
Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
strength reduction rule introduced above.
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Int @1 = lsb
Int @2 = amount
Int @3 = Shl(@0, @2)
Int @4 = SShr(@3, @2)
Int @5 = Shl(@4, @1)
Void@6 = Return(@5, Terminal)
Before Adding BIC:
// Old optimized AIR
Lshift %x0, amount, %x0, @3
Rshift %x0, amount, %x0, @4
Lshift %x0, lsb, %x0, @5
Ret %x0, @6
After Adding BIC:
// New optimized AIR
InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
Ret %x0, @6
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
(JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
(JSC::MacroAssemblerARM64::extractSignedBitfield32):
(JSC::MacroAssemblerARM64::extractSignedBitfield64):
* assembler/testmasm.cpp:
(JSC::testInsertSignedBitfieldInZero32):
(JSC::testInsertSignedBitfieldInZero64):
(JSC::testExtractSignedBitfield32):
(JSC::testExtractSignedBitfield64):
* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(addBitTests):
* b3/testb3_3.cpp:
(testInsertSignedBitfieldInZero32):
(testInsertSignedBitfieldInZero64):
(testExtractSignedBitfield32):
(testExtractSignedBitfield64):
Modified Paths
Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (279377 => 279378)
--- trunk/Source/_javascript_Core/ChangeLog 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-06-29 18:41:28 UTC (rev 279378)
@@ -1,3 +1,155 @@
+2021-06-29 Yijia Huang <[email protected]>
+
+ Add a new pattern to instruction selector to use SBX and SBFIZ supported by ARM64
+ https://bugs.webkit.org/show_bug.cgi?id=227203
+
+ Reviewed by Filip Pizlo.
+
+ This patch includes two modifications:
+ 1. Introduce a strength reduction rule for sign extending bitfield.
+ 2. Add Signed Bitfield Extract (SBFX) and Signed Bitfield Insert
+ in Zero (SBFIZ) to Air opcode to serve instruction selector.
+
+ -------------------------------------------------------
+ ### Part A Sign extending from a variable bit-width ###
+ -------------------------------------------------------
+ According to Bit Twiddling Hacks, there are two ways to sign extend bitfield.
+ (https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend)
+
+ int bf; // sign extend this width-bit number to bfsx
+ int width; // number of bits representing the number in bf
+ int bfsx; // resulting sign-extended number
+
+ Approach 1
+ mask1 = (1 << width) - 1
+ mask2 = 1 << (width - 1)
+ bf = bf & mask1 // zero bits in bf above position width
+ bfsx = (bf ^ mask2) - mask2
+
+ Approach 2
+ amount = CHAR_BIT * sizeof(bf) - width
+ bfsx = (bf << amount) >> amount
+
+ Then, a new strength reduction rule is introduced:
+ Turn this: ((bf & mask1) ^ mask2) - mask2
+ Into this: (bf << amount) >> amount
+
+ -------------------
+ ### Part B SBFX ###
+ -------------------
+ Given instruction:
+ sbfx Rd, Rn, lsb, width
+
+ Signed Bitfield Extract (SBFX) extracts width number of adjacent bits at lsb position
+ from a register Rn, sign-extends them to the size of the register, and writes the
+ result to the destination register Rd.
+
+ The equivalent patterns of this instruction are:
+
+ Pattern 1:
+ bf = src >> lsb
+ dst = ((bf & mask1) ^ mask2) - mask2
+ mask1 = (1 << width) - 1
+ mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
+
+ Pattern 2:
+ bf = src >> lsb
+ amount = CHAR_BIT * sizeof(bf) - width
+ dst = (bf << amount) >> amount
+
+ Then, (bf << amount) >> amount is selected as the canonical form with the strength reduction
+ rule introduced above.
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = lsb
+ Int @2 = amount
+ Int @3 = ZShr(@0, @1)
+ Int @4 = Shl(@3, @2)
+ Int @5 = SShr(@4, @2)
+ Void@6 = Return(@5, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ Urshift %x0, lsb, %x0, @3
+ Lshift %x0, amount, %x0, @4
+ Rshift %x0, amount, %x0, @5
+ Ret %x0, @6
+
+ After Adding BIC:
+ // New optimized AIR
+ ExtractSignedBitfield %x0, lsb, width, %x0, @5
+ Ret %x0, @6
+
+ --------------------
+ ### Part B SBFIZ ###
+ --------------------
+ Given instruction:
+ sbfiz Rd, Rn, lsb, width
+
+ Signed Bitfield Insert in Zero (SBFIZ) zeroes the destination register Rd and copies
+ width number of contiguous bits from a source register Rn into lsb position in the
+ destination register, sign-extending the most significant bit of the transferred value.
+
+ The equivalent patterns of this instruction are:
+
+ Pattern 1:
+ bfsx = ((src & mask1) ^ mask2) - mask2
+ dst = bfsx << lsb
+ mask1 = (1 << width) - 1
+ mask2 = 1 << (width - 1) // (mask2 << 1) - 1 = mask1
+
+ Pattern 2:
+ amount = CHAR_BIT * sizeof(bf) - width
+ bfsx = (src << amount) >> amount
+ dst = bfsx << lsb
+
+ Then, ((src << amount) >> amount) << lsb is selected as the canonical form with the
+ strength reduction rule introduced above.
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = lsb
+ Int @2 = amount
+ Int @3 = Shl(@0, @2)
+ Int @4 = SShr(@3, @2)
+ Int @5 = Shl(@4, @1)
+ Void@6 = Return(@5, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ Lshift %x0, amount, %x0, @3
+ Rshift %x0, amount, %x0, @4
+ Lshift %x0, lsb, %x0, @5
+ Ret %x0, @6
+
+ After Adding BIC:
+ // New optimized AIR
+ InsertSignedBitfieldInZero %x0, lsb, width, %x0, @5
+ Ret %x0, @6
+
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::insertSignedBitfieldInZero32):
+ (JSC::MacroAssemblerARM64::insertSignedBitfieldInZero64):
+ (JSC::MacroAssemblerARM64::extractSignedBitfield32):
+ (JSC::MacroAssemblerARM64::extractSignedBitfield64):
+ * assembler/testmasm.cpp:
+ (JSC::testInsertSignedBitfieldInZero32):
+ (JSC::testInsertSignedBitfieldInZero64):
+ (JSC::testExtractSignedBitfield32):
+ (JSC::testExtractSignedBitfield64):
+ * b3/B3LowerToAir.cpp:
+ * b3/B3ReduceStrength.cpp:
+ * b3/air/AirOpcode.opcodes:
+ * b3/testb3.h:
+ * b3/testb3_2.cpp:
+ (addBitTests):
+ * b3/testb3_3.cpp:
+ (testInsertSignedBitfieldInZero32):
+ (testInsertSignedBitfieldInZero64):
+ (testExtractSignedBitfield32):
+ (testExtractSignedBitfield64):
+
2021-06-28 Yijia Huang <[email protected]>
Add a new pattern to instruction selector to use BIC supported by ARM64
@@ -6,7 +158,7 @@
Reviewed by Filip Pizlo.
This patch includes three modifications:
- 1. Add bit clear (BIC), or not (ORN), and extract and insert bitfield at lower end (FBXIL)
+ 1. Add bit clear (BIC), or not (ORN), and extract and insert bitfield at lower end (BFXIL)
to Air opcode to serve intruciton selector.
2. Add bitfield clear (BFC) to MacroAssembler.
4. Do refactoring - rename Air opcodes added in the previous patches.
@@ -97,13 +249,13 @@
Ret %x0, @5
--------------------
- ### Part A FBXIL ###
+ ### Part A BFXIL ###
--------------------
Given the operation:
bfxil Rd, Rn, lsb, width
- Bitfield extract and insert at low end(FBXIL) copies any number of low-order bits
+ Bitfield extract and insert at low end(BFXIL) copies any number of low-order bits
from a source register into the same number of adjacent bits at the low end in
the destination register, leaving other bits unchanged.
@@ -130,7 +282,7 @@
4. 0 < width < datasize
5. shiftAmount + width <= datasize
- The canonical form to match FBXIL is d = ((n >> lsb) & mask1) | (d & mask2).
+ The canonical form to match BFXIL is d = ((n >> lsb) & mask1) | (d & mask2).
Given B3 IR:
Int @0 = ArgumentReg(%x0)
Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (279377 => 279378)
--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-06-29 18:41:28 UTC (rev 279378)
@@ -448,7 +448,7 @@
m_assembler.and_<64>(dest, dest, dataTempRegister);
}
- // Bit Operations:
+ // Bit operations:
void extractUnsignedBitfield32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
{
m_assembler.ubfx<32>(dest, src, lsb.m_value, width.m_value);
@@ -519,6 +519,26 @@
m_assembler.bfxil<64>(dest, src, lsb.m_value, width.m_value);
}
+ void insertSignedBitfieldInZero32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.sbfiz<32>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void insertSignedBitfieldInZero64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.sbfiz<64>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void extractSignedBitfield32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.sbfx<32>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void extractSignedBitfield64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.sbfx<64>(dest, src, lsb.m_value, width.m_value);
+ }
+
void clearBit64(RegisterID bitToClear, RegisterID dest, RegisterID scratchForMask = InvalidGPRReg)
{
if (scratchForMask == InvalidGPRReg)
Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (279377 => 279378)
--- trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-06-29 18:41:28 UTC (rev 279378)
@@ -1451,6 +1451,125 @@
CHECK_EQ(invoke<int64_t>(test, 0ULL, mask), ~mask);
}
}
+
+void testInsertSignedBitfieldInZero32()
+{
+ uint32_t src = ""
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ for (auto lsb : imms) {
+ for (auto width : imms) {
+ if (lsb >= 0 && width > 0 && lsb + width < 32) {
+ auto insertSignedBitfieldInZero32 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.insertSignedBitfieldInZero32(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ int32_t bf = src;
+ int32_t mask1 = (1 << width) - 1;
+ int32_t mask2 = 1 << (width - 1);
+ int32_t bfsx = ((bf & mask1) ^ mask2) - mask2;
+
+ CHECK_EQ(invoke<int32_t>(insertSignedBitfieldInZero32, src), bfsx << lsb);
+ }
+ }
+ }
+}
+
+void testInsertSignedBitfieldInZero64()
+{
+ int64_t src = ""
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ for (auto lsb : imms) {
+ for (auto width : imms) {
+ if (lsb >= 0 && width > 0 && lsb + width < 64) {
+ auto insertSignedBitfieldInZero64 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.insertSignedBitfieldInZero64(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ int64_t bf = src;
+ int64_t amount = CHAR_BIT * sizeof(bf) - width;
+ int64_t bfsx = (bf << amount) >> amount;
+
+ CHECK_EQ(invoke<int64_t>(insertSignedBitfieldInZero64, src), bfsx << lsb);
+ }
+ }
+ }
+}
+
+void testExtractSignedBitfield32()
+{
+ int32_t src = ""
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ for (auto lsb : imms) {
+ for (auto width : imms) {
+ if (lsb >= 0 && width > 0 && lsb + width < 32) {
+ auto extractSignedBitfield32 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractSignedBitfield32(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ int32_t bf = src >> lsb;
+ int32_t mask1 = (1 << width) - 1;
+ int32_t mask2 = 1 << (width - 1);
+ int32_t bfsx = ((bf & mask1) ^ mask2) - mask2;
+
+ CHECK_EQ(invoke<int32_t>(extractSignedBitfield32, src), bfsx);
+ }
+ }
+ }
+}
+
+void testExtractSignedBitfield64()
+{
+ int64_t src = ""
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ for (auto lsb : imms) {
+ for (auto width : imms) {
+ if (lsb >= 0 && width > 0 && lsb + width < 64) {
+ auto extractSignedBitfield64 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractSignedBitfield64(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ int64_t bf = src >> lsb;
+ int64_t amount = CHAR_BIT * sizeof(bf) - width;
+ int64_t bfsx = (bf << amount) >> amount;
+
+ CHECK_EQ(invoke<int64_t>(extractSignedBitfield64, src), bfsx);
+ }
+ }
+ }
+}
+
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
@@ -3636,6 +3755,11 @@
RUN(testOrNot32());
RUN(testOrNot64());
+
+ RUN(testInsertSignedBitfieldInZero32());
+ RUN(testInsertSignedBitfieldInZero64());
+ RUN(testExtractSignedBitfield32());
+ RUN(testExtractSignedBitfield64());
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-06-29 18:41:28 UTC (rev 279378)
@@ -2998,7 +2998,7 @@
// UBFIZ Pattern: d = (n & mask) << lsb
// Where: mask = (1 << width) - 1
- auto tryAppendUBFZ = [&] () -> bool {
+ auto tryAppendUBFIZ = [&] () -> bool {
Air::Opcode opcode = opcodeForType(InsertUnsignedBitfieldInZero32, InsertUnsignedBitfieldInZero64, m_value->type());
if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
return false;
@@ -3024,9 +3024,48 @@
return true;
};
- if (tryAppendUBFZ())
+ if (tryAppendUBFIZ())
return;
+ // SBFIZ Pattern: d = ((src << amount) >> amount) << lsb
+ // where: amount = datasize - width
+ auto tryAppendSBFIZ = [&] () -> bool {
+ Air::Opcode opcode = opcodeForType(InsertSignedBitfieldInZero32, InsertSignedBitfieldInZero64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
+ return false;
+ if (left->opcode() != SShr || !canBeInternal(left))
+ return false;
+ if (left->child(0)->opcode() != Shl || !canBeInternal(left->child(0)))
+ return false;
+
+ Value* srcValue = left->child(0)->child(0);
+ Value* amount1Value = left->child(0)->child(1);
+ Value* amount2Value = left->child(1);
+ Value* lsbValue = right;
+ if (m_locked.contains(srcValue))
+ return false;
+ if (!imm(amount1Value) || !imm(amount2Value) || !imm(lsbValue))
+ return false;
+ if (amount1Value->asInt() < 0 || amount2Value->asInt() < 0 || lsbValue->asInt() < 0)
+ return false;
+
+ uint64_t amount1 = amount1Value->asInt();
+ uint64_t amount2 = amount2Value->asInt();
+ uint64_t lsb = lsbValue->asInt();
+ uint64_t datasize = opcode == InsertSignedBitfieldInZero32 ? 32 : 64;
+ uint64_t width = datasize - amount1;
+ if (amount1 != amount2 || !width || lsb + width > datasize)
+ return false;
+
+ append(opcode, tmp(srcValue), imm(lsbValue), imm(width), tmp(m_value));
+ commitInternal(left->child(0));
+ commitInternal(left);
+ return true;
+ };
+
+ if (tryAppendSBFIZ())
+ return;
+
if (right->isInt32(1)) {
appendBinOp<Add32, Add64, AddDouble, AddFloat, Commutative>(left, left);
return;
@@ -3037,7 +3076,49 @@
}
case SShr: {
- appendShift<Rshift32, Rshift64>(m_value->child(0), m_value->child(1));
+ Value* left = m_value->child(0);
+ Value* right = m_value->child(1);
+
+ // SBFX Pattern: ((src >> lsb) << amount) >> amount
+ // Where: amount = datasize - width
+ auto tryAppendSBFX = [&] () -> bool {
+ Air::Opcode opcode = opcodeForType(ExtractSignedBitfield32, ExtractSignedBitfield64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
+ return false;
+ if (left->opcode() != Shl || !canBeInternal(left))
+ return false;
+ if ((left->child(0)->opcode() != ZShr && left->child(0)->opcode() != SShr) || !canBeInternal(left->child(0)))
+ return false;
+
+ Value* srcValue = left->child(0)->child(0);
+ Value* lsbValue = left->child(0)->child(1);
+ Value* amount1Value = left->child(1);
+ Value* amount2Value = right;
+ if (m_locked.contains(srcValue))
+ return false;
+ if (!imm(lsbValue) || !imm(amount1Value) || !imm(amount2Value))
+ return false;
+ if (lsbValue->asInt() < 0 || amount1Value->asInt() < 0 || amount2Value->asInt() < 0)
+ return false;
+
+ uint64_t amount1 = amount1Value->asInt();
+ uint64_t amount2 = amount2Value->asInt();
+ uint64_t lsb = lsbValue->asInt();
+ uint64_t datasize = opcode == ExtractSignedBitfield32 ? 32 : 64;
+ uint64_t width = datasize - amount1;
+ if (amount1 != amount2 || !width || lsb + width > datasize)
+ return false;
+
+ append(opcode, tmp(srcValue), imm(lsbValue), imm(width), tmp(m_value));
+ commitInternal(left->child(0));
+ commitInternal(left);
+ return true;
+ };
+
+ if (tryAppendSBFX())
+ return;
+
+ appendShift<Rshift32, Rshift64>(left, right);
return;
}
Modified: trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-06-29 18:41:28 UTC (rev 279378)
@@ -619,6 +619,33 @@
break;
case Sub:
+ // Turn this: Sub(BitXor(BitAnd(value, mask1), mask2), mask2)
+ // Into this: SShr(Shl(value, amount), amount)
+ // Conditions:
+ // 1. mask1 = (1 << width) - 1
+ // 2. mask2 = 1 << (width - 1)
+ // 3. amount = datasize - width
+ // 4. 0 < width < datasize
+ if (m_value->child(0)->opcode() == BitXor
+ && m_value->child(0)->child(0)->opcode() == BitAnd
+ && m_value->child(0)->child(0)->child(1)->hasInt()
+ && m_value->child(0)->child(1)->hasInt()
+ && m_value->child(1)->hasInt()) {
+ uint64_t mask1 = m_value->child(0)->child(0)->child(1)->asInt();
+ uint64_t mask2 = m_value->child(0)->child(1)->asInt();
+ uint64_t mask3 = m_value->child(1)->asInt();
+ uint64_t width = WTF::bitCount(mask1);
+ uint64_t datasize = m_value->child(0)->child(0)->type() == Int64 ? 64 : 32;
+ bool isValidMask1 = mask1 && !(mask1 & (mask1 + 1)) && width < datasize;
+ bool isValidMask2 = mask2 == mask3 && ((mask2 << 1) - 1) == mask1;
+ if (isValidMask1 && isValidMask2) {
+ Value* amount = m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), datasize - width);
+ Value* shlValue = m_insertionSet.insert<Value>(m_index, Shl, m_value->origin(), m_value->child(0)->child(0)->child(0), amount);
+ replaceWithNew<Value>(SShr, m_value->origin(), shlValue, amount);
+ break;
+ }
+ }
+
// Turn this: Sub(constant1, constant2)
// Into this: constant1 - constant2
if (Value* constantSub = m_value->child(0)->subConstant(m_proc, m_value->child(1))) {
Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-06-29 18:41:28 UTC (rev 279378)
@@ -854,6 +854,18 @@
arm64: ExtractInsertBitfieldAtLowEnd64 U:G:64, U:G:32, U:G:32, D:G:64
Tmp, Imm, Imm, Tmp
+arm64: InsertSignedBitfieldInZero32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: InsertSignedBitfieldInZero64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Imm, Imm, Tmp
+
+arm64: ExtractSignedBitfield32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: ExtractSignedBitfield64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Imm, Imm, Tmp
+
# The first operand is rax.
# FIXME: This formulation means that the boolean result cannot be put in eax, even though all users
# of this would be OK with that.
Modified: trunk/Source/_javascript_Core/b3/testb3.h (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/testb3.h 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/testb3.h 2021-06-29 18:41:28 UTC (rev 279378)
@@ -436,6 +436,10 @@
void testBIC64();
void testOrNot32();
void testOrNot64();
+void testInsertSignedBitfieldInZero32();
+void testInsertSignedBitfieldInZero64();
+void testExtractSignedBitfield32();
+void testExtractSignedBitfield64();
void testBitAndZeroShiftRightArgImmMask32();
void testBitAndZeroShiftRightArgImmMask64();
void testBasicSelect();
Modified: trunk/Source/_javascript_Core/b3/testb3_2.cpp (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-06-29 18:41:28 UTC (rev 279378)
@@ -4599,6 +4599,10 @@
RUN(testBIC64());
RUN(testOrNot32());
RUN(testOrNot64());
+ RUN(testInsertSignedBitfieldInZero32());
+ RUN(testInsertSignedBitfieldInZero64());
+ RUN(testExtractSignedBitfield32());
+ RUN(testExtractSignedBitfield64());
RUN(testBitAndZeroShiftRightArgImmMask32());
RUN(testBitAndZeroShiftRightArgImmMask64());
RUN(testBitAndArgs(43, 43));
Modified: trunk/Source/_javascript_Core/b3/testb3_3.cpp (279377 => 279378)
--- trunk/Source/_javascript_Core/b3/testb3_3.cpp 2021-06-29 18:07:34 UTC (rev 279377)
+++ trunk/Source/_javascript_Core/b3/testb3_3.cpp 2021-06-29 18:41:28 UTC (rev 279378)
@@ -28,6 +28,178 @@
#if ENABLE(B3_JIT)
+void testInsertSignedBitfieldInZero32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ int32_t src = ""
+ Vector<int32_t> lsbs = { 1, 14, 29 };
+ Vector<int32_t> widths = { 30, 17, 2 };
+
+ // Test Pattern: (((src & mask1) ^ mask2) - mask2) << lsb
+ // where: mask1 = (1 << width) - 1
+ // mask2 = 1 << (width - 1)
+ auto test = [&] (int32_t lsb, int32_t mask1, int32_t mask2) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* srcValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* mask1Value = root->appendNew<Const32Value>(proc, Origin(), mask1);
+ Value* mask2Value = root->appendNew<Const32Value>(proc, Origin(), mask2);
+
+ Value* andValue = root->appendNew<Value>(proc, BitAnd, Origin(), srcValue, mask1Value);
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), andValue, mask2Value);
+ Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), xorValue, mask2Value);
+
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Shl, Origin(), subValue, lsbValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "sbfiz");
+ return invoke<int32_t>(*code, src);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ int32_t lsb = lsbs.at(i);
+ int32_t width = widths.at(i);
+ int32_t mask1 = (1 << width) - 1;
+ int32_t mask2 = 1 << (width - 1);
+ int32_t bfsx = ((src & mask1) ^ mask2) - mask2;
+ CHECK(test(lsb, mask1, mask2) == (bfsx << lsb));
+ }
+}
+
+void testInsertSignedBitfieldInZero64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ int64_t src = ""
+ Vector<int64_t> lsbs = { 1, 30, 62 };
+ Vector<int64_t> widths = { 62, 33, 1 };
+
+ // Test Pattern: ((src << amount) >> amount) << lsb
+ // where: amount = datasize - width
+ auto test = [&] (int64_t lsb, int64_t amount) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* srcValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* amountValue = root->appendNew<Const64Value>(proc, Origin(), amount);
+
+ Value* signedRightShiftValue = root->appendNew<Value>(
+ proc, SShr, Origin(),
+ root->appendNew<Value>(proc, Shl, Origin(), srcValue, amountValue),
+ amountValue);
+
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Shl, Origin(), signedRightShiftValue, lsbValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "sbfiz");
+ return invoke<uint64_t>(*code, src);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ int64_t lsb = lsbs.at(i);
+ int64_t width = widths.at(i);
+ int64_t amount = CHAR_BIT * sizeof(src) - width;
+ int64_t bfsx = (src << amount) >> amount;
+ CHECK(test(lsb, amount) == (bfsx << lsb));
+ }
+}
+
+void testExtractSignedBitfield32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ int32_t src = ""
+ Vector<int32_t> lsbs = { 1, 14, 29 };
+ Vector<int32_t> widths = { 30, 17, 2 };
+
+ // Test Pattern: (((src >> lsb) & mask1) ^ mask2) - mask2
+ // where: mask1 = (1 << width) - 1
+ // mask2 = 1 << (width - 1)
+ auto test = [&] (int32_t lsb, int32_t mask1, int32_t mask2) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* srcValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* mask1Value = root->appendNew<Const32Value>(proc, Origin(), mask1);
+ Value* mask2Value = root->appendNew<Const32Value>(proc, Origin(), mask2);
+
+ Value* shiftValue = root->appendNew<Value>(proc, SShr, Origin(), srcValue, lsbValue);
+ Value* andValue = root->appendNew<Value>(proc, BitAnd, Origin(), shiftValue, mask1Value);
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), andValue, mask2Value);
+
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Sub, Origin(), xorValue, mask2Value));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "sbfx");
+ return invoke<int32_t>(*code, src);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ int32_t lsb = lsbs.at(i);
+ int32_t width = widths.at(i);
+ int32_t mask1 = (1 << width) - 1;
+ int32_t mask2 = 1 << (width - 1);
+ int32_t result = (((src >> lsb) & mask1) ^ mask2) - mask2;
+ CHECK(test(lsb, mask1, mask2) == result);
+ }
+}
+
+void testExtractSignedBitfield64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ int64_t src = ""
+ Vector<int64_t> lsbs = { 1, 30, 62 };
+ Vector<int64_t> widths = { 62, 33, 1 };
+
+ // Test Pattern: ((src >> lsb) << amount) >> amount
+ // where: amount = datasize - width
+ auto test = [&] (int64_t lsb, int64_t amount) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* srcValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* amountValue = root->appendNew<Const64Value>(proc, Origin(), amount);
+
+ Value* rightShiftValue = root->appendNew<Value>(proc, ZShr, Origin(), srcValue, lsbValue);
+ Value* leftShiftValue = root->appendNew<Value>(proc, Shl, Origin(), rightShiftValue, amountValue);
+ Value* signedRightShiftValue = root->appendNew<Value>(proc, SShr, Origin(), leftShiftValue, amountValue);
+ root->appendNewControlValue(proc, Return, Origin(), signedRightShiftValue);
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "sbfx");
+ return invoke<uint64_t>(*code, src);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ int64_t lsb = lsbs.at(i);
+ int64_t width = widths.at(i);
+ int64_t amount = CHAR_BIT * sizeof(src) - width;
+ int64_t result = ((src >> lsb) << amount) >> amount;
+ CHECK(test(lsb, amount) == result);
+ }
+}
+
void testBitOrBitOrArgImmImm32(int a, int b, int c)
{
Procedure proc;