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:
win7-7 2025-12-30 12:03:48 +02:00 committed by wuggy
commit 225d2c73db

View file

@ -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);