Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (279469 => 279470)
--- trunk/Source/_javascript_Core/ChangeLog 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-07-01 17:25:26 UTC (rev 279470)
@@ -1,3 +1,85 @@
+2021-07-01 Yijia Huang <yijia_hu...@apple.com>
+
+ Add a new pattern to instruction selector to use EXTR supported by ARM64
+ https://bugs.webkit.org/show_bug.cgi?id=227171
+
+ Reviewed by Robin Morisset.
+
+ This patch includes two modifications:
+ 1. Introduce a strength reduction rule to zero extend bitfield.
+ 2. Add Extract Register (EXTR) to Air opcode to serve instruction selector.
+
+ -------------------------------------------------------
+ ### Part A zero extend bitfield ###
+ -------------------------------------------------------
+ A new strength reduction rule is added for the canonical form of the zero-extend
+ bitfield.
+
+ Turn this: ZShr(Shl(value, amount)), amount)
+ Into this: BitAnd(value, mask)
+
+ with constraints:
+ 1. 0 <= amount < datasize
+ 2. width = datasize - amount
+ 3. mask is !(mask & (mask + 1)) where bitCount(mask) == width
+
+ -------------------
+ ### Part B EXTR ###
+ -------------------
+
+ Given instruction:
+ extr Rd, Rn, Rm, lowWidth
+
+ Extract register (EXTR) extracts a register from a pair of registers, where
+ concat = Rn:Rm and Rd = concat<lowWidth + datasize - 1:lowWidth>.
+
+ The equivalent pattern of this instruction is:
+
+ d = ((n & mask) << highWidth) | (m >> lowWidth)
+ highWidth = datasize - lowWidth
+ mask = (1 << lowWidth) - 1
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = ArgumentReg(%x1)
+ Int @2 = mask
+ Int @3 = BitAnd(@0, @2)
+ Int @4 = highWidth
+ Int @5 = Shl(@3, @4)
+ Int @6 = lowWidth
+ Int @7 = ZShr(@1, @6)
+ Int @8 = BitOr(@7, @5)
+ Void@9 = Return(@10, Terminal)
+
+ Before Adding BIC:
+ // Old optimized AIR
+ InsertUnsignedBitfieldInZero %x0, highWidth, lowWidth, %x0, @5
+ Urshift %x1, lowWidth, %x1, @7
+ Or %x0, %x1, %x0, @8
+ Ret %x0, @9
+
+ After Adding BIC:
+ // New optimized AIR
+ ExtractRegister %x0, %x1, lowWidth, %x0, @8
+ Ret %x0, @9
+
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::extractRegister32):
+ (JSC::MacroAssemblerARM64::extractRegister64):
+ * assembler/testmasm.cpp:
+ (JSC::testExtractRegister32):
+ (JSC::testExtractRegister64):
+ * b3/B3LowerToAir.cpp:
+ * b3/B3ReduceStrength.cpp:
+ * b3/air/AirOpcode.opcodes:
+ * b3/testb3.h:
+ * b3/testb3_2.cpp:
+ (testBitfieldZeroExtend32):
+ (testBitfieldZeroExtend64):
+ (testExtractRegister32):
+ (testExtractRegister64):
+ (addBitTests):
+
2021-06-30 Saam Barati <sbar...@apple.com>
Turn off data ICs by default
@@ -347,8 +429,8 @@
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 (BFXIL)
- to Air opcode to serve intruciton selector.
+ 1. Add bit clear (BIC), or not (ORN), and extract and insert bitfield at the
+ lower end (BFXIL) to Air opcode to serve instruction selector.
2. Add bitfield clear (BFC) to MacroAssembler.
4. Do refactoring - rename Air opcodes added in the previous patches.
@@ -370,7 +452,7 @@
Pattern 2:
d = n & (m ^ -1)
- In order to get benefits for complement operation, current instruction selector uses
+ In order to get benefits for complement operation, the current instruction selector uses
mvn instruction to lower the pattern value ^ -1. Then, a new strength reduction rule is
introduced:
Turn this: -value - 1
@@ -1202,7 +1284,7 @@
A) Add UBFIZ to instruction selector.
B) Fix UBFX, introduced in https://bugs.webkit.org/show_bug.cgi?id=226984,
to match all patterns.
- C) Fix error condition in one strength reduction introduced
+ C) Fix error condition in one strength reduction rule introduced
in https://bugs.webkit.org/show_bug.cgi?id=227138.
Part A
Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (279469 => 279470)
--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-07-01 17:25:26 UTC (rev 279470)
@@ -539,6 +539,16 @@
m_assembler.sbfx<64>(dest, src, lsb.m_value, width.m_value);
}
+ void extractRegister32(RegisterID n, RegisterID m, TrustedImm32 lsb, RegisterID d)
+ {
+ m_assembler.extr<32>(d, n, m, lsb.m_value);
+ }
+
+ void extractRegister64(RegisterID n, RegisterID m, TrustedImm32 lsb, RegisterID d)
+ {
+ m_assembler.extr<64>(d, n, m, lsb.m_value);
+ }
+
void clearBit64(RegisterID bitToClear, RegisterID dest, RegisterID scratchForMask = InvalidGPRReg)
{
if (scratchForMask == InvalidGPRReg)
Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (279469 => 279470)
--- trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp 2021-07-01 17:25:26 UTC (rev 279470)
@@ -1570,6 +1570,76 @@
}
}
+void testExtractRegister32()
+{
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ uint32_t datasize = CHAR_BIT * sizeof(uint32_t);
+
+ for (auto n : int32Operands()) {
+ for (auto m : int32Operands()) {
+ for (auto lsb : imms) {
+ if (0 <= lsb && lsb < datasize) {
+ auto extractRegister32 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractRegister32(GPRInfo::argumentGPR0,
+ GPRInfo::argumentGPR1,
+ CCallHelpers::TrustedImm32(lsb),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ // ((n & mask) << highWidth) | (m >> lowWidth)
+ // Where: highWidth = datasize - lowWidth
+ // mask = (1 << lowWidth) - 1
+ uint32_t highWidth = datasize - lsb;
+ uint32_t mask = (1U << lsb) - 1U;
+ uint32_t left = highWidth == datasize ? 0U : (n & mask) << highWidth;
+ uint32_t right = (static_cast<uint32_t>(m) >> lsb);
+ uint32_t rhs = left | right;
+ uint32_t lhs = invoke<uint32_t>(extractRegister32, n, m);
+ CHECK_EQ(lhs, rhs);
+ }
+ }
+ }
+ }
+}
+
+void testExtractRegister64()
+{
+ Vector<uint32_t> imms = { 0, 1, 5, 7, 30, 31, 32, 42, 56, 62, 63, 64 };
+ uint64_t datasize = CHAR_BIT * sizeof(uint64_t);
+
+ for (auto n : int64Operands()) {
+ for (auto m : int64Operands()) {
+ for (auto lsb : imms) {
+ if (0 <= lsb && lsb < datasize) {
+ auto extractRegister64 = compile([=] (CCallHelpers& jit) {
+ emitFunctionPrologue(jit);
+
+ jit.extractRegister64(GPRInfo::argumentGPR0,
+ GPRInfo::argumentGPR1,
+ CCallHelpers::TrustedImm32(lsb),
+ GPRInfo::returnValueGPR);
+
+ emitFunctionEpilogue(jit);
+ jit.ret();
+ });
+
+ uint64_t highWidth = datasize - lsb;
+ uint64_t mask = (1ULL << lsb) - 1ULL;
+ uint64_t left = highWidth == datasize ? 0ULL : (n & mask) << highWidth;
+ uint64_t right = (static_cast<uint64_t>(m) >> lsb);
+ uint64_t rhs = left | right;
+ uint64_t lhs = invoke<uint64_t>(extractRegister64, n, m);
+ CHECK_EQ(lhs, rhs);
+ }
+ }
+ }
+ }
+}
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
@@ -3760,6 +3830,8 @@
RUN(testInsertSignedBitfieldInZero64());
RUN(testExtractSignedBitfield32());
RUN(testExtractSignedBitfield64());
+ RUN(testExtractRegister32());
+ RUN(testExtractRegister64());
#endif
#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (279469 => 279470)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-07-01 17:25:26 UTC (rev 279470)
@@ -2824,6 +2824,45 @@
Value* left = m_value->child(0);
Value* right = m_value->child(1);
+ // EXTR Pattern: d = ((n & mask) << highWidth) | (m >> lowWidth)
+ // Where: highWidth = datasize - lowWidth
+ // mask = (1 << lowWidth) - 1
+ auto tryAppendEXTR = [&] (Value* left, Value* right) -> bool {
+ Air::Opcode opcode = opcodeForType(ExtractRegister32, ExtractRegister64, m_value->type());
+ if (!isValidForm(opcode, Arg::Tmp, Arg::Tmp, Arg::Imm, Arg::Tmp))
+ return false;
+ if (left->opcode() != Shl || left->child(0)->opcode() != BitAnd || right->opcode() != ZShr)
+ return false;
+
+ Value* nValue = left->child(0)->child(0);
+ Value* maskValue = left->child(0)->child(1);
+ Value* highWidthValue = left->child(1);
+ Value* mValue = right->child(0);
+ Value* lowWidthValue = right->child(1);
+ if (m_locked.contains(nValue) || m_locked.contains(mValue) || !maskValue->hasInt())
+ return false;
+ if (!imm(highWidthValue) || highWidthValue->asInt() < 0)
+ return false;
+ if (!imm(lowWidthValue) || lowWidthValue->asInt() < 0)
+ return false;
+
+ uint64_t mask = maskValue->asInt();
+ if (!mask || mask & (mask + 1))
+ return false;
+ uint64_t maskBitCount = WTF::bitCount(mask);
+ uint64_t highWidth = highWidthValue->asInt();
+ uint64_t lowWidth = lowWidthValue->asInt();
+ uint64_t datasize = opcode == ExtractRegister32 ? 32 : 64;
+ if (lowWidth + highWidth != datasize || maskBitCount != lowWidth)
+ return false;
+
+ append(opcode, tmp(nValue), tmp(mValue), imm(lowWidthValue), tmp(m_value));
+ return true;
+ };
+
+ if (tryAppendEXTR(left, right) || tryAppendEXTR(right, left))
+ return;
+
// BFI Pattern: d = ((n & mask1) << lsb) | (d & mask2)
// Where: mask1 = ((1 << width) - 1)
// mask2 = ~(mask1 << lsb)
Modified: trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp (279469 => 279470)
--- trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2021-07-01 17:25:26 UTC (rev 279470)
@@ -1463,6 +1463,33 @@
break;
}
+ // Turn this: ZShr(Shl(value, amount)), amount)
+ // Into this: BitAnd(value, mask)
+ // Conditions:
+ // 1. 0 <= amount < datasize
+ // 2. width = datasize - amount
+ // 3. mask is !(mask & (mask + 1)) where bitCount(mask) == width
+ if (m_value->child(0)->opcode() == Shl
+ && m_value->child(0)->child(1)->hasInt()
+ && m_value->child(0)->child(1)->asInt() >= 0
+ && m_value->child(1)->hasInt()
+ && m_value->child(1)->asInt() >= 0) {
+ uint64_t amount1 = m_value->child(0)->child(1)->asInt();
+ uint64_t amount2 = m_value->child(1)->asInt();
+ uint64_t datasize = m_value->child(0)->child(0)->type() == Int64 ? 64 : 32;
+ if (amount1 == amount2 && amount1 < datasize) {
+ uint64_t width = datasize - amount1;
+ uint64_t mask = (1ULL << width) - 1ULL;
+ 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);
+ replaceWithNew<Value>(BitAnd, m_value->origin(), m_value->child(0)->child(0), maskValue);
+ break;
+ }
+ }
+
// Turn this: ZShr(BitAnd(value, maskShift), shiftAmount)
// Into this: BitAnd(ZShr(value, shiftAmount), mask)
// Conditions:
Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (279469 => 279470)
--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2021-07-01 17:25:26 UTC (rev 279470)
@@ -866,6 +866,12 @@
arm64: ExtractSignedBitfield64 U:G:64, U:G:32, U:G:32, D:G:64
Tmp, Imm, Imm, Tmp
+arm64: ExtractRegister32 U:G:32, U:G:32, U:G:32, ZD:G:32
+ Tmp, Tmp, Imm, Tmp
+
+arm64: ExtractRegister64 U:G:64, U:G:32, U:G:32, D:G:64
+ Tmp, Tmp, 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 (279469 => 279470)
--- trunk/Source/_javascript_Core/b3/testb3.h 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/b3/testb3.h 2021-07-01 17:25:26 UTC (rev 279470)
@@ -436,6 +436,10 @@
void testBIC64();
void testOrNot32();
void testOrNot64();
+void testBitfieldZeroExtend32();
+void testBitfieldZeroExtend64();
+void testExtractRegister32();
+void testExtractRegister64();
void testInsertSignedBitfieldInZero32();
void testInsertSignedBitfieldInZero64();
void testExtractSignedBitfield32();
Modified: trunk/Source/_javascript_Core/b3/testb3_2.cpp (279469 => 279470)
--- trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-07-01 17:14:18 UTC (rev 279469)
+++ trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-07-01 17:25:26 UTC (rev 279470)
@@ -3703,6 +3703,200 @@
}
}
+void testBitfieldZeroExtend32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ Vector<uint32_t> amounts = { 0, 14, 31 };
+
+ // Turn this: ZShr(Shl(n, amount)), amount)
+ // Into this: BitAnd(n, mask)
+ // Conditions:
+ // 1. 0 <= amount < datasize
+ // 2. width = datasize - amount
+ // 3. mask is !(mask & (mask + 1)) where bitCount(mask) == width
+ auto test = [&] (uint32_t n, uint32_t amount) -> uint32_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* amountValue = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* shlValue = root->appendNew<Value>(proc, Shl, Origin(), nValue, amountValue);
+ Value* zshrValue = root->appendNew<Value>(proc, ZShr, Origin(), shlValue, amountValue);
+ root->appendNewControlValue(proc, Return, Origin(), zshrValue);
+
+ auto code = compileProc(proc);
+ if (isARM64() && amount > 0)
+ checkUsesInstruction(*code, "and");
+ return invoke<uint32_t>(*code, n, amount);
+ };
+
+ uint32_t datasize = CHAR_BIT * sizeof(uint32_t);
+ for (auto nOperand : int32Operands()) {
+ for (auto amount : amounts) {
+ uint32_t n = nOperand.value;
+ uint32_t width = datasize - amount;
+ uint32_t mask = width == datasize ? std::numeric_limits<uint32_t>::max() : (1U << width) - 1U;
+ uint32_t lhs = test(n, amount);
+ uint32_t rhs = (n & mask);
+ CHECK(lhs == rhs);
+ }
+ }
+}
+
+void testBitfieldZeroExtend64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ Vector<uint64_t> amounts = { 0, 34, 63 };
+
+ auto test = [&] (uint64_t n, uint64_t amount) -> uint64_t {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* nValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* amountValue = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* shlValue = root->appendNew<Value>(proc, Shl, Origin(), nValue, amountValue);
+ Value* zshrValue = root->appendNew<Value>(proc, ZShr, Origin(), shlValue, amountValue);
+ root->appendNewControlValue(proc, Return, Origin(), zshrValue);
+
+ auto code = compileProc(proc);
+ if (isARM64() && amount > 0)
+ checkUsesInstruction(*code, "and");
+ return invoke<uint64_t>(*code, n, amount);
+ };
+
+ uint64_t datasize = CHAR_BIT * sizeof(uint64_t);
+ for (auto nOperand : int64Operands()) {
+ for (auto amount : amounts) {
+ uint64_t n = nOperand.value;
+ uint64_t width = datasize - amount;
+ uint64_t mask = width == datasize ? std::numeric_limits<uint64_t>::max() : (1ULL << width) - 1ULL;
+ uint64_t lhs = test(n, amount);
+ uint64_t rhs = (n & mask);
+ CHECK(lhs == rhs);
+ }
+ }
+}
+
+void testExtractRegister32()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ Vector<uint32_t> lowWidths = { 0, 17, 31 };
+
+ // Test Pattern: ((n & mask1) << highWidth) | ((m & mask2) >> lowWidth)
+ // Where: highWidth = datasize - lowWidth
+ // mask1 = (1 << lowWidth) - 1
+ // mask2 = ~mask1
+ auto test = [&] (uint32_t n, uint32_t m, uint32_t mask1, uint32_t mask2, uint32_t highWidth, uint32_t lowWidth) -> uint32_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* mask1Value = root->appendNew<Const32Value>(proc, Origin(), mask1);
+ Value* mask2Value = root->appendNew<Const32Value>(proc, Origin(), mask2);
+ Value* highWidthValue = root->appendNew<Const32Value>(proc, Origin(), highWidth);
+ Value* lowWidthValue = root->appendNew<Const32Value>(proc, Origin(), lowWidth);
+
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), nValue, mask1Value);
+ Value* left = root->appendNew<Value>(proc, Shl, Origin(), leftAndValue, highWidthValue);
+
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), mValue, mask2Value);
+ Value* right = root->appendNew<Value>(proc, ZShr, Origin(), rightAndValue, lowWidthValue);
+
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), left, right));
+
+ auto code = compileProc(proc);
+ if (isARM64() && lowWidth > 0)
+ checkUsesInstruction(*code, "extr");
+ return invoke<uint32_t>(*code, n, m);
+ };
+
+ uint32_t datasize = CHAR_BIT * sizeof(uint32_t);
+ for (auto nOperand : int32Operands()) {
+ for (auto mOperand : int32Operands()) {
+ for (auto lowWidth : lowWidths) {
+ uint32_t n = nOperand.value;
+ uint32_t m = mOperand.value;
+ uint32_t highWidth = datasize - lowWidth;
+ uint32_t mask1 = (1U << lowWidth) - 1U;
+ uint32_t mask2 = ~mask1;
+ uint32_t left = highWidth == datasize ? 0U : ((n & mask1) << highWidth);
+ uint32_t right = ((m & mask2) >> lowWidth);
+ uint32_t rhs = left | right;
+ uint32_t lhs = test(n, m, mask1, mask2, highWidth, lowWidth);
+ CHECK(lhs == rhs);
+ }
+ }
+ }
+}
+
+void testExtractRegister64()
+{
+ if (JSC::Options::defaultB3OptLevel() < 2)
+ return;
+ Vector<uint64_t> lowWidths = { 0, 34, 63 };
+
+ // Test Pattern: ((n & mask1) << highWidth) | ((m & mask2) >> lowWidth)
+ // Where: highWidth = datasize - lowWidth
+ // mask1 = (1 << lowWidth) - 1
+ // mask2 = ~mask1
+ auto test = [&] (uint64_t n, uint64_t m, uint64_t mask1, uint64_t mask2, uint64_t highWidth, uint64_t lowWidth) -> uint64_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* mask1Value = root->appendNew<Const64Value>(proc, Origin(), mask1);
+ Value* mask2Value = root->appendNew<Const64Value>(proc, Origin(), mask2);
+ Value* highWidthValue = root->appendNew<Const32Value>(proc, Origin(), highWidth);
+ Value* lowWidthValue = root->appendNew<Const32Value>(proc, Origin(), lowWidth);
+
+ Value* leftAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), nValue, mask1Value);
+ Value* left = root->appendNew<Value>(proc, Shl, Origin(), leftAndValue, highWidthValue);
+
+ Value* rightAndValue = root->appendNew<Value>(proc, BitAnd, Origin(), mValue, mask2Value);
+ Value* right = root->appendNew<Value>(proc, ZShr, Origin(), rightAndValue, lowWidthValue);
+
+ root->appendNewControlValue(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, BitOr, Origin(), left, right));
+
+ auto code = compileProc(proc);
+ if (isARM64() && lowWidth > 0)
+ checkUsesInstruction(*code, "extr");
+ return invoke<uint64_t>(*code, n, m);
+ };
+
+ uint64_t datasize = CHAR_BIT * sizeof(uint64_t);
+ for (auto nOperand : int64Operands()) {
+ for (auto mOperand : int64Operands()) {
+ for (auto lowWidth : lowWidths) {
+ uint64_t n = nOperand.value;
+ uint64_t m = mOperand.value;
+ uint64_t highWidth = datasize - lowWidth;
+ uint64_t mask1 = (1ULL << lowWidth) - 1ULL;
+ uint64_t mask2 = ~mask1;
+ uint64_t left = highWidth == datasize ? 0ULL : ((n & mask1) << highWidth);
+ uint64_t right = ((m & mask2) >> lowWidth);
+ uint64_t rhs = left | right;
+ uint64_t lhs = test(n, m, mask1, mask2, highWidth, lowWidth);
+ CHECK(lhs == rhs);
+ }
+ }
+ }
+}
+
void testBitAndZeroShiftRightArgImmMask32()
{
// Turn this: (tmp >> imm) & mask
@@ -4603,6 +4797,10 @@
RUN(testBIC64());
RUN(testOrNot32());
RUN(testOrNot64());
+ RUN(testBitfieldZeroExtend32());
+ RUN(testBitfieldZeroExtend64());
+ RUN(testExtractRegister32());
+ RUN(testExtractRegister64());
RUN(testInsertSignedBitfieldInZero32());
RUN(testInsertSignedBitfieldInZero64());
RUN(testExtractSignedBitfield32());