Commit aee0ec97 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm-gc][turbofan] Implement typing phase

We introduce a typing phase into the Turbofan compilation pipeline for
wasm-gc. It has two functionalities: (1) to type nodes that were not
typed during code generation (mainly phi nodes) and (2) to narrow types
as much as possible.
The following nodes are handled, which should be enough for our
purposes: TypeGuard, WasmTypeCast, AssertNotNull, Phi, LoadFromObject,
and LoadImmutableFromObject.
Loop phi types are computed by first assigning the type of the
non-recursive input, and updating once we have the type of the recursive
inputs, and repeating this process to a fixed point.

Drive-by: Remove the narrowing of function signatures during wasm
inlining, as it created some issues and should not be needed after this
series of changes.

Bug: v8:7748
Change-Id: I8a72488d5c221c4ae8257fc5abf6f0368cf10e96
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3678208
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80912}
parent 97a3d69d
......@@ -2880,6 +2880,8 @@ filegroup(
"src/compiler/wasm-graph-assembler.h",
"src/compiler/wasm-inlining.cc",
"src/compiler/wasm-inlining.h",
"src/compiler/wasm-typer.cc",
"src/compiler/wasm-typer.h",
],
"//conditions:default": [],
}),
......
......@@ -3562,6 +3562,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/wasm-graph-assembler.h",
"src/compiler/wasm-inlining.h",
"src/compiler/wasm-loop-peeling.h",
"src/compiler/wasm-typer.h",
"src/debug/debug-wasm-objects-inl.h",
"src/debug/debug-wasm-objects.h",
"src/third_party/utf8-decoder/generalized-utf8-decoder.h",
......@@ -4062,6 +4063,7 @@ if (v8_enable_webassembly) {
"src/compiler/wasm-graph-assembler.cc",
"src/compiler/wasm-inlining.cc",
"src/compiler/wasm-loop-peeling.cc",
"src/compiler/wasm-typer.cc",
]
}
......
This diff is collapsed.
......@@ -35,7 +35,8 @@ class V8_EXPORT_PRIVATE Int64Lowering {
Int64Lowering(
Graph* graph, MachineOperatorBuilder* machine,
CommonOperatorBuilder* common, SimplifiedOperatorBuilder* simplified_,
Zone* zone, Signature<MachineRepresentation>* signature,
Zone* zone, const wasm::WasmModule* module,
Signature<MachineRepresentation>* signature,
std::unique_ptr<Int64LoweringSpecialCase> special_case = nullptr);
void LowerGraph();
......@@ -72,6 +73,8 @@ class V8_EXPORT_PRIVATE Int64Lowering {
const CallDescriptor* LowerCallDescriptor(
const CallDescriptor* call_descriptor);
void SetInt32Type(Node* node);
void SetFloat64Type(Node* node);
void ReplaceNode(Node* old, Node* new_low, Node* new_high);
bool HasReplacementLow(Node* node);
......@@ -88,17 +91,20 @@ class V8_EXPORT_PRIVATE Int64Lowering {
int input_index;
};
Zone* zone_;
Graph* const graph_;
MachineOperatorBuilder* machine_;
CommonOperatorBuilder* common_;
SimplifiedOperatorBuilder* simplified_;
Zone* zone_;
Signature<MachineRepresentation>* signature_;
std::unique_ptr<Int64LoweringSpecialCase> special_case_;
std::vector<State> state_;
ZoneDeque<NodeState> stack_;
Replacement* replacements_;
Signature<MachineRepresentation>* signature_;
Node* placeholder_;
std::unique_ptr<Int64LoweringSpecialCase> special_case_;
// Caches for node types, so we do not waste memory.
Type int32_type_;
Type float64_type_;
};
} // namespace compiler
......
......@@ -109,6 +109,7 @@
#include "src/compiler/wasm-gc-lowering.h"
#include "src/compiler/wasm-inlining.h"
#include "src/compiler/wasm-loop-peeling.h"
#include "src/compiler/wasm-typer.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/wasm-engine.h"
......@@ -2059,6 +2060,19 @@ struct TurboshaftRecreateSchedulePhase {
};
#if V8_ENABLE_WEBASSEMBLY
struct WasmTypingPhase {
DECL_PIPELINE_PHASE_CONSTANTS(WasmTyping)
void Run(PipelineData* data, Zone* temp_zone, uint32_t function_index) {
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
WasmTyper typer(&graph_reducer, data->mcgraph(), function_index);
AddReducer(data, &graph_reducer, &typer);
graph_reducer.ReduceGraph();
}
};
struct WasmGCLoweringPhase {
DECL_PIPELINE_PHASE_CONSTANTS(WasmGCLowering)
......@@ -3283,6 +3297,8 @@ void Pipeline::GenerateCodeForWasmFunction(
const bool is_asm_js = is_asmjs_module(module);
if (FLAG_experimental_wasm_gc) {
pipeline.Run<WasmTypingPhase>(function_index);
pipeline.RunPrintAndVerify(WasmTypingPhase::phase_name(), true);
pipeline.Run<WasmGCLoweringPhase>();
pipeline.RunPrintAndVerify(WasmGCLoweringPhase::phase_name(), true);
}
......
......@@ -3990,7 +3990,8 @@ CallDescriptor* WasmGraphBuilder::GetI64AtomicWaitCallDescriptor() {
void WasmGraphBuilder::LowerInt64(Signature<MachineRepresentation>* sig) {
if (mcgraph()->machine()->Is64()) return;
Int64Lowering r(mcgraph()->graph(), mcgraph()->machine(), mcgraph()->common(),
gasm_->simplified(), mcgraph()->zone(), sig,
gasm_->simplified(), mcgraph()->zone(),
env_ != nullptr ? env_->module : nullptr, sig,
std::move(lowering_special_case_));
r.LowerGraph();
}
......@@ -7284,7 +7285,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Signature<MachineRepresentation> c_entry_sig(1, 4, sig_reps);
Int64Lowering r(mcgraph()->graph(), mcgraph()->machine(),
mcgraph()->common(), gasm_->simplified(),
mcgraph()->zone(), &c_entry_sig);
mcgraph()->zone(), module_, &c_entry_sig);
r.LowerGraph();
}
}
......
......@@ -139,27 +139,6 @@ void WasmInliner::Finalize() {
&module()->functions[candidate.inlinee_index];
base::Vector<const byte> function_bytes =
wire_bytes_->GetCode(inlinee->code);
// We use the signature based on the real argument types stored in the call
// node. This is more specific than the callee's formal signature and might
// enable some optimizations.
const wasm::FunctionSig* specialized_sig =
CallDescriptorOf(call->op())->wasm_sig();
#if DEBUG
// Check that the real signature is a subtype of the formal one.
const wasm::FunctionSig* formal_sig =
WasmGraphBuilder::Int64LoweredSig(zone(), inlinee->sig);
CHECK_EQ(specialized_sig->parameter_count(), formal_sig->parameter_count());
CHECK_EQ(specialized_sig->return_count(), formal_sig->return_count());
for (size_t i = 0; i < specialized_sig->parameter_count(); i++) {
CHECK(wasm::IsSubtypeOf(specialized_sig->GetParam(i),
formal_sig->GetParam(i), module()));
}
for (size_t i = 0; i < specialized_sig->return_count(); i++) {
CHECK(wasm::IsSubtypeOf(formal_sig->GetReturn(i),
specialized_sig->GetReturn(i), module()));
}
#endif
wasm::WasmFeatures detected;
std::vector<WasmLoopInfo> inlinee_loop_infos;
......@@ -167,12 +146,12 @@ void WasmInliner::Finalize() {
size_t subgraph_min_node_id = graph()->NodeCount();
Node* inlinee_start;
Node* inlinee_end;
for (const wasm::FunctionSig* sig = specialized_sig;;) {
const wasm::FunctionBody inlinee_body(sig, inlinee->code.offset(),
function_bytes.begin(),
function_bytes.end());
WasmGraphBuilder builder(env_, zone(), mcgraph_, inlinee_body.sig,
source_positions_);
const wasm::FunctionBody inlinee_body(inlinee->sig, inlinee->code.offset(),
function_bytes.begin(),
function_bytes.end());
WasmGraphBuilder builder(env_, zone(), mcgraph_, inlinee_body.sig,
source_positions_);
{
Graph::SubgraphScope scope(graph());
wasm::DecodeResult result = wasm::BuildTFGraph(
zone()->allocator(), env_->enabled_features, module(), &builder,
......@@ -185,19 +164,11 @@ void WasmInliner::Finalize() {
builder.LowerInt64(WasmGraphBuilder::kCalledFromWasm);
inlinee_start = graph()->start();
inlinee_end = graph()->end();
break;
}
if (sig == specialized_sig) {
// One possible reason for failure is the opportunistic signature
// specialization. Try again without that.
sig = inlinee->sig;
inlinee_loop_infos.clear();
Trace(candidate, "retrying with original signature");
continue;
} else {
// Otherwise report failure.
Trace(candidate, "failed to compile");
return;
}
// Otherwise report failure.
Trace(candidate, "failed to compile");
return;
}
size_t additional_nodes = graph()->NodeCount() - subgraph_min_node_id;
......
This diff is collapsed.
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_COMPILER_WASM_TYPER_H_
#define V8_COMPILER_WASM_TYPER_H_
#include "src/compiler/graph-reducer.h"
#include "src/compiler/wasm-graph-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
class MachineGraph;
class WasmTyper final : public AdvancedReducer {
public:
WasmTyper(Editor* editor, MachineGraph* mcgraph, uint32_t function_index);
const char* reducer_name() const override { return "WasmTyper"; }
Reduction Reduce(Node* node) final;
private:
uint32_t function_index_;
MachineGraph* mcgraph_;
Zone* graph_zone_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_WASM_TYPER_H_
......@@ -1122,6 +1122,7 @@ DEFINE_BOOL(wasm_speculative_inlining, false,
DEFINE_BOOL(trace_wasm_inlining, false, "trace wasm inlining")
DEFINE_BOOL(trace_wasm_speculative_inlining, false,
"trace wasm speculative inlining")
DEFINE_BOOL(trace_wasm_typer, false, "trace wasm typer")
DEFINE_BOOL(wasm_type_canonicalization, false,
"apply isorecursive canonicalization on wasm types")
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_dynamic_tiering)
......
......@@ -384,6 +384,7 @@ class RuntimeCallTimer final {
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmLoopPeeling) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmLoopUnrolling) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmOptimization) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmTyping) \
\
ADD_THREAD_SPECIFIC_COUNTER(V, Parse, ArrowFunctionLiteral) \
ADD_THREAD_SPECIFIC_COUNTER(V, Parse, FunctionLiteral) \
......
......@@ -60,6 +60,9 @@ class StructType : public ZoneObject {
}
bool operator!=(const StructType& other) const { return !(*this == other); }
// Returns the offset of this field in the runtime representation of the
// object, from the start of the object fields (disregarding the object
// header).
uint32_t field_offset(uint32_t index) const {
DCHECK_LT(index, field_count());
if (index == 0) return 0;
......@@ -124,6 +127,15 @@ class StructType : public ZoneObject {
const bool* const mutabilities_;
};
inline std::ostream& operator<<(std::ostream& out, StructType type) {
out << "[";
for (ValueType field : type.fields()) {
out << field.name() << ", ";
}
out << "]";
return out;
}
class ArrayType : public ZoneObject {
public:
constexpr explicit ArrayType(ValueType rep, bool mutability)
......
......@@ -112,6 +112,8 @@ struct TypeInModule {
TypeInModule(ValueType type, const WasmModule* module)
: type(type), module(module) {}
TypeInModule() : TypeInModule(kWasmBottom, nullptr) {}
bool operator==(const TypeInModule& other) const {
return type == other.type && module == other.module;
}
......
......@@ -517,7 +517,7 @@ Handle<Code> WasmFunctionWrapper::GetWrapperCode(Isolate* isolate) {
rep_builder.AddParam(MachineRepresentation::kWord32);
}
compiler::Int64Lowering r(graph(), machine(), common(), simplified(),
zone(), rep_builder.Build());
zone(), nullptr, rep_builder.Build());
r.LowerGraph();
}
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc --no-liftoff
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Test that we can eliminate type checks based on narrowed argument types
// (by inspecting the resulting graph).
(function WasmTypedOptimizationsTest() {
let builder = new WasmModuleBuilder();
let top = builder.addStruct([makeField(kWasmI32, true)]);
let middle = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI64, false)],
top);
let bottom1 = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI64, false),
makeField(kWasmI32, true)],
middle);
let bottom2 = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI64, false),
makeField(kWasmI64, false)],
middle);
builder.addFunction("main", makeSig(
[wasmRefType(bottom1), wasmRefType(bottom2)], [kWasmI32]))
.addLocals(wasmOptRefType(top), 1)
.addLocals(kWasmI32, 1)
.addBody([
// temp = x0;
kExprLocalGet, 0, kExprLocalSet, 2,
// while (true) {
kExprLoop, kWasmVoid,
// if (ref.test temp bottom1) {
kExprLocalGet, 2, kGCPrefix, kExprRefTestStatic, bottom1,
kExprIf, kWasmVoid,
// counter += ((bottom1) temp).field_2;
// TODO(manoskouk): Implement path-based type tracking so we can
// eliminate this check.
kExprLocalGet, 2, kGCPrefix, kExprRefCastStatic, bottom1,
kGCPrefix, kExprStructGet, bottom1, 2,
kExprLocalGet, 3, kExprI32Add, kExprLocalSet, 3,
// temp = x1;
kExprLocalGet, 1, kExprLocalSet, 2,
// } else {
kExprElse,
// counter += (i32) ((middle) temp).field_1
// Note: This cast should get optimized away, as temp only gets
// assigned to {bottom1} and {bottom2}.
kExprLocalGet, 2, kGCPrefix, kExprRefCastStatic, middle,
kGCPrefix, kExprStructGet, middle, 1, kExprI32ConvertI64,
kExprLocalGet, 3, kExprI32Add, kExprLocalSet, 3,
// temp = x0;
kExprLocalGet, 0, kExprLocalSet, 2,
// }
kExprEnd,
// if (counter < 100) continue; break;
kExprLocalGet, 3, kExprI32Const, 100, kExprI32LtS,
kExprBrIf, 0,
// }
kExprEnd,
// return counter;
kExprLocalGet, 3])
.exportFunc();
builder.instantiate({});
})();
......@@ -50,7 +50,7 @@ class Int64LoweringTest : public GraphTest {
NodeProperties::MergeControlToEnd(graph(), common(), ret);
Int64Lowering lowering(graph(), machine(), common(), simplified(), zone(),
signature);
nullptr, signature);
lowering.LowerGraph();
}
......@@ -69,7 +69,8 @@ class Int64LoweringTest : public GraphTest {
sig_builder.AddReturn(rep);
Int64Lowering lowering(graph(), machine(), common(), simplified(), zone(),
sig_builder.Build(), std::move(special_case));
nullptr, sig_builder.Build(),
std::move(special_case));
lowering.LowerGraph();
}
......@@ -153,8 +154,7 @@ TEST_F(Int64LoweringTest, Int64Constant) {
#define LOAD_VERIFY(kLoad) \
Matcher<Node*> high_word_load_matcher = \
Is##kLoad(MachineType::Int32(), IsInt32Constant(base), \
IsInt32Add(IsInt32Constant(index), IsInt32Constant(0x4)), \
start(), start()); \
IsInt32Constant(index + 4), start(), start()); \
\
EXPECT_THAT( \
graph()->end()->InputAt(1), \
......@@ -218,9 +218,8 @@ TEST_F(Int64LoweringTest, Int64LoadImmutable) {
Capture<Node*> high_word_load;
#if defined(V8_TARGET_LITTLE_ENDIAN)
Matcher<Node*> high_word_load_matcher =
IsLoadImmutable(MachineType::Int32(), IsInt32Constant(base),
IsInt32Add(IsInt32Constant(index), IsInt32Constant(0x4)));
Matcher<Node*> high_word_load_matcher = IsLoadImmutable(
MachineType::Int32(), IsInt32Constant(base), IsInt32Constant(index + 4));
EXPECT_THAT(
graph()->end()->InputAt(1),
......@@ -234,9 +233,8 @@ TEST_F(Int64LoweringTest, Int64LoadImmutable) {
EXPECT_THAT(
graph()->end()->InputAt(1),
IsReturn2(IsLoadImmutable(
MachineType::Int32(), IsInt32Constant(base),
IsInt32Add(IsInt32Constant(index), IsInt32Constant(0x4))),
IsReturn2(IsLoadImmutable(MachineType::Int32(), IsInt32Constant(base),
IsInt32Constant(index + 4)),
AllOf(CaptureEq(&high_word_load), high_word_load_matcher),
start(), start()));
#endif
......@@ -247,14 +245,13 @@ TEST_F(Int64LoweringTest, Int64LoadImmutable) {
EXPECT_THAT( \
graph()->end()->InputAt(1), \
IsReturn(IsInt32Constant(return_value), \
Is##kStore( \
kRep, IsInt32Constant(base), IsInt32Constant(index), \
IsInt32Constant(low_word_value(0)), \
Is##kStore( \
kRep, IsInt32Constant(base), \
IsInt32Add(IsInt32Constant(index), IsInt32Constant(4)), \
IsInt32Constant(high_word_value(0)), start(), start()), \
start()), \
Is##kStore(kRep, IsInt32Constant(base), IsInt32Constant(index), \
IsInt32Constant(low_word_value(0)), \
Is##kStore(kRep, IsInt32Constant(base), \
IsInt32Constant(index + 4), \
IsInt32Constant(high_word_value(0)), \
start(), start()), \
start()), \
start()));
#elif defined(V8_TARGET_BIG_ENDIAN)
#define STORE_VERIFY(kStore, kRep) \
......@@ -291,7 +288,7 @@ TEST_F(Int64LoweringTest, Int64LoadImmutable) {
NodeProperties::MergeControlToEnd(graph(), common(), ret); \
\
Int64Lowering lowering(graph(), machine(), common(), simplified(), zone(), \
sig_builder.Build()); \
nullptr, sig_builder.Build()); \
lowering.LowerGraph(); \
\
STORE_VERIFY(kStore, kRep32)
......@@ -325,7 +322,7 @@ TEST_F(Int64LoweringTest, Int32Store) {
NodeProperties::MergeControlToEnd(graph(), common(), ret);
Int64Lowering lowering(graph(), machine(), common(), simplified(), zone(),
sig_builder.Build());
nullptr, sig_builder.Build());
lowering.LowerGraph();
EXPECT_THAT(
......
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