mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +09:00
1316831 - Wasm baseline: Disable bounds checking on constant accesses below the minimum heap length
1316831 - wasm baseline, omit known-good bounds checks Also includes 1353347 - wasm baseline, properly chop int32 constants to 32 bits.
This commit is contained in:
parent
82b486fe9b
commit
225d2c73db
1 changed files with 123 additions and 50 deletions
|
|
@ -1093,11 +1093,11 @@ class BaseCompiler
|
|||
}
|
||||
|
||||
void loadConstI32(Register r, Stk& src) {
|
||||
#if defined(JS_CODEGEN_LOONGARCH64)
|
||||
masm.move32(Imm32(src.i32val()), r);
|
||||
#else
|
||||
masm.mov(ImmWord((uint32_t)src.i32val() & 0xFFFFFFFFU), r);
|
||||
#endif
|
||||
masm.mov(ImmWord(uint32_t(src.i32val())), r);
|
||||
}
|
||||
|
||||
void loadConstI32(Register r, int32_t v) {
|
||||
masm.mov(ImmWord(uint32_t(v)), r);
|
||||
}
|
||||
|
||||
void loadMemI32(Register r, Stk& src) {
|
||||
|
|
@ -3501,21 +3501,23 @@ ScratchI32 tmp(*this);
|
|||
|
||||
// ptr and dest may be the same iff dest is I32.
|
||||
// This may destroy ptr even if ptr and dest are not the same.
|
||||
[[nodiscard]] bool load(MemoryAccessDesc& access, RegI32 ptr, AnyReg dest,
|
||||
RegI32 tmp1, RegI32 tmp2, RegI32 tmp3)
|
||||
[[nodiscard]] bool load(MemoryAccessDesc& access, RegI32 ptr, bool omitBoundsCheck, AnyReg dest,
|
||||
RegI32 tmp1, RegI32 tmp2, RegI32 tmp3)
|
||||
{
|
||||
checkOffset(&access, ptr);
|
||||
|
||||
OutOfLineCode* ool = nullptr;
|
||||
#ifndef WASM_HUGE_MEMORY
|
||||
if (access.isPlainAsmJS()) {
|
||||
ool = new (alloc_) AsmJSLoadOOB(access.type(), dest.any());
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
if (!omitBoundsCheck) {
|
||||
if (access.isPlainAsmJS()) {
|
||||
ool = new (alloc_) AsmJSLoadOOB(access.type(), dest.any());
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, ool->entry());
|
||||
} else {
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, trap(Trap::OutOfBounds));
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, ool->entry());
|
||||
} else {
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, trap(Trap::OutOfBounds));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -3642,17 +3644,19 @@ ScratchI32 tmp(*this);
|
|||
|
||||
// ptr and src must not be the same register.
|
||||
// This may destroy ptr but will not destroy src.
|
||||
[[nodiscard]] bool store(MemoryAccessDesc access, RegI32 ptr, AnyReg src,
|
||||
[[nodiscard]] bool store(MemoryAccessDesc access, RegI32 ptr, bool omitBoundsCheck, AnyReg src,
|
||||
RegI32 tmp)
|
||||
{
|
||||
checkOffset(&access, ptr);
|
||||
|
||||
Label rejoin;
|
||||
#ifndef WASM_HUGE_MEMORY
|
||||
if (access.isPlainAsmJS())
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, &rejoin);
|
||||
else
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, trap(Trap::OutOfBounds));
|
||||
if (!omitBoundsCheck) {
|
||||
if (access.isPlainAsmJS())
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, &rejoin);
|
||||
else
|
||||
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, trap(Trap::OutOfBounds));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Emit the store
|
||||
|
|
@ -3816,6 +3820,36 @@ ScratchI32 tmp(*this);
|
|||
*r0 = popF64();
|
||||
}
|
||||
|
||||
RegI32 popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck);
|
||||
|
||||
template<bool freeIt> void freeOrPushI32(RegI32 r) {
|
||||
if (freeIt)
|
||||
freeI32(r);
|
||||
else
|
||||
pushI32(r);
|
||||
}
|
||||
|
||||
template<bool freeIt> void freeOrPushI64(RegI64 r) {
|
||||
if (freeIt)
|
||||
freeI64(r);
|
||||
else
|
||||
pushI64(r);
|
||||
}
|
||||
|
||||
template<bool freeIt> void freeOrPushF32(RegF32 r) {
|
||||
if (freeIt)
|
||||
freeF32(r);
|
||||
else
|
||||
pushF32(r);
|
||||
}
|
||||
|
||||
template<bool freeIt> void freeOrPushF64(RegF64 r) {
|
||||
if (freeIt)
|
||||
freeF64(r);
|
||||
else
|
||||
pushF64(r);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Sundry helpers.
|
||||
|
|
@ -6499,6 +6533,49 @@ BaseCompiler::emitTeeGlobal()
|
|||
return true;
|
||||
}
|
||||
|
||||
// See EffectiveAddressAnalysis::analyzeAsmJSHeapAccess() for comparable Ion code.
|
||||
//
|
||||
// TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
|
||||
// code by not moving a constant address with a zero offset into a register.
|
||||
|
||||
BaseCompiler::RegI32
|
||||
BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck)
|
||||
{
|
||||
// Caller must initialize.
|
||||
MOZ_ASSERT(!*omitBoundsCheck);
|
||||
|
||||
if (isCompilingAsmJS())
|
||||
return popI32();
|
||||
|
||||
int32_t addrTmp;
|
||||
if (popConstI32(addrTmp)) {
|
||||
uint32_t addr = addrTmp;
|
||||
|
||||
// We can eliminate the bounds check if the sum of the constant address
|
||||
// and the known offset are below the sum of the minimum memory length
|
||||
// and the offset guard length.
|
||||
|
||||
uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
|
||||
uint64_t limit = uint64_t(env_.minMemoryLength) + uint64_t(wasm::OffsetGuardLimit);
|
||||
|
||||
*omitBoundsCheck = ea < limit;
|
||||
|
||||
// Fold the offset into the pointer if we can, as this is always
|
||||
// beneficial.
|
||||
|
||||
if (ea <= UINT32_MAX) {
|
||||
addr = uint32_t(ea);
|
||||
access->clearOffset();
|
||||
}
|
||||
|
||||
RegI32 r = needI32();
|
||||
loadConstI32(r, int32_t(addr));
|
||||
return r;
|
||||
}
|
||||
|
||||
return popI32();
|
||||
}
|
||||
|
||||
bool
|
||||
BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
|
||||
{
|
||||
|
|
@ -6509,8 +6586,7 @@ BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
|
|||
if (deadCode_)
|
||||
return true;
|
||||
|
||||
// TODO / OPTIMIZE (bug 1316831): Disable bounds checking on constant
|
||||
// accesses below the minimum heap length.
|
||||
bool omitBoundsCheck = false;
|
||||
|
||||
MemoryAccessDesc access(viewType, addr.align, addr.offset, trapIfNotAsmJS());
|
||||
|
||||
|
|
@ -6521,13 +6597,13 @@ BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
|
|||
|
||||
switch (type) {
|
||||
case ValType::I32: {
|
||||
RegI32 rp = popI32();
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
RegI32 rv = IsUnaligned(access) ? needI32() : rp;
|
||||
#else
|
||||
RegI32 rv = rp;
|
||||
#endif
|
||||
if (!load(access, rp, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
if (!load(access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
return false;
|
||||
pushI32(rv);
|
||||
if (rp != rv)
|
||||
|
|
@ -6540,30 +6616,30 @@ BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
|
|||
#ifdef JS_CODEGEN_X86
|
||||
rv = abiReturnRegI64;
|
||||
needI64(rv);
|
||||
rp = popI32();
|
||||
rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
#else
|
||||
rp = popI32();
|
||||
rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
rv = needI64();
|
||||
#endif
|
||||
if (!load(access, rp, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
if (!load(access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
return false;
|
||||
pushI64(rv);
|
||||
freeI32(rp);
|
||||
break;
|
||||
}
|
||||
case ValType::F32: {
|
||||
RegI32 rp = popI32();
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
RegF32 rv = needF32();
|
||||
if (!load(access, rp, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
if (!load(access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
return false;
|
||||
pushF32(rv);
|
||||
freeI32(rp);
|
||||
break;
|
||||
}
|
||||
case ValType::F64: {
|
||||
RegI32 rp = popI32();
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
RegF64 rv = needF64();
|
||||
if (!load(access, rp, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
if (!load(access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
|
||||
return false;
|
||||
pushF64(rv);
|
||||
freeI32(rp);
|
||||
|
|
@ -6595,8 +6671,7 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
|
|||
if (deadCode_)
|
||||
return true;
|
||||
|
||||
// TODO / OPTIMIZE (bug 1316831): Disable bounds checking on constant
|
||||
// accesses below the minimum heap length.
|
||||
bool omitBoundsCheck = false;
|
||||
|
||||
MemoryAccessDesc access(viewType, addr.align, addr.offset, trapIfNotAsmJS());
|
||||
|
||||
|
|
@ -6605,9 +6680,9 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
|
|||
|
||||
switch (resultType) {
|
||||
case ValType::I32: {
|
||||
RegI32 rp, rv;
|
||||
pop2xI32(&rp, &rv);
|
||||
if (!store(access, rp, AnyReg(rv), tmp1))
|
||||
RegI32 rv = popI32();
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rv), tmp1))
|
||||
return false;
|
||||
freeI32(rp);
|
||||
freeI32(rv);
|
||||
|
|
@ -6615,8 +6690,8 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
|
|||
}
|
||||
case ValType::I64: {
|
||||
RegI64 rv = popI64();
|
||||
RegI32 rp = popI32();
|
||||
if (!store(access, rp, AnyReg(rv), tmp1))
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rv), tmp1))
|
||||
return false;
|
||||
freeI32(rp);
|
||||
freeI64(rv);
|
||||
|
|
@ -6624,8 +6699,8 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
|
|||
}
|
||||
case ValType::F32: {
|
||||
RegF32 rv = popF32();
|
||||
RegI32 rp = popI32();
|
||||
if (!store(access, rp, AnyReg(rv), tmp1))
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rv), tmp1))
|
||||
return false;
|
||||
freeI32(rp);
|
||||
freeF32(rv);
|
||||
|
|
@ -6633,8 +6708,8 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
|
|||
}
|
||||
case ValType::F64: {
|
||||
RegF64 rv = popF64();
|
||||
RegI32 rp = popI32();
|
||||
if (!store(access, rp, AnyReg(rv), tmp1))
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rv), tmp1))
|
||||
return false;
|
||||
freeI32(rp);
|
||||
freeF64(rv);
|
||||
|
|
@ -6955,9 +7030,7 @@ BaseCompiler::emitTeeStoreWithCoercion(ValType resultType, Scalar::Type viewType
|
|||
if (deadCode_)
|
||||
return true;
|
||||
|
||||
// TODO / OPTIMIZE (bug 1316831): Disable bounds checking on constant
|
||||
// accesses below the minimum heap length.
|
||||
|
||||
bool omitBoundsCheck = false;
|
||||
MemoryAccessDesc access(viewType, addr.align, addr.offset, trapIfNotAsmJS());
|
||||
|
||||
size_t temps = storeTemps(access);
|
||||
|
|
@ -6966,9 +7039,9 @@ BaseCompiler::emitTeeStoreWithCoercion(ValType resultType, Scalar::Type viewType
|
|||
if (resultType == ValType::F32 && viewType == Scalar::Float64) {
|
||||
RegF32 rv = popF32();
|
||||
RegF64 rw = needF64();
|
||||
masm.convertFloat32ToDouble(rv.reg, rw.reg);
|
||||
RegI32 rp = popI32();
|
||||
if (!store(access, rp, AnyReg(rw), tmp1))
|
||||
masm.convertFloat32ToDouble(rv, rw);
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rw), tmp1))
|
||||
return false;
|
||||
pushF32(rv);
|
||||
freeI32(rp);
|
||||
|
|
@ -6977,9 +7050,9 @@ BaseCompiler::emitTeeStoreWithCoercion(ValType resultType, Scalar::Type viewType
|
|||
else if (resultType == ValType::F64 && viewType == Scalar::Float32) {
|
||||
RegF64 rv = popF64();
|
||||
RegF32 rw = needF32();
|
||||
masm.convertDoubleToFloat32(rv.reg, rw.reg);
|
||||
RegI32 rp = popI32();
|
||||
if (!store(access, rp, AnyReg(rw), tmp1))
|
||||
masm.convertDoubleToFloat32(rv, rw);
|
||||
RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
|
||||
if (!store(access, rp, omitBoundsCheck, AnyReg(rw), tmp1))
|
||||
return false;
|
||||
pushF64(rv);
|
||||
freeI32(rp);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue