From 51d662f7122ca291295d81d1e1447fb5965f80cd Mon Sep 17 00:00:00 2001 From: Manos Koukoutos Date: Fri, 3 Jun 2022 04:41:19 +0000 Subject: [PATCH] [wasm-gc] Introduce typed-based optimizations We introduce a Turbofan pass which optimizes wasm-gc nodes based on the types of their inputs. Bug: v8:7748 Change-Id: I281eb0785e9e4201ef925ec201d76dc3d274ad05 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3679198 Reviewed-by: Maya Lekova Reviewed-by: Jakob Kummerow Commit-Queue: Manos Koukoutos Cr-Commit-Position: refs/heads/main@{#80929} --- BUILD.bazel | 2 + BUILD.gn | 2 + src/compiler/opcodes.h | 1 + src/compiler/pipeline.cc | 17 ++ src/compiler/simplified-operator.cc | 10 ++ src/compiler/simplified-operator.h | 1 + src/compiler/verifier.cc | 1 + src/compiler/wasm-gc-lowering.cc | 9 ++ src/compiler/wasm-gc-lowering.h | 1 + src/compiler/wasm-gc-operator-reducer.cc | 195 +++++++++++++++++++++++ src/compiler/wasm-gc-operator-reducer.h | 50 ++++++ src/compiler/wasm-graph-assembler.cc | 4 + src/compiler/wasm-graph-assembler.h | 2 + src/compiler/wasm-typer.cc | 29 ++++ src/logging/runtime-call-stats.h | 1 + src/wasm/function-body-decoder-impl.h | 8 +- src/wasm/wasm-subtyping.h | 7 + 17 files changed, 335 insertions(+), 5 deletions(-) create mode 100644 src/compiler/wasm-gc-operator-reducer.cc create mode 100644 src/compiler/wasm-gc-operator-reducer.h diff --git a/BUILD.bazel b/BUILD.bazel index 657c10cdf5..ca19807831 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -2876,6 +2876,8 @@ filegroup( "src/compiler/wasm-loop-peeling.h", "src/compiler/wasm-gc-lowering.cc", "src/compiler/wasm-gc-lowering.h", + "src/compiler/wasm-gc-operator-reducer.cc", + "src/compiler/wasm-gc-operator-reducer.h", "src/compiler/wasm-graph-assembler.cc", "src/compiler/wasm-graph-assembler.h", "src/compiler/wasm-inlining.cc", diff --git a/BUILD.gn b/BUILD.gn index 7800b39d30..00770169bf 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3562,6 +3562,7 @@ v8_header_set("v8_internal_headers") { "src/compiler/wasm-compiler.h", "src/compiler/wasm-escape-analysis.h", "src/compiler/wasm-gc-lowering.h", + "src/compiler/wasm-gc-operator-reducer.h", "src/compiler/wasm-graph-assembler.h", "src/compiler/wasm-inlining.h", "src/compiler/wasm-loop-peeling.h", @@ -4063,6 +4064,7 @@ if (v8_enable_webassembly) { "src/compiler/wasm-compiler.cc", "src/compiler/wasm-escape-analysis.cc", "src/compiler/wasm-gc-lowering.cc", + "src/compiler/wasm-gc-operator-reducer.cc", "src/compiler/wasm-graph-assembler.cc", "src/compiler/wasm-inlining.cc", "src/compiler/wasm-loop-peeling.cc", diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index fae9b06eca..87f03b8462 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -507,6 +507,7 @@ #define SIMPLIFIED_WASM_OP_LIST(V) \ V(AssertNotNull) \ V(IsNull) \ + V(IsNotNull) \ V(Null) \ V(RttCanon) \ V(WasmTypeCast) \ diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index a9044abb0b..27e89d590d 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -107,6 +107,7 @@ #include "src/compiler/wasm-compiler.h" #include "src/compiler/wasm-escape-analysis.h" #include "src/compiler/wasm-gc-lowering.h" +#include "src/compiler/wasm-gc-operator-reducer.h" #include "src/compiler/wasm-inlining.h" #include "src/compiler/wasm-loop-peeling.h" #include "src/compiler/wasm-typer.h" @@ -2073,6 +2074,20 @@ struct WasmTypingPhase { } }; +struct WasmGCOptimizationPhase { + DECL_PIPELINE_PHASE_CONSTANTS(WasmGCOptimization) + + void Run(PipelineData* data, Zone* temp_zone, + const wasm::WasmModule* module) { + GraphReducer graph_reducer( + temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(), + data->jsgraph()->Dead(), data->observe_node_manager()); + WasmGCOperatorReducer wasm_gc(&graph_reducer, data->mcgraph(), module); + AddReducer(data, &graph_reducer, &wasm_gc); + graph_reducer.ReduceGraph(); + } +}; + struct WasmGCLoweringPhase { DECL_PIPELINE_PHASE_CONSTANTS(WasmGCLowering) @@ -3299,6 +3314,8 @@ void Pipeline::GenerateCodeForWasmFunction( if (FLAG_experimental_wasm_gc) { pipeline.Run(function_index); pipeline.RunPrintAndVerify(WasmTypingPhase::phase_name(), true); + pipeline.Run(module); + pipeline.RunPrintAndVerify(WasmGCOptimizationPhase::phase_name(), true); pipeline.Run(); pipeline.RunPrintAndVerify(WasmGCLoweringPhase::phase_name(), true); } diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 45b2dd43cf..c8bbe05bf2 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -1153,6 +1153,13 @@ struct SimplifiedOperatorGlobalCache final { }; IsNullOperator kIsNull; + struct IsNotNullOperator final : public Operator { + explicit IsNotNullOperator() + : Operator(IrOpcode::kIsNotNull, Operator::kPure, "IsNotNull", 1, 0, 0, + 1, 0, 0) {} + }; + IsNotNullOperator kIsNotNull; + struct NullOperator final : public Operator { NullOperator() : Operator(IrOpcode::kNull, Operator::kPure, "Null", 0, 0, 0, 1, 0, 0) { @@ -1360,6 +1367,9 @@ const Operator* SimplifiedOperatorBuilder::AssertNotNull() { } const Operator* SimplifiedOperatorBuilder::IsNull() { return &cache_.kIsNull; } +const Operator* SimplifiedOperatorBuilder::IsNotNull() { + return &cache_.kIsNotNull; +} #endif // V8_ENABLE_WEBASSEMBLY const Operator* SimplifiedOperatorBuilder::CheckIf( diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index 6391678c3e..a7358c4c47 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -1064,6 +1064,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final #if V8_ENABLE_WEBASSEMBLY const Operator* AssertNotNull(); const Operator* IsNull(); + const Operator* IsNotNull(); const Operator* Null(); const Operator* RttCanon(int index); const Operator* WasmTypeCheck(WasmTypeCheckConfig config); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index fbefc78675..b93d99e728 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -1651,6 +1651,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { case IrOpcode::kRttCanon: case IrOpcode::kNull: case IrOpcode::kIsNull: + case IrOpcode::kIsNotNull: case IrOpcode::kAssertNotNull: // TODO(manoskouk): What are the constraints here? break; diff --git a/src/compiler/wasm-gc-lowering.cc b/src/compiler/wasm-gc-lowering.cc index 7a6d94321a..30b2f6f778 100644 --- a/src/compiler/wasm-gc-lowering.cc +++ b/src/compiler/wasm-gc-lowering.cc @@ -50,6 +50,8 @@ Reduction WasmGCLowering::Reduce(Node* node) { return ReduceNull(node); case IrOpcode::kIsNull: return ReduceIsNull(node); + case IrOpcode::kIsNotNull: + return ReduceIsNotNull(node); case IrOpcode::kRttCanon: return ReduceRttCanon(node); case IrOpcode::kTypeGuard: @@ -202,6 +204,13 @@ Reduction WasmGCLowering::ReduceIsNull(Node* node) { return Replace(gasm_.TaggedEqual(object, Null())); } +Reduction WasmGCLowering::ReduceIsNotNull(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kIsNotNull); + Node* object = NodeProperties::GetValueInput(node, 0); + return Replace(gasm_.Word32Equal(gasm_.TaggedEqual(object, Null()), + gasm_.Int32Constant(0))); +} + Reduction WasmGCLowering::ReduceRttCanon(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kRttCanon); int type_index = OpParameter(node->op()); diff --git a/src/compiler/wasm-gc-lowering.h b/src/compiler/wasm-gc-lowering.h index 72639ff723..d655873835 100644 --- a/src/compiler/wasm-gc-lowering.h +++ b/src/compiler/wasm-gc-lowering.h @@ -33,6 +33,7 @@ class WasmGCLowering final : public AdvancedReducer { Reduction ReduceAssertNotNull(Node* node); Reduction ReduceNull(Node* node); Reduction ReduceIsNull(Node* node); + Reduction ReduceIsNotNull(Node* node); Reduction ReduceRttCanon(Node* node); Reduction ReduceTypeGuard(Node* node); Node* Null(); diff --git a/src/compiler/wasm-gc-operator-reducer.cc b/src/compiler/wasm-gc-operator-reducer.cc new file mode 100644 index 0000000000..3770f32ff1 --- /dev/null +++ b/src/compiler/wasm-gc-operator-reducer.cc @@ -0,0 +1,195 @@ +// 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. + +#include "src/compiler/wasm-gc-operator-reducer.h" + +#include "src/compiler/node-properties.h" +#include "src/compiler/simplified-operator.h" +#include "src/compiler/wasm-compiler-definitions.h" +#include "src/wasm/wasm-subtyping.h" + +namespace v8 { +namespace internal { +namespace compiler { + +WasmGCOperatorReducer::WasmGCOperatorReducer(Editor* editor, + MachineGraph* mcgraph, + const wasm::WasmModule* module) + : AdvancedReducer(editor), + mcgraph_(mcgraph), + gasm_(mcgraph, mcgraph->zone()), + module_(module) {} + +Reduction WasmGCOperatorReducer::Reduce(Node* node) { + switch (node->opcode()) { + case IrOpcode::kAssertNotNull: + return ReduceAssertNotNull(node); + case IrOpcode::kIsNull: + return ReduceIsNull(node); + case IrOpcode::kWasmTypeCheck: + return ReduceWasmTypeCheck(node); + case IrOpcode::kWasmTypeCast: + return ReduceWasmTypeCast(node); + default: + return NoChange(); + } +} + +namespace { +bool InDeadBranch(Node* node) { + return node->opcode() == IrOpcode::kDead || + NodeProperties::GetType(node).AsWasm().type.is_bottom(); +} +} // namespace + +Node* WasmGCOperatorReducer::SetType(Node* node, wasm::ValueType type) { + NodeProperties::SetType(node, Type::Wasm(type, module_, graph()->zone())); + return node; +} + +Reduction WasmGCOperatorReducer::ReduceAssertNotNull(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kAssertNotNull); + Node* object = NodeProperties::GetValueInput(node, 0); + + if (InDeadBranch(object)) return NoChange(); + + // Optimize the check away if the argument is known to be non-null. + if (!NodeProperties::GetType(object).AsWasm().type.is_nullable()) { + ReplaceWithValue(node, object); + node->Kill(); + return Replace(object); + } + + return NoChange(); +} + +Reduction WasmGCOperatorReducer::ReduceIsNull(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kIsNull); + Node* object = NodeProperties::GetValueInput(node, 0); + + if (InDeadBranch(object)) return NoChange(); + + // Optimize the check away if the argument is known to be non-null. + if (!NodeProperties::GetType(object).AsWasm().type.is_nullable()) { + ReplaceWithValue(node, gasm_.Int32Constant(0)); + node->Kill(); + return Replace(object); // Irrelevant replacement. + } + + // Optimize the check away if the argument is known to be null. + if (object->opcode() == IrOpcode::kNull) { + ReplaceWithValue(node, gasm_.Int32Constant(1)); + node->Kill(); + return Replace(object); // Irrelevant replacement. + } + + return NoChange(); +} + +Reduction WasmGCOperatorReducer::ReduceWasmTypeCast(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCast); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + Node* object = NodeProperties::GetValueInput(node, 0); + Node* rtt = NodeProperties::GetValueInput(node, 1); + + if (InDeadBranch(object) || InDeadBranch(rtt)) return NoChange(); + + wasm::TypeInModule object_type = NodeProperties::GetType(object).AsWasm(); + wasm::TypeInModule rtt_type = NodeProperties::GetType(rtt).AsWasm(); + + if (object_type.type.is_bottom()) return NoChange(); + + if (wasm::IsHeapSubtypeOf(object_type.type.heap_type(), + wasm::HeapType(rtt_type.type.ref_index()), + object_type.module, rtt_type.module)) { + // Type cast will always succeed. Remove it. + ReplaceWithValue(node, object); + node->Kill(); + return Replace(object); + } + + if (wasm::HeapTypesUnrelated(object_type.type.heap_type(), + wasm::HeapType(rtt_type.type.ref_index()), + object_type.module, rtt_type.module)) { + gasm_.InitializeEffectControl(effect, control); + // A cast between unrelated types can only succeed if the argument is null. + // Otherwise, it always fails. + Node* non_trapping_condition = object_type.type.is_nullable() + ? gasm_.IsNull(object) + : gasm_.Int32Constant(0); + Node* trap = + gasm_.TrapUnless(SetType(non_trapping_condition, wasm::kWasmI32), + TrapId::kTrapIllegalCast); + // TODO(manoskouk): Improve the type when we have nullref. + Node* null_node = gasm_.Null(); + ReplaceWithValue( + node, + SetType(null_node, wasm::ValueType::Ref(rtt_type.type.ref_index(), + wasm::kNullable)), + effect, trap); + node->Kill(); + return Replace(null_node); + } + + // Remove the null check from the cast if able. + if (!object_type.type.is_nullable() && + OpParameter(node->op()).object_can_be_null) { + uint8_t rtt_depth = OpParameter(node->op()).rtt_depth; + NodeProperties::ChangeOp( + node, gasm_.simplified()->WasmTypeCast( + {/* object_can_be_null = */ false, rtt_depth})); + return Changed(node); + } + + return NoChange(); +} + +Reduction WasmGCOperatorReducer::ReduceWasmTypeCheck(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCheck); + Node* object = NodeProperties::GetValueInput(node, 0); + Node* rtt = NodeProperties::GetValueInput(node, 1); + + if (InDeadBranch(object) || InDeadBranch(rtt)) return NoChange(); + + wasm::TypeInModule object_type = NodeProperties::GetType(object).AsWasm(); + wasm::TypeInModule rtt_type = NodeProperties::GetType(rtt).AsWasm(); + + if (wasm::IsHeapSubtypeOf(object_type.type.heap_type(), + wasm::HeapType(rtt_type.type.ref_index()), + object_type.module, rtt_type.module)) { + // Type cast will fail only on null. + Node* condition = + SetType(object_type.type.is_nullable() ? gasm_.IsNotNull(object) + : gasm_.Int32Constant(1), + wasm::kWasmI32); + ReplaceWithValue(node, condition); + node->Kill(); + return Replace(condition); + } + + if (wasm::HeapTypesUnrelated(object_type.type.heap_type(), + wasm::HeapType(rtt_type.type.ref_index()), + object_type.module, rtt_type.module)) { + Node* condition = SetType(gasm_.Int32Constant(0), wasm::kWasmI32); + ReplaceWithValue(node, condition); + node->Kill(); + return Replace(condition); + } + + // Remove the null check from the typecheck if able. + if (!object_type.type.is_nullable() && + OpParameter(node->op()).object_can_be_null) { + uint8_t rtt_depth = OpParameter(node->op()).rtt_depth; + NodeProperties::ChangeOp( + node, gasm_.simplified()->WasmTypeCheck({false, rtt_depth})); + return Changed(node); + } + + return NoChange(); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/wasm-gc-operator-reducer.h b/src/compiler/wasm-gc-operator-reducer.h new file mode 100644 index 0000000000..3b866287fb --- /dev/null +++ b/src/compiler/wasm-gc-operator-reducer.h @@ -0,0 +1,50 @@ +// 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_GC_OPERATOR_REDUCER_H_ +#define V8_COMPILER_WASM_GC_OPERATOR_REDUCER_H_ + +#include "src/compiler/graph-reducer.h" +#include "src/compiler/wasm-graph-assembler.h" + +namespace v8 { +namespace internal { +namespace compiler { + +class MachineGraph; + +class WasmGCOperatorReducer final : public AdvancedReducer { + public: + WasmGCOperatorReducer(Editor* editor, MachineGraph* mcgraph, + const wasm::WasmModule* module); + + const char* reducer_name() const override { return "WasmGCOperatorReducer"; } + + Reduction Reduce(Node* node) final; + + private: + Reduction ReduceAssertNotNull(Node* node); + Reduction ReduceIsNull(Node* node); + Reduction ReduceWasmTypeCheck(Node* node); + Reduction ReduceWasmTypeCast(Node* node); + + Node* SetType(Node* node, wasm::ValueType type); + + Graph* graph() { return mcgraph_->graph(); } + CommonOperatorBuilder* common() { return mcgraph_->common(); } + + MachineGraph* mcgraph_; + WasmGraphAssembler gasm_; + const wasm::WasmModule* module_; +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_WASM_GC_OPERATOR_REDUCER_H_ diff --git a/src/compiler/wasm-graph-assembler.cc b/src/compiler/wasm-graph-assembler.cc index c92f5833d5..af35049b77 100644 --- a/src/compiler/wasm-graph-assembler.cc +++ b/src/compiler/wasm-graph-assembler.cc @@ -368,6 +368,10 @@ Node* WasmGraphAssembler::IsNull(Node* object) { return AddNode(graph()->NewNode(simplified_.IsNull(), object)); } +Node* WasmGraphAssembler::IsNotNull(Node* object) { + return AddNode(graph()->NewNode(simplified_.IsNotNull(), object)); +} + Node* WasmGraphAssembler::AssertNotNull(Node* object) { return AddNode(graph()->NewNode(simplified_.AssertNotNull(), object, effect(), control())); diff --git a/src/compiler/wasm-graph-assembler.h b/src/compiler/wasm-graph-assembler.h index 1dc1e942f4..d24590902b 100644 --- a/src/compiler/wasm-graph-assembler.h +++ b/src/compiler/wasm-graph-assembler.h @@ -248,6 +248,8 @@ class WasmGraphAssembler : public GraphAssembler { Node* IsNull(Node* object); + Node* IsNotNull(Node* object); + Node* AssertNotNull(Node* object); // Generic helpers. diff --git a/src/compiler/wasm-typer.cc b/src/compiler/wasm-typer.cc index c89d1ba8db..fa3fa509cc 100644 --- a/src/compiler/wasm-typer.cc +++ b/src/compiler/wasm-typer.cc @@ -103,6 +103,35 @@ Reduction WasmTyper::Reduce(Node* node) { break; } case IrOpcode::kAssertNotNull: { + { + Node* object = NodeProperties::GetValueInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // Optimize the common pattern where a TypeCast is followed by an + // AssertNotNull: Reverse the order of these operations, as this will + // unlock more optimizations later. + // We are implementing this in the typer so we can retype the nodes. + if (control->opcode() == IrOpcode::kWasmTypeCast && effect == object && + control == object) { + Node* initial_object = NodeProperties::GetValueInput(object, 0); + Node* previous_control = NodeProperties::GetControlInput(object); + Node* previous_effect = NodeProperties::GetEffectInput(object); + ReplaceWithValue(node, object); + node->ReplaceInput(NodeProperties::FirstValueIndex(node), + initial_object); + node->ReplaceInput(NodeProperties::FirstEffectIndex(node), + previous_effect); + node->ReplaceInput(NodeProperties::FirstControlIndex(node), + previous_control); + // We do not replace {object}'s effect input to {node} because {node} + // has no effect output. + object->ReplaceInput(NodeProperties::FirstValueIndex(object), node); + object->ReplaceInput(NodeProperties::FirstControlIndex(object), node); + Revisit(object); + } + } + if (!AllInputsTyped(node)) return NoChange(); TypeInModule object_type = NodeProperties::GetType(NodeProperties::GetValueInput(node, 0)) diff --git a/src/logging/runtime-call-stats.h b/src/logging/runtime-call-stats.h index 26dff2abc1..1b1371cd1c 100644 --- a/src/logging/runtime-call-stats.h +++ b/src/logging/runtime-call-stats.h @@ -380,6 +380,7 @@ class RuntimeCallTimer final { ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, VerifyGraph) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmBaseOptimization) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmGCLowering) \ + ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmGCOptimization) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmInlining) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmLoopPeeling) \ ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, WasmLoopUnrolling) \ diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index 26a4527b85..4db891f480 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -4220,12 +4220,10 @@ class WasmFullDecoder : public WasmDecoder { this->module_); } - // Checks it {obj} is a nominal type which is a subtype of {rtt}'s index, thus - // checking will always succeed. Does not account for nullability. + // Checks it {obj} is a subtype of {rtt}'s type, thus checking will always + // succeed. Does not account for nullability. bool TypeCheckAlwaysSucceeds(Value obj, Value rtt) { - return obj.type.has_index() && - this->module_->has_supertype(obj.type.ref_index()) && - IsSubtypeOf(obj.type, + return IsSubtypeOf(obj.type, ValueType::Ref(rtt.type.ref_index(), kNullable), this->module_); } diff --git a/src/wasm/wasm-subtyping.h b/src/wasm/wasm-subtyping.h index 343cd5b441..d963434492 100644 --- a/src/wasm/wasm-subtyping.h +++ b/src/wasm/wasm-subtyping.h @@ -90,6 +90,13 @@ V8_INLINE bool IsHeapSubtypeOf(HeapType subtype, HeapType supertype, return IsHeapSubtypeOfImpl(subtype, supertype, module, module); } +V8_INLINE bool HeapTypesUnrelated(HeapType heap1, HeapType heap2, + const WasmModule* module1, + const WasmModule* module2) { + return !IsHeapSubtypeOf(heap1, heap2, module1, module2) && + !IsHeapSubtypeOf(heap2, heap1, module2, module1); +} + // Checks whether {subtype_index} is valid as a declared subtype of // {supertype_index}. // - Both type must be of the same kind (function, struct, or array).