[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 { struct BlockTypeImmediate {
uint32_t length = 1; uint32_t length = 1;
ValueType type = kWasmVoid; // After decoding, either {sig_index} is set XOR {sig} points to
uint32_t sig_index = 0; // {single_return_sig_storage}.
const FunctionSig* sig = nullptr; 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> template <typename ValidationTag>
BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder, BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
@ -579,34 +589,26 @@ struct BlockTypeImmediate {
block_type); block_type);
return; return;
} }
if (static_cast<ValueTypeCode>(block_type & 0x7F) == kVoidCode) return; if (static_cast<ValueTypeCode>(block_type & 0x7F) != kVoidCode) {
type = value_type_reader::read_value_type<ValidationTag>( sig = FunctionSig{1, 0, single_return_sig_storage};
decoder, pc, &length, enabled); single_return_sig_storage[0] =
value_type_reader::read_value_type<ValidationTag>(decoder, pc,
&length, enabled);
}
} else { } else {
type = kWasmBottom; sig = FunctionSig{0, 0, nullptr};
sig_index = static_cast<uint32_t>(block_type); sig_index = static_cast<uint32_t>(block_type);
} }
} }
uint32_t in_arity() const { 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 { uint32_t out_arity() const {
if (type == kWasmVoid) return 0; return static_cast<uint32_t>(sig.return_count());
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;
} }
ValueType in_type(uint32_t index) { return sig.GetParam(index); }
ValueType out_type(uint32_t index) { return sig.GetReturn(index); }
}; };
struct BranchDepthImmediate { struct BranchDepthImmediate {
@ -1653,14 +1655,21 @@ class WasmDecoder : public Decoder {
} }
bool Validate(const byte* pc, BlockTypeImmediate& imm) { bool Validate(const byte* pc, BlockTypeImmediate& imm) {
if (!ValidateValueType(pc, imm.type)) return false; if (imm.sig.all().begin() == nullptr) {
if (imm.type == kWasmBottom) { // Then use {sig_index} to initialize the signature.
if (!VALIDATE(module_->has_signature(imm.sig_index))) { if (!VALIDATE(module_->has_signature(imm.sig_index))) {
DecodeError(pc, "block type index %u is not a signature definition", DecodeError(pc, "block type index %u is not a signature definition",
imm.sig_index); imm.sig_index);
return false; 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; return true;
} }
@ -3000,11 +3009,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
DECODE(Block) { DECODE(Block) {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate); BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0; 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()); Control* block = PushControl(kControlBlock, args.length());
SetBlockType(block, imm, args.begin()); SetBlockType(block, imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(Block, block); CALL_INTERFACE_IF_OK_AND_REACHABLE(Block, block);
DropArgs(imm.sig); DropArgs(&imm.sig);
PushMergeValues(block, &block->start_merge); PushMergeValues(block, &block->start_merge);
return 1 + imm.length; return 1 + imm.length;
} }
@ -3038,13 +3047,13 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
this->detected_->Add(kFeature_eh); this->detected_->Add(kFeature_eh);
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate); BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0; 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()); Control* try_block = PushControl(kControlTry, args.length());
SetBlockType(try_block, imm, args.begin()); SetBlockType(try_block, imm, args.begin());
try_block->previous_catch = current_catch_; try_block->previous_catch = current_catch_;
current_catch_ = static_cast<int>(control_depth() - 1); current_catch_ = static_cast<int>(control_depth() - 1);
CALL_INTERFACE_IF_OK_AND_REACHABLE(Try, try_block); CALL_INTERFACE_IF_OK_AND_REACHABLE(Try, try_block);
DropArgs(imm.sig); DropArgs(&imm.sig);
PushMergeValues(try_block, &try_block->start_merge); PushMergeValues(try_block, &try_block->start_merge);
return 1 + imm.length; return 1 + imm.length;
} }
@ -3225,11 +3234,11 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
DECODE(Loop) { DECODE(Loop) {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate); BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0; 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()); Control* block = PushControl(kControlLoop, args.length());
SetBlockType(&control_.back(), imm, args.begin()); SetBlockType(&control_.back(), imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(Loop, block); CALL_INTERFACE_IF_OK_AND_REACHABLE(Loop, block);
DropArgs(imm.sig); DropArgs(&imm.sig);
PushMergeValues(block, &block->start_merge); PushMergeValues(block, &block->start_merge);
return 1 + imm.length; return 1 + imm.length;
} }
@ -3238,13 +3247,13 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate); BlockTypeImmediate imm(this->enabled_, this, this->pc_ + 1, validate);
if (!this->Validate(this->pc_ + 1, imm)) return 0; if (!this->Validate(this->pc_ + 1, imm)) return 0;
Value cond = Peek(0, 0, kWasmI32); Value cond = Peek(0, 0, kWasmI32);
ArgVector args = PeekArgs(imm.sig, 1); ArgVector args = PeekArgs(&imm.sig, 1);
if (!VALIDATE(this->ok())) return 0; if (!VALIDATE(this->ok())) return 0;
Control* if_block = PushControl(kControlIf, 1 + args.length()); Control* if_block = PushControl(kControlIf, 1 + args.length());
SetBlockType(if_block, imm, args.begin()); SetBlockType(if_block, imm, args.begin());
CALL_INTERFACE_IF_OK_AND_REACHABLE(If, cond, if_block); CALL_INTERFACE_IF_OK_AND_REACHABLE(If, cond, if_block);
Drop(cond); Drop(cond);
DropArgs(imm.sig); // Drop {args}. DropArgs(&imm.sig);
PushMergeValues(if_block, &if_block->start_merge); PushMergeValues(if_block, &if_block->start_merge);
return 1 + imm.length; return 1 + imm.length;
} }
@ -4033,7 +4042,7 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
// Peeks arguments as required by signature. // Peeks arguments as required by signature.
V8_INLINE ArgVector PeekArgs(const FunctionSig* sig, int depth = 0) { 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 {}; if (count == 0) return {};
EnsureStackArguments(depth + count); EnsureStackArguments(depth + count);
ArgVector args(stack_value(depth + count), 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 // 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. // {sig} is null), or all of them if less are present.
V8_INLINE void DropArgs(const FunctionSig* sig) { 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); Drop(count);
} }

View File

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

View File

@ -310,16 +310,7 @@ class ImmediatesPrinter {
} }
void BlockType(BlockTypeImmediate& imm) { void BlockType(BlockTypeImmediate& imm) {
if (imm.type == kWasmBottom) { PrintSignatureOneLine(out_, &imm.sig, 0 /* ignored */, names(), false);
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_ << ")";
}
} }
void HeapType(HeapTypeImmediate& imm) { void HeapType(HeapTypeImmediate& imm) {

View File

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