LibWasm: Store try_table catches out of line

TryTableArgs only needs catches for try_table instructions. Reuse the
structured-instruction layout with catch storage in place of the else
target, and keep the catch elements in a FixedArray.

This avoids storing a full Vector inline in every Instruction variant
alternative and keeps the catch storage copyable for the variant.
This commit is contained in:
Andreas Kling 2026-05-14 10:35:51 +02:00 committed by Andreas Kling
parent d893700808
commit f374e72c54
6 changed files with 89 additions and 32 deletions

View file

@ -2099,8 +2099,8 @@ HANDLE_INSTRUCTION(if_)
auto value = configuration.take_source<source_address_mix>(0, addresses.sources).template to<i32>();
auto end_label = Label(meta.arity, args.end_ip.value(), configuration.value_stack().size() - meta.parameter_count);
if (value == 0) {
if (args.else_ip.has_value()) {
short_ip.current_ip_value = args.else_ip->value() - 1;
if (args.else_ip().has_value()) {
short_ip.current_ip_value = args.else_ip()->value() - 1;
configuration.label_stack().unchecked_append(end_label);
} else {
short_ip.current_ip_value = args.end_ip.value();
@ -6303,15 +6303,10 @@ CompiledInstructions try_compile_instructions(Expression const& expression, Span
return offset;
};
InstructionPointer end_ip = ptr->end_ip.value() - offset_accumulated - offset_to(ptr->end_ip - ptr->else_ip.has_value());
auto else_ip = ptr->else_ip.map([&](InstructionPointer const& ip) -> InstructionPointer { return ip.value() - offset_accumulated - offset_to(ip - 1); });
InstructionPointer end_ip = ptr->end_ip.value() - offset_accumulated - offset_to(ptr->end_ip - ptr->else_ip().has_value());
auto else_ip = ptr->else_ip().map([&](InstructionPointer const& ip) -> InstructionPointer { return ip.value() - offset_accumulated - offset_to(ip - 1); });
auto instruction = *result.dispatches[i].instruction;
instruction.arguments() = Instruction::StructuredInstructionArgs {
.block_type = ptr->block_type,
.end_ip = end_ip,
.else_ip = else_ip,
.meta = ptr->meta,
};
instruction.arguments() = Instruction::StructuredInstructionArgs { ptr->block_type, end_ip, else_ip, ptr->meta };
auto& extra_instruction = append_extra_instruction(move(instruction));
result.dispatches[i].instruction = &extra_instruction;
result.dispatches[i].instruction_opcode = result.dispatches[i].instruction->opcode();
@ -7132,8 +7127,8 @@ CompiledInstructions try_compile_instructions(Expression const& expression, Span
if (dispatch.instruction->opcode() == Instructions::if_) {
// if (else) (end), verify (else) - 1 points at a synthetic:else_, and (end)-1+(!has-else) points at a synthetic:end.
auto args = dispatch.instruction->arguments().get<Instruction::StructuredInstructionArgs>();
if (args.else_ip.has_value()) {
size_t else_ip = args.else_ip->value() - 1;
if (args.else_ip().has_value()) {
size_t else_ip = args.else_ip()->value() - 1;
if (result.dispatches[else_ip].instruction->opcode() != Instructions::structured_else) {
dbgln("Invalid else_ip target at instruction {}: else_ip {}", i, else_ip);
dbgln("Instructions around the invalid else_ip:");
@ -7141,7 +7136,7 @@ CompiledInstructions try_compile_instructions(Expression const& expression, Span
VERIFY_NOT_REACHED();
}
}
size_t end_ip = args.end_ip.value() - 1 + (args.else_ip.has_value() ? 0 : 1);
size_t end_ip = args.end_ip.value() - 1 + (args.else_ip().has_value() ? 0 : 1);
if (result.dispatches[end_ip].instruction->opcode() != Instructions::structured_end) {
dbgln("Invalid end_ip target at instruction {}: end_ip {}", i, end_ip);
dbgln("Instructions around the invalid end_ip:");

View file

@ -2296,13 +2296,13 @@ VALIDATE_INSTRUCTION(throw_ref)
VALIDATE_INSTRUCTION(try_table)
{
auto& args = instruction.arguments().get<Instruction::TryTableArgs>();
auto block_type = TRY(validate(args.try_.block_type));
auto block_type = TRY(validate(args.block_type));
auto& parameters = block_type.parameters();
for (size_t i = 1; i <= parameters.size(); ++i)
TRY(stack.take(parameters[parameters.size() - i]));
args.try_.meta = Instruction::StructuredInstructionArgs::Meta {
args.meta = Instruction::TryTableArgs::Meta {
.arity = static_cast<u32>(block_type.results().size()),
.parameter_count = static_cast<u32>(parameters.size()),
};
@ -2312,7 +2312,7 @@ VALIDATE_INSTRUCTION(try_table)
for (auto& parameter : parameters)
stack.append(parameter);
for (auto& catch_ : args.catches) {
for (auto& catch_ : args.catches()) {
auto label = catch_.target_label();
TRY(validate(label));
auto& target_label_type = m_frames[(m_frames.size() - 1) - label.value()].labels();

View file

@ -766,8 +766,8 @@ static CraneliftInsn serialize_insn(Dispatch const& dispatch, SourcesAndDestinat
} else if (opc == Instructions::block.value() || opc == Instructions::loop.value() || opc == Instructions::if_.value()) {
auto const& struct_args = args.get<Instruction::StructuredInstructionArgs>();
out.imm1 = static_cast<i64>(struct_args.end_ip.value());
out.imm2 = struct_args.else_ip.has_value()
? static_cast<i64>(struct_args.else_ip->value())
out.imm2 = struct_args.else_ip().has_value()
? static_cast<i64>(struct_args.else_ip()->value())
: -1;
u32 arity = struct_args.meta.arity;
u32 param_count = struct_args.meta.parameter_count;

View file

@ -348,9 +348,8 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
// try_table block_type (catch*) (instruction*) end
auto block_type = TRY(BlockType::parse(stream));
auto catch_types = TRY(parse_vector<Catch>(stream));
auto structured_args = StructuredInstructionArgs { block_type, {}, {} };
return Instruction {
opcode, TryTableArgs { move(structured_args), move(catch_types) }
opcode, TryTableArgs { block_type, {}, catch_types.span() }
};
}
case Instructions::throw_.value(): {
@ -1160,11 +1159,11 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
auto entry = stack.take_last();
bool valid_type = instructions[entry.value()].arguments().visit(
[&](Instruction::StructuredInstructionArgs& args) {
args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0);
args.end_ip = ip + (args.else_ip().has_value() ? 1 : 0);
return true;
},
[&](Instruction::TryTableArgs& args) {
args.try_.end_ip = ip + 1;
args.end_ip = ip + 1;
return true;
},
[](auto&) { return false; });
@ -1182,7 +1181,7 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
if (!args)
return ParseError::InvalidType;
args->else_ip = ip + 1;
args->else_ip() = ip + 1;
break;
}
}

View file

@ -510,7 +510,7 @@ void Printer::print(Wasm::Instruction const& instruction)
TemporaryChange change { m_indent, m_indent + 1 };
print(args.block_type);
print_indent();
print("(else {}) (end {})", args.else_ip.has_value() ? ByteString::number(args.else_ip->value()) : "(none)", args.end_ip.value());
print("(else {}) (end {})", args.else_ip().has_value() ? ByteString::number(args.else_ip()->value()) : "(none)", args.end_ip.value());
if (args.meta.arity != 0 || args.meta.parameter_count != 0)
print(" (meta arity {} params {})", args.meta.arity, args.meta.parameter_count);
else
@ -519,13 +519,13 @@ void Printer::print(Wasm::Instruction const& instruction)
},
[&](Instruction::TryTableArgs const& args) {
print("(try_table ");
print(args.try_.block_type);
print(args.block_type);
print(" (catches\n");
TemporaryChange change { m_indent, m_indent + 1 };
for (auto& catch_ : args.catches)
for (auto& catch_ : args.catches())
print(catch_);
print_indent();
print(") (end {}))", args.try_.end_ip.value());
print(") (end {}))", args.end_ip.value());
},
[&](Instruction::TableBranchArgs const& args) {
print("(table_branch");

View file

@ -13,6 +13,7 @@
#include <AK/LEB128.h>
#include <AK/NumericLimits.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <AK/Result.h>
#include <AK/String.h>
#include <AK/UFixedBigInt.h>
@ -572,10 +573,13 @@ public:
TableIndex rhs;
};
struct StructuredInstructionArgs {
template<typename ExtraData>
struct StructuredInstructionArgsBase {
using Extra = ExtraData;
BlockType block_type;
InstructionPointer end_ip; // 'end' instruction IP if there is no 'else'; otherwise IP of instruction after 'end'.
Optional<InstructionPointer> else_ip;
ExtraData extra;
struct Meta {
u32 arity;
@ -584,6 +588,19 @@ public:
mutable Meta meta {};
};
struct StructuredInstructionArgs : StructuredInstructionArgsBase<Optional<InstructionPointer>> {
using Base = StructuredInstructionArgsBase<Optional<InstructionPointer>>;
using Meta = typename Base::Meta;
StructuredInstructionArgs(BlockType block_type, InstructionPointer end_ip, Optional<InstructionPointer> else_ip, Meta meta = {})
: Base { block_type, end_ip, else_ip, meta }
{
}
auto& else_ip() { return extra; }
auto& else_ip() const { return extra; }
};
struct TableBranchArgs {
Vector<LabelIndex> labels;
LabelIndex default_;
@ -630,9 +647,55 @@ public:
};
// Proposal "exception-handling"
struct TryTableArgs {
StructuredInstructionArgs try_; // "else" unused.
Vector<Catch> catches;
struct TryTableArgs : StructuredInstructionArgsBase<OwnPtr<FixedArray<Catch>>> {
using Base = StructuredInstructionArgsBase<OwnPtr<FixedArray<Catch>>>;
using Meta = typename Base::Meta;
TryTableArgs(BlockType block_type, InstructionPointer end_ip, ReadonlySpan<Catch> catches, Meta meta = {})
: Base { block_type, end_ip, create_catches(catches), meta }
{
}
TryTableArgs(TryTableArgs const& other)
: Base { other.block_type, other.end_ip, clone_catches(other.extra), other.meta }
{
}
TryTableArgs& operator=(TryTableArgs const& other)
{
if (this == &other)
return *this;
block_type = other.block_type;
end_ip = other.end_ip;
extra = clone_catches(other.extra);
meta = other.meta;
return *this;
}
TryTableArgs(TryTableArgs&&) = default;
TryTableArgs& operator=(TryTableArgs&&) = default;
ReadonlySpan<Catch> catches() const
{
if (!extra)
return {};
return extra->span();
}
private:
static OwnPtr<FixedArray<Catch>> create_catches(ReadonlySpan<Catch> catches)
{
if (catches.is_empty())
return nullptr;
return make<FixedArray<Catch>>(MUST(FixedArray<Catch>::create(catches)));
}
static OwnPtr<FixedArray<Catch>> clone_catches(OwnPtr<FixedArray<Catch>> const& catches)
{
if (!catches)
return nullptr;
return make<FixedArray<Catch>>(MUST(catches->clone()));
}
};
struct ShuffleArgument {