From 0e6943b17d053b263522a4589c2750ca67757122 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Fri, 23 Jan 2026 12:44:40 +0100 Subject: [PATCH] LibWasm: Avoid unnecessary bounds checks in memory ops --- .../AbstractMachine/BytecodeInterpreter.cpp | 54 ++++++++++--------- Utilities/wasm.cpp | 1 + 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index 3664309054..176d612745 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -1997,7 +1997,7 @@ HANDLE_INSTRUCTION(global_set) HANDLE_INSTRUCTION(memory_size) { LOG_INSN; - auto& args = instruction->arguments().get(); + auto& args = instruction->arguments().unsafe_get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); auto pages = instance->size() / Constants::page_size; @@ -2009,7 +2009,7 @@ HANDLE_INSTRUCTION(memory_size) HANDLE_INSTRUCTION(memory_grow) { LOG_INSN; - auto& args = instruction->arguments().get(); + auto& args = instruction->arguments().unsafe_get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); i32 old_pages = instance->size() / Constants::page_size; @@ -2027,7 +2027,7 @@ HANDLE_INSTRUCTION(memory_fill) { LOG_INSN; { - auto& args = instruction->arguments().get(); + auto& args = instruction->arguments().unsafe_get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); // bounds checked by verifier. @@ -2054,7 +2054,7 @@ HANDLE_INSTRUCTION(memory_fill) HANDLE_INSTRUCTION(memory_copy) { LOG_INSN; - auto& args = instruction->arguments().get(); + auto& args = instruction->arguments().unsafe_get(); auto source_address = configuration.frame().module().memories().data()[args.src_index.value()]; auto destination_address = configuration.frame().module().memories().data()[args.dst_index.value()]; auto source_instance = configuration.store().get(source_address); @@ -2095,7 +2095,7 @@ HANDLE_INSTRUCTION(memory_copy) HANDLE_INSTRUCTION(memory_init) { LOG_INSN; - auto& args = instruction->arguments().get(); + auto& args = instruction->arguments().unsafe_get(); auto& data_address = configuration.frame().module().datas()[args.data_index.value()]; auto& data = *configuration.store().get(data_address); auto memory_address = configuration.frame().module().memories().data()[args.memory_index.value()]; @@ -4526,31 +4526,35 @@ FLATTEN void BytecodeInterpreter::interpret_impl(Configuration& configuration, E } } +template InstructionPointer BytecodeInterpreter::branch_to_label(Configuration& configuration, LabelIndex index) { dbgln_if(WASM_TRACE_DEBUG, "Branch to label with index {}...", index.value()); auto& label_stack = configuration.label_stack(); label_stack.shrink(label_stack.size() - index.value(), true); - auto label = configuration.label_stack().last(); + auto const& label = configuration.label_stack().last(); dbgln_if(WASM_TRACE_DEBUG, "...which is actually IP {}, and has {} result(s)", label.continuation().value(), label.arity()); - configuration.value_stack().remove(label.stack_height(), configuration.value_stack().size() - label.stack_height() - label.arity()); + if constexpr (NeedsStackAdjustment) + configuration.value_stack().remove(label.stack_height(), configuration.value_stack().size() - label.stack_height() - label.arity()); + else + VERIFY(configuration.value_stack().size() - label.stack_height() - label.arity() == 0); return label.continuation().value() - 1; } template bool BytecodeInterpreter::load_and_push(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto& arg = instruction.arguments().get(); - auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; - auto memory = configuration.store().get(address); + auto& arg = instruction.arguments().unsafe_get(); + auto& address = configuration.frame().module().memories().data()[arg.memory_index.value()]; + auto memory = configuration.store().unsafe_get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.template to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; dbgln_if(WASM_TRACE_DEBUG, "load({} : {}) -> stack", instance_address, sizeof(ReadType)); if (instance_address + sizeof(ReadType) > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); - dbgln("LibWasm: load_and_push - Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + sizeof(ReadType), memory->size()); + dbgln_if(WASM_TRACE_DEBUG, "LibWasm: load_and_push - Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + sizeof(ReadType), memory->size()); return true; } auto slice = memory->data().bytes().slice(instance_address, sizeof(ReadType)); @@ -4568,9 +4572,9 @@ ALWAYS_INLINE static TDst convert_vector(TSrc v) template typename SetSign> bool BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto& arg = instruction.arguments().get(); - auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; - auto memory = configuration.store().get(address); + auto& arg = instruction.arguments().unsafe_get(); + auto& address = configuration.frame().module().memories().data()[arg.memory_index.value()]; + auto memory = configuration.store().unsafe_get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.template to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; @@ -4598,9 +4602,9 @@ bool BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru template bool BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto memarg_and_lane = instruction.arguments().get(); - auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()]; - auto memory = configuration.store().get(address); + auto memarg_and_lane = instruction.arguments().unsafe_get(); + auto& address = configuration.frame().module().memories().data()[memarg_and_lane.memory.memory_index.value()]; + auto memory = configuration.store().unsafe_get(address); // bounds checked by verifier. auto vector = configuration.take_source(0, addresses.sources).template to(); auto base = configuration.take_source(1, addresses.sources).template to(); @@ -4622,9 +4626,9 @@ bool BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins template bool BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto memarg_and_lane = instruction.arguments().get(); - auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()]; - auto memory = configuration.store().get(address); + auto memarg_and_lane = instruction.arguments().unsafe_get(); + auto& address = configuration.frame().module().memories().data()[memarg_and_lane.memory_index.value()]; + auto memory = configuration.store().unsafe_get(address); // bounds checked by verifier. auto base = configuration.take_source(0, addresses.sources).template to(); u64 instance_address = static_cast(bit_cast(base)) + memarg_and_lane.offset; @@ -4645,9 +4649,9 @@ bool BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins template bool BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto& arg = instruction.arguments().get(); - auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; - auto memory = configuration.store().get(address); + auto& arg = instruction.arguments().unsafe_get(); + auto& address = configuration.frame().module().memories().data()[arg.memory_index.value()]; + auto memory = configuration.store().unsafe_get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.template to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; @@ -4858,7 +4862,7 @@ bool BytecodeInterpreter::store_value(Configuration& configuration, Instruction template bool BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { - auto& memarg_and_lane = instruction.arguments().get(); + auto& memarg_and_lane = instruction.arguments().unsafe_get(); // bounds checked by verifier. auto vector = configuration.take_source(0, addresses.sources).template to(); auto src = bit_cast(&vector) + memarg_and_lane.lane * N / 8; @@ -4869,7 +4873,7 @@ bool BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Ins bool BytecodeInterpreter::store_to_memory(Configuration& configuration, Instruction::MemoryArgument const& arg, ReadonlyBytes data, u32 base) { auto const& address = configuration.frame().module().memories().data()[arg.memory_index.value()]; - auto memory = configuration.store().get(address); + auto memory = configuration.store().unsafe_get(address); u64 instance_address = static_cast(base) + arg.offset; return store_to_memory(*memory, instance_address, data); } diff --git a/Utilities/wasm.cpp b/Utilities/wasm.cpp index 4043732301..2f2dc43797 100644 --- a/Utilities/wasm.cpp +++ b/Utilities/wasm.cpp @@ -740,6 +740,7 @@ ErrorOr ladybird_main(Main::Arguments arguments) } TRY(g_stdout->write_until_depleted(ByteString::formatted("Function #{}{} (stack usage = {}):\n", address.value(), export_name, expression.stack_usage_hint()))); + Wasm::Printer printer { *g_stdout, 1 }; for (size_t ip = 0; ip < expression.compiled_instructions.dispatches.size(); ++ip) { auto& dispatch = expression.compiled_instructions.dispatches[ip];