Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (280012 => 280013)
--- trunk/Source/_javascript_Core/ChangeLog 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-07-17 00:53:05 UTC (rev 280013)
@@ -1,3 +1,100 @@
+2021-07-16 Yijia Huang <[email protected]>
+
+ Add ExtendType to Air::Arg Index to fully utilize address computation in memory instruction for ARM64
+ https://bugs.webkit.org/show_bug.cgi?id=227970
+
+ Reviewed by Saam Barati.
+
+ The pattern recognition of address computation in the instructions, e.g., Load
+ Resistor (LDR), Store Register (STR), etc., can benefit the instruction selector.
+ Then, the Air operand BaseIndex containing base, index, and scale is introduced
+ to Air opcode. However, the <extend> option of index address is not fully leveraged
+ in the previous implementation.
+
+ To fill that gap, this patch adds a new member, MacroAssembler::Extend, to the current
+ design of BaseIndex to trigger zero/sign extension on the Index address. And this is
+ enabled for Store/Load with valid index address and shift amount.
+
+ Maybe, the ideal approach is to introduce a decorator (Index@EXT) to the Air operand
+ to provide an extension opportunity for the specific form of the Air opcode.
+
+ Load Register (LDR) calculates an address from a base register value and an
+ offset register value, loads a word from memory, and writes it to a register.
+ The offset register value can optionally be shifted and extended.
+
+ Given B3 IR:
+ Int @0 = ArgumentReg(%x0)
+ Int @1 = Z/SExt32(Trunc(ArgumentReg(%x1)))
+ Int @2 = scale
+ Int @3 = Shl(@1, @2)
+ Int @4 = Add(@0, @3)
+ Int @5 = Load(@4, ControlDependent|Reads:Top)
+ Void@6 = Return(@5, Terminal)
+
+ // Old optimized AIR
+ Move %x1, %x1, @1
+ Move (%x0,%x1,2^scale), %x0, @5
+ Ret %x0, @6
+
+ // New optimized AIR
+ Move (%x0,%x1,2^scale), %x0, @5
+ Ret %x0, @6
+
+ Store Register (STR) calculates an address from a base register value and an
+ offset register value, and stores a 32-bit word or a 64-bit doubleword to the
+ calculated address, from a register.
+
+ Given B3 IR:
+ Int @0 = value
+ Int @1 = ArgumentReg(%x0)
+ Int @2 = Z/SExt32(Trunc(ArgumentReg(%x1))
+ Int @3 = scale
+ Int @4 = Shl(@2, @3)
+ Int @5 = Add(@1, @4)
+ Void@6 = Store(@0, @5, ControlDependent|Writes:Top)
+ Void@7 = Return(@0, Terminal)
+
+ // Old optimized AIR
+ Move32 %x1, %x1, @2
+ Store32 %xzr, (%x0,%x1,2^scale), @6
+ Move $0, %x0, @7
+ Ret32 %x0, @7
+
+ // New optimized AIR
+ Store32 %xzr, (%x0,%x1,2^scale), @6
+ Move $0, %x0, @7
+ Ret32 %x0, @7
+
+ * assembler/AbstractMacroAssembler.h:
+ (JSC::AbstractMacroAssembler::BaseIndex::BaseIndex):
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::indexExtendType):
+ (JSC::MacroAssemblerARM64::load64):
+ (JSC::MacroAssemblerARM64::load32):
+ (JSC::MacroAssemblerARM64::load16):
+ (JSC::MacroAssemblerARM64::load16SignedExtendTo32):
+ (JSC::MacroAssemblerARM64::load8):
+ (JSC::MacroAssemblerARM64::load8SignedExtendTo32):
+ (JSC::MacroAssemblerARM64::store64):
+ (JSC::MacroAssemblerARM64::store32):
+ (JSC::MacroAssemblerARM64::store16):
+ (JSC::MacroAssemblerARM64::store8):
+ (JSC::MacroAssemblerARM64::loadDouble):
+ (JSC::MacroAssemblerARM64::loadFloat):
+ (JSC::MacroAssemblerARM64::storeDouble):
+ (JSC::MacroAssemblerARM64::storeFloat):
+ * b3/B3LowerToAir.cpp:
+ * b3/air/AirArg.h:
+ (JSC::B3::Air::Arg::index):
+ (JSC::B3::Air::Arg::asBaseIndex const):
+ * b3/testb3.h:
+ * b3/testb3_2.cpp:
+ (testLoadZeroExtendIndexAddress):
+ (testLoadSignExtendIndexAddress):
+ (testStoreZeroExtendIndexAddress):
+ (testStoreSignExtendIndexAddress):
+ (addBitTests):
+
2021-07-16 Saam Barati <[email protected]>
Grab the lock in FTL::Thunks::keyForSlowPathCallThunk
Modified: trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h (280012 => 280013)
--- trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h 2021-07-17 00:53:05 UTC (rev 280013)
@@ -124,7 +124,13 @@
TimesEight,
ScalePtr = isAddress64Bit() ? TimesEight : TimesFour,
};
-
+
+ enum class Extend : uint8_t {
+ ZExt32,
+ SExt32,
+ None
+ };
+
struct BaseIndex;
static RegisterID withSwappedRegister(RegisterID original, RegisterID left, RegisterID right)
@@ -210,12 +216,16 @@
//
// Describes a complex addressing mode.
struct BaseIndex {
- BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0)
+ BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0, Extend extend = Extend::None)
: base(base)
, index(index)
, scale(scale)
, offset(offset)
+ , extend(extend)
{
+#if !CPU(ARM64)
+ ASSERT(extend == Extend::None);
+#endif
}
RegisterID base;
@@ -222,6 +232,7 @@
RegisterID index;
Scale scale;
int32_t offset;
+ Extend extend;
BaseIndex withOffset(int32_t additionalOffset)
{
Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (280012 => 280013)
--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h 2021-07-17 00:53:05 UTC (rev 280013)
@@ -1367,6 +1367,17 @@
}
// Memory access operations:
+ Assembler::ExtendType indexExtendType(BaseIndex address)
+ {
+ switch (address.extend) {
+ case Extend::ZExt32:
+ return Assembler::UXTW;
+ case Extend::SExt32:
+ return Assembler::SXTW;
+ case Extend::None:
+ return Assembler::UXTX;
+ }
+ }
void load64(ImplicitAddress address, RegisterID dest)
{
@@ -1380,12 +1391,12 @@
void load64(BaseIndex address, RegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 3)) {
- m_assembler.ldr<64>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldr<64>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldr<64>(dest, address.base, memoryTempRegister);
}
@@ -1479,12 +1490,12 @@
void load32(BaseIndex address, RegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 2)) {
- m_assembler.ldr<32>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldr<32>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldr<32>(dest, address.base, memoryTempRegister);
}
@@ -1526,12 +1537,12 @@
void load16(BaseIndex address, RegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 1)) {
- m_assembler.ldrh(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldrh(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldrh(dest, address.base, memoryTempRegister);
}
@@ -1570,12 +1581,12 @@
void load16SignedExtendTo32(BaseIndex address, RegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 1)) {
- m_assembler.ldrsh<32>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldrsh<32>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldrsh<32>(dest, address.base, memoryTempRegister);
}
@@ -1601,12 +1612,12 @@
void load8(BaseIndex address, RegisterID dest)
{
if (!address.offset && !address.scale) {
- m_assembler.ldrb(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldrb(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldrb(dest, address.base, memoryTempRegister);
}
@@ -1635,12 +1646,12 @@
void load8SignedExtendTo32(BaseIndex address, RegisterID dest)
{
if (!address.offset && !address.scale) {
- m_assembler.ldrsb<32>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldrsb<32>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldrsb<32>(dest, address.base, memoryTempRegister);
}
@@ -1674,12 +1685,12 @@
void store64(RegisterID src, BaseIndex address)
{
if (!address.offset && (!address.scale || address.scale == 3)) {
- m_assembler.str<64>(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.str<64>(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.str<64>(src, address.base, memoryTempRegister);
}
@@ -1781,12 +1792,12 @@
void store32(RegisterID src, BaseIndex address)
{
if (!address.offset && (!address.scale || address.scale == 2)) {
- m_assembler.str<32>(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.str<32>(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.str<32>(src, address.base, memoryTempRegister);
}
@@ -1848,12 +1859,12 @@
void store16(RegisterID src, BaseIndex address)
{
if (!address.offset && (!address.scale || address.scale == 1)) {
- m_assembler.strh(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.strh(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.strh(src, address.base, memoryTempRegister);
}
@@ -1876,12 +1887,12 @@
void store8(RegisterID src, BaseIndex address)
{
if (!address.offset && !address.scale) {
- m_assembler.strb(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.strb(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.strb(src, address.base, memoryTempRegister);
}
@@ -2189,12 +2200,12 @@
void loadDouble(BaseIndex address, FPRegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 3)) {
- m_assembler.ldr<64>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldr<64>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldr<64>(dest, address.base, memoryTempRegister);
}
@@ -2216,12 +2227,12 @@
void loadFloat(BaseIndex address, FPRegisterID dest)
{
if (!address.offset && (!address.scale || address.scale == 2)) {
- m_assembler.ldr<32>(dest, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.ldr<32>(dest, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.ldr<32>(dest, address.base, memoryTempRegister);
}
@@ -2473,12 +2484,12 @@
void storeDouble(FPRegisterID src, BaseIndex address)
{
if (!address.offset && (!address.scale || address.scale == 3)) {
- m_assembler.str<64>(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.str<64>(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.str<64>(src, address.base, memoryTempRegister);
}
@@ -2494,12 +2505,12 @@
void storeFloat(FPRegisterID src, BaseIndex address)
{
if (!address.offset && (!address.scale || address.scale == 2)) {
- m_assembler.str<32>(src, address.base, address.index, Assembler::UXTX, address.scale);
+ m_assembler.str<32>(src, address.base, address.index, indexExtendType(address), address.scale);
return;
}
signExtend32ToPtr(TrustedImm32(address.offset), getCachedMemoryTempRegisterIDAndInvalidate());
- m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, Assembler::UXTX, address.scale);
+ m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
m_assembler.str<32>(src, address.base, memoryTempRegister);
}
Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (280012 => 280013)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2021-07-17 00:53:05 UTC (rev 280013)
@@ -499,7 +499,32 @@
return true;
}
+ bool isMergeableValue(Value* v, B3::Opcode b3Opcode, bool checkCanBeInternal)
+ {
+ if (v->opcode() != b3Opcode)
+ return false;
+ if (checkCanBeInternal && !canBeInternal(v))
+ return false;
+ if (m_locked.contains(v->child(0)))
+ return false;
+ return true;
+ }
+
template<typename Int, typename = Value::IsLegalOffset<Int>>
+ Arg indexArg(Tmp base, Value* index, unsigned scale, Int offset)
+ {
+#if CPU(ARM64)
+ // Maybe, the ideal approach is to introduce a decorator (Index@EXT) to the Air operand
+ // to provide an extension opportunity for the specific form under the Air opcode.
+ if (isMergeableValue(index, ZExt32, false))
+ return Arg::index(base, tmp(index->child(0)), scale, offset, MacroAssembler::Extend::ZExt32);
+ if (isMergeableValue(index, SExt32, false))
+ return Arg::index(base, tmp(index->child(0)), scale, offset, MacroAssembler::Extend::SExt32);
+#endif
+ return Arg::index(base, tmp(index), scale, offset);
+ }
+
+ template<typename Int, typename = Value::IsLegalOffset<Int>>
std::optional<unsigned> scaleForShl(Value* shl, Int offset, std::optional<Width> width = std::nullopt)
{
if (shl->opcode() != Shl)
@@ -549,7 +574,7 @@
return Arg();
if (m_locked.contains(index->child(0)) || m_locked.contains(base))
return Arg();
- return Arg::index(tmp(base), tmp(index->child(0)), *scale, offset);
+ return indexArg(tmp(base), index->child(0), *scale, offset);
};
if (Arg result = tryIndex(left, right))
@@ -561,7 +586,7 @@
|| !Arg::isValidIndexForm(1, offset, width))
return fallback();
- return Arg::index(tmp(left), tmp(right), 1, offset);
+ return indexArg(tmp(left), right, 1, offset);
}
case Shl: {
@@ -574,7 +599,7 @@
|| !Arg::isValidIndexForm(1, offset, width))
return fallback();
- return Arg::index(tmp(left), tmp(left), 1, offset);
+ return indexArg(tmp(left), left, 1, offset);
}
case FramePointer:
@@ -594,7 +619,7 @@
// perhaps more importantly, it would allow us to avoid a truncating
// move. See: https://bugs.webkit.org/show_bug.cgi?id=163465
- return Arg::index(Tmp(wasmAddress->pinnedGPR()), tmp(pointer), 1, offset);
+ return indexArg(Tmp(wasmAddress->pinnedGPR()), pointer, 1, offset);
}
default:
@@ -2117,7 +2142,7 @@
ASSERT(!m_locked.contains(shl->child(0)));
ASSERT(!m_locked.contains(other));
- append(leaOpcode, Arg::index(tmp(other), tmp(shl->child(0)), *scale, offset), tmp(m_value));
+ append(leaOpcode, indexArg(tmp(other), shl->child(0), *scale, offset), tmp(m_value));
commitInternal(innerAdd);
commitInternal(shl);
return true;
@@ -2134,7 +2159,7 @@
return false;
ASSERT(!m_locked.contains(value->child(0)));
ASSERT(!m_locked.contains(value->child(1)));
- append(leaOpcode, Arg::index(tmp(value->child(0)), tmp(value->child(1)), 1, offset), tmp(m_value));
+ append(leaOpcode, indexArg(tmp(value->child(0)), value->child(1), 1, offset), tmp(m_value));
commitInternal(innerAdd);
return true;
}
@@ -2490,15 +2515,6 @@
void lower()
{
using namespace Air;
-
- auto isMergeableB3Opcode = [&] (Value* v, B3::Opcode b3Opcode) -> bool {
- if (v->opcode() != b3Opcode || !canBeInternal(v))
- return false;
- if (m_locked.contains(v->child(0)))
- return false;
- return true;
- };
-
switch (m_value->opcode()) {
case B3::Nop: {
// Yes, we will totally see Nop's because some phases will replaceWithNop() instead of
@@ -2599,10 +2615,10 @@
if (airOpcode != MultiplyAdd64)
return Air::Oops;
// SMADDL: d = SExt32(n) * SExt32(m) + a
- if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32))
+ if (isMergeableValue(multiplyLeft, SExt32, true) && isMergeableValue(multiplyRight, SExt32, true))
return MultiplyAddSignExtend32;
// UMADDL: d = ZExt32(n) * ZExt32(m) + a
- if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32))
+ if (isMergeableValue(multiplyLeft, ZExt32, true) && isMergeableValue(multiplyRight, ZExt32, true))
return MultiplyAddZeroExtend32;
return Air::Oops;
};
@@ -2687,10 +2703,10 @@
if (airOpcode != MultiplySub64)
return Air::Oops;
// SMSUBL: d = a - SExt32(n) * SExt32(m)
- if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32))
+ if (isMergeableValue(multiplyLeft, SExt32, true) && isMergeableValue(multiplyRight, SExt32, true))
return MultiplySubSignExtend32;
// UMSUBL: d = a - ZExt32(n) * ZExt32(m)
- if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32))
+ if (isMergeableValue(multiplyLeft, ZExt32, true) && isMergeableValue(multiplyRight, ZExt32, true))
return MultiplySubZeroExtend32;
return Air::Oops;
};
@@ -2766,10 +2782,10 @@
if (airOpcode != MultiplyNeg64)
return Air::Oops;
// SMNEGL: d = -(SExt32(n) * SExt32(m))
- if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32))
+ if (isMergeableValue(multiplyLeft, SExt32, true) && isMergeableValue(multiplyRight, SExt32, true))
return MultiplyNegSignExtend32;
// UMNEGL: d = -(ZExt32(n) * ZExt32(m))
- if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32))
+ if (isMergeableValue(multiplyLeft, ZExt32, true) && isMergeableValue(multiplyRight, ZExt32, true))
return MultiplyNegZeroExtend32;
return Air::Oops;
};
Modified: trunk/Source/_javascript_Core/b3/air/AirArg.h (280012 => 280013)
--- trunk/Source/_javascript_Core/b3/air/AirArg.h 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.h 2021-07-17 00:53:05 UTC (rev 280013)
@@ -629,7 +629,7 @@
}
template<typename Int, typename = Value::IsLegalOffset<Int>>
- static Arg index(Air::Tmp base, Air::Tmp index, unsigned scale, Int offset)
+ static Arg index(Air::Tmp base, Air::Tmp index, unsigned scale, Int offset, MacroAssembler::Extend extend = MacroAssembler::Extend::None)
{
ASSERT(base.isGP());
ASSERT(index.isGP());
@@ -640,6 +640,7 @@
result.m_index = index;
result.m_scale = static_cast<int32_t>(scale);
result.m_offset = offset;
+ result.m_extend = extend;
return result;
}
@@ -1379,7 +1380,7 @@
ASSERT(isIndex());
return MacroAssembler::BaseIndex(
m_base.gpr(), m_index.gpr(), static_cast<MacroAssembler::Scale>(logScale()),
- static_cast<Value::OffsetType>(m_offset));
+ static_cast<Value::OffsetType>(m_offset), m_extend);
}
MacroAssembler::RelationalCondition asRelationalCondition() const
@@ -1484,6 +1485,7 @@
private:
int64_t m_offset { 0 };
Kind m_kind { Invalid };
+ MacroAssembler::Extend m_extend { MacroAssembler::Extend::None };
int32_t m_scale { 1 };
Air::Tmp m_base;
Air::Tmp m_index;
Modified: trunk/Source/_javascript_Core/b3/testb3.h (280012 => 280013)
--- trunk/Source/_javascript_Core/b3/testb3.h 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/b3/testb3.h 2021-07-17 00:53:05 UTC (rev 280013)
@@ -826,6 +826,10 @@
void testArg(int argument);
void testReturnConst64(int64_t value);
void testReturnVoid();
+void testLoadZeroExtendIndexAddress();
+void testLoadSignExtendIndexAddress();
+void testStoreZeroExtendIndexAddress();
+void testStoreSignExtendIndexAddress();
void testAddArg(int);
void testAddArgs(int, int);
void testAddArgImm(int, int);
Modified: trunk/Source/_javascript_Core/b3/testb3_2.cpp (280012 => 280013)
--- trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-07-17 00:47:54 UTC (rev 280012)
+++ trunk/Source/_javascript_Core/b3/testb3_2.cpp 2021-07-17 00:53:05 UTC (rev 280013)
@@ -297,6 +297,314 @@
compileAndRun<void>(proc);
}
+void testLoadZeroExtendIndexAddress()
+{
+ if (Options::defaultB3OptLevel() < 2)
+ return;
+
+ auto test32 = [&] (uint32_t index, int32_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* baseValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, ZExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), baseValue,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<Value>(
+ proc, Return, Origin(),
+ root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address));
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 2 ? ".*ldr.*uxtw#2.*" : ".*ldr.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ intptr_t addr = bitwise_cast<intptr_t>(&num);
+ intptr_t base = addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount));
+ CHECK_EQ(invoke<int32_t>(*code, base, index), num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int32Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test32(index.value, num.value, amount);
+ }
+ }
+
+ auto test64 = [&] (uint32_t index, int64_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* baseValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, ZExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), baseValue,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<Value>(
+ proc, Return, Origin(),
+ root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address));
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 3 ? ".*ldr.*uxtw#3.*" : ".*ldr.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ intptr_t addr = bitwise_cast<intptr_t>(&num);
+ intptr_t base = addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount));
+ CHECK_EQ(invoke<int64_t>(*code, base, index), num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int64Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test64(index.value, num.value, amount);
+ }
+ }
+}
+
+void testLoadSignExtendIndexAddress()
+{
+ if (Options::defaultB3OptLevel() < 2)
+ return;
+
+ auto test32 = [&] (int32_t index, int32_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* baseValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, SExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), baseValue,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<Value>(
+ proc, Return, Origin(),
+ root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), address));
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 2 ? ".*ldr.*sxtw#2.*" : ".*ldr.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ intptr_t addr = bitwise_cast<intptr_t>(&num);
+ intptr_t base = addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount));
+ CHECK_EQ(invoke<int32_t>(*code, base, index), num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int32Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test32(index.value, num.value, amount);
+ }
+ }
+
+ auto test64 = [&] (int32_t index, int64_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* baseValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, SExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), baseValue,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<Value>(
+ proc, Return, Origin(),
+ root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), address));
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 3 ? ".*ldr.*sxtw#3.*" : ".*ldr.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ intptr_t addr = bitwise_cast<intptr_t>(&num);
+ intptr_t base = addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount));
+ CHECK_EQ(invoke<int64_t>(*code, base, index), num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int64Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test64(index.value, num.value, amount);
+ }
+ }
+}
+
+void testStoreZeroExtendIndexAddress()
+{
+ if (Options::defaultB3OptLevel() < 2)
+ return;
+
+ auto test32 = [&] (uint32_t index, int32_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* value = root->appendNew<Const32Value>(proc, Origin(), num);
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, ZExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
+ root->appendNew<Value>(proc, Return, Origin(), value);
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 2 ? ".*str.*uxtw#2.*" : ".*str.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ int32_t slot = 12341234;
+ intptr_t addr = bitwise_cast<intptr_t>(&slot);
+ invoke<int32_t>(*code, addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount)), index);
+ CHECK_EQ(slot, num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int32Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test32(index.value, num.value, amount);
+ }
+ }
+
+ auto test64 = [&] (uint32_t index, int64_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* value = root->appendNew<Const64Value>(proc, Origin(), num);
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, ZExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
+ root->appendNew<Value>(proc, Return, Origin(), value);
+
+ auto code = compileProc(proc);
+ if (isARM64() && amount == 3) {
+ std::string regex(amount == 3 ? ".*str.*uxtw#3.*" : ".*str.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ int64_t slot = 12341234;
+ intptr_t addr = bitwise_cast<intptr_t>(&slot);
+ invoke<int64_t>(*code, addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount)), index);
+ CHECK_EQ(slot, num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int64Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test64(index.value, num.value, amount);
+ }
+ }
+}
+
+void testStoreSignExtendIndexAddress()
+{
+ if (Options::defaultB3OptLevel() < 2)
+ return;
+
+ auto test32 = [&] (int32_t index, int32_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* value = root->appendNew<Const32Value>(proc, Origin(), num);
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, SExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
+ root->appendNew<Value>(proc, Return, Origin(), value);
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 2 ? ".*str.*sxtw#2.*" : ".*str.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ int32_t slot = 12341234;
+ intptr_t addr = bitwise_cast<intptr_t>(&slot);
+ invoke<int32_t>(*code, addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount)), index);
+ CHECK_EQ(slot, num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int32Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test32(index.value, num.value, amount);
+ }
+ }
+
+ auto test64 = [&] (int32_t index, int64_t num, int32_t amount) {
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* value = root->appendNew<Const64Value>(proc, Origin(), num);
+ Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* index32 = root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* index64 = root->appendNew<Value>(proc, SExt32, Origin(), index32);
+ Value* scale = root->appendNew<Const32Value>(proc, Origin(), amount);
+ Value* address = root->appendNew<Value>(
+ proc, Add, Origin(), base,
+ root->appendNew<Value>(proc, Shl, Origin(), index64, scale));
+
+ root->appendNew<MemoryValue>(proc, Store, Origin(), value, address);
+ root->appendNew<Value>(proc, Return, Origin(), value);
+
+ auto code = compileProc(proc);
+ if (isARM64()) {
+ std::string regex(amount == 3 ? ".*str.*sxtw#3.*" : ".*str.*[.*,.*].*");
+ checkUsesInstruction(*code, regex.c_str(), true);
+ }
+ int64_t slot = 12341234;
+ intptr_t addr = bitwise_cast<intptr_t>(&slot);
+ invoke<int64_t>(*code, addr - (static_cast<intptr_t>(index) << static_cast<intptr_t>(amount)), index);
+ CHECK_EQ(slot, num);
+ };
+
+ for (auto index : int32Operands()) {
+ for (auto num : int64Operands()) {
+ for (int32_t amount = 0; amount < 10; ++amount)
+ test64(index.value, num.value, amount);
+ }
+ }
+}
+
void testAddArg(int a)
{
Procedure proc;
@@ -5944,6 +6252,11 @@
RUN_BINARY(testBitXorTreeArgImm, int64Operands(), int64Operands());
RUN_UNARY(testBitAndTreeArg32, int32Operands());
RUN_UNARY(testBitOrTreeArg32, int32Operands());
+
+ RUN(testLoadZeroExtendIndexAddress());
+ RUN(testLoadSignExtendIndexAddress());
+ RUN(testStoreZeroExtendIndexAddress());
+ RUN(testStoreSignExtendIndexAddress());
}
#endif // ENABLE(B3_JIT)