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 { ...@@ -4822,45 +4822,82 @@ class LiftoffCompiler {
void AtomicFence(FullDecoder* decoder) { __ AtomicFence(); } void AtomicFence(FullDecoder* decoder) { __ AtomicFence(); }
// Pop a memtype (i32 or i64 depending on {WasmModule::is_memory64}) to a // Pop a memtype (i32 or i64 depending on {WasmModule::is_memory64}) to a
// register. Returns the ptrsized register holding the popped value. // register, updating {*high_word} to contain the ORed combination of all
LiftoffRegister PopMemTypeToRegister(LiftoffRegList pinned) { // popped high words. Returns the ptrsized register holding the popped value.
LiftoffRegister reg = __ PopToRegister(pinned); LiftoffRegister PopMemTypeToRegister(FullDecoder* decoder,
// On 64-bit hosts, potentially zero-extend, then return. Register* high_word,
if (kSystemPointerSize == kInt64Size) { LiftoffRegList* pinned) {
if (!env_->module->is_memory64) { LiftoffRegister reg = __ PopToRegister(*pinned);
__ emit_u32_to_uintptr(reg.gp(), reg.gp()); 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(); return reg.low();
} }
void MemoryInit(FullDecoder* decoder, void MemoryInit(FullDecoder* decoder,
const MemoryInitImmediate<validate>& imm, const Value&, const MemoryInitImmediate<validate>& imm, const Value&,
const Value&, const Value&) { const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned; LiftoffRegList pinned;
LiftoffRegister size = pinned.set(__ PopToRegister()); LiftoffRegister size = pinned.set(__ PopToRegister());
LiftoffRegister src = pinned.set(__ PopToRegister(pinned)); LiftoffRegister src = pinned.set(__ PopToRegister(pinned));
LiftoffRegister dst = pinned.set(PopMemTypeToRegister(pinned)); LiftoffRegister dst =
PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned);
LiftoffRegister segment_index =
pinned.set(__ GetUnusedRegister(kGpReg, pinned));
__ LoadConstant(segment_index, WasmValue(imm.data_segment.index));
Register instance = __ cache_state()->cached_instance; Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) { if (instance == no_reg) {
instance = __ GetUnusedRegister(kGpReg, pinned).gp(); instance = __ GetUnusedRegister(kGpReg, pinned).gp();
__ LoadInstanceFromFrame(instance); __ 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, auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind, kI32,
kI32, kI32); kI32, kI32);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, src, LiftoffRegister args[] = {LiftoffRegister(instance), dst, src,
...@@ -4868,10 +4905,8 @@ class LiftoffCompiler { ...@@ -4868,10 +4905,8 @@ class LiftoffCompiler {
// We don't need the instance anymore after the call. We can use the // We don't need the instance anymore after the call. We can use the
// register for the result. // register for the result.
LiftoffRegister result(instance); LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref); GenerateCCall(&result, &sig, kVoid, args,
// TODO(manoskouk): Also throw kDataSegmentOutOfBounds. ExternalReference::wasm_memory_init());
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp()); __ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
} }
...@@ -4898,10 +4933,14 @@ class LiftoffCompiler { ...@@ -4898,10 +4933,14 @@ class LiftoffCompiler {
void MemoryCopy(FullDecoder* decoder, void MemoryCopy(FullDecoder* decoder,
const MemoryCopyImmediate<validate>& imm, const Value&, const MemoryCopyImmediate<validate>& imm, const Value&,
const Value&, const Value&) { const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned; LiftoffRegList pinned;
LiftoffRegister size = pinned.set(PopMemTypeToRegister(pinned)); LiftoffRegister size = pinned.set(
LiftoffRegister src = pinned.set(PopMemTypeToRegister(pinned)); PopMemTypeToRegister(decoder, &mem_offsets_high_word, &pinned));
LiftoffRegister dst = pinned.set(PopMemTypeToRegister(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; Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) { if (instance == no_reg) {
...@@ -4909,26 +4948,36 @@ class LiftoffCompiler { ...@@ -4909,26 +4948,36 @@ class LiftoffCompiler {
__ LoadInstanceFromFrame(instance); __ 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, auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind,
kPointerKind, kPointerKind); kPointerKind, kPointerKind);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, src, size}; LiftoffRegister args[] = {LiftoffRegister(instance), dst, src, size};
// We don't need the instance anymore after the call. We can use the // We don't need the instance anymore after the call. We can use the
// register for the result. // register for the result.
LiftoffRegister result(instance); LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref); GenerateCCall(&result, &sig, kVoid, args,
Label* trap_label = ExternalReference::wasm_memory_copy());
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp()); __ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
} }
void MemoryFill(FullDecoder* decoder, void MemoryFill(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm, const Value&, const MemoryIndexImmediate<validate>& imm, const Value&,
const Value&, const Value&) { const Value&, const Value&) {
Register mem_offsets_high_word = no_reg;
LiftoffRegList pinned; 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 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; Register instance = __ cache_state()->cached_instance;
if (instance == no_reg) { if (instance == no_reg) {
...@@ -4936,16 +4985,23 @@ class LiftoffCompiler { ...@@ -4936,16 +4985,23 @@ class LiftoffCompiler {
__ LoadInstanceFromFrame(instance); __ 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, auto sig = MakeSig::Returns(kI32).Params(kPointerKind, kPointerKind, kI32,
kPointerKind); kPointerKind);
LiftoffRegister args[] = {LiftoffRegister(instance), dst, value, size}; LiftoffRegister args[] = {LiftoffRegister(instance), dst, value, size};
// We don't need the instance anymore after the call. We can use the // We don't need the instance anymore after the call. We can use the
// register for the result. // register for the result.
LiftoffRegister result(instance); LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref); GenerateCCall(&result, &sig, kVoid, args,
Label* trap_label = ExternalReference::wasm_memory_fill());
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp()); __ 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