v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-inlining-into-js.cc
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
11#include "src/wasm/decoder.h"
15
16namespace v8::internal::compiler {
17
18namespace {
19
21using wasm::WasmOpcodes;
22
23static constexpr bool kNotShared = false;
24
25class WasmIntoJSInlinerImpl : private wasm::Decoder {
26 using ValidationTag = NoValidationTag;
27
28 struct Value {
29 Node* node = nullptr;
30 wasm::ValueType type = wasm::kWasmBottom;
31 };
32
33 public:
34 WasmIntoJSInlinerImpl(Zone* zone, const wasm::WasmModule* module,
35 MachineGraph* mcgraph, const wasm::FunctionBody& body,
36 base::Vector<const uint8_t> bytes,
37 SourcePositionTable* source_position_table,
38 int inlining_id)
39 : wasm::Decoder(bytes.begin(), bytes.end()),
40 module_(module),
41 mcgraph_(mcgraph),
42 body_(body),
43 graph_(mcgraph->graph()),
44 gasm_(mcgraph, zone),
45 source_position_table_(source_position_table),
46 inlining_id_(inlining_id) {
47 // +1 for instance node.
48 size_t params = body.sig->parameter_count() + 1;
49 Node* start =
50 graph_->NewNode(mcgraph->common()->Start(static_cast<int>(params)));
51 graph_->SetStart(start);
52 graph_->SetEnd(graph_->NewNode(mcgraph->common()->End(0)));
53 gasm_.InitializeEffectControl(start, start);
54
55 // Initialize parameter nodes.
56 // We have to add another +1 as the minimum parameter index is actually
57 // -1, not 0...
58 size_t params_extended = params + 1;
59 parameters_ = zone->AllocateArray<Node*>(params_extended);
60 for (unsigned i = 0; i < params_extended; i++) {
61 parameters_[i] = nullptr;
62 }
63 // Instance node at parameter 0.
65 }
66
67 Node* Param(int index, const char* debug_name = nullptr) {
68 DCHECK_NOT_NULL(graph_->start());
69 // Turbofan allows negative parameter indices.
71 int array_index = index - kMinParameterIndex;
72 if (parameters_[array_index] == nullptr) {
73 Node* param = graph_->NewNode(
74 mcgraph_->common()->Parameter(index, debug_name), graph_->start());
76 // Add a type guard to keep type information based on the inlinee's
77 // signature.
78 wasm::ValueType type = body_.sig->GetParam(index - 1);
79 Type tf_type = compiler::Type::Wasm(type, module_, graph_->zone());
80 param = gasm_.TypeGuard(tf_type, param);
81 }
82 parameters_[array_index] = param;
83 }
84 return parameters_[array_index];
85 }
86
87 bool TryInlining() {
88 if (body_.sig->return_count() > 1) {
89 return false; // Multi-return is not supported.
90 }
91 // Parse locals.
92 if (consume_u32v() != 0) {
93 // Functions with locals are not supported.
94 return false;
95 }
96 // Parse body.
97 base::SmallVector<Value, 4> stack;
98 while (is_inlineable_) {
99 WasmOpcode opcode = ReadOpcode();
100 switch (opcode) {
101 case wasm::kExprAnyConvertExtern:
102 DCHECK(!stack.empty());
103 stack.back() = ParseAnyConvertExtern(stack.back());
104 continue;
105 case wasm::kExprExternConvertAny:
106 DCHECK(!stack.empty());
107 stack.back() = ParseExternConvertAny(stack.back());
108 continue;
109 case wasm::kExprRefCast:
110 case wasm::kExprRefCastNull:
111 DCHECK(!stack.empty());
112 stack.back() =
113 ParseRefCast(stack.back(), opcode == wasm::kExprRefCastNull);
114 continue;
115 case wasm::kExprArrayLen:
116 DCHECK(!stack.empty());
117 stack.back() = ParseArrayLen(stack.back());
118 continue;
119 case wasm::kExprArrayGet:
120 case wasm::kExprArrayGetS:
121 case wasm::kExprArrayGetU: {
122 DCHECK_GE(stack.size(), 2);
123 Value index = stack.back();
124 stack.pop_back();
125 Value array = stack.back();
126 stack.back() = ParseArrayGet(array, index, opcode);
127 continue;
128 }
129 case wasm::kExprArraySet: {
130 DCHECK_GE(stack.size(), 3);
131 Value value = stack.back();
132 stack.pop_back();
133 Value index = stack.back();
134 stack.pop_back();
135 Value array = stack.back();
136 stack.pop_back();
137 ParseArraySet(array, index, value);
138 continue;
139 }
140 case wasm::kExprStructGet:
141 case wasm::kExprStructGetS:
142 case wasm::kExprStructGetU:
143 DCHECK(!stack.empty());
144 stack.back() = ParseStructGet(stack.back(), opcode);
145 continue;
146 case wasm::kExprStructSet: {
147 DCHECK_GE(stack.size(), 2);
148 Value value = stack.back();
149 stack.pop_back();
150 Value wasm_struct = stack.back();
151 stack.pop_back();
152 ParseStructSet(wasm_struct, value);
153 continue;
154 }
155 case wasm::kExprLocalGet:
156 stack.push_back(ParseLocalGet());
157 continue;
158 case wasm::kExprDrop:
159 DCHECK(!stack.empty());
160 stack.pop_back();
161 continue;
162 case wasm::kExprEnd: {
163 DCHECK_LT(stack.size(), 2);
164 int return_count = static_cast<int>(stack.size());
165 base::SmallVector<Node*, 8> buf(return_count + 3);
166 buf[0] = mcgraph_->Int32Constant(0);
167 if (return_count) {
168 buf[1] = stack.back().node;
169 }
170 buf[return_count + 1] = gasm_.effect();
171 buf[return_count + 2] = gasm_.control();
172 Node* ret = graph_->NewNode(mcgraph_->common()->Return(return_count),
173 return_count + 3, buf.data());
174
175 gasm_.MergeControlToEnd(ret);
176 return true;
177 }
178 default:
179 // Instruction not supported for inlining.
180 return false;
181 }
182 }
183 // The decoder found an instruction it couldn't inline successfully.
184 return false;
185 }
186
187 private:
188 Value ParseAnyConvertExtern(Value input) {
189 DCHECK(input.type.is_reference_to(wasm::HeapType::kExtern) ||
190 input.type.is_reference_to(wasm::HeapType::kNoExtern));
191 wasm::ValueType result_type = wasm::ValueType::Generic(
192 wasm::GenericKind::kAny, input.type.nullability(), kNotShared);
193 Node* internalized = gasm_.WasmAnyConvertExtern(input.node);
194 return TypeNode(internalized, result_type);
195 }
196
197 Value ParseExternConvertAny(Value input) {
198 DCHECK(input.type.is_reference());
199 wasm::ValueType result_type = wasm::ValueType::Generic(
200 wasm::GenericKind::kExtern, input.type.nullability(), kNotShared);
201 Node* internalized = gasm_.WasmExternConvertAny(input.node);
202 return TypeNode(internalized, result_type);
203 }
204
205 Value ParseLocalGet() {
206 uint32_t index = consume_u32v();
207 DCHECK_LT(index, body_.sig->parameter_count());
208 return TypeNode(Param(index + 1), body_.sig->GetParam(index));
209 }
210
211 Value ParseStructGet(Value struct_val, WasmOpcode opcode) {
212 wasm::ModuleTypeIndex struct_index{consume_u32v()};
213 DCHECK(module_->has_struct(struct_index));
214 const wasm::StructType* struct_type = module_->struct_type(struct_index);
215 uint32_t field_index = consume_u32v();
216 DCHECK_GT(struct_type->field_count(), field_index);
217 const bool is_signed = opcode == wasm::kExprStructGetS;
218 const CheckForNull null_check =
219 struct_val.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
220 Node* member = gasm_.StructGet(struct_val.node, struct_type, field_index,
221 is_signed, null_check);
222 SetSourcePosition(member);
223 return TypeNode(member, struct_type->field(field_index).Unpacked());
224 }
225
226 void ParseStructSet(Value wasm_struct, Value value) {
227 wasm::ModuleTypeIndex struct_index{consume_u32v()};
228 DCHECK(module_->has_struct(struct_index));
229 const wasm::StructType* struct_type = module_->struct_type(struct_index);
230 uint32_t field_index = consume_u32v();
231 DCHECK_GT(struct_type->field_count(), field_index);
232 const CheckForNull null_check =
233 wasm_struct.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
234 gasm_.StructSet(wasm_struct.node, value.node, struct_type, field_index,
235 null_check);
236 SetSourcePosition(gasm_.effect());
237 }
238
239 Value ParseRefCast(Value input, bool null_succeeds) {
240 auto [heap_index, length] = read_i33v<ValidationTag>(pc_);
241 pc_ += length;
242 if (heap_index < 0) {
243 if ((heap_index & 0x7f) != wasm::kArrayRefCode) {
244 // Abstract casts for non array type are not supported.
245 is_inlineable_ = false;
246 return {};
247 }
248 auto done = gasm_.MakeLabel();
249 // Abstract cast to array.
250 if (input.type.is_nullable() && null_succeeds) {
251 gasm_.GotoIf(gasm_.IsNull(input.node, input.type), &done);
252 }
253 gasm_.TrapIf(gasm_.IsSmi(input.node), TrapId::kTrapIllegalCast);
254 gasm_.TrapUnless(gasm_.HasInstanceType(input.node, WASM_ARRAY_TYPE),
255 TrapId::kTrapIllegalCast);
256 SetSourcePosition(gasm_.effect());
257 gasm_.Goto(&done);
258 gasm_.Bind(&done);
259 // Add TypeGuard for graph typing.
260 TFGraph* graph = mcgraph_->graph();
261 wasm::ValueType result_type = wasm::ValueType::Generic(
262 wasm::GenericKind::kArray,
264 Node* type_guard =
265 graph->NewNode(mcgraph_->common()->TypeGuard(
266 Type::Wasm(result_type, module_, graph->zone())),
267 input.node, gasm_.effect(), gasm_.control());
268 gasm_.InitializeEffectControl(type_guard, gasm_.control());
269 return TypeNode(type_guard, result_type);
270 }
271 wasm::ModuleTypeIndex target_type_index{static_cast<uint32_t>(heap_index)};
272 if (module_->has_signature(target_type_index)) {
273 is_inlineable_ = false;
274 return {};
275 }
276 wasm::ValueType target_type = wasm::ValueType::RefMaybeNull(
277 module_->heap_type(target_type_index),
279 Node* rtt = mcgraph_->graph()->NewNode(
280 gasm_.simplified()->RttCanon(target_type.ref_index()),
281 trusted_data_node_);
282 // Technically this is incorrect: the {rtt} node doesn't hold a reference
283 // to an object of type {target_type}, but to such an object's map. But
284 // we only need this type annotation so {ReduceWasmTypeCast} can get to
285 // the {ref_index}, we never need the type's {kind()}.
286 TypeNode(rtt, wasm::ValueType::Ref(target_type.heap_type()));
287 Node* cast = gasm_.WasmTypeCast(
288 input.node, rtt,
289 {input.type, target_type,
290 module_->type(target_type_index).is_final ? kExactMatchOnly
291 : kMayBeSubtype});
292 SetSourcePosition(cast);
293 return TypeNode(cast, target_type);
294 }
295
296 Value ParseArrayLen(Value input) {
297 DCHECK(wasm::IsHeapSubtypeOf(input.type.heap_type(),
298 wasm::kWasmArrayRef.heap_type(), module_));
299 const CheckForNull null_check =
300 input.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
301 Node* len = gasm_.ArrayLength(input.node, null_check);
302 SetSourcePosition(len);
303 return TypeNode(len, wasm::kWasmI32);
304 }
305
306 Value ParseArrayGet(Value array, Value index, WasmOpcode opcode) {
307 wasm::ModuleTypeIndex array_index{consume_u32v()};
308 DCHECK(module_->has_array(array_index));
309 const wasm::ArrayType* array_type = module_->array_type(array_index);
310 const bool is_signed = opcode == WasmOpcode::kExprArrayGetS;
311 const CheckForNull null_check =
312 array.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
313 // Perform bounds check.
314 Node* length = gasm_.ArrayLength(array.node, null_check);
315 SetSourcePosition(length);
316 gasm_.TrapUnless(gasm_.Uint32LessThan(index.node, length),
317 TrapId::kTrapArrayOutOfBounds);
318 SetSourcePosition(gasm_.effect());
319 // Perform array.get.
320 Node* element =
321 gasm_.ArrayGet(array.node, index.node, array_type, is_signed);
322 return TypeNode(element, array_type->element_type().Unpacked());
323 }
324
325 void ParseArraySet(Value array, Value index, Value value) {
326 wasm::ModuleTypeIndex array_index{consume_u32v()};
327 DCHECK(module_->has_array(array_index));
328 const wasm::ArrayType* array_type = module_->array_type(array_index);
329 const CheckForNull null_check =
330 array.type.is_nullable() ? kWithNullCheck : kWithoutNullCheck;
331 // Perform bounds check.
332 Node* length = gasm_.ArrayLength(array.node, null_check);
333 SetSourcePosition(length);
334 gasm_.TrapUnless(gasm_.Uint32LessThan(index.node, length),
335 TrapId::kTrapArrayOutOfBounds);
336 SetSourcePosition(gasm_.effect());
337 // Perform array.set.
338 gasm_.ArraySet(array.node, index.node, value.node, array_type);
339 }
340
341 WasmOpcode ReadOpcode() {
344 WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
345 if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
346 ++pc_;
347 return opcode;
348 }
349 auto [opcode_with_prefix, length] =
350 read_prefixed_opcode<ValidationTag>(pc_);
351 pc_ += length;
352 return opcode_with_prefix;
353 }
354
355 Value TypeNode(Node* node, wasm::ValueType type) {
357 node, compiler::Type::Wasm(type, module_, graph_->zone()));
358 return {node, type};
359 }
360
361 void SetSourcePosition(Node* node) {
362 if (!source_position_table_->IsEnabled()) return;
363 int offset = static_cast<int>(instruction_start_ - start());
364 source_position_table_->SetSourcePosition(
365 node, SourcePosition(offset, inlining_id_));
366 }
367
368 const wasm::WasmModule* module_;
369 MachineGraph* mcgraph_;
370 const wasm::FunctionBody& body_;
372 TFGraph* graph_;
374 WasmGraphAssembler gasm_;
375 SourcePositionTable* source_position_table_ = nullptr;
376 const uint8_t* instruction_start_ = pc_;
378 bool is_inlineable_ = true;
379};
380
381} // anonymous namespace
382
384 MachineGraph* mcgraph,
385 const wasm::FunctionBody& body,
387 SourcePositionTable* source_position_table,
388 int inlining_id) {
389 WasmIntoJSInlinerImpl inliner(zone, module, mcgraph, body, bytes,
390 source_position_table, inlining_id);
391 return inliner.TryInlining();
392}
393
394} // namespace v8::internal::compiler
friend Zone
Definition asm-types.cc:195
static void SetType(Node *node, Type type)
static bool TryInlining(Zone *zone, const wasm::WasmModule *module, MachineGraph *mcgraph, const wasm::FunctionBody &body, base::Vector< const uint8_t > bytes, SourcePositionTable *source_position_table, int inlining_id)
constexpr HeapType heap_type() const
static constexpr ValueType Generic(GenericKind kind, Nullability nullable, bool shared)
Definition value-type.h:883
static constexpr ValueType Ref(ModuleTypeIndex index, bool shared, RefTypeKind kind)
Definition value-type.h:887
static constexpr ValueType RefMaybeNull(ModuleTypeIndex index, Nullability nullable, bool shared, RefTypeKind kind)
Definition value-type.h:903
const v8::base::TimeTicks end_
Definition sweeper.cc:54
int start
int end
int32_t offset
Node * node
ZoneStack< RpoNumber > & stack
bool null_succeeds
Node::Uses::const_iterator begin(const Node::Uses &uses)
Definition node.h:708
constexpr int kWasmInstanceDataParameterIndex
V8_INLINE bool IsHeapSubtypeOf(HeapType subtype, HeapType supertype, const WasmModule *sub_module, const WasmModule *super_module)
constexpr IndependentValueType kWasmI32
constexpr IndependentHeapType kWasmBottom
constexpr IndependentHeapType kWasmArrayRef
constexpr int kMinParameterIndex
Definition globals.h:2790
bool is_signed(Condition cond)
Definition c-api.cc:87
uint32_t cast
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_GT(v1, v2)
Definition logging.h:487
Node * trusted_data_node_
const uint8_t * instruction_start_
TFGraph * graph_
Node ** parameters_
MachineGraph * mcgraph_
const wasm::FunctionBody & body_
SourcePositionTable * source_position_table_
bool is_inlineable_
WasmGraphAssembler gasm_
int inlining_id_
const wasm::WasmModule * module_