[wasm] Eliminate vector bounds checks with assume

Add V8_ASSUME statements such that the compiler can statically exploit
information in Liftoff and TurboFan code that was checked to be true
during validation beforehand. In particular, this removes bounds checks
for std::vector accesses that the compiler could not elide.

The main benefit of this change is not so much the removed branches,
but rather reduced code size and fewer clobbered registers.
In case of a failed bounds check, there were about 50 bytes of x64
instructions just for reporting the error via __libcpp_verbose_abort.
For that call alone, rdi, rsi, rcx, r8, edx, and eax were clobbered.

In total, this change reduces the d8 release code size by about 4KB.

R=clemensb@chromium.org

Bug: v8:13673
Change-Id: Iaccef478b75ba086941f70a8f39fa612f1a7e50d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4191764
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Daniel Lehmann <dlehmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85456}
This commit is contained in:
Daniel Lehmann 2023-01-24 12:49:29 +00:00 committed by V8 LUCI CQ
parent 6825359018
commit f71d232a67
2 changed files with 29 additions and 7 deletions

View File

@ -1475,10 +1475,12 @@ class WasmDecoder : public Decoder {
}
bool Validate(const byte* pc, TagIndexImmediate& imm) {
if (!VALIDATE(imm.index < module_->tags.size())) {
size_t num_tags = module_->tags.size();
if (!VALIDATE(imm.index < num_tags)) {
DecodeError(pc, "Invalid tag index: %u", imm.index);
return false;
}
V8_ASSUME(imm.index < num_tags);
imm.tag = &module_->tags[imm.index];
return true;
}
@ -1486,10 +1488,12 @@ class WasmDecoder : public Decoder {
bool Validate(const byte* pc, GlobalIndexImmediate& imm) {
// We compare with the current size of the globals vector. This is important
// if we are decoding a constant expression in the global section.
if (!VALIDATE(imm.index < module_->globals.size())) {
size_t num_globals = module_->globals.size();
if (!VALIDATE(imm.index < num_globals)) {
DecodeError(pc, "Invalid global index: %u", imm.index);
return false;
}
V8_ASSUME(imm.index < num_globals);
imm.global = &module_->globals[imm.index];
if constexpr (decoding_mode == kConstantExpression) {
@ -1557,10 +1561,12 @@ class WasmDecoder : public Decoder {
}
bool Validate(const byte* pc, CallFunctionImmediate& imm) {
if (!VALIDATE(imm.index < module_->functions.size())) {
size_t num_functions = module_->functions.size();
if (!VALIDATE(imm.index < num_functions)) {
DecodeError(pc, "function index #%u is out of bounds", imm.index);
return false;
}
V8_ASSUME(imm.index < num_functions);
imm.sig = module_->functions[imm.index].sig;
return true;
}
@ -1738,6 +1744,9 @@ class WasmDecoder : public Decoder {
bool Validate(const byte* pc, TableCopyImmediate& imm) {
if (!ValidateTable(pc, imm.table_src)) return false;
if (!ValidateTable(pc + imm.table_src.length, imm.table_dst)) return false;
size_t num_tables = module_->tables.size();
V8_ASSUME(imm.table_src.index < num_tables);
V8_ASSUME(imm.table_dst.index < num_tables);
ValueType src_type = module_->tables[imm.table_src.index].type;
if (!VALIDATE(IsSubtypeOf(
src_type, module_->tables[imm.table_dst.index].type, module_))) {
@ -1762,18 +1771,22 @@ class WasmDecoder : public Decoder {
if (imm.index > 0 || imm.length > 1) {
this->detected_->Add(kFeature_reftypes);
}
if (!VALIDATE(imm.index < module_->tables.size())) {
size_t num_tables = module_->tables.size();
if (!VALIDATE(imm.index < num_tables)) {
DecodeError(pc, "invalid table index: %u", imm.index);
return false;
}
V8_ASSUME(imm.index < num_tables);
return true;
}
bool ValidateElementSegment(const byte* pc, IndexImmediate& imm) {
if (!VALIDATE(imm.index < module_->elem_segments.size())) {
size_t num_elem_segments = module_->elem_segments.size();
if (!VALIDATE(imm.index < num_elem_segments)) {
DecodeError(pc, "invalid element segment index: %u", imm.index);
return false;
}
V8_ASSUME(imm.index < num_elem_segments);
return true;
}
@ -1802,10 +1815,12 @@ class WasmDecoder : public Decoder {
}
bool ValidateFunction(const byte* pc, IndexImmediate& imm) {
if (!VALIDATE(imm.index < module_->functions.size())) {
size_t num_functions = module_->functions.size();
if (!VALIDATE(imm.index < num_functions)) {
DecodeError(pc, "function index #%u is out of bounds", imm.index);
return false;
}
V8_ASSUME(imm.index < num_functions);
if (decoding_mode == kFunctionBody &&
!VALIDATE(module_->functions[imm.index].declared)) {
DecodeError(pc, "undeclared reference to function #%u", imm.index);

View File

@ -586,6 +586,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
}
const FunctionSig* signature(uint32_t index) const {
DCHECK(has_signature(index));
size_t num_types = types.size();
V8_ASSUME(index < num_types);
return types[index].function_sig;
}
@ -599,6 +601,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
}
const StructType* struct_type(uint32_t index) const {
DCHECK(has_struct(index));
size_t num_types = types.size();
V8_ASSUME(index < num_types);
return types[index].struct_type;
}
@ -612,11 +616,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
}
const ArrayType* array_type(uint32_t index) const {
DCHECK(has_array(index));
size_t num_types = types.size();
V8_ASSUME(index < num_types);
return types[index].array_type;
}
uint32_t supertype(uint32_t index) const {
DCHECK(index < types.size());
size_t num_types = types.size();
V8_ASSUME(index < num_types);
return types[index].supertype;
}
bool has_supertype(uint32_t index) const {