From f71d232a676213adb334144e20667bb7c8b3a480 Mon Sep 17 00:00:00 2001 From: Daniel Lehmann Date: Tue, 24 Jan 2023 12:49:29 +0000 Subject: [PATCH] [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 Commit-Queue: Daniel Lehmann Cr-Commit-Position: refs/heads/main@{#85456} --- src/wasm/function-body-decoder-impl.h | 27 +++++++++++++++++++++------ src/wasm/wasm-module.h | 9 ++++++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index 89fc91b5ae..1978b29ca9 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -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); diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index 0043928889..d4fddb90c3 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -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 {