[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:
parent
6825359018
commit
f71d232a67
@ -1475,10 +1475,12 @@ class WasmDecoder : public Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Validate(const byte* pc, TagIndexImmediate& imm) {
|
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);
|
DecodeError(pc, "Invalid tag index: %u", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_tags);
|
||||||
imm.tag = &module_->tags[imm.index];
|
imm.tag = &module_->tags[imm.index];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1486,10 +1488,12 @@ class WasmDecoder : public Decoder {
|
|||||||
bool Validate(const byte* pc, GlobalIndexImmediate& imm) {
|
bool Validate(const byte* pc, GlobalIndexImmediate& imm) {
|
||||||
// We compare with the current size of the globals vector. This is important
|
// 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 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);
|
DecodeError(pc, "Invalid global index: %u", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_globals);
|
||||||
imm.global = &module_->globals[imm.index];
|
imm.global = &module_->globals[imm.index];
|
||||||
|
|
||||||
if constexpr (decoding_mode == kConstantExpression) {
|
if constexpr (decoding_mode == kConstantExpression) {
|
||||||
@ -1557,10 +1561,12 @@ class WasmDecoder : public Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Validate(const byte* pc, CallFunctionImmediate& imm) {
|
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);
|
DecodeError(pc, "function index #%u is out of bounds", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_functions);
|
||||||
imm.sig = module_->functions[imm.index].sig;
|
imm.sig = module_->functions[imm.index].sig;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1738,6 +1744,9 @@ class WasmDecoder : public Decoder {
|
|||||||
bool Validate(const byte* pc, TableCopyImmediate& imm) {
|
bool Validate(const byte* pc, TableCopyImmediate& imm) {
|
||||||
if (!ValidateTable(pc, imm.table_src)) return false;
|
if (!ValidateTable(pc, imm.table_src)) return false;
|
||||||
if (!ValidateTable(pc + imm.table_src.length, imm.table_dst)) 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;
|
ValueType src_type = module_->tables[imm.table_src.index].type;
|
||||||
if (!VALIDATE(IsSubtypeOf(
|
if (!VALIDATE(IsSubtypeOf(
|
||||||
src_type, module_->tables[imm.table_dst.index].type, module_))) {
|
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) {
|
if (imm.index > 0 || imm.length > 1) {
|
||||||
this->detected_->Add(kFeature_reftypes);
|
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);
|
DecodeError(pc, "invalid table index: %u", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_tables);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateElementSegment(const byte* pc, IndexImmediate& imm) {
|
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);
|
DecodeError(pc, "invalid element segment index: %u", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_elem_segments);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1802,10 +1815,12 @@ class WasmDecoder : public Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateFunction(const byte* pc, IndexImmediate& imm) {
|
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);
|
DecodeError(pc, "function index #%u is out of bounds", imm.index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
V8_ASSUME(imm.index < num_functions);
|
||||||
if (decoding_mode == kFunctionBody &&
|
if (decoding_mode == kFunctionBody &&
|
||||||
!VALIDATE(module_->functions[imm.index].declared)) {
|
!VALIDATE(module_->functions[imm.index].declared)) {
|
||||||
DecodeError(pc, "undeclared reference to function #%u", imm.index);
|
DecodeError(pc, "undeclared reference to function #%u", imm.index);
|
||||||
|
@ -586,6 +586,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
const FunctionSig* signature(uint32_t index) const {
|
const FunctionSig* signature(uint32_t index) const {
|
||||||
DCHECK(has_signature(index));
|
DCHECK(has_signature(index));
|
||||||
|
size_t num_types = types.size();
|
||||||
|
V8_ASSUME(index < num_types);
|
||||||
return types[index].function_sig;
|
return types[index].function_sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,6 +601,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
const StructType* struct_type(uint32_t index) const {
|
const StructType* struct_type(uint32_t index) const {
|
||||||
DCHECK(has_struct(index));
|
DCHECK(has_struct(index));
|
||||||
|
size_t num_types = types.size();
|
||||||
|
V8_ASSUME(index < num_types);
|
||||||
return types[index].struct_type;
|
return types[index].struct_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,11 +616,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
|||||||
}
|
}
|
||||||
const ArrayType* array_type(uint32_t index) const {
|
const ArrayType* array_type(uint32_t index) const {
|
||||||
DCHECK(has_array(index));
|
DCHECK(has_array(index));
|
||||||
|
size_t num_types = types.size();
|
||||||
|
V8_ASSUME(index < num_types);
|
||||||
return types[index].array_type;
|
return types[index].array_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t supertype(uint32_t index) const {
|
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;
|
return types[index].supertype;
|
||||||
}
|
}
|
||||||
bool has_supertype(uint32_t index) const {
|
bool has_supertype(uint32_t index) const {
|
||||||
|
Loading…
Reference in New Issue
Block a user