[wasm] Provide a signature for every block

This removes a few dynamic checks whether a block type immediate is
using the MVP syntax with 0-1 return types, or has a full signature (for
multi-return).
We now always provide a signature, using another field in the
{BlockTypeImmediate} as storage for MVP types.

This makes {BlockTypeImmediate} slightly bigger, which should not be a
problem as there is always only one of them alive, and it's stored on
the stack.

R=jkummerow@chromium.org

Change-Id: Ie36b73f7213826f32cd349c33f23d834c9de0a50
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3991249
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84168}
This commit is contained in:
Clemens Backes 2022-11-08 18:11:14 +01:00 committed by V8 LUCI CQ
parent d7f28a4369
commit 6f5f7b807b
4 changed files with 70 additions and 78 deletions

View File

@ -561,9 +561,19 @@ struct SelectTypeImmediate {
struct BlockTypeImmediate {
uint32_t length = 1;
ValueType type = kWasmVoid;
uint32_t sig_index = 0;
const FunctionSig* sig = nullptr;
// After decoding, either {sig_index} is set XOR {sig} points to
// {single_return_sig_storage}.
uint32_t sig_index;
FunctionSig sig{0, 0, single_return_sig_storage};
// Internal field, potentially pointed to by {sig}. Do not access directly.
ValueType single_return_sig_storage[1];
// Do not copy or move, as {sig} might point to {single_return_sig_storage} so
// this cannot trivially be copied. If needed, define those operators later.
BlockTypeImmediate(const BlockTypeImmediate&) = delete;
BlockTypeImmediate(BlockTypeImmediate&&) = delete;
BlockTypeImmediate& operator=(const BlockTypeImmediate&) = delete;
BlockTypeImmediate& operator=(BlockTypeImmediate&&) = delete;
template <typename ValidationTag>
BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
@ -579,34 +589,26 @@ struct BlockTypeImmediate {
block_type);
return;
}
if (static_cast<ValueTypeCode>(block_type & 0x7F) == kVoidCode) return;
type = value_type_reader::read_value_type<ValidationTag>(
decoder, pc, &length, enabled);
if (static_cast<ValueTypeCode>(block_type & 0x7F) != kVoidCode) {
sig = FunctionSig{1, 0, single_return_sig_storage};
single_return_sig_storage[0] =
value_type_reader::read_value_type<ValidationTag>(decoder, pc,
&length, enabled);
}
} else {
type = kWasmBottom;
sig = FunctionSig{0, 0, nullptr};
sig_index = static_cast<uint32_t>(block_type);
}
}
uint32_t in_arity() const {
if (type != kWasmBottom) return 0;
return static_cast<uint32_t>(sig->parameter_count());
return static_cast<uint32_t>(sig.parameter_count());
}
uint32_t out_arity() const {
if (type == kWasmVoid) return 0;
if (type != kWasmBottom) return 1;
return static_cast<uint32_t>(sig->return_count());
}
ValueType in_type(uint32_t index) {
DCHECK_EQ(kWasmBottom, type);
return sig->GetParam(index);
}
ValueType out_type(uint32_t index) {
if (type == kWasmBottom) return sig->GetReturn(index);
DCHECK_NE(kWasmVoid, type);
DCHECK_EQ(0, index);
return type;
return static_cast<uint32_t>(sig.return_count());
}
ValueType in_type(uint32_t index) { return sig.GetParam(index); }
ValueType out_type(uint32_t index) { return sig.GetReturn(index); }
};
struct BranchDepthImmediate {
@ -1653,14 +1655,21 @@ class WasmDecoder : public Decoder {
}
bool Validate(const byte* pc, BlockTypeImmediate& imm) {
if (!ValidateValueType(pc, imm.type)) return false;
if (imm.type == kWasmBottom) {
if (imm.sig.all().begin() == nullptr) {
// Then use {sig_index} to initialize the signature.
if (!VALIDATE(module_->has_signature(imm.sig_index))) {
DecodeError(pc, "block type index %u is not a signature definition",
imm.sig_index);
return false;
}
imm.sig = module_->signature(imm.sig_index);
imm.sig = *module_->signature(imm.sig_index);
} else {
// Then it's an MVP immediate with 0 parameters and 0-1 returns.
DCHECK_EQ(0, imm.sig.parameter_count());
DCHECK_GE(1, imm.sig.return_count());
if (imm.sig.return_count()) {
if (!ValidateValueType(pc, imm.sig.GetReturn(0))) return false;
}
}
return true;
}
@ -3000,11 +3009,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
DECODE(Block) {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
ArgVector args = PeekArgs(imm.sig);
ArgVector args = PeekArgs(&imm.sig);
Control* block = PushControl(kControlBlock, args.length());
SetBlockType(block, imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(Block, block);
DropArgs(imm.sig);
DropArgs(&imm.sig);
PushMergeValues(block, &block->start_merge);
return 1 + imm.length;
}
@ -3038,13 +3047,13 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
this->detected_->Add(kFeature_eh);
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
ArgVector args = PeekArgs(imm.sig);
ArgVector args = PeekArgs(&imm.sig);
Control* try_block = PushControl(kControlTry, args.length());
SetBlockType(try_block, imm, args.begin());
try_block->previous_catch = current_catch_;
current_catch_ = static_cast<int>(control_depth() - 1);
CALL_INTERFACE_IF_OK_AND_REACHABLE(Try, try_block);
DropArgs(imm.sig);
DropArgs(&imm.sig);
PushMergeValues(try_block, &try_block->start_merge);
return 1 + imm.length;
}
@ -3225,11 +3234,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
DECODE(Loop) {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
ArgVector args = PeekArgs(imm.sig);
ArgVector args = PeekArgs(&imm.sig);
Control* block = PushControl(kControlLoop, args.length());
SetBlockType(&control_.back(), imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(Loop, block);
DropArgs(imm.sig);
DropArgs(&imm.sig);
PushMergeValues(block, &block->start_merge);
return 1 + imm.length;
}
@ -3238,13 +3247,13 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
Value cond = Peek(0, 0, kWasmI32);
ArgVector args = PeekArgs(imm.sig, 1);
ArgVector args = PeekArgs(&imm.sig, 1);
if (!VALIDATE(this->ok())) return 0;
Control* if_block = PushControl(kControlIf, 1 + args.length());
SetBlockType(if_block, imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(If, cond, if_block);
Drop(cond);
DropArgs(imm.sig); // Drop {args}.
DropArgs(&imm.sig);
PushMergeValues(if_block, &if_block->start_merge);
return 1 + imm.length;
}
@ -4033,7 +4042,7 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
// Peeks arguments as required by signature.
V8_INLINE ArgVector PeekArgs(const FunctionSig* sig, int depth = 0) {
int count = sig ? static_cast<int>(sig->parameter_count()) : 0;
int count = static_cast<int>(sig->parameter_count());
if (count == 0) return {};
EnsureStackArguments(depth + count);
ArgVector args(stack_value(depth + count), count);
@ -4045,7 +4054,7 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
// Drops a number of stack elements equal to the {sig}'s parameter count (0 if
// {sig} is null), or all of them if less are present.
V8_INLINE void DropArgs(const FunctionSig* sig) {
int count = sig ? static_cast<int>(sig->parameter_count()) : 0;
int count = static_cast<int>(sig->parameter_count());
Drop(count);
}

View File

@ -12,21 +12,11 @@ namespace wasm {
base::Optional<wasm::ValueKind> WasmReturnTypeFromSignature(
const FunctionSig* wasm_signature) {
if (wasm_signature->return_count() == 0) {
return {};
} else {
DCHECK_EQ(wasm_signature->return_count(), 1);
ValueType return_type = wasm_signature->GetReturn(0);
switch (return_type.kind()) {
case kI32:
case kI64:
case kF32:
case kF64:
return {return_type.kind()};
default:
UNREACHABLE();
}
}
if (wasm_signature->return_count() == 0) return {};
DCHECK_EQ(wasm_signature->return_count(), 1);
ValueType return_type = wasm_signature->GetReturn(0);
return {return_type.kind()};
}
#if DEBUG

View File

@ -310,16 +310,7 @@ class ImmediatesPrinter {
}
void BlockType(BlockTypeImmediate& imm) {
if (imm.type == kWasmBottom) {
const FunctionSig* sig = owner_->module_->signature(imm.sig_index);
PrintSignatureOneLine(out_, sig, 0 /* ignored */, names(), false);
} else if (imm.type == kWasmVoid) {
// Just be silent.
} else {
out_ << " (result ";
names()->PrintValueType(out_, imm.type);
out_ << ")";
}
PrintSignatureOneLine(out_, &imm.sig, 0 /* ignored */, names(), false);
}
void HeapType(HeapTypeImmediate& imm) {

View File

@ -751,6 +751,14 @@ class SideTable : public ZoneObject {
max_exception_arity, static_cast<int>(tag.sig->parameter_count()));
}
}
WasmFeatures unused_detected_features;
WasmDecoder<Decoder::NoValidationTag> decoder{zone,
module,
WasmFeatures::All(),
&unused_detected_features,
code->function->sig,
code->start,
code->end};
for (BytecodeIterator i(code->start, code->end, &code->locals, zone);
i.has_next(); i.next()) {
WasmOpcode opcode = i.current();
@ -794,11 +802,9 @@ class SideTable : public ZoneObject {
case kExprBlock:
case kExprLoop: {
bool is_loop = opcode == kExprLoop;
BlockTypeImmediate imm(WasmFeatures::All(), &i, i.pc() + 1,
BlockTypeImmediate imm(WasmFeatures::All(), &decoder, i.pc() + 1,
kNoValidate);
if (imm.type == kWasmBottom) {
imm.sig = module->signature(imm.sig_index);
}
CHECK(decoder.Validate(i.pc() + 1, imm));
TRACE("control @%u: %s, arity %d->%d\n", i.pc_offset(),
is_loop ? "Loop" : "Block", imm.in_arity(), imm.out_arity());
DCHECK_IMPLIES(!unreachable,
@ -816,11 +822,9 @@ class SideTable : public ZoneObject {
break;
}
case kExprIf: {
BlockTypeImmediate imm(WasmFeatures::All(), &i, i.pc() + 1,
BlockTypeImmediate imm(WasmFeatures::All(), &decoder, i.pc() + 1,
kNoValidate);
if (imm.type == kWasmBottom) {
imm.sig = module->signature(imm.sig_index);
}
CHECK(decoder.Validate(i.pc() + 1, imm));
TRACE("control @%u: If, arity %d->%d\n", i.pc_offset(),
imm.in_arity(), imm.out_arity());
DCHECK_IMPLIES(!unreachable,
@ -877,11 +881,9 @@ class SideTable : public ZoneObject {
break;
}
case kExprTry: {
BlockTypeImmediate imm(WasmFeatures::All(), &i, i.pc() + 1,
BlockTypeImmediate imm(WasmFeatures::All(), &decoder, i.pc() + 1,
kNoValidate);
if (imm.type == kWasmBottom) {
imm.sig = module->signature(imm.sig_index);
}
CHECK(decoder.Validate(i.pc() + 1, imm));
TRACE("control @%u: Try, arity %d->%d\n", i.pc_offset(),
imm.in_arity(), imm.out_arity());
int target_stack_height = stack_height - imm.in_arity();
@ -901,7 +903,7 @@ class SideTable : public ZoneObject {
break;
}
case kExprRethrow: {
BranchDepthImmediate imm(&i, i.pc() + 1, kNoValidate);
BranchDepthImmediate imm(&decoder, i.pc() + 1, kNoValidate);
int index = static_cast<int>(control_stack.size()) - 1 - imm.depth;
rethrow_map_.emplace(i.pc() - i.start(), index);
break;
@ -912,7 +914,7 @@ class SideTable : public ZoneObject {
// Only pop the exception stack once when we enter the first catch.
exception_stack.pop_back();
}
TagIndexImmediate imm(&i, i.pc() + 1, kNoValidate);
TagIndexImmediate imm(&decoder, i.pc() + 1, kNoValidate);
Control* c = &control_stack.back();
copy_unreachable();
TRACE("control @%u: Catch\n", i.pc_offset());
@ -980,7 +982,7 @@ class SideTable : public ZoneObject {
break;
}
case kExprDelegate: {
BranchDepthImmediate imm(&i, i.pc() + 1, kNoValidate);
BranchDepthImmediate imm(&decoder, i.pc() + 1, kNoValidate);
TRACE("control @%u: Delegate[depth=%u]\n", i.pc_offset(), imm.depth);
Control* c = &control_stack.back();
const size_t new_stack_size = control_stack.size() - 1;
@ -1018,21 +1020,21 @@ class SideTable : public ZoneObject {
break;
}
case kExprBr: {
BranchDepthImmediate imm(&i, i.pc() + 1, kNoValidate);
BranchDepthImmediate imm(&decoder, i.pc() + 1, kNoValidate);
TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), imm.depth);
Control* c = &control_stack[control_stack.size() - imm.depth - 1];
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break;
}
case kExprBrIf: {
BranchDepthImmediate imm(&i, i.pc() + 1, kNoValidate);
BranchDepthImmediate imm(&decoder, i.pc() + 1, kNoValidate);
TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), imm.depth);
Control* c = &control_stack[control_stack.size() - imm.depth - 1];
if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break;
}
case kExprBrTable: {
BranchTableImmediate imm(&i, i.pc() + 1, kNoValidate);
BranchTableImmediate imm(&decoder, i.pc() + 1, kNoValidate);
BranchTableIterator<Decoder::NoValidationTag> iterator(&i, imm);
TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(),
imm.table_count);