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:
parent
d893700808
commit
f374e72c54
6 changed files with 89 additions and 32 deletions
|
|
@ -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:");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue