Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (279361 => 279362)
--- trunk/Source/_javascript_Core/ChangeLog 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-06-29 04:05:14 UTC (rev 279362)
@@ -1,3 +1,248 @@
+2021-06-28 Yijia Huang <[email protected]>
+
+ Add a new pattern to instruction selector to use BIC supported by ARM64
+ https://bugs.webkit.org/show_bug.cgi?id=227202
+
+ 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)
+ 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.
+
+ ------------------
+ ### Part A BIC ###
+ ------------------
+ Given the operation:
+
+ bic Rd, Rn, Rm
+
+ The BIC (Bit Clear) instruction performs an AND operation on the bits in Rn with the
+ complements of the corresponding bits in the value of Rm. The instruction selector can
+ utilize this to lowering certain patterns in B3 IR before further Air optimization.
+ The equivalent patterns of this instruction are:
+
+ Pattern 1:
+ d = n & (-m - 1)
+
+ Pattern 2:
+ d = n & (m ^ -1)
+
+ In order to get benefits for complement operation, current instruction selector uses
+ mvn instruction to lower the pattern value ^ -1. Then, a new strength reduction rule is
+ introduced:
+ Turn this: -value - 1
+ Into this: value ^ -1
+
+ So, d = n & (m ^ -1) is selected as the canonical form.
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = ArgumentReg(%x1)
+ Int @2 = -1
+ Int @3 = BitXor(@1, @2)
+ Int @4 = BitAnd(@0, @3)
+ Void@5 = Return(@4, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ Not %x1, %x1, @3
+ And %x0, %x1, %x0, @4
+ Ret %x0, @5
+
+ After Adding BIC:
+ // New optimized AIR
+ Bic %x0, %x1, %x0, @4
+ Ret %x0, @5
+
+ ------------------
+ ### Part A ORN ###
+ ------------------
+ Given the operation:
+
+ orn Rd, Rn, Rm
+
+ Bitwise OR NOT (shifted register) performs a bitwise (inclusive) OR of a register value
+ Rn and the complement of an optionally-shifted register value Rm, and writes the result
+ to the destination register Rd.
+
+ The equivalent patterns of this instruction are:
+
+ Pattern 1:
+ d = n | (-m - 1)
+
+ Pattern 2:
+ d = n | (m ^ -1)
+
+ Then, d = n | (m ^ -1) is selected as the canonical form.
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = ArgumentReg(%x1)
+ Int @2 = -1
+ Int @3 = BitXor(@1, @2)
+ Int @4 = BitOr(@0, @3)
+ Void@5 = Return(@4, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ Not %x1, %x1, @3
+ Or %x0, %x1, %x0, @4
+ Ret %x0, @5
+
+ After Adding BIC:
+ // New optimized AIR
+ Orn %x0, %x1, %x0, @4
+ Ret %x0, @5
+
+ --------------------
+ ### Part A FBXIL ###
+ --------------------
+ Given the operation:
+
+ bfxil Rd, Rn, lsb, width
+
+ Bitfield extract and insert at low end(FBXIL) 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.
+
+ The equivalent patterns of this instruction are:
+
+ Pattern 1:
+ mask1 = (1 << width) - 1
+ mask2 = ~mask1
+ ((n >> lsb) & mask1) | (d & mask2)
+
+ Pattern 2:
+ mask1 = ((1 << width) - 1) << lsb
+ mask2 = ~(mask1 >> lsb)
+ ((n & mask1) >> lsb) | (d & mask2)
+
+ Then, introduce a strength reduction rule for easier recognization.
+ Turn this: (v & maskShift) >> shiftAmount
+ Into this: (v >> shiftAmount) & mask
+
+ with constraints:
+ 1. maskShift = mask << lsb
+ 2. mask = (1 << width) - 1
+ 3. 0 <= shiftAmount < datasize
+ 4. 0 < width < datasize
+ 5. shiftAmount + width <= datasize
+
+ The canonical form to match FBXIL is d = ((n >> lsb) & mask1) | (d & mask2).
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = ArgumentReg(%x1)
+ Int @2 = lsb
+ Int @3 = mask1
+ Int @4 = mask2
+ Int @5 = BitAnd(@1, @3)
+ Int @6 = BitAnd(@0, @4))
+ Int @7 = ZShr(@5, @2)
+ Int @8 = BitOr(@7, @6)
+ Void@9 = Return(@8, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ And mask2, %x0, %x0, @6
+ ExtractUnsignedBitfield %x1, lsb, width, %x1, @7
+ Or %x0, %x1, %x0, @8
+ Ret %x0, @9
+
+ After Adding BIC:
+ // New optimized AIR
+ ExtractInsertBitfieldAtLowEnd %x1, lsb, width, %x0, @8
+ Ret64 %x0, @9
+
+ --------------
+ ### Part B ###
+ --------------
+ The Bitfield Clear (BFC), leaving other bits unchanged, is similar to BFI which is an
+ alias of BFM. Given the operation:
+
+ bfc Rd, lsb, width
+
+ The equivalent pattern of this instruction is:
+
+ mask = ((1 << width) - 1) << lsb
+ d = d & ~mask
+
+ Since mask is a constant and B3 performs constant fold in the optimization phase, this
+ pattern will directly lower to the BitAnd binary operation. So, no need to match this pattern.
+
+ --------------
+ ### Part C ###
+ --------------
+ At MacroAssembler level, the emitters are exepected to be expressed in english
+ (e.g. something like clearBitField for BFC). Do refactoring to rename Air opcode for
+ UBFX, UBFIZ, BFI, and BIC.
+
+ * assembler/ARM64Assembler.h:
+ (JSC::ARM64Assembler::bfc):
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::extractUnsignedBitfield32):
+ (JSC::MacroAssemblerARM64::extractUnsignedBitfield64):
+ (JSC::MacroAssemblerARM64::insertUnsignedBitfieldInZero32):
+ (JSC::MacroAssemblerARM64::insertUnsignedBitfieldInZero64):
+ (JSC::MacroAssemblerARM64::insertBitField32):
+ (JSC::MacroAssemblerARM64::insertBitField64):
+ (JSC::MacroAssemblerARM64::clearBitField32):
+ (JSC::MacroAssemblerARM64::clearBitField64):
+ (JSC::MacroAssemblerARM64::clearBitsWithMask32):
+ (JSC::MacroAssemblerARM64::clearBitsWithMask64):
+ (JSC::MacroAssemblerARM64::orNot32):
+ (JSC::MacroAssemblerARM64::orNot64):
+ (JSC::MacroAssemblerARM64::ubfx32): Deleted.
+ (JSC::MacroAssemblerARM64::ubfx64): Deleted.
+ (JSC::MacroAssemblerARM64::ubfiz32): Deleted.
+ (JSC::MacroAssemblerARM64::ubfiz64): Deleted.
+ (JSC::MacroAssemblerARM64::bitFieldInsert32): Deleted.
+ (JSC::MacroAssemblerARM64::bitFieldInsert64): Deleted.
+ * assembler/testmasm.cpp:
+ (JSC::testMultiplySignExtend32):
+ (JSC::testMultiplySubSignExtend32):
+ (JSC::testExtractUnsignedBitfield32):
+ (JSC::testExtractUnsignedBitfield64):
+ (JSC::testInsertUnsignedBitfieldInZero32):
+ (JSC::testInsertUnsignedBitfieldInZero64):
+ (JSC::testInsertBitField32):
+ (JSC::testInsertBitField64):
+ (JSC::testClearBitField32):
+ (JSC::testClearBitField64):
+ (JSC::testClearBitsWithMask32):
+ (JSC::testClearBitsWithMask64):
+ (JSC::testOrNot32):
+ (JSC::testOrNot64):
+ (JSC::testMul32SignExtend): Deleted.
+ (JSC::testMulSubSignExtend32): Deleted.
+ (JSC::testUbfx32): Deleted.
+ (JSC::testUbfx64): Deleted.
+ (JSC::testUbfiz32): Deleted.
+ (JSC::testUbfiz64): Deleted.
+ (JSC::testBitFieldInsert32): Deleted.
+ (JSC::testBitFieldInsert64): Deleted.
+ * b3/B3LowerToAir.cpp:
+ * b3/B3ReduceStrength.cpp:
+ * b3/air/AirOpcode.opcodes:
+ * b3/testb3.h:
+ * b3/testb3_2.cpp:
+ (testInsertBitField32):
+ (testInsertBitField64):
+ (testBIC32):
+ (testBIC64):
+ (testOrNot32):
+ (testOrNot64):
+ (addBitTests):
+ (testBitFieldInsert32): Deleted.
+ (testBitFieldInsert64): Deleted.
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+ * jit/AssemblyHelpers.cpp:
+ (JSC::AssemblyHelpers::cageWithoutUntagging):
+ (JSC::AssemblyHelpers::cageConditionallyAndUntag):
+
2021-06-28 Mikhail R. Gadelha <[email protected]>
Add LLInt fast path for less, lesseq, greater and greatereq
Modified: trunk/Source/_javascript_Core/assembler/ARM64Assembler.h (279361 => 279362)
--- trunk/Source/_javascript_Core/assembler/ARM64Assembler.h 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/assembler/ARM64Assembler.h 2021-06-29 04:05:14 UTC (rev 279362)
@@ -833,6 +833,12 @@
}
template<int datasize>
+ ALWAYS_INLINE void bfc(RegisterID rd, int lsb, int width)
+ {
+ bfi<datasize>(rd, ARM64Registers::zr, lsb, width);
+ }
+
+ template<int datasize>
ALWAYS_INLINE void bfi(RegisterID rd, RegisterID rn, int lsb, int width)
{
bfm<datasize>(rd, rn, (datasize - lsb) & (datasize - 1), width - 1);
Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (279361 => 279362)
--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-06-29 04:05:14 UTC (rev 279362)
@@ -390,26 +390,6 @@
and32(dataTempRegister, dest);
}
- void ubfx32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.ubfx<32>(dest, src, lsb.m_value, width.m_value);
- }
-
- void ubfx64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.ubfx<64>(dest, src, lsb.m_value, width.m_value);
- }
-
- void ubfiz32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.ubfiz<32>(dest, src, lsb.m_value, width.m_value);
- }
-
- void ubfiz64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.ubfiz<64>(dest, src, lsb.m_value, width.m_value);
- }
-
void and64(RegisterID src1, RegisterID src2, RegisterID dest)
{
m_assembler.and_<64>(dest, src1, src2);
@@ -468,6 +448,77 @@
m_assembler.and_<64>(dest, dest, dataTempRegister);
}
+ // Bit Operations:
+ void extractUnsignedBitfield32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.ubfx<32>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void extractUnsignedBitfield64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.ubfx<64>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void insertUnsignedBitfieldInZero32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.ubfiz<32>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void insertUnsignedBitfieldInZero64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.ubfiz<64>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void insertBitField32(RegisterID source, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfi<32>(dest, source, lsb.m_value, width.m_value);
+ }
+
+ void insertBitField64(RegisterID source, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfi<64>(dest, source, lsb.m_value, width.m_value);
+ }
+
+ void clearBitField32(TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfc<32>(dest, lsb.m_value, width.m_value);
+ }
+
+ void clearBitField64(TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfc<64>(dest, lsb.m_value, width.m_value);
+ }
+
+ void clearBitsWithMask32(RegisterID src, RegisterID mask, RegisterID dest)
+ {
+ m_assembler.bic<32>(dest, src, mask);
+ }
+
+ void clearBitsWithMask64(RegisterID src, RegisterID mask, RegisterID dest)
+ {
+ m_assembler.bic<64>(dest, src, mask);
+ }
+
+ void orNot32(RegisterID src, RegisterID mask, RegisterID dest)
+ {
+ m_assembler.orn<32>(dest, src, mask);
+ }
+
+ void orNot64(RegisterID src, RegisterID mask, RegisterID dest)
+ {
+ m_assembler.orn<64>(dest, src, mask);
+ }
+
+ void extractInsertBitfieldAtLowEnd32(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfxil<32>(dest, src, lsb.m_value, width.m_value);
+ }
+
+ void extractInsertBitfieldAtLowEnd64(RegisterID src, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
+ {
+ m_assembler.bfxil<64>(dest, src, lsb.m_value, width.m_value);
+ }
+
void clearBit64(RegisterID bitToClear, RegisterID dest, RegisterID scratchForMask = InvalidGPRReg)
{
if (scratchForMask == InvalidGPRReg)
@@ -2752,18 +2803,6 @@
m_assembler.fcsel<64>(dest, thenCase, elseCase, ARM64Condition(cond));
}
- // Bit field operations:
- void bitFieldInsert32(RegisterID source, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.bfi<32>(dest, source, lsb.m_value, width.m_value);
- }
-
- // destBitOffset is the top bit of the destination where the bits should be copied to. Zero is the lowest order bit.
- void bitFieldInsert64(RegisterID source, TrustedImm32 lsb, TrustedImm32 width, RegisterID dest)
- {
- m_assembler.bfi<64>(dest, source, lsb.m_value, width.m_value);
- }
-
// Forwards / external control flow operations:
//
// This set of jump and conditional branch operations return a Jump
Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -904,7 +904,7 @@
}
#if CPU(ARM64)
-void testMul32SignExtend()
+void testMultiplySignExtend32()
{
for (auto value : int32Operands()) {
auto mul = compile([=] (CCallHelpers& jit) {
@@ -1089,7 +1089,7 @@
}
}
-void testMulSubSignExtend32()
+void testMultiplySubSignExtend32()
{
// d = a - SExt32(n) * SExt32(m)
auto sub = compile([=] (CCallHelpers& jit) {
@@ -1112,7 +1112,7 @@
}
}
-void testUbfx32()
+void testExtractUnsignedBitfield32()
{
uint32_t src = ""
Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
@@ -1122,7 +1122,7 @@
auto ubfx32 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.ubfx32(GPRInfo::argumentGPR0,
+ jit.extractUnsignedBitfield32(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::returnValueGPR);
@@ -1136,7 +1136,7 @@
}
}
-void testUbfx64()
+void testExtractUnsignedBitfield64()
{
uint64_t src = ""
Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
@@ -1146,7 +1146,7 @@
auto ubfx64 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.ubfx64(GPRInfo::argumentGPR0,
+ jit.extractUnsignedBitfield64(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::returnValueGPR);
@@ -1160,7 +1160,7 @@
}
}
-void testUbfiz32()
+void testInsertUnsignedBitfieldInZero32()
{
uint32_t src = ""
Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
@@ -1170,7 +1170,7 @@
auto ubfiz32 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.ubfiz32(GPRInfo::argumentGPR0,
+ jit.insertUnsignedBitfieldInZero32(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::returnValueGPR);
@@ -1185,7 +1185,7 @@
}
}
-void testUbfiz64()
+void testInsertUnsignedBitfieldInZero64()
{
uint64_t src = ""
Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
@@ -1195,7 +1195,7 @@
auto ubfiz64 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.ubfiz64(GPRInfo::argumentGPR0,
+ jit.insertUnsignedBitfieldInZero64(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::returnValueGPR);
@@ -1210,7 +1210,7 @@
}
}
-void testBitFieldInsert32()
+void testInsertBitField32()
{
uint32_t src = ""
uint32_t dst = 0xf0f0f0f0;
@@ -1221,7 +1221,7 @@
auto bfi32 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.bitFieldInsert64(GPRInfo::argumentGPR0,
+ jit.insertBitField32(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::argumentGPR1);
@@ -1240,7 +1240,7 @@
}
}
-void testBitFieldInsert64()
+void testInsertBitField64()
{
uint64_t src = ""
uint64_t dst = 0xf0f0f0f0f0f0f0f0;
@@ -1251,7 +1251,7 @@
auto bfi64 = compile([=] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
- jit.bitFieldInsert64(GPRInfo::argumentGPR0,
+ jit.insertBitField64(GPRInfo::argumentGPR0,
CCallHelpers::TrustedImm32(lsb),
CCallHelpers::TrustedImm32(width),
GPRInfo::argumentGPR1);
@@ -1269,6 +1269,188 @@
}
}
}
+
+void testExtractInsertBitfieldAtLowEnd32()
+{
+ uint32_t src = ""
+ uint32_t dst = 0x0f0f0f0f;
+ 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 bfxil32 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractInsertBitfieldAtLowEnd32(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::argumentGPR1);
+ jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+ uint32_t mask1 = (1U << width) - 1U;
+ uint32_t mask2 = ~mask1;
+ uint32_t rhs = invoke<uint32_t>(bfxil32, src, dst);
+ uint32_t lhs = ((src >> lsb) & mask1) | (dst & mask2);
+ CHECK_EQ(rhs, lhs);
+ }
+ }
+ }
+}
+
+void testExtractInsertBitfieldAtLowEnd64()
+{
+ uint64_t src = ""
+ uint64_t dst = 0xf0f0f0f0f0f0f0f0;
+ Vector<uint64_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 bfxil64 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractInsertBitfieldAtLowEnd64(GPRInfo::argumentGPR0,
+ CCallHelpers::TrustedImm32(lsb),
+ CCallHelpers::TrustedImm32(width),
+ GPRInfo::argumentGPR1);
+ jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+ uint64_t mask1 = (1ULL << width) - 1ULL;
+ uint64_t mask2 = ~mask1;
+ uint64_t rhs = invoke<uint64_t>(bfxil64, src, dst);
+ uint64_t lhs = ((src >> lsb) & mask1) | (dst & mask2);
+ CHECK_EQ(rhs, lhs);
+ }
+ }
+ }
+}
+
+void testClearBitField32()
+{
+ 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 bfc32 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.clearBitField32(CCallHelpers::TrustedImm32(lsb), CCallHelpers::TrustedImm32(width), GPRInfo::argumentGPR0);
+ jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+ uint32_t mask = ((1U << width) - 1U) << lsb;
+ uint32_t rhs = invoke<uint32_t>(bfc32, src);
+ uint32_t lhs = src & ~mask;
+ CHECK_EQ(rhs, lhs);
+ }
+ }
+ }
+}
+
+void testClearBitField64()
+{
+ uint64_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 bfc64 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.clearBitField64(CCallHelpers::TrustedImm32(lsb), CCallHelpers::TrustedImm32(width), GPRInfo::argumentGPR0);
+ jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+ uint64_t mask = ((1ULL << width) - 1ULL) << lsb;
+ uint64_t rhs = invoke<uint64_t>(bfc64, src);
+ uint64_t lhs = src & ~mask;
+ CHECK_EQ(rhs, lhs);
+ }
+ }
+ }
+}
+
+void testClearBitsWithMask32()
+{
+ auto test = compile([] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.clearBitsWithMask32(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ for (auto mask : int32Operands()) {
+ uint32_t src = ""
+ CHECK_EQ(invoke<uint32_t>(test, src, mask), (src & ~mask));
+ CHECK_EQ(invoke<uint32_t>(test, 0U, mask), 0U);
+ }
+}
+
+void testClearBitsWithMask64()
+{
+ auto test = compile([] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.clearBitsWithMask64(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ for (auto mask : int64Operands()) {
+ uint64_t src = ""
+ CHECK_EQ(invoke<uint64_t>(test, src, mask), (src & ~mask));
+ CHECK_EQ(invoke<uint64_t>(test, 0ULL, mask), 0ULL);
+ }
+}
+
+void testOrNot32()
+{
+ auto test = compile([] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.orNot32(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ for (auto mask : int32Operands()) {
+ int32_t src = ""
+ CHECK_EQ(invoke<int32_t>(test, src, mask), (src | ~mask));
+ CHECK_EQ(invoke<int32_t>(test, 0U, mask), ~mask);
+ }
+}
+
+void testOrNot64()
+{
+ auto test = compile([] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.orNot64(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ for (auto mask : int64Operands()) {
+ int64_t src = ""
+ CHECK_EQ(invoke<int64_t>(test, src, mask), (src | ~mask));
+ CHECK_EQ(invoke<int64_t>(test, 0ULL, mask), ~mask);
+ }
+}
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
@@ -3425,9 +3607,8 @@
#if CPU(ARM64)
RUN(testLoadStorePair64Int64());
RUN(testLoadStorePair64Double());
- RUN(testMul32SignExtend());
- RUN(testMultiplyAddSignExtend32Left());
- RUN(testMultiplyAddSignExtend32Right());
+ RUN(testMultiplySignExtend32());
+
RUN(testSub32Args());
RUN(testSub32Imm());
RUN(testSub32ArgImm());
@@ -3435,13 +3616,26 @@
RUN(testSub64ArgImm32());
RUN(testSub64Imm64());
RUN(testSub64ArgImm64());
- RUN(testMulSubSignExtend32());
- RUN(testUbfx32());
- RUN(testUbfx64());
- RUN(testUbfiz32());
- RUN(testUbfiz64());
- RUN(testBitFieldInsert32());
- RUN(testBitFieldInsert64());
+
+ RUN(testMultiplyAddSignExtend32Left());
+ RUN(testMultiplyAddSignExtend32Right());
+ RUN(testMultiplySubSignExtend32());
+
+ RUN(testExtractUnsignedBitfield32());
+ RUN(testExtractUnsignedBitfield64());
+ RUN(testInsertUnsignedBitfieldInZero32());
+ RUN(testInsertUnsignedBitfieldInZero64());
+ RUN(testInsertBitField32());
+ RUN(testInsertBitField64());
+ RUN(testExtractInsertBitfieldAtLowEnd32());
+ RUN(testExtractInsertBitfieldAtLowEnd64());
+ RUN(testClearBitField32());
+ RUN(testClearBitField64());
+ RUN(testClearBitsWithMask32());
+ RUN(testClearBitsWithMask64());
+
+ RUN(testOrNot32());
+ RUN(testOrNot64());
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -2764,7 +2764,7 @@
// UBFX Pattern: dest = (src >> lsb) & mask
// Where: mask = (1 << width) - 1
auto tryAppendUBFX = [&] () -> bool {
- Air::Opcode opcode = opcodeForType(Ubfx32, Ubfx64, m_value->type());
+ Air::Opcode opcode = opcodeForType(ExtractUnsignedBitfield32, ExtractUnsignedBitfield64, m_value->type());
if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
return false;
if (left->opcode() != ZShr || !canBeInternal(left))
@@ -2780,7 +2780,7 @@
if (!mask || mask & (mask + 1))
return false;
uint64_t width = WTF::bitCount(mask);
- uint64_t datasize = opcode == Ubfx32 ? 32 : 64;
+ uint64_t datasize = opcode == ExtractUnsignedBitfield32 ? 32 : 64;
if (lsb + width > datasize)
return false;
@@ -2792,6 +2792,28 @@
if (tryAppendUBFX())
return;
+ // BIC Pattern: d = n & (m ^ -1)
+ auto tryAppendBIC = [&] (Value* left, Value* right) -> bool {
+ Air::Opcode opcode = opcodeForType(ClearBitsWithMask32, ClearBitsWithMask64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Tmp, Arg::Tmp))
+ return false;
+ if (right->opcode() != BitXor || !canBeInternal(right))
+ return false;
+
+ Value* nValue = left;
+ Value* mValue = right->child(0);
+ Value* minusOne = right->child(1);
+ if (m_locked.contains(nValue) || m_locked.contains(mValue) || !minusOne->hasInt() || !minusOne->isInt(-1))
+ return false;
+
+ append(opcode, tmp(nValue), tmp(mValue), tmp(m_value));
+ commitInternal(right);
+ return true;
+ };
+
+ if (tryAppendBIC(left, right) || tryAppendBIC(right, left))
+ return;
+
appendBinOp<And32, And64, AndDouble, AndFloat, Commutative>(left, right);
return;
}
@@ -2804,7 +2826,7 @@
// Where: mask1 = ((1 << width) - 1)
// mask2 = ~(mask1 << lsb)
auto tryAppendBFI = [&] (Value* left, Value* right) -> bool {
- Air::Opcode opcode = opcodeForType(BitFieldInsert32, BitFieldInsert64, m_value->type());
+ Air::Opcode opcode = opcodeForType(InsertBitField32, InsertBitField64, m_value->type());
if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
return false;
if (left->opcode() != Shl || !canBeInternal(left))
@@ -2828,7 +2850,7 @@
uint64_t mask1 = maskValue1->asInt();
if (!mask1 || mask1 & (mask1 + 1))
return false;
- uint64_t datasize = opcode == BitFieldInsert32 ? 32 : 64;
+ uint64_t datasize = opcode == InsertBitField32 ? 32 : 64;
uint64_t width = WTF::bitCount(mask1);
if (lsb + width > datasize)
return false;
@@ -2857,6 +2879,84 @@
if (tryAppendBFI(left, right) || tryAppendBFI(right, left))
return;
+ // BFXIL Pattern: d = ((n >> lsb) & mask1) | (d & mask2)
+ // Where: mask1 = ((1 << width) - 1)
+ // mask2 = ~mask1
+ auto tryAppendBFXIL = [&] (Value* left, Value* right) -> bool {
+ Air::Opcode opcode = opcodeForType(ExtractInsertBitfieldAtLowEnd32, ExtractInsertBitfieldAtLowEnd64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
+ return false;
+ if (left->opcode() != BitAnd || !canBeInternal(left))
+ return false;
+ if (left->child(0)->opcode() != ZShr || !canBeInternal(left->child(0)))
+ return false;
+ if (right->opcode() != BitAnd || !canBeInternal(right))
+ return false;
+
+ Value* nValue = left->child(0)->child(0);
+ Value* maskValue1 = left->child(1);
+ Value* lsbValue = left->child(0)->child(1);
+ Value* dValue = right->child(0);
+ Value* maskValue2 = right->child(1);
+ if (m_locked.contains(nValue) || m_locked.contains(dValue))
+ return false;
+ if (!maskValue1->hasInt() || !imm(lsbValue) || lsbValue->asInt() < 0 || !maskValue2->hasInt())
+ return false;
+
+ uint64_t lsb = lsbValue->asInt();
+ uint64_t mask1 = maskValue1->asInt();
+ if (!mask1 || mask1 & (mask1 + 1))
+ return false;
+ uint64_t width = WTF::bitCount(mask1);
+ uint64_t datasize = opcode == ExtractInsertBitfieldAtLowEnd32 ? 32 : 64;
+ if (lsb + width > datasize)
+ return false;
+ uint64_t mask2 = maskValue2->asInt();
+
+ auto isValidMask2 = [&] () -> bool {
+ uint64_t mask = mask1 ^ mask2;
+ uint64_t lowerSet = 0xffffffff;
+ uint64_t fullSet = 0xffffffffffffffff;
+ return datasize == 32 ? !((mask & lowerSet) ^ lowerSet) : !(mask ^ fullSet);
+ };
+
+ if (!isValidMask2())
+ return false;
+
+ Tmp result = tmp(m_value);
+ append(relaxedMoveForType(m_value->type()), tmp(dValue), result);
+ append(opcode, tmp(nValue), imm(lsbValue), imm(width), result);
+ commitInternal(left->child(0));
+ commitInternal(left);
+ commitInternal(right);
+ return true;
+ };
+
+ if (tryAppendBFXIL(left, right) || tryAppendBFXIL(right, left))
+ return;
+
+ // ORN Pattern: d = n | (m ^ -1)
+ auto tryAppendORN = [&] (Value* left, Value* right) -> bool {
+ Air::Opcode opcode = opcodeForType(OrNot32, OrNot64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Tmp, Arg::Tmp))
+ return false;
+ if (right->opcode() != BitXor || !canBeInternal(right))
+ return false;
+
+ Value* nValue = left;
+ Value* mValue = right->child(0);
+ Value* minusOne = right->child(1);
+ if (m_locked.contains(nValue) || m_locked.contains(mValue) || !minusOne->hasInt() || !minusOne->isInt(-1))
+ return false;
+
+ append(opcode, tmp(nValue), tmp(mValue), tmp(m_value));
+ commitInternal(right);
+ return true;
+ };
+
+ if (tryAppendORN(left, right) || tryAppendORN(right, left))
+ return;
+
appendBinOp<Or32, Or64, OrDouble, OrFloat, Commutative>(left, right);
return;
}
@@ -2899,7 +2999,7 @@
// UBFIZ Pattern: d = (n & mask) << lsb
// Where: mask = (1 << width) - 1
auto tryAppendUBFZ = [&] () -> bool {
- Air::Opcode opcode = opcodeForType(Ubfiz32, Ubfiz64, m_value->type());
+ Air::Opcode opcode = opcodeForType(InsertUnsignedBitfieldInZero32, InsertUnsignedBitfieldInZero64, m_value->type());
if (!isValidForm(opcode, Arg::Tmp, Arg::Imm, Arg::Imm, Arg::Tmp))
return false;
if (left->opcode() != BitAnd || !canBeInternal(left))
@@ -2915,7 +3015,7 @@
if (!mask || mask & (mask + 1))
return false;
uint64_t width = WTF::bitCount(mask);
- uint64_t datasize = opcode == Ubfiz32 ? 32 : 64;
+ uint64_t datasize = opcode == InsertUnsignedBitfieldInZero32 ? 32 : 64;
if (lsb + width > datasize)
return false;
@@ -2942,7 +3042,10 @@
}
case ZShr: {
- appendShift<Urshift32, Urshift64>(m_value->child(0), m_value->child(1));
+ Value* left = m_value->child(0);
+ Value* right = m_value->child(1);
+
+ appendShift<Urshift32, Urshift64>(left, right);
return;
}
Modified: trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -627,6 +627,18 @@
}
if (m_value->isInteger()) {
+ // Turn this: Sub(Neg(value), 1)
+ // Into this: BitXor(value, -1)
+ if (m_value->child(0)->opcode() == Neg && m_value->child(1)->isInt(1)) {
+ Value* minusOne;
+ if (m_value->child(0)->child(0)->type() == Int32)
+ minusOne = m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), -1);
+ else
+ minusOne = m_insertionSet.insert<Const64Value>(m_index, m_value->origin(), -1);
+ replaceWithNew<Value>(BitXor, m_value->origin(), m_value->child(0)->child(0), minusOne);
+ break;
+ }
+
// Turn this: Sub(value, constant)
// Into this: Add(value, -constant)
if (Value* negatedConstant = m_value->child(1)->negConstant(m_proc)) {
@@ -1060,6 +1072,7 @@
}
// Turn this: BitAnd(Shl(value, shiftAmount), maskShift)
+ // Into this: Shl(BitAnd(value, mask), shiftAmount)
// Conditions:
// 1. maskShift = mask << shiftAmount
// 2. mask = (1 << width) - 1
@@ -1066,7 +1079,6 @@
// 3. 0 <= shiftAmount < datasize
// 4. 0 < width < datasize
// 5. shiftAmount + width <= datasize
- // Into this: Shl(BitAnd(value, mask), shiftAmount)
if (m_value->child(0)->opcode() == Shl
&& m_value->child(0)->child(1)->hasInt()
&& m_value->child(0)->child(1)->asInt() >= 0
@@ -1424,6 +1436,38 @@
break;
}
+ // Turn this: ZShr(BitAnd(value, maskShift), shiftAmount)
+ // Into this: BitAnd(ZShr(value, shiftAmount), mask)
+ // Conditions:
+ // 1. maskShift = mask << shiftAmount
+ // 2. mask = (1 << width) - 1
+ // 3. 0 <= shiftAmount < datasize
+ // 4. 0 < width < datasize
+ // 5. shiftAmount + width <= datasize
+ if (m_value->child(0)->opcode() == BitAnd
+ && m_value->child(0)->child(1)->hasInt()
+ && m_value->child(1)->hasInt()
+ && m_value->child(1)->asInt() >= 0) {
+ uint64_t shiftAmount = m_value->child(1)->asInt();
+ uint64_t maskShift = m_value->child(0)->child(1)->asInt();
+ uint64_t maskShiftAmount = WTF::countTrailingZeros(maskShift);
+ uint64_t mask = maskShift >> maskShiftAmount;
+ uint64_t width = WTF::bitCount(mask);
+ uint64_t datasize = m_value->child(0)->child(0)->type() == Int64 ? 64 : 32;
+ bool isValidShiftAmount = maskShiftAmount == shiftAmount && shiftAmount < datasize;
+ bool isValidMask = mask && !(mask & (mask + 1)) && width < datasize;
+ if (isValidShiftAmount && isValidMask && shiftAmount + width <= datasize) {
+ Value* maskValue;
+ if (datasize == 32)
+ maskValue = m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), mask);
+ else
+ maskValue = m_insertionSet.insert<Const64Value>(m_index, m_value->origin(), mask);
+ Value* shiftValue = m_insertionSet.insert<Value>(m_index, ZShr, m_value->origin(), m_value->child(0)->child(0), m_value->child(1));
+ replaceWithNew<Value>(BitAnd, m_value->origin(), shiftValue, maskValue);
+ break;
+ }
+ }
+
handleShiftAmount();
break;
Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (279361 => 279362)
--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-06-29 04:05:14 UTC (rev 279362)
@@ -363,24 +363,6 @@
x86: Imm, Addr
x86: Imm, Index
-arm64: Ubfx32 U:G:32, U:G:32, U:G:32, ZD:G:32
- Tmp, Imm, Imm, Tmp
-
-arm64: Ubfx64 U:G:64, U:G:32, U:G:32, D:G:64
- Tmp, Imm, Imm, Tmp
-
-arm64: Ubfiz32 U:G:32, U:G:32, U:G:32, ZD:G:32
- Tmp, Imm, Imm, Tmp
-
-arm64: Ubfiz64 U:G:64, U:G:32, U:G:32, D:G:64
- Tmp, Imm, Imm, Tmp
-
-arm64: BitFieldInsert32 U:G:32, U:G:32, U:G:32, ZD:G:32
- Tmp, Imm, Imm, Tmp
-
-arm64: BitFieldInsert64 U:G:64, U:G:32, U:G:32, D:G:64
- Tmp, Imm, Imm, Tmp
-
64: And64 U:G:64, U:G:64, D:G:64
Tmp, Tmp, Tmp
arm64: BitImm64, Tmp, Tmp
@@ -830,6 +812,48 @@
Tmp, Addr
Tmp, Index
+arm64: ExtractUnsignedBitfield32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: ExtractUnsignedBitfield64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Imm, Imm, Tmp
+
+arm64: InsertUnsignedBitfieldInZero32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: InsertUnsignedBitfieldInZero64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Imm, Imm, Tmp
+
+arm64: InsertBitField32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: InsertBitField64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Imm, Imm, Tmp
+
+arm64: ClearBitField32 U:G:32, U:G:32, ZD:G:32
+ Imm, Imm, Tmp
+
+arm64: ClearBitField64 U:G:32, U:G:32, D:G:64
+ Imm, Imm, Tmp
+
+arm64: ClearBitsWithMask32 U:G:32, U:G:32, ZD:G:32
+ Tmp, Tmp, Tmp
+
+arm64: ClearBitsWithMask64 U:G:64, U:G:64, D:G:64
+ Tmp, Tmp, Tmp
+
+arm64: OrNot32 U:G:32, U:G:32, ZD:G:32
+ Tmp, Tmp, Tmp
+
+arm64: OrNot64 U:G:64, U:G:64, D:G:64
+ Tmp, Tmp, Tmp
+
+arm64: ExtractInsertBitfieldAtLowEnd32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Imm, Imm, Tmp
+
+arm64: ExtractInsertBitfieldAtLowEnd64 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 (279361 => 279362)
--- trunk/Source/_javascript_Core/b3/testb3.h 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/b3/testb3.h 2021-06-29 04:05:14 UTC (rev 279362)
@@ -428,8 +428,14 @@
void testUbfiz64AndShiftMaskValue();
void testUbfiz64ShiftAnd();
void testUbfiz64AndShift();
-void testBitFieldInsert32();
-void testBitFieldInsert64();
+void testInsertBitField32();
+void testInsertBitField64();
+void testExtractInsertBitfieldAtLowEnd32();
+void testExtractInsertBitfieldAtLowEnd64();
+void testBIC32();
+void testBIC64();
+void testOrNot32();
+void testOrNot64();
void testBitAndZeroShiftRightArgImmMask32();
void testBitAndZeroShiftRightArgImmMask64();
void testBasicSelect();
Modified: trunk/Source/_javascript_Core/b3/testb3_2.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -3158,13 +3158,13 @@
}
}
-void testBitFieldInsert32()
+void testInsertBitField32()
{
if (JSC::Options::defaultB3OptLevel() < 2)
return;
uint32_t d = 0xf0f0f0f0;
uint32_t n = 0xffffffff;
- Vector<uint32_t> lsbs = { 1, 2, 14, 30, 30 };
+ Vector<uint32_t> lsbs = { 2, 2, 14, 30, 30 };
Vector<uint32_t> widths = { 30, 3, 17, 1, 2 };
// Test Pattern: d = ((n & mask1) << lsb) | (d & mask2)
@@ -3236,13 +3236,13 @@
}
}
-void testBitFieldInsert64()
+void testInsertBitField64()
{
if (JSC::Options::defaultB3OptLevel() < 2)
return;
uint64_t d = 0xf0f0f0f0f0f0f0f0;
uint64_t n = 0xffffffffffffffff;
- Vector<uint64_t> lsbs = { 1, 30, 14, 62, 62 };
+ Vector<uint64_t> lsbs = { 2, 30, 14, 62, 62 };
Vector<uint64_t> widths = { 62, 33, 17, 1, 2 };
// Test Pattern: d = ((n & mask1) << lsb) | (d & mask2)
@@ -3306,6 +3306,399 @@
}
}
+void testExtractInsertBitfieldAtLowEnd32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ uint32_t d = 0xf0f0f0f0;
+ uint32_t n = 0xffffffff;
+ Vector<uint32_t> lsbs = { 1, 10, 14, 30 };
+ Vector<uint32_t> widths = { 30, 11, 17, 1 };
+
+ // BFXIL Pattern: d = ((n >> lsb) & mask1) | (d & mask2)
+ // Where: mask1 = ((1 << width) - 1)
+ // mask2 = ~mask1
+ auto test1 = [&] (uint32_t lsb, uint32_t mask1, uint32_t mask2) -> uint32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* dValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* maskValue1 = root->appendNew<Const32Value>(proc, Origin(), mask1);
+ Value* maskValue2 = root->appendNew<Const32Value>(proc, Origin(), mask2);
+
+ Value* shiftValue = root->appendNew<Value>(proc, ZShr, Origin(), nValue, lsbValue);
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), shiftValue, maskValue1);
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), dValue, maskValue2);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), leftAndValue, rightAndValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bfxil");
+ return invoke<uint32_t>(*code, d, n);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ uint32_t lsb = lsbs.at(i);
+ uint32_t mask1 = (1U << widths.at(i)) - 1U;
+ uint32_t mask2 = ~mask1;
+ CHECK(test1(lsb, mask1, mask2) == (((n >> lsb) & mask1) | (d & mask2)));
+ }
+
+ // BFXIL Pattern: d = ((n & mask1) >> lsb) | (d & mask2)
+ // Where: mask1 = ((1 << width) - 1) << lsb
+ // mask2 = ~(mask1 >> lsb)
+ auto test2 = [&] (uint32_t lsb, uint32_t mask1, uint32_t mask2) -> uint32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* dValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* maskValue1 = root->appendNew<Const32Value>(proc, Origin(), mask1);
+ Value* maskValue2 = root->appendNew<Const32Value>(proc, Origin(), mask2);
+
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), nValue, maskValue1);
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), dValue, maskValue2);
+ Value* shiftValue = root->appendNew<Value>(proc, ZShr, Origin(), leftAndValue, lsbValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), shiftValue, rightAndValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bfxil");
+ return invoke<uint32_t>(*code, d, n);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ uint32_t lsb = lsbs.at(i);
+ uint32_t mask1 = ((1U << widths.at(i)) - 1U) << lsb;
+ uint32_t mask2 = ~(mask1 >> lsb);
+ CHECK(test2(lsb, mask1, mask2) == (((n & mask1) >> lsb) | (d & mask2)));
+ }
+}
+
+void testExtractInsertBitfieldAtLowEnd64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ uint64_t d = 0xf0f0f0f0f0f0f0f0;
+ uint64_t n = 0xffffffffffffffff;
+ Vector<uint64_t> lsbs = { 1, 30, 14, 62 };
+ Vector<uint64_t> widths = { 62, 33, 17, 1 };
+
+ // BFXIL Pattern: d = ((n >> lsb) & mask1) | (d & mask2)
+ // Where: mask1 = ((1 << width) - 1)
+ // mask2 = ~mask1
+ auto test1 = [&] (uint64_t lsb, uint64_t mask1, uint64_t mask2) -> uint64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* dValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* maskValue1 = root->appendNew<Const64Value>(proc, Origin(), mask1);
+ Value* maskValue2 = root->appendNew<Const64Value>(proc, Origin(), mask2);
+
+ Value* shiftValue = root->appendNew<Value>(proc, ZShr, Origin(), nValue, lsbValue);
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), shiftValue, maskValue1);
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), dValue, maskValue2);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), leftAndValue, rightAndValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bfxil");
+ return invoke<uint64_t>(*code, d, n);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ uint64_t lsb = lsbs.at(i);
+ uint64_t mask1 = (1ULL << widths.at(i)) - 1ULL;
+ uint64_t mask2 = ~mask1;
+ CHECK(test1(lsb, mask1, mask2) == (((n >> lsb) & mask1) | (d & mask2)));
+ }
+
+ // BFXIL Pattern: d = ((n & mask1) >> lsb) | (d & mask2)
+ // Where: mask1 = ((1 << width) - 1) << lsb
+ // mask2 = ~(mask1 >> lsb)
+ auto test2 = [&] (uint64_t lsb, uint64_t mask1, uint64_t mask2) -> uint64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* dValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* lsbValue = root->appendNew<Const32Value>(proc, Origin(), lsb);
+ Value* maskValue1 = root->appendNew<Const64Value>(proc, Origin(), mask1);
+ Value* maskValue2 = root->appendNew<Const64Value>(proc, Origin(), mask2);
+
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), nValue, maskValue1);
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), dValue, maskValue2);
+ Value* shiftValue = root->appendNew<Value>(proc, ZShr, Origin(), leftAndValue, lsbValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), shiftValue, rightAndValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bfxil");
+ return invoke<uint64_t>(*code, d, n);
+ };
+
+ for (size_t i = 0; i < lsbs.size(); ++i) {
+ uint64_t lsb = lsbs.at(i);
+ uint64_t mask1 = ((1ULL << widths.at(i)) - 1ULL) << lsb;
+ uint64_t mask2 = ~(mask1 >> lsb);
+ CHECK(test2(lsb, mask1, mask2) == (((n & mask1) >> lsb) | (d & mask2)));
+ }
+}
+
+void testBIC32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+
+ // Test Pattern: d = n & (-m - 1)
+ auto test1 = [&] (int32_t n, int32_t m) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* mValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* const1 = root->appendNew<Const32Value>(proc, Origin(), 1);
+
+ Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mValue);
+ Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), negValue, const1);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitAnd, Origin(), nValue, subValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bic");
+ return invoke<int32_t>(*code, n, m);
+ };
+
+ // Test Pattern: d = n & (m ^ -1)
+ auto test2 = [&] (int32_t n, int32_t m) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* mValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* minusOneValue = root->appendNew<Const32Value>(proc, Origin(), -1);
+
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), mValue, minusOneValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitAnd, Origin(), nValue, xorValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bic");
+ return invoke<int32_t>(*code, n, m);
+ };
+
+ for (auto n : int32Operands()) {
+ for (auto m : int32Operands()) {
+ CHECK(test1(n.value, m.value) == (n.value & (-m.value - 1)));
+ CHECK(test2(n.value, m.value) == (n.value & (m.value ^ -1)));
+ }
+ }
+}
+
+void testBIC64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+
+ // Test Pattern: d = n & (-m - 1)
+ auto test1 = [&] (int64_t n, int64_t m) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* mValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* const1 = root->appendNew<Const64Value>(proc, Origin(), 1);
+
+ Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mValue);
+ Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), negValue, const1);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitAnd, Origin(), nValue, subValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bic");
+ return invoke<int64_t>(*code, n, m);
+ };
+
+ // Test Pattern: d = n & (m ^ -1)
+ auto test2 = [&] (int64_t n, int64_t m) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* mValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* minusOneValue = root->appendNew<Const64Value>(proc, Origin(), -1);
+
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), mValue, minusOneValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitAnd, Origin(), nValue, xorValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "bic");
+ return invoke<int64_t>(*code, n, m);
+ };
+
+ for (auto n : int64Operands()) {
+ for (auto m : int64Operands()) {
+ CHECK(test1(n.value, m.value) == (n.value & (-m.value - 1LL)));
+ CHECK(test2(n.value, m.value) == (n.value & (m.value ^ -1LL)));
+ }
+ }
+}
+
+void testOrNot32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+
+ // Test Pattern: d = n | (-m - 1)
+ auto test1 = [&] (int32_t n, int32_t m) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* mValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* const1 = root->appendNew<Const32Value>(proc, Origin(), 1);
+
+ Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mValue);
+ Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), negValue, const1);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), nValue, subValue));
+
+ auto code = compileProc(proc);
+
+ if (isARM64())
+ checkUsesInstruction(*code, "orn");
+ return invoke<int32_t>(*code, n, m);
+ };
+
+ // Test Pattern: d = n | (m ^ -1)
+ auto test2 = [&] (int32_t n, int32_t m) -> int32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* mValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* minusOneValue = root->appendNew<Const32Value>(proc, Origin(), -1);
+
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), mValue, minusOneValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), nValue, xorValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "orn");
+ return invoke<int32_t>(*code, n, m);
+ };
+
+ for (auto n : int32Operands()) {
+ for (auto m : int32Operands()) {
+ CHECK(test1(n.value, m.value) == (n.value | (-m.value - 1)));
+ CHECK(test2(n.value, m.value) == (n.value | (m.value ^ -1)));
+ }
+ }
+}
+
+void testOrNot64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+
+ // Test Pattern: d = n | (-m - 1)
+ auto test1 = [&] (int64_t n, int64_t m) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* mValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* const1 = root->appendNew<Const64Value>(proc, Origin(), 1);
+
+ Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mValue);
+ Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), negValue, const1);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), nValue, subValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "orn");
+ return invoke<int64_t>(*code, n, m);
+ };
+
+ // Test Pattern: d = n | (m ^ -1)
+ auto test2 = [&] (int64_t n, int64_t m) -> int64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* mValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ Value* minusOneValue = root->appendNew<Const64Value>(proc, Origin(), -1);
+
+ Value* xorValue = root->appendNew<Value>(proc, BitXor, Origin(), mValue, minusOneValue);
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), nValue, xorValue));
+
+ auto code = compileProc(proc);
+ if (isARM64())
+ checkUsesInstruction(*code, "orn");
+ return invoke<int64_t>(*code, n, m);
+ };
+
+ for (auto n : int64Operands()) {
+ for (auto m : int64Operands()) {
+ CHECK(test1(n.value, m.value) == (n.value | (-m.value - 1LL)));
+ CHECK(test2(n.value, m.value) == (n.value | (m.value ^ -1LL)));
+ }
+ }
+}
+
void testBitAndZeroShiftRightArgImmMask32()
{
// Turn this: (tmp >> imm) & mask
@@ -4198,8 +4591,14 @@
RUN(testUbfiz64AndShiftMaskValue());
RUN(testUbfiz64ShiftAnd());
RUN(testUbfiz64AndShift());
- RUN(testBitFieldInsert32());
- RUN(testBitFieldInsert64());
+ RUN(testInsertBitField32());
+ RUN(testInsertBitField64());
+ RUN(testExtractInsertBitfieldAtLowEnd32());
+ RUN(testExtractInsertBitfieldAtLowEnd64());
+ RUN(testBIC32());
+ RUN(testBIC64());
+ RUN(testOrNot32());
+ RUN(testOrNot64());
RUN(testBitAndZeroShiftRightArgImmMask32());
RUN(testBitAndZeroShiftRightArgImmMask64());
RUN(testBitAndArgs(43, 43));
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -16754,7 +16754,7 @@
merge->appendSomeRegister(ptr);
merge->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
jit.move(params[2].gpr(), params[0].gpr());
- jit.bitFieldInsert64(params[1].gpr(), CCallHelpers::TrustedImm32(0), CCallHelpers::TrustedImm32(64 - MacroAssembler::maxNumberOfAllowedPACBits), params[0].gpr());
+ jit.insertBitField64(params[1].gpr(), CCallHelpers::TrustedImm32(0), CCallHelpers::TrustedImm32(64 - MacroAssembler::maxNumberOfAllowedPACBits), params[0].gpr());
});
result = doUntagArrayPtr(merge);
Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp (279361 => 279362)
--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp 2021-06-29 03:11:28 UTC (rev 279361)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp 2021-06-29 04:05:14 UTC (rev 279362)
@@ -1111,7 +1111,7 @@
addPtr(TrustedImmPtr(Gigacage::basePtr(kind)), storage);
#if CPU(ARM64E)
if (kind == Gigacage::Primitive)
- bitFieldInsert64(storage, TrustedImm32(0), TrustedImm32(64 - maxNumberOfAllowedPACBits), tempReg);
+ insertBitField64(storage, TrustedImm32(0), TrustedImm32(64 - maxNumberOfAllowedPACBits), tempReg);
if (skip.isSet())
skip.link(this);
#endif
@@ -1148,7 +1148,7 @@
ASSERT(LogicalImmediate::create64(Gigacage::mask(kind)).isValid());
andPtr(TrustedImmPtr(Gigacage::mask(kind)), tempReg);
addPtr(scratch, tempReg);
- bitFieldInsert64(tempReg, TrustedImm32(0), TrustedImm32(64 - maxNumberOfAllowedPACBits), storage);
+ insertBitField64(tempReg, TrustedImm32(0), TrustedImm32(64 - maxNumberOfAllowedPACBits), storage);
#else
andPtr(TrustedImmPtr(Gigacage::mask(kind)), storage);
addPtr(scratch, storage);