Commit 80326050 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[wasm] Implement memory64 bulk memory on 32-bit

This adds the missing implementation of bulk memory operations on 64-bit
memory on 32-bit systems. This is tricky because especially on ia32 we
don't have a lot of registers, so we cannot keep three 64-bit values
in registers at the same time.
Thus combine the high words into a single register early, and use a
single zero-check afterwards.

R=thibaudm@chromium.org

Bug: v8:10949, chromium:1281995
Change-Id: I017bc43989e4b6195b46b5d0738552a685362e43
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3468335Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79196}
parent f6cd451c
......@@ -4822,45 +4822,82 @@ class LiftoffCompiler {
void AtomicFence(FullDecoder* decoder) { __ AtomicFence(); }
// Pop a memtype (i32 or i64 depending on {WasmModule::is_memory64}) to a
// register. Returns the ptrsized register holding the popped value.
LiftoffRegister PopMemTypeToRegister(LiftoffRegList pinned) {
LiftoffRegister reg = __ PopToRegister(pinned);
// On 64-bit hosts, potentially zero-extend, then return.
if (kSystemPointerSize == kInt64Size) {
if (!env_->module->is_memory64) {
__ emit_u32_to_uintptr(reg.gp(), reg.gp());
// register, updating {*high_word} to contain the ORed combination of all
// popped high words. Returns the ptrsized register holding the popped value.
LiftoffRegister PopMemTypeToRegister(FullDecoder* decoder,
Register* high_word,
LiftoffRegList* pinned) {
LiftoffRegister reg = __ PopToRegister(*pinned);
LiftoffRegister intptr_reg = reg;
// For memory32 on 64-bit hosts, zero-extend.
if (kSystemPointerSize == kInt64Size && !env_->module->is_memory64) {
// Only overwrite {reg} if it's not used otherwise.
if (pinned->has(reg) || __ cache_state()->is_used(reg)) {
intptr_reg = __ GetUnusedRegister(kGpReg, *pinned);
}
return reg;
__ emit_u32_to_uintptr(intptr_reg.gp(), reg.gp());
}
// For memory32 or memory64 on 64-bit, we are done here.
if (kSystemPointerSize == kInt64Size || !env_->module->is_memory64) {
pinned->set(intptr_reg);
return intptr_reg;
}
// For memory64 on 32-bit systems, combine all high words for a zero-check
// and only use the low words afterwards. This keeps the register pressure
// managable.
DCHECK_GE(kMaxUInt32, env_->max_memory_size);
pinned->set(reg.low());
if (*high_word == no_reg) {
// Choose a register to hold the (combination of) high word(s). It cannot
// be one of the pinned registers, and it cannot be used in the value
// stack.
*high_word =
pinned->has(reg.high())
? __ GetUnusedRegister(kGpReg, *pinned).gp()
: __ GetUnusedRegister(kGpReg, {reg.high()}, *pinned).gp();
pinned->set(*high_word);
if (*high_word != reg.high_gp()) {
__ Move(*high_word, reg.high_gp(), kI32);
}
} else if (*high_word != reg.high_gp()) {
// Combine the new high word into existing high words.
__ emit_i32_or(*high_word, *high_word, reg.high_gp());
}
// For memory32 on 32-bit systems, also nothing to do.
if (!env_->module->is_memory64) return reg;
// TODO(v8:10949): Implement bounds-checking of the high word, while keeping
// register pressure low enough on ia32 to pop three 64-bit values (for
// memory.copy).
__ bailout(kOtherReason, "memory64 on 32-bit platform");
return reg.low();
}
void MemoryInit(FullDecoder* decoder,
const MemoryInitImmediate<validate>& imm, const Value&,
const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned;
LiftoffRegister size = pinned.set(__ PopToRegister());
LiftoffRegister src = pinned.set(__ PopToRegister(pinned));
LiftoffRegister dst = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister segment_index =
pinned.set(__ GetUnusedRegister(kGpReg, pinned));
__ LoadConstant(segment_index, WasmValue(imm.data_segment.index));
LiftoffRegister dst =
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned);
Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) {
instance = __ GetUnusedRegister(kGpReg, pinned).gp();
__ LoadInstanceFromFrame(instance);
}
pinned.set(instance);
// Only allocate the OOB code now, so the state of the stack is reflected
// correctly.
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
if (mem_offsets_high_word != no_reg) {
// If any high word has bits set, jump to the OOB trap.
__ emit_cond_jump(kNotEqualZero, trap_label, kI32, mem_offsets_high_word);
pinned.clear(mem_offsets_high_word);
}
LiftoffRegister segment_index =
pinned.set(__ GetUnusedRegister(kGpReg, pinned));
__ LoadConstant(segment_index, WasmValue(imm.data_segment.index));
ExternalReference ext_ref = ExternalReference::wasm_memory_init();
auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind, kI32,
kI32, kI32);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, src,
......@@ -4868,10 +4905,8 @@ class LiftoffCompiler {
// We don't need the instance anymore after the call. We can use the
// register for the result.
LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref);
// TODO(manoskouk): Also throw kDataSegmentOutOfBounds.
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
GenerateCCall(&result, &sig, kVoid, args,
ExternalReference::wasm_memory_init());
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
}
......@@ -4898,10 +4933,14 @@ class LiftoffCompiler {
void MemoryCopy(FullDecoder* decoder,
const MemoryCopyImmediate<validate>& imm, const Value&,
const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned;
LiftoffRegister size = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister src = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister dst = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister size = pinned.set(
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
LiftoffRegister src = pinned.set(
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
LiftoffRegister dst = pinned.set(
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) {
......@@ -4909,26 +4948,36 @@ class LiftoffCompiler {
__ LoadInstanceFromFrame(instance);
}
ExternalReference ext_ref = ExternalReference::wasm_memory_copy();
// Only allocate the OOB code now, so the state of the stack is reflected
// correctly.
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
if (mem_offsets_high_word != no_reg) {
// If any high word has bits set, jump to the OOB trap.
__ emit_cond_jump(kNotEqualZero, trap_label, kI32, mem_offsets_high_word);
}
auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind,
kPointerKind, kPointerKind);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, src, size};
// We don't need the instance anymore after the call. We can use the
// register for the result.
LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref);
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
GenerateCCall(&result, &sig, kVoid, args,
ExternalReference::wasm_memory_copy());
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
}
void MemoryFill(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm, const Value&,
const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned;
LiftoffRegister size = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister size = pinned.set(
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
LiftoffRegister value = pinned.set(__ PopToRegister(pinned));
LiftoffRegister dst = pinned.set(PopMemTypeToRegister(pinned));
LiftoffRegister dst = pinned.set(
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) {
......@@ -4936,16 +4985,23 @@ class LiftoffCompiler {
__ LoadInstanceFromFrame(instance);
}
ExternalReference ext_ref = ExternalReference::wasm_memory_fill();
// Only allocate the OOB code now, so the state of the stack is reflected
// correctly.
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
if (mem_offsets_high_word != no_reg) {
// If any high word has bits set, jump to the OOB trap.
__ emit_cond_jump(kNotEqualZero, trap_label, kI32, mem_offsets_high_word);
}
auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind, kI32,
kPointerKind);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, value, size};
// We don't need the instance anymore after the call. We can use the
// register for the result.
LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref);
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
GenerateCCall(&result, &sig, kVoid, args,
ExternalReference::wasm_memory_fill());
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment