summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/wasm-inlining-into-js.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/wasm-inlining-into-js.cc')
-rw-r--r--deps/v8/src/compiler/wasm-inlining-into-js.cc346
1 files changed, 346 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/wasm-inlining-into-js.cc b/deps/v8/src/compiler/wasm-inlining-into-js.cc
new file mode 100644
index 0000000000..ac52e9ee52
--- /dev/null
+++ b/deps/v8/src/compiler/wasm-inlining-into-js.cc
@@ -0,0 +1,346 @@
+// Copyright 2023 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-inlining-into-js.h"
+
+#include "src/compiler/wasm-compiler-definitions.h"
+#include "src/compiler/wasm-compiler.h"
+#include "src/compiler/wasm-graph-assembler.h"
+#include "src/wasm/decoder.h"
+#include "src/wasm/wasm-linkage.h"
+#include "src/wasm/wasm-opcodes-inl.h"
+#include "src/wasm/wasm-subtyping.h"
+
+namespace v8::internal::compiler {
+
+namespace {
+
+using wasm::WasmOpcode;
+using wasm::WasmOpcodes;
+
+class WasmIntoJSInlinerImpl : private wasm::Decoder {
+ using ValidationTag = NoValidationTag;
+
+ struct Value {
+ Node* node = nullptr;
+ wasm::ValueType type = wasm::kWasmBottom;
+ };
+
+ public:
+ WasmIntoJSInlinerImpl(Zone* zone, const wasm::WasmModule* module,
+ MachineGraph* mcgraph, const wasm::FunctionBody& body,
+ const base::Vector<const byte>& bytes)
+ : wasm::Decoder(bytes.begin(), bytes.end()),
+ module_(module),
+ mcgraph_(mcgraph),
+ body_(body),
+ graph_(mcgraph->graph()),
+ gasm_(mcgraph, zone) {
+ // +1 for instance node.
+ size_t params = body.sig->parameter_count() + 1;
+ Node* start =
+ graph_->NewNode(mcgraph->common()->Start(static_cast<int>(params)));
+ graph_->SetStart(start);
+ graph_->SetEnd(graph_->NewNode(mcgraph->common()->End(0)));
+ gasm_.InitializeEffectControl(start, start);
+
+ // Initialize parameter nodes.
+ // We have to add another +1 as the minimum parameter index is actually
+ // -1, not 0...
+ size_t params_extended = params + 1;
+ parameters_ = zone->NewArray<Node*>(params_extended);
+ for (unsigned i = 0; i < params_extended; i++) {
+ parameters_[i] = nullptr;
+ }
+ // Instance node at parameter 0.
+ instance_node_ = Param(wasm::kWasmInstanceParameterIndex);
+ }
+
+ Node* Param(int index, const char* debug_name = nullptr) {
+ DCHECK_NOT_NULL(graph_->start());
+ // Turbofan allows negative parameter indices.
+ static constexpr int kMinParameterIndex = -1;
+ DCHECK_GE(index, kMinParameterIndex);
+ int array_index = index - kMinParameterIndex;
+ if (parameters_[array_index] == nullptr) {
+ parameters_[array_index] = graph_->NewNode(
+ mcgraph_->common()->Parameter(index, debug_name), graph_->start());
+ }
+ return parameters_[array_index];
+ }
+
+ bool TryInlining() {
+ if (body_.sig->return_count() > 1) {
+ return false; // Multi-return is not supported.
+ }
+ // Parse locals.
+ if (consume_u32v() != 0) {
+ // Functions with locals are not supported.
+ return false;
+ }
+ // Parse body.
+ // TODO(mliedtke): Use zone vector?
+ base::SmallVector<Value, 4> stack;
+ while (is_inlineable_) {
+ WasmOpcode opcode = ReadOpcode();
+ switch (opcode) {
+ case wasm::kExprExternInternalize:
+ DCHECK(!stack.empty());
+ stack.back() = ParseExternInternalize(stack.back());
+ continue;
+ case wasm::kExprExternExternalize:
+ DCHECK(!stack.empty());
+ stack.back() = ParseExternExternalize(stack.back());
+ continue;
+ case wasm::kExprRefCast:
+ case wasm::kExprRefCastNull:
+ DCHECK(!stack.empty());
+ stack.back() =
+ ParseRefCast(stack.back(), opcode == wasm::kExprRefCastNull);
+ continue;
+ case wasm::kExprArrayLen:
+ DCHECK(!stack.empty());
+ stack.back() = ParseArrayLen(stack.back());
+ continue;
+ case wasm::kExprArrayGet:
+ case wasm::kExprArrayGetS:
+ case wasm::kExprArrayGetU: {
+ DCHECK_GE(stack.size(), 2);
+ Value index = stack.back();
+ stack.pop_back();
+ Value array = stack.back();
+ stack.back() = ParseArrayGet(array, index, opcode);
+ continue;
+ }
+ case wasm::kExprArraySet: {
+ DCHECK_GE(stack.size(), 3);
+ Value value = stack.back();
+ stack.pop_back();
+ Value index = stack.back();
+ stack.pop_back();
+ Value array = stack.back();
+ stack.pop_back();
+ ParseArraySet(array, index, value);
+ continue;
+ }
+ case wasm::kExprStructGet:
+ case wasm::kExprStructGetS:
+ case wasm::kExprStructGetU:
+ DCHECK(!stack.empty());
+ stack.back() = ParseStructGet(stack.back(), opcode);
+ continue;
+ case wasm::kExprStructSet: {
+ DCHECK_GE(stack.size(), 2);
+ Value value = stack.back();
+ stack.pop_back();
+ Value wasm_struct = stack.back();
+ stack.pop_back();
+ ParseStructSet(wasm_struct, value);
+ continue;
+ }
+ case wasm::kExprLocalGet:
+ stack.push_back(ParseLocalGet());
+ continue;
+ case wasm::kExprDrop:
+ DCHECK(!stack.empty());
+ stack.pop_back();
+ continue;
+ case wasm::kExprEnd: {
+ DCHECK_LT(stack.size(), 2);
+ int return_count = static_cast<int>(stack.size());
+ base::SmallVector<Node*, 8> buf(return_count + 3);
+ buf[0] = mcgraph_->Int32Constant(0);
+ if (return_count) {
+ buf[1] = stack.back().node;
+ }
+ buf[return_count + 1] = gasm_.effect();
+ buf[return_count + 2] = gasm_.control();
+ Node* ret = graph_->NewNode(mcgraph_->common()->Return(return_count),
+ return_count + 3, buf.data());
+
+ gasm_.MergeControlToEnd(ret);
+ return true;
+ }
+ default:
+ // Instruction not supported for inlining.
+ return false;
+ }
+ }
+ // The decoder found an instruction it couldn't inline successfully.
+ return false;
+ }
+
+ private:
+ Value ParseExternInternalize(Value input) {
+ DCHECK(input.type.is_reference_to(wasm::HeapType::kExtern) ||
+ input.type.is_reference_to(wasm::HeapType::kNoExtern));
+ wasm::ValueType result_type = wasm::ValueType::RefMaybeNull(
+ wasm::HeapType::kAny, input.type.is_nullable()
+ ? wasm::Nullability::kNullable
+ : wasm::Nullability::kNonNullable);
+ Node* internalized = gasm_.WasmExternInternalize(input.node);
+ return {internalized, result_type};
+ }
+
+ Value ParseExternExternalize(Value input) {
+ DCHECK(input.type.is_reference());
+ wasm::ValueType result_type = wasm::ValueType::RefMaybeNull(
+ wasm::HeapType::kExtern, input.type.is_nullable()
+ ? wasm::Nullability::kNullable
+ : wasm::Nullability::kNonNullable);
+ Node* internalized = gasm_.WasmExternExternalize(input.node);
+ return {internalized, result_type};
+ }
+
+ Value ParseLocalGet() {
+ uint32_t index = consume_u32v();
+ DCHECK_LT(index, body_.sig->parameter_count());
+ return {Param(index + 1), body_.sig->GetParam(index)};
+ }
+
+ Value ParseStructGet(Value struct_val, WasmOpcode opcode) {
+ uint32_t struct_index = consume_u32v();
+ DCHECK(module_->has_struct(struct_index));
+ const wasm::StructType* struct_type = module_->struct_type(struct_index);
+ uint32_t field_index = consume_u32v();
+ DCHECK_GT(struct_type->field_count(), field_index);
+ const bool is_signed = opcode == wasm::kExprStructGetS;
+ const CheckForNull null_check =
+ struct_val.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
+ Node* member = gasm_.StructGet(struct_val.node, struct_type, field_index,
+ is_signed, null_check);
+ return {member, struct_type->field(field_index).Unpacked()};
+ }
+
+ void ParseStructSet(Value wasm_struct, Value value) {
+ uint32_t struct_index = consume_u32v();
+ DCHECK(module_->has_struct(struct_index));
+ const wasm::StructType* struct_type = module_->struct_type(struct_index);
+ uint32_t field_index = consume_u32v();
+ DCHECK_GT(struct_type->field_count(), field_index);
+ const CheckForNull null_check =
+ wasm_struct.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
+ gasm_.StructSet(wasm_struct.node, value.node, struct_type, field_index,
+ null_check);
+ }
+
+ Value ParseRefCast(Value input, bool null_succeeds) {
+ auto [heap_index, length] = read_i33v<ValidationTag>(pc_);
+ pc_ += length;
+ if (heap_index < 0) {
+ if ((heap_index & 0x7f) != wasm::kArrayRefCode) {
+ // Abstract casts for non array type are not supported.
+ is_inlineable_ = false;
+ return {};
+ }
+ auto done = gasm_.MakeLabel();
+ // Abstract cast to array.
+ if (input.type.is_nullable() && null_succeeds) {
+ gasm_.GotoIf(gasm_.IsNull(input.node, input.type), &done);
+ }
+ gasm_.TrapIf(gasm_.IsSmi(input.node), TrapId::kTrapIllegalCast);
+ gasm_.TrapUnless(gasm_.HasInstanceType(input.node, WASM_ARRAY_TYPE),
+ TrapId::kTrapIllegalCast);
+ gasm_.Goto(&done);
+ gasm_.Bind(&done);
+ // Add TypeGuard for graph typing.
+ Graph* graph = mcgraph_->graph();
+ wasm::ValueType result_type = wasm::ValueType::RefMaybeNull(
+ wasm::HeapType::kArray,
+ null_succeeds ? wasm::kNullable : wasm::kNonNullable);
+ Node* type_guard =
+ graph->NewNode(mcgraph_->common()->TypeGuard(
+ Type::Wasm(result_type, module_, graph->zone())),
+ input.node, gasm_.effect(), gasm_.control());
+ gasm_.InitializeEffectControl(type_guard, gasm_.control());
+ return {type_guard, result_type};
+ }
+ if (module_->has_signature(static_cast<uint32_t>(heap_index))) {
+ is_inlineable_ = false;
+ return {};
+ }
+ wasm::ValueType target_type = wasm::ValueType::RefMaybeNull(
+ static_cast<uint32_t>(heap_index),
+ null_succeeds ? wasm::kNullable : wasm::kNonNullable);
+ Node* rtt = mcgraph_->graph()->NewNode(
+ gasm_.simplified()->RttCanon(target_type.ref_index()), instance_node_);
+ Node* cast = gasm_.WasmTypeCast(input.node, rtt, {input.type, target_type});
+ return {cast, target_type};
+ }
+
+ Value ParseArrayLen(Value input) {
+ DCHECK(wasm::IsHeapSubtypeOf(input.type.heap_type(),
+ wasm::HeapType(wasm::HeapType::kArray),
+ module_));
+ const CheckForNull null_check =
+ input.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
+ Node* len = gasm_.ArrayLength(input.node, null_check);
+ return {len, wasm::kWasmI32};
+ }
+
+ Value ParseArrayGet(Value array, Value index, WasmOpcode opcode) {
+ uint32_t array_index = consume_u32v();
+ DCHECK(module_->has_array(array_index));
+ const wasm::ArrayType* array_type = module_->array_type(array_index);
+ const bool is_signed = opcode == WasmOpcode::kExprArrayGetS;
+ const CheckForNull null_check =
+ array.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
+ // Perform bounds check.
+ Node* length = gasm_.ArrayLength(array.node, null_check);
+ gasm_.TrapUnless(gasm_.Uint32LessThan(index.node, length),
+ TrapId::kTrapArrayOutOfBounds);
+ // Perform array.get.
+ Node* element =
+ gasm_.ArrayGet(array.node, index.node, array_type, is_signed);
+ return {element, array_type->element_type().Unpacked()};
+ }
+
+ void ParseArraySet(Value array, Value index, Value value) {
+ uint32_t array_index = consume_u32v();
+ DCHECK(module_->has_array(array_index));
+ const wasm::ArrayType* array_type = module_->array_type(array_index);
+ const CheckForNull null_check =
+ array.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
+ // Perform bounds check.
+ Node* length = gasm_.ArrayLength(array.node, null_check);
+ gasm_.TrapUnless(gasm_.Uint32LessThan(index.node, length),
+ TrapId::kTrapArrayOutOfBounds);
+ // Perform array.set.
+ gasm_.ArraySet(array.node, index.node, value.node, array_type);
+ }
+
+ WasmOpcode ReadOpcode() {
+ DCHECK_LT(pc_, end_);
+ WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
+ if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
+ ++pc_;
+ return opcode;
+ }
+ auto [opcode_with_prefix, length] =
+ read_prefixed_opcode<ValidationTag>(pc_);
+ pc_ += length;
+ return opcode_with_prefix;
+ }
+
+ const wasm::WasmModule* module_;
+ MachineGraph* mcgraph_;
+ const wasm::FunctionBody& body_;
+ Node** parameters_;
+ Graph* graph_;
+ Node* instance_node_;
+ WasmGraphAssembler gasm_;
+ bool is_inlineable_ = true;
+};
+
+} // anonymous namespace
+
+bool WasmIntoJSInliner::TryInlining(Zone* zone, const wasm::WasmModule* module,
+ MachineGraph* mcgraph,
+ const wasm::FunctionBody& body,
+ const base::Vector<const byte>& bytes) {
+ WasmIntoJSInlinerImpl inliner(zone, module, mcgraph, body, bytes);
+ return inliner.TryInlining();
+}
+
+} // namespace v8::internal::compiler