Commit 1ad3d77c authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Refactor type checks in wasm-compiler

ref.test, ref.cast, and br_on_cast instructions all need to type check
a value against an rtt. With new classification functions on the
horizon, the wasm-compiler code needed to be refactored to avoid
excessive code duplication.
This CL factors out a function TypeCheck that takes as arguments a set
of three callbacks functions: a conditional success, a conditional
failure, and a negated conditional failure. Each of RefTest, RefCast,
and BrOnCast call TypeCheck with a different set of callbacks.

Bug: v8:7748
Change-Id: I1dd8893fc26d5b0228f85587c9250706d0ce16cf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2647262
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72362}
parent 8dacbacb
...@@ -5750,72 +5750,101 @@ void AssertFalse(MachineGraph* mcgraph, GraphAssembler* gasm, Node* condition) { ...@@ -5750,72 +5750,101 @@ void AssertFalse(MachineGraph* mcgraph, GraphAssembler* gasm, Node* condition) {
#endif #endif
} }
Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt, WasmGraphBuilder::Callbacks WasmGraphBuilder::TestCallbacks(
ObjectReferenceKnowledge config) { GraphAssemblerLabel<1>* label) {
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32); return {// succeed_if
[=](Node* condition) -> void {
gasm_->GotoIf(condition, label, gasm_->Int32Constant(1));
},
// fail_if
[=](Node* condition) -> void {
gasm_->GotoIf(condition, label, gasm_->Int32Constant(0));
},
// fail_if_not
[=](Node* condition) -> void {
gasm_->GotoIfNot(condition, label, gasm_->Int32Constant(0));
}};
}
WasmGraphBuilder::Callbacks WasmGraphBuilder::CastCallbacks(
GraphAssemblerLabel<0>* label, wasm::WasmCodePosition position) {
return {// succeed_if
[=](Node* condition) -> void { gasm_->GotoIf(condition, label); },
// fail_if
[=](Node* condition) -> void {
TrapIfTrue(wasm::kTrapIllegalCast, condition, position);
},
// fail_if_not
[=](Node* condition) -> void {
TrapIfFalse(wasm::kTrapIllegalCast, condition, position);
}};
}
WasmGraphBuilder::Callbacks WasmGraphBuilder::BranchCallbacks(
SmallNodeVector& no_match_controls, SmallNodeVector& no_match_effects,
SmallNodeVector& match_controls, SmallNodeVector& match_effects) {
return {
// succeed_if
[&](Node* condition) -> void {
Node* branch =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
condition, control());
match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
match_effects.emplace_back(effect());
SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
},
// fail_if
[&](Node* condition) -> void {
Node* branch =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kFalse),
condition, control());
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
no_match_effects.emplace_back(effect());
SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
},
// fail_if_not
[&](Node* condition) -> void {
Node* branch =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
condition, control());
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
no_match_effects.emplace_back(effect());
SetControl(graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
}};
}
void WasmGraphBuilder::TypeCheck(
Node* object, Node* rtt, WasmGraphBuilder::ObjectReferenceKnowledge config,
bool null_succeeds, Callbacks callbacks) {
if (config.object_can_be_i31) { if (config.object_can_be_i31) {
gasm_->GotoIf(gasm_->IsI31(object), &done, gasm_->Int32Constant(0)); callbacks.fail_if(gasm_->IsI31(object));
} else { } else {
AssertFalse(mcgraph(), gasm_.get(), gasm_->IsI31(object)); AssertFalse(mcgraph(), gasm_.get(), gasm_->IsI31(object));
} }
if (config.object_can_be_null) { if (config.object_can_be_null) {
gasm_->GotoIf(gasm_->WordEqual(object, RefNull()), &done, (null_succeeds ? callbacks.succeed_if
gasm_->Int32Constant(0)); : callbacks.fail_if)(gasm_->WordEqual(object, RefNull()));
} }
Node* map = gasm_->LoadMap(object); Node* map = gasm_->LoadMap(object);
gasm_->GotoIf(gasm_->TaggedEqual(map, rtt), &done, gasm_->Int32Constant(1)); callbacks.succeed_if(gasm_->TaggedEqual(map, rtt));
if (!config.object_must_be_data_ref) { if (!config.object_must_be_data_ref) {
gasm_->GotoIfNot(gasm_->IsDataRefMap(map), &done, gasm_->Int32Constant(0)); callbacks.fail_if_not(gasm_->IsDataRefMap(map));
} }
Node* type_info = gasm_->LoadWasmTypeInfo(map); Node* type_info = gasm_->LoadWasmTypeInfo(map);
Node* supertypes = gasm_->LoadSupertypes(type_info); Node* supertypes = gasm_->LoadSupertypes(type_info);
Node* length = Node* length =
BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(supertypes)); BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(supertypes));
gasm_->GotoIfNot( callbacks.fail_if_not(
gasm_->Uint32LessThan(gasm_->Int32Constant(config.rtt_depth), length), gasm_->Uint32LessThan(gasm_->Int32Constant(config.rtt_depth), length));
&done, gasm_->Int32Constant(0));
Node* maybe_match = gasm_->LoadFixedArrayElement( Node* maybe_match = gasm_->LoadFixedArrayElement(
supertypes, config.rtt_depth, MachineType::TaggedPointer()); supertypes, config.rtt_depth, MachineType::TaggedPointer());
gasm_->Goto(&done, gasm_->TaggedEqual(maybe_match, rtt));
gasm_->Bind(&done);
return done.PhiAt(0); callbacks.fail_if_not(gasm_->TaggedEqual(maybe_match, rtt));
}
Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
ObjectReferenceKnowledge config,
wasm::WasmCodePosition position) {
if (config.object_can_be_i31) {
TrapIfTrue(wasm::kTrapIllegalCast, gasm_->IsI31(object), position);
} else {
AssertFalse(mcgraph(), gasm_.get(), gasm_->IsI31(object));
}
auto done = gasm_->MakeLabel();
if (config.object_can_be_null) {
gasm_->GotoIf(gasm_->WordEqual(object, RefNull()), &done);
}
Node* map = gasm_->LoadMap(object);
gasm_->GotoIf(gasm_->TaggedEqual(map, rtt), &done);
if (!config.object_must_be_data_ref) {
TrapIfFalse(wasm::kTrapIllegalCast, gasm_->IsDataRefMap(map), position);
}
Node* type_info = gasm_->LoadWasmTypeInfo(map);
Node* supertypes = gasm_->LoadSupertypes(type_info);
Node* length =
BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(supertypes));
TrapIfFalse(
wasm::kTrapIllegalCast,
gasm_->Uint32LessThan(gasm_->Int32Constant(config.rtt_depth), length),
position);
Node* maybe_match = gasm_->LoadFixedArrayElement(
supertypes, config.rtt_depth, MachineType::TaggedPointer());
TrapIfFalse(wasm::kTrapIllegalCast, gasm_->TaggedEqual(maybe_match, rtt),
position);
gasm_->Goto(&done);
gasm_->Bind(&done);
return object;
} }
Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt, Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
...@@ -5823,78 +5852,15 @@ Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt, ...@@ -5823,78 +5852,15 @@ Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
Node** match_control, Node** match_effect, Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_control,
Node** no_match_effect) { Node** no_match_effect) {
// We have up to 5 control nodes to merge; the EffectPhi needs an additional SmallNodeVector no_match_controls, no_match_effects, match_controls,
// input. match_effects;
base::SmallVector<Node*, 5> no_match_controls;
base::SmallVector<Node*, 6> no_match_effects;
// We always have 2 match_controls; use the same mechanism for uniformity.
base::SmallVector<Node*, 2> match_controls;
base::SmallVector<Node*, 3> match_effects;
Node* is_i31 = gasm_->IsI31(object);
if (config.object_can_be_i31) {
Node* i31_branch = graph()->NewNode(
mcgraph()->common()->Branch(BranchHint::kFalse), is_i31, control());
SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), i31_branch));
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), i31_branch));
no_match_effects.emplace_back(effect());
} else {
AssertFalse(mcgraph(), gasm_.get(), is_i31);
}
if (config.object_can_be_null) { TypeCheck(object, rtt, config, false,
Node* null_branch = BranchCallbacks(no_match_controls, no_match_effects, match_controls,
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kFalse), match_effects));
gasm_->WordEqual(object, RefNull()), control());
SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), null_branch));
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), null_branch));
no_match_effects.emplace_back(effect());
}
// At this point, {object} is neither null nor an i31ref/Smi. match_controls.emplace_back(control());
Node* map = gasm_->LoadMap(object);
Node* exact_match =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
gasm_->TaggedEqual(map, rtt), control());
match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), exact_match));
match_effects.emplace_back(effect()); match_effects.emplace_back(effect());
SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), exact_match));
if (!config.object_must_be_data_ref) {
Node* is_data_ref =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
gasm_->IsDataRefMap(map), control());
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfFalse(), is_data_ref));
no_match_effects.emplace_back(effect());
SetControl(graph()->NewNode(mcgraph()->common()->IfTrue(), is_data_ref));
}
Node* type_info = gasm_->LoadWasmTypeInfo(map);
Node* supertypes = gasm_->LoadSupertypes(type_info);
Node* length =
BuildChangeSmiToInt32(gasm_->LoadFixedArrayLengthAsSmi(supertypes));
Node* length_sufficient = graph()->NewNode(
mcgraph()->common()->Branch(BranchHint::kTrue),
gasm_->Uint32LessThan(gasm_->Int32Constant(config.rtt_depth), length),
control());
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfFalse(), length_sufficient));
no_match_effects.emplace_back(effect());
SetControl(
graph()->NewNode(mcgraph()->common()->IfTrue(), length_sufficient));
Node* maybe_match = gasm_->LoadFixedArrayElement(
supertypes, config.rtt_depth, MachineType::TaggedPointer());
Node* supertype_match =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
gasm_->TaggedEqual(maybe_match, rtt), control());
match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfTrue(), supertype_match));
match_effects.emplace_back(effect());
no_match_controls.emplace_back(
graph()->NewNode(mcgraph()->common()->IfFalse(), supertype_match));
no_match_effects.emplace_back(effect());
// Wire up the control/effect nodes. // Wire up the control/effect nodes.
unsigned count = static_cast<unsigned>(match_controls.size()); unsigned count = static_cast<unsigned>(match_controls.size());
...@@ -5916,6 +5882,25 @@ Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt, ...@@ -5916,6 +5882,25 @@ Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
return nullptr; return nullptr;
} }
Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
ObjectReferenceKnowledge config) {
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
TypeCheck(object, rtt, config, false, TestCallbacks(&done));
gasm_->Goto(&done, gasm_->Int32Constant(1));
gasm_->Bind(&done);
return done.PhiAt(0);
}
Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
ObjectReferenceKnowledge config,
wasm::WasmCodePosition position) {
auto done = gasm_->MakeLabel();
TypeCheck(object, rtt, config, true, CastCallbacks(&done, position));
gasm_->Goto(&done);
gasm_->Bind(&done);
return object;
}
Node* WasmGraphBuilder::StructGet(Node* struct_object, Node* WasmGraphBuilder::StructGet(Node* struct_object,
const wasm::StructType* struct_type, const wasm::StructType* struct_type,
uint32_t field_index, CheckForNull null_check, uint32_t field_index, CheckForNull null_check,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
// Clients of this interface shouldn't depend on lots of compiler internals. // Clients of this interface shouldn't depend on lots of compiler internals.
// Do not include anything from src/compiler here! // Do not include anything from src/compiler here!
#include "src/base/small-vector.h"
#include "src/runtime/runtime.h" #include "src/runtime/runtime.h"
#include "src/wasm/function-body-decoder.h" #include "src/wasm/function-body-decoder.h"
#include "src/wasm/function-compiler.h" #include "src/wasm/function-compiler.h"
...@@ -36,6 +37,8 @@ class WasmDecorator; ...@@ -36,6 +37,8 @@ class WasmDecorator;
class WasmGraphAssembler; class WasmGraphAssembler;
enum class TrapId : uint32_t; enum class TrapId : uint32_t;
struct Int64LoweringSpecialCase; struct Int64LoweringSpecialCase;
template <size_t VarCount>
class GraphAssemblerLabel;
} // namespace compiler } // namespace compiler
namespace wasm { namespace wasm {
...@@ -582,6 +585,30 @@ class WasmGraphBuilder { ...@@ -582,6 +585,30 @@ class WasmGraphBuilder {
// generates {index > max ? Smi(max) : Smi(index)} // generates {index > max ? Smi(max) : Smi(index)}
Node* BuildConvertUint32ToSmiWithSaturation(Node* index, uint32_t maxval); Node* BuildConvertUint32ToSmiWithSaturation(Node* index, uint32_t maxval);
using NodeConsumer = std::function<void(Node*)>;
struct Callbacks {
NodeConsumer succeed_if;
NodeConsumer fail_if;
NodeConsumer fail_if_not;
};
// This type is used to collect control/effect nodes we need to merge at the
// end of BrOn* functions. Nodes are collected in {TypeCheck} etc. by calling
// the passed callbacks succeed_if, fail_if and fail_if_not. We have up to 5
// control nodes to merge; the EffectPhi needs an additional input.
using SmallNodeVector = base::SmallVector<Node*, 6>;
Callbacks TestCallbacks(GraphAssemblerLabel<1>* label);
Callbacks CastCallbacks(GraphAssemblerLabel<0>* label,
wasm::WasmCodePosition position);
Callbacks BranchCallbacks(SmallNodeVector& no_match_controls,
SmallNodeVector& no_match_effects,
SmallNodeVector& match_controls,
SmallNodeVector& match_effects);
void TypeCheck(Node* object, Node* rtt, ObjectReferenceKnowledge config,
bool null_succeeds, Callbacks callbacks);
// Asm.js specific functionality. // Asm.js specific functionality.
Node* BuildI32AsmjsSConvertF32(Node* input); Node* BuildI32AsmjsSConvertF32(Node* input);
Node* BuildI32AsmjsSConvertF64(Node* input); Node* BuildI32AsmjsSConvertF64(Node* input);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment