1 // Copyright 2015 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
5 #include "src/compiler/wasm-compiler.h"
6
7 #include <memory>
8
9 #include "src/api/api-inl.h"
10 #include "src/base/optional.h"
11 #include "src/base/platform/elapsed-timer.h"
12 #include "src/base/platform/platform.h"
13 #include "src/base/platform/wrappers.h"
14 #include "src/base/small-vector.h"
15 #include "src/base/v8-fallthrough.h"
16 #include "src/base/vector.h"
17 #include "src/codegen/assembler-inl.h"
18 #include "src/codegen/assembler.h"
19 #include "src/codegen/code-factory.h"
20 #include "src/codegen/compiler.h"
21 #include "src/codegen/interface-descriptors-inl.h"
22 #include "src/codegen/machine-type.h"
23 #include "src/codegen/optimized-compilation-info.h"
24 #include "src/compiler/backend/code-generator.h"
25 #include "src/compiler/backend/instruction-selector.h"
26 #include "src/compiler/common-operator.h"
27 #include "src/compiler/compiler-source-position-table.h"
28 #include "src/compiler/diamond.h"
29 #include "src/compiler/fast-api-calls.h"
30 #include "src/compiler/graph-assembler.h"
31 #include "src/compiler/graph-visualizer.h"
32 #include "src/compiler/graph.h"
33 #include "src/compiler/int64-lowering.h"
34 #include "src/compiler/linkage.h"
35 #include "src/compiler/machine-operator.h"
36 #include "src/compiler/node-matchers.h"
37 #include "src/compiler/node-origin-table.h"
38 #include "src/compiler/node-properties.h"
39 #include "src/compiler/pipeline.h"
40 #include "src/compiler/zone-stats.h"
41 #include "src/execution/isolate-inl.h"
42 #include "src/execution/simulator.h"
43 #include "src/heap/factory.h"
44 #include "src/logging/counters.h"
45 #include "src/logging/log.h"
46 #include "src/objects/heap-number.h"
47 #include "src/objects/instance-type.h"
48 #include "src/roots/roots.h"
49 #include "src/tracing/trace-event.h"
50 #include "src/trap-handler/trap-handler.h"
51 #include "src/wasm/code-space-access.h"
52 #include "src/wasm/function-body-decoder-impl.h"
53 #include "src/wasm/function-compiler.h"
54 #include "src/wasm/graph-builder-interface.h"
55 #include "src/wasm/jump-table-assembler.h"
56 #include "src/wasm/memory-tracing.h"
57 #include "src/wasm/object-access.h"
58 #include "src/wasm/wasm-code-manager.h"
59 #include "src/wasm/wasm-constants.h"
60 #include "src/wasm/wasm-engine.h"
61 #include "src/wasm/wasm-limits.h"
62 #include "src/wasm/wasm-linkage.h"
63 #include "src/wasm/wasm-module.h"
64 #include "src/wasm/wasm-objects-inl.h"
65 #include "src/wasm/wasm-opcodes-inl.h"
66
67 namespace v8 {
68 namespace internal {
69 namespace compiler {
70
71 namespace {
72
73 #define FATAL_UNSUPPORTED_OPCODE(opcode) \
74 FATAL("Unsupported opcode 0x%x:%s", (opcode), \
75 wasm::WasmOpcodes::OpcodeName(opcode));
76
assert_size(int expected_size,MachineType type)77 MachineType assert_size(int expected_size, MachineType type) {
78 DCHECK_EQ(expected_size, ElementSizeInBytes(type.representation()));
79 return type;
80 }
81
82 #define WASM_INSTANCE_OBJECT_SIZE(name) \
83 (WasmInstanceObject::k##name##OffsetEnd - \
84 WasmInstanceObject::k##name##Offset + 1) // NOLINT(whitespace/indent)
85
86 #define LOAD_MUTABLE_INSTANCE_FIELD(name, type) \
87 gasm_->LoadFromObject( \
88 assert_size(WASM_INSTANCE_OBJECT_SIZE(name), type), GetInstance(), \
89 wasm::ObjectAccess::ToTagged(WasmInstanceObject::k##name##Offset))
90
91 #define LOAD_INSTANCE_FIELD(name, type) \
92 gasm_->LoadImmutable( \
93 assert_size(WASM_INSTANCE_OBJECT_SIZE(name), type), GetInstance(), \
94 wasm::ObjectAccess::ToTagged(WasmInstanceObject::k##name##Offset))
95
96 #define LOAD_INSTANCE_FIELD_NO_ELIMINATION(name, type) \
97 gasm_->Load( \
98 assert_size(WASM_INSTANCE_OBJECT_SIZE(name), type), GetInstance(), \
99 wasm::ObjectAccess::ToTagged(WasmInstanceObject::k##name##Offset))
100
101 // Use MachineType::Pointer() over Tagged() to load root pointers because they
102 // do not get compressed.
103 #define LOAD_ROOT(root_name, factory_name) \
104 (parameter_mode_ == kNoSpecialParameterMode \
105 ? graph()->NewNode(mcgraph()->common()->HeapConstant( \
106 isolate_->factory()->factory_name())) \
107 : gasm_->LoadImmutable( \
108 MachineType::Pointer(), BuildLoadIsolateRoot(), \
109 IsolateData::root_slot_offset(RootIndex::k##root_name)))
110
ContainsSimd(const wasm::FunctionSig * sig)111 bool ContainsSimd(const wasm::FunctionSig* sig) {
112 for (auto type : sig->all()) {
113 if (type == wasm::kWasmS128) return true;
114 }
115 return false;
116 }
117
ContainsInt64(const wasm::FunctionSig * sig)118 bool ContainsInt64(const wasm::FunctionSig* sig) {
119 for (auto type : sig->all()) {
120 if (type == wasm::kWasmI64) return true;
121 }
122 return false;
123 }
124
WasmRuntimeStubIdToBuiltinName(wasm::WasmCode::RuntimeStubId runtime_stub_id)125 constexpr Builtin WasmRuntimeStubIdToBuiltinName(
126 wasm::WasmCode::RuntimeStubId runtime_stub_id) {
127 switch (runtime_stub_id) {
128 #define DEF_CASE(name) \
129 case wasm::WasmCode::k##name: \
130 return Builtin::k##name;
131 #define DEF_TRAP_CASE(name) DEF_CASE(ThrowWasm##name)
132 WASM_RUNTIME_STUB_LIST(DEF_CASE, DEF_TRAP_CASE)
133 #undef DEF_CASE
134 #undef DEF_TRAP_CASE
135 default:
136 UNREACHABLE();
137 }
138 }
139
GetBuiltinCallDescriptor(Builtin name,Zone * zone,StubCallMode stub_mode,bool needs_frame_state=false,Operator::Properties properties=Operator::kNoProperties)140 CallDescriptor* GetBuiltinCallDescriptor(
141 Builtin name, Zone* zone, StubCallMode stub_mode,
142 bool needs_frame_state = false,
143 Operator::Properties properties = Operator::kNoProperties) {
144 CallInterfaceDescriptor interface_descriptor =
145 Builtins::CallInterfaceDescriptorFor(name);
146 return Linkage::GetStubCallDescriptor(
147 zone, // zone
148 interface_descriptor, // descriptor
149 interface_descriptor.GetStackParameterCount(), // stack parameter count
150 needs_frame_state ? CallDescriptor::kNeedsFrameState
151 : CallDescriptor::kNoFlags, // flags
152 properties, // properties
153 stub_mode); // stub call mode
154 }
155
ObjectAccessForGCStores(wasm::ValueType type)156 ObjectAccess ObjectAccessForGCStores(wasm::ValueType type) {
157 return ObjectAccess(
158 MachineType::TypeForRepresentation(type.machine_representation(),
159 !type.is_packed()),
160 type.is_reference() ? kFullWriteBarrier : kNoWriteBarrier);
161 }
162 } // namespace
163
JSWasmCallData(const wasm::FunctionSig * wasm_signature)164 JSWasmCallData::JSWasmCallData(const wasm::FunctionSig* wasm_signature)
165 : result_needs_conversion_(wasm_signature->return_count() == 1 &&
166 wasm_signature->GetReturn().kind() ==
167 wasm::kI64) {
168 arg_needs_conversion_.resize(wasm_signature->parameter_count());
169 for (size_t i = 0; i < wasm_signature->parameter_count(); i++) {
170 wasm::ValueType type = wasm_signature->GetParam(i);
171 arg_needs_conversion_[i] = type.kind() == wasm::kI64;
172 }
173 }
174
175 class WasmGraphAssembler : public GraphAssembler {
176 public:
WasmGraphAssembler(MachineGraph * mcgraph,Zone * zone)177 WasmGraphAssembler(MachineGraph* mcgraph, Zone* zone)
178 : GraphAssembler(mcgraph, zone), simplified_(zone) {}
179
180 template <typename... Args>
CallRuntimeStub(wasm::WasmCode::RuntimeStubId stub_id,Operator::Properties properties,Args * ...args)181 Node* CallRuntimeStub(wasm::WasmCode::RuntimeStubId stub_id,
182 Operator::Properties properties, Args*... args) {
183 auto* call_descriptor = GetBuiltinCallDescriptor(
184 WasmRuntimeStubIdToBuiltinName(stub_id), temp_zone(),
185 StubCallMode::kCallWasmRuntimeStub, false, properties);
186 // A direct call to a wasm runtime stub defined in this module.
187 // Just encode the stub index. This will be patched at relocation.
188 Node* call_target = mcgraph()->RelocatableIntPtrConstant(
189 stub_id, RelocInfo::WASM_STUB_CALL);
190 return Call(call_descriptor, call_target, args...);
191 }
192
193 template <typename... Args>
CallBuiltin(Builtin name,Operator::Properties properties,Args * ...args)194 Node* CallBuiltin(Builtin name, Operator::Properties properties,
195 Args*... args) {
196 auto* call_descriptor = GetBuiltinCallDescriptor(
197 name, temp_zone(), StubCallMode::kCallBuiltinPointer, false,
198 properties);
199 Node* call_target = GetBuiltinPointerTarget(name);
200 return Call(call_descriptor, call_target, args...);
201 }
202
MergeControlToEnd(Node * node)203 void MergeControlToEnd(Node* node) {
204 NodeProperties::MergeControlToEnd(graph(), mcgraph()->common(), node);
205 }
206
AssertFalse(Node * condition)207 void AssertFalse(Node* condition) {
208 #if DEBUG
209 if (FLAG_debug_code) {
210 auto ok = MakeLabel();
211 GotoIfNot(condition, &ok);
212 Unreachable();
213 Bind(&ok);
214 }
215 #endif
216 }
217
GetBuiltinPointerTarget(Builtin builtin)218 Node* GetBuiltinPointerTarget(Builtin builtin) {
219 static_assert(std::is_same<Smi, BuiltinPtr>(), "BuiltinPtr must be Smi");
220 return NumberConstant(static_cast<int>(builtin));
221 }
222
223 // Sets {true_node} and {false_node} to their corresponding Branch outputs.
224 // Returns the Branch node. Does not change control().
Branch(Node * cond,Node ** true_node,Node ** false_node,BranchHint hint)225 Node* Branch(Node* cond, Node** true_node, Node** false_node,
226 BranchHint hint) {
227 DCHECK_NOT_NULL(cond);
228 Node* branch =
229 graph()->NewNode(mcgraph()->common()->Branch(hint), cond, control());
230 *true_node = graph()->NewNode(mcgraph()->common()->IfTrue(), branch);
231 *false_node = graph()->NewNode(mcgraph()->common()->IfFalse(), branch);
232 return branch;
233 }
234
NumberConstant(volatile double value)235 Node* NumberConstant(volatile double value) {
236 return graph()->NewNode(mcgraph()->common()->NumberConstant(value));
237 }
238
239 // Helper functions for dealing with HeapObjects.
240 // Rule of thumb: if access to a given field in an object is required in
241 // at least two places, put a helper function here.
242
Allocate(int size)243 Node* Allocate(int size) {
244 AllowLargeObjects allow_large = size < kMaxRegularHeapObjectSize
245 ? AllowLargeObjects::kFalse
246 : AllowLargeObjects::kTrue;
247 return Allocate(Int32Constant(size), allow_large);
248 }
249
Allocate(Node * size,AllowLargeObjects allow_large=AllowLargeObjects::kTrue)250 Node* Allocate(Node* size,
251 AllowLargeObjects allow_large = AllowLargeObjects::kTrue) {
252 return AddNode(
253 graph()->NewNode(simplified_.AllocateRaw(
254 Type::Any(), AllocationType::kYoung, allow_large),
255 size, effect(), control()));
256 }
257
LoadFromObject(MachineType type,Node * base,Node * offset)258 Node* LoadFromObject(MachineType type, Node* base, Node* offset) {
259 return AddNode(graph()->NewNode(
260 simplified_.LoadFromObject(ObjectAccess(type, kNoWriteBarrier)), base,
261 offset, effect(), control()));
262 }
263
LoadFromObject(MachineType type,Node * base,int offset)264 Node* LoadFromObject(MachineType type, Node* base, int offset) {
265 return LoadFromObject(type, base, IntPtrConstant(offset));
266 }
267
LoadImmutableFromObject(MachineType type,Node * base,Node * offset)268 Node* LoadImmutableFromObject(MachineType type, Node* base, Node* offset) {
269 return AddNode(graph()->NewNode(simplified_.LoadImmutableFromObject(
270 ObjectAccess(type, kNoWriteBarrier)),
271 base, offset, effect(), control()));
272 }
273
LoadImmutableFromObject(MachineType type,Node * base,int offset)274 Node* LoadImmutableFromObject(MachineType type, Node* base, int offset) {
275 return LoadImmutableFromObject(type, base, IntPtrConstant(offset));
276 }
277
LoadImmutable(LoadRepresentation rep,Node * base,Node * offset)278 Node* LoadImmutable(LoadRepresentation rep, Node* base, Node* offset) {
279 return AddNode(graph()->NewNode(mcgraph()->machine()->LoadImmutable(rep),
280 base, offset));
281 }
282
LoadImmutable(LoadRepresentation rep,Node * base,int offset)283 Node* LoadImmutable(LoadRepresentation rep, Node* base, int offset) {
284 return LoadImmutable(rep, base, IntPtrConstant(offset));
285 }
286
StoreToObject(ObjectAccess access,Node * base,Node * offset,Node * value)287 Node* StoreToObject(ObjectAccess access, Node* base, Node* offset,
288 Node* value) {
289 return AddNode(graph()->NewNode(simplified_.StoreToObject(access), base,
290 offset, value, effect(), control()));
291 }
292
StoreToObject(ObjectAccess access,Node * base,int offset,Node * value)293 Node* StoreToObject(ObjectAccess access, Node* base, int offset,
294 Node* value) {
295 return StoreToObject(access, base, IntPtrConstant(offset), value);
296 }
297
InitializeImmutableInObject(ObjectAccess access,Node * base,Node * offset,Node * value)298 Node* InitializeImmutableInObject(ObjectAccess access, Node* base,
299 Node* offset, Node* value) {
300 return AddNode(
301 graph()->NewNode(simplified_.InitializeImmutableInObject(access), base,
302 offset, value, effect(), control()));
303 }
304
InitializeImmutableInObject(ObjectAccess access,Node * base,int offset,Node * value)305 Node* InitializeImmutableInObject(ObjectAccess access, Node* base, int offset,
306 Node* value) {
307 return InitializeImmutableInObject(access, base, IntPtrConstant(offset),
308 value);
309 }
310
IsI31(Node * object)311 Node* IsI31(Node* object) {
312 if (COMPRESS_POINTERS_BOOL) {
313 return Word32Equal(Word32And(object, Int32Constant(kSmiTagMask)),
314 Int32Constant(kSmiTag));
315 } else {
316 return WordEqual(WordAnd(object, IntPtrConstant(kSmiTagMask)),
317 IntPtrConstant(kSmiTag));
318 }
319 }
320
321 // Maps and their contents.
LoadMap(Node * object)322 Node* LoadMap(Node* object) {
323 Node* map_word =
324 LoadImmutableFromObject(MachineType::TaggedPointer(), object,
325 HeapObject::kMapOffset - kHeapObjectTag);
326 #ifdef V8_MAP_PACKING
327 return UnpackMapWord(map_word);
328 #else
329 return map_word;
330 #endif
331 }
332
StoreMap(Node * heap_object,Node * map)333 void StoreMap(Node* heap_object, Node* map) {
334 ObjectAccess access(MachineType::TaggedPointer(), kMapWriteBarrier);
335 #ifdef V8_MAP_PACKING
336 map = PackMapWord(TNode<Map>::UncheckedCast(map));
337 #endif
338 InitializeImmutableInObject(access, heap_object,
339 HeapObject::kMapOffset - kHeapObjectTag, map);
340 }
341
LoadInstanceType(Node * map)342 Node* LoadInstanceType(Node* map) {
343 return LoadImmutableFromObject(
344 MachineType::Uint16(), map,
345 wasm::ObjectAccess::ToTagged(Map::kInstanceTypeOffset));
346 }
LoadWasmTypeInfo(Node * map)347 Node* LoadWasmTypeInfo(Node* map) {
348 int offset = Map::kConstructorOrBackPointerOrNativeContextOffset;
349 return LoadImmutableFromObject(MachineType::TaggedPointer(), map,
350 wasm::ObjectAccess::ToTagged(offset));
351 }
352
LoadSupertypes(Node * wasm_type_info)353 Node* LoadSupertypes(Node* wasm_type_info) {
354 return LoadImmutableFromObject(
355 MachineType::TaggedPointer(), wasm_type_info,
356 wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset));
357 }
358
359 // FixedArrays.
360
LoadFixedArrayLengthAsSmi(Node * fixed_array)361 Node* LoadFixedArrayLengthAsSmi(Node* fixed_array) {
362 return LoadImmutableFromObject(
363 MachineType::TaggedSigned(), fixed_array,
364 wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset));
365 }
366
LoadFixedArrayElement(Node * fixed_array,Node * index_intptr,MachineType type=MachineType::AnyTagged ())367 Node* LoadFixedArrayElement(Node* fixed_array, Node* index_intptr,
368 MachineType type = MachineType::AnyTagged()) {
369 Node* offset = IntAdd(
370 IntMul(index_intptr, IntPtrConstant(kTaggedSize)),
371 IntPtrConstant(wasm::ObjectAccess::ToTagged(FixedArray::kHeaderSize)));
372 return LoadFromObject(type, fixed_array, offset);
373 }
374
LoadImmutableFixedArrayElement(Node * fixed_array,Node * index_intptr,MachineType type=MachineType::AnyTagged ())375 Node* LoadImmutableFixedArrayElement(
376 Node* fixed_array, Node* index_intptr,
377 MachineType type = MachineType::AnyTagged()) {
378 Node* offset = IntAdd(
379 IntMul(index_intptr, IntPtrConstant(kTaggedSize)),
380 IntPtrConstant(wasm::ObjectAccess::ToTagged(FixedArray::kHeaderSize)));
381 return LoadImmutableFromObject(type, fixed_array, offset);
382 }
383
LoadFixedArrayElement(Node * array,int index,MachineType type)384 Node* LoadFixedArrayElement(Node* array, int index, MachineType type) {
385 return LoadFromObject(
386 type, array,
387 wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(index));
388 }
389
LoadFixedArrayElementSmi(Node * array,int index)390 Node* LoadFixedArrayElementSmi(Node* array, int index) {
391 return LoadFixedArrayElement(array, index, MachineType::TaggedSigned());
392 }
393
LoadFixedArrayElementPtr(Node * array,int index)394 Node* LoadFixedArrayElementPtr(Node* array, int index) {
395 return LoadFixedArrayElement(array, index, MachineType::TaggedPointer());
396 }
397
LoadFixedArrayElementAny(Node * array,int index)398 Node* LoadFixedArrayElementAny(Node* array, int index) {
399 return LoadFixedArrayElement(array, index, MachineType::AnyTagged());
400 }
401
StoreFixedArrayElement(Node * array,int index,Node * value,ObjectAccess access)402 Node* StoreFixedArrayElement(Node* array, int index, Node* value,
403 ObjectAccess access) {
404 return StoreToObject(
405 access, array,
406 wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(index), value);
407 }
408
StoreFixedArrayElementSmi(Node * array,int index,Node * value)409 Node* StoreFixedArrayElementSmi(Node* array, int index, Node* value) {
410 return StoreFixedArrayElement(
411 array, index, value,
412 ObjectAccess(MachineType::TaggedSigned(), kNoWriteBarrier));
413 }
414
StoreFixedArrayElementAny(Node * array,int index,Node * value)415 Node* StoreFixedArrayElementAny(Node* array, int index, Node* value) {
416 return StoreFixedArrayElement(
417 array, index, value,
418 ObjectAccess(MachineType::AnyTagged(), kFullWriteBarrier));
419 }
420
421 // Functions, SharedFunctionInfos, FunctionData.
422
LoadSharedFunctionInfo(Node * js_function)423 Node* LoadSharedFunctionInfo(Node* js_function) {
424 return LoadFromObject(
425 MachineType::TaggedPointer(), js_function,
426 wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction());
427 }
LoadContextFromJSFunction(Node * js_function)428 Node* LoadContextFromJSFunction(Node* js_function) {
429 return LoadFromObject(
430 MachineType::TaggedPointer(), js_function,
431 wasm::ObjectAccess::ContextOffsetInTaggedJSFunction());
432 }
433
LoadFunctionDataFromJSFunction(Node * js_function)434 Node* LoadFunctionDataFromJSFunction(Node* js_function) {
435 Node* shared = LoadSharedFunctionInfo(js_function);
436 return LoadFromObject(
437 MachineType::TaggedPointer(), shared,
438 wasm::ObjectAccess::ToTagged(SharedFunctionInfo::kFunctionDataOffset));
439 }
440
LoadExportedFunctionIndexAsSmi(Node * exported_function_data)441 Node* LoadExportedFunctionIndexAsSmi(Node* exported_function_data) {
442 return LoadImmutableFromObject(
443 MachineType::TaggedSigned(), exported_function_data,
444 wasm::ObjectAccess::ToTagged(
445 WasmExportedFunctionData::kFunctionIndexOffset));
446 }
LoadExportedFunctionInstance(Node * exported_function_data)447 Node* LoadExportedFunctionInstance(Node* exported_function_data) {
448 return LoadImmutableFromObject(
449 MachineType::TaggedPointer(), exported_function_data,
450 wasm::ObjectAccess::ToTagged(
451 WasmExportedFunctionData::kInstanceOffset));
452 }
453
454 // JavaScript objects.
455
LoadJSArrayElements(Node * js_array)456 Node* LoadJSArrayElements(Node* js_array) {
457 return LoadFromObject(
458 MachineType::AnyTagged(), js_array,
459 wasm::ObjectAccess::ToTagged(JSObject::kElementsOffset));
460 }
461
462 // WasmGC objects.
463
FieldOffset(const wasm::StructType * type,uint32_t field_index)464 Node* FieldOffset(const wasm::StructType* type, uint32_t field_index) {
465 return IntPtrConstant(wasm::ObjectAccess::ToTagged(
466 WasmStruct::kHeaderSize + type->field_offset(field_index)));
467 }
468
StoreStructField(Node * struct_object,const wasm::StructType * type,uint32_t field_index,Node * value)469 Node* StoreStructField(Node* struct_object, const wasm::StructType* type,
470 uint32_t field_index, Node* value) {
471 ObjectAccess access = ObjectAccessForGCStores(type->field(field_index));
472 return type->mutability(field_index)
473 ? StoreToObject(access, struct_object,
474 FieldOffset(type, field_index), value)
475 : InitializeImmutableInObject(access, struct_object,
476 FieldOffset(type, field_index),
477 value);
478 }
479
WasmArrayElementOffset(Node * index,wasm::ValueType element_type)480 Node* WasmArrayElementOffset(Node* index, wasm::ValueType element_type) {
481 Node* index_intptr =
482 mcgraph()->machine()->Is64() ? ChangeUint32ToUint64(index) : index;
483 return IntAdd(
484 IntPtrConstant(wasm::ObjectAccess::ToTagged(WasmArray::kHeaderSize)),
485 IntMul(index_intptr, IntPtrConstant(element_type.value_kind_size())));
486 }
487
LoadWasmArrayLength(Node * array)488 Node* LoadWasmArrayLength(Node* array) {
489 return LoadImmutableFromObject(
490 MachineType::Uint32(), array,
491 wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset));
492 }
493
IsDataRefMap(Node * map)494 Node* IsDataRefMap(Node* map) {
495 Node* instance_type = LoadInstanceType(map);
496 // We're going to test a range of WasmObject instance types with a single
497 // unsigned comparison.
498 Node* comparison_value =
499 Int32Sub(instance_type, Int32Constant(FIRST_WASM_OBJECT_TYPE));
500 return Uint32LessThanOrEqual(
501 comparison_value,
502 Int32Constant(LAST_WASM_OBJECT_TYPE - FIRST_WASM_OBJECT_TYPE));
503 }
504
505 // Generic HeapObject helpers.
506
HasInstanceType(Node * heap_object,InstanceType type)507 Node* HasInstanceType(Node* heap_object, InstanceType type) {
508 Node* map = LoadMap(heap_object);
509 Node* instance_type = LoadInstanceType(map);
510 return Word32Equal(instance_type, Int32Constant(type));
511 }
512
simplified()513 SimplifiedOperatorBuilder* simplified() { return &simplified_; }
514
515 private:
516 SimplifiedOperatorBuilder simplified_;
517 };
518
WasmGraphBuilder(wasm::CompilationEnv * env,Zone * zone,MachineGraph * mcgraph,const wasm::FunctionSig * sig,compiler::SourcePositionTable * source_position_table,Parameter0Mode parameter_mode,Isolate * isolate)519 WasmGraphBuilder::WasmGraphBuilder(
520 wasm::CompilationEnv* env, Zone* zone, MachineGraph* mcgraph,
521 const wasm::FunctionSig* sig,
522 compiler::SourcePositionTable* source_position_table,
523 Parameter0Mode parameter_mode, Isolate* isolate)
524 : gasm_(std::make_unique<WasmGraphAssembler>(mcgraph, zone)),
525 zone_(zone),
526 mcgraph_(mcgraph),
527 env_(env),
528 has_simd_(ContainsSimd(sig)),
529 sig_(sig),
530 source_position_table_(source_position_table),
531 parameter_mode_(parameter_mode),
532 isolate_(isolate) {
533 DCHECK_EQ(isolate == nullptr, parameter_mode_ != kNoSpecialParameterMode);
534 DCHECK_IMPLIES(env && env->bounds_checks == wasm::kTrapHandler,
535 trap_handler::IsTrapHandlerEnabled());
536 DCHECK_NOT_NULL(mcgraph_);
537 }
538
539 // Destructor define here where the definition of {WasmGraphAssembler} is
540 // available.
541 WasmGraphBuilder::~WasmGraphBuilder() = default;
542
Start(unsigned params)543 void WasmGraphBuilder::Start(unsigned params) {
544 Node* start = graph()->NewNode(mcgraph()->common()->Start(params));
545 graph()->SetStart(start);
546 SetEffectControl(start);
547 // Initialize parameter nodes.
548 parameters_ = zone_->NewArray<Node*>(params);
549 for (unsigned i = 0; i < params; i++) {
550 parameters_[i] = nullptr;
551 }
552 // Initialize instance node.
553 switch (parameter_mode_) {
554 case kInstanceMode:
555 instance_node_ = Param(wasm::kWasmInstanceParameterIndex);
556 break;
557 case kNoSpecialParameterMode:
558 instance_node_ = gasm_->LoadExportedFunctionInstance(
559 gasm_->LoadFunctionDataFromJSFunction(
560 Param(Linkage::kJSCallClosureParamIndex, "%closure")));
561 break;
562 case kWasmApiFunctionRefMode:
563 // We need an instance node anyway, because FromJS() needs to pass it to
564 // the WasmIsValidRefValue runtime function.
565 instance_node_ = UndefinedValue();
566 break;
567 }
568 graph()->SetEnd(graph()->NewNode(mcgraph()->common()->End(0)));
569 }
570
Param(int index,const char * debug_name)571 Node* WasmGraphBuilder::Param(int index, const char* debug_name) {
572 DCHECK_NOT_NULL(graph()->start());
573 // Turbofan allows negative parameter indices.
574 static constexpr int kMinParameterIndex = -1;
575 DCHECK_GE(index, kMinParameterIndex);
576 int array_index = index - kMinParameterIndex;
577 if (parameters_[array_index] == nullptr) {
578 parameters_[array_index] = graph()->NewNode(
579 mcgraph()->common()->Parameter(index, debug_name), graph()->start());
580 }
581 return parameters_[array_index];
582 }
583
Loop(Node * entry)584 Node* WasmGraphBuilder::Loop(Node* entry) {
585 return graph()->NewNode(mcgraph()->common()->Loop(1), entry);
586 }
587
TerminateLoop(Node * effect,Node * control)588 void WasmGraphBuilder::TerminateLoop(Node* effect, Node* control) {
589 Node* terminate =
590 graph()->NewNode(mcgraph()->common()->Terminate(), effect, control);
591 gasm_->MergeControlToEnd(terminate);
592 }
593
LoopExit(Node * loop_node)594 Node* WasmGraphBuilder::LoopExit(Node* loop_node) {
595 DCHECK(loop_node->opcode() == IrOpcode::kLoop);
596 Node* loop_exit =
597 graph()->NewNode(mcgraph()->common()->LoopExit(), control(), loop_node);
598 Node* loop_exit_effect = graph()->NewNode(
599 mcgraph()->common()->LoopExitEffect(), effect(), loop_exit);
600 SetEffectControl(loop_exit_effect, loop_exit);
601 return loop_exit;
602 }
603
LoopExitValue(Node * value,MachineRepresentation representation)604 Node* WasmGraphBuilder::LoopExitValue(Node* value,
605 MachineRepresentation representation) {
606 DCHECK(control()->opcode() == IrOpcode::kLoopExit);
607 return graph()->NewNode(mcgraph()->common()->LoopExitValue(representation),
608 value, control());
609 }
610
TerminateThrow(Node * effect,Node * control)611 void WasmGraphBuilder::TerminateThrow(Node* effect, Node* control) {
612 Node* terminate =
613 graph()->NewNode(mcgraph()->common()->Throw(), effect, control);
614 gasm_->MergeControlToEnd(terminate);
615 }
616
IsPhiWithMerge(Node * phi,Node * merge)617 bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
618 return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
619 NodeProperties::GetControlInput(phi) == merge;
620 }
621
ThrowsException(Node * node,Node ** if_success,Node ** if_exception)622 bool WasmGraphBuilder::ThrowsException(Node* node, Node** if_success,
623 Node** if_exception) {
624 if (node->op()->HasProperty(compiler::Operator::kNoThrow)) {
625 return false;
626 }
627
628 *if_success = graph()->NewNode(mcgraph()->common()->IfSuccess(), node);
629 *if_exception =
630 graph()->NewNode(mcgraph()->common()->IfException(), node, node);
631
632 return true;
633 }
634
AppendToMerge(Node * merge,Node * from)635 void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) {
636 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
637 merge->AppendInput(mcgraph()->zone(), from);
638 int new_size = merge->InputCount();
639 NodeProperties::ChangeOp(
640 merge, mcgraph()->common()->ResizeMergeOrPhi(merge->op(), new_size));
641 }
642
AppendToPhi(Node * phi,Node * from)643 void WasmGraphBuilder::AppendToPhi(Node* phi, Node* from) {
644 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
645 int new_size = phi->InputCount();
646 phi->InsertInput(mcgraph()->zone(), phi->InputCount() - 1, from);
647 NodeProperties::ChangeOp(
648 phi, mcgraph()->common()->ResizeMergeOrPhi(phi->op(), new_size));
649 }
650
651 template <typename... Nodes>
Merge(Node * fst,Nodes * ...args)652 Node* WasmGraphBuilder::Merge(Node* fst, Nodes*... args) {
653 return graph()->NewNode(this->mcgraph()->common()->Merge(1 + sizeof...(args)),
654 fst, args...);
655 }
656
Merge(unsigned count,Node ** controls)657 Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) {
658 return graph()->NewNode(mcgraph()->common()->Merge(count), count, controls);
659 }
660
Phi(wasm::ValueType type,unsigned count,Node ** vals_and_control)661 Node* WasmGraphBuilder::Phi(wasm::ValueType type, unsigned count,
662 Node** vals_and_control) {
663 DCHECK(IrOpcode::IsMergeOpcode(vals_and_control[count]->opcode()));
664 DCHECK_EQ(vals_and_control[count]->op()->ControlInputCount(), count);
665 return graph()->NewNode(
666 mcgraph()->common()->Phi(type.machine_representation(), count), count + 1,
667 vals_and_control);
668 }
669
EffectPhi(unsigned count,Node ** effects_and_control)670 Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects_and_control) {
671 DCHECK(IrOpcode::IsMergeOpcode(effects_and_control[count]->opcode()));
672 return graph()->NewNode(mcgraph()->common()->EffectPhi(count), count + 1,
673 effects_and_control);
674 }
675
RefNull()676 Node* WasmGraphBuilder::RefNull() { return LOAD_ROOT(NullValue, null_value); }
677
RefFunc(uint32_t function_index)678 Node* WasmGraphBuilder::RefFunc(uint32_t function_index) {
679 return gasm_->CallRuntimeStub(wasm::WasmCode::kWasmRefFunc,
680 Operator::kNoThrow,
681 gasm_->Uint32Constant(function_index));
682 }
683
RefAsNonNull(Node * arg,wasm::WasmCodePosition position)684 Node* WasmGraphBuilder::RefAsNonNull(Node* arg,
685 wasm::WasmCodePosition position) {
686 if (!FLAG_experimental_wasm_skip_null_checks) {
687 TrapIfTrue(wasm::kTrapIllegalCast, IsNull(arg), position);
688 }
689 return arg;
690 }
691
NoContextConstant()692 Node* WasmGraphBuilder::NoContextConstant() {
693 return mcgraph()->IntPtrConstant(0);
694 }
695
GetInstance()696 Node* WasmGraphBuilder::GetInstance() { return instance_node_.get(); }
697
BuildLoadIsolateRoot()698 Node* WasmGraphBuilder::BuildLoadIsolateRoot() {
699 switch (parameter_mode_) {
700 case kInstanceMode:
701 // For wasm functions, the IsolateRoot is loaded from the instance node so
702 // that the generated code is Isolate independent.
703 return LOAD_INSTANCE_FIELD(IsolateRoot, MachineType::Pointer());
704 case kWasmApiFunctionRefMode:
705 // Note: Even if V8_SANDBOXED_EXTERNAL_POINTERS, the pointer to the
706 // isolate root is not encoded, much like the case above. TODO(manoskouk):
707 // Decode the pointer here if that changes.
708 return gasm_->Load(
709 MachineType::Pointer(), Param(0),
710 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kIsolateRootOffset));
711 case kNoSpecialParameterMode:
712 return mcgraph()->IntPtrConstant(isolate_->isolate_root());
713 }
714 }
715
Int32Constant(int32_t value)716 Node* WasmGraphBuilder::Int32Constant(int32_t value) {
717 return mcgraph()->Int32Constant(value);
718 }
719
Int64Constant(int64_t value)720 Node* WasmGraphBuilder::Int64Constant(int64_t value) {
721 return mcgraph()->Int64Constant(value);
722 }
723
UndefinedValue()724 Node* WasmGraphBuilder::UndefinedValue() {
725 return LOAD_ROOT(UndefinedValue, undefined_value);
726 }
727
StackCheck(WasmInstanceCacheNodes * shared_memory_instance_cache,wasm::WasmCodePosition position)728 void WasmGraphBuilder::StackCheck(
729 WasmInstanceCacheNodes* shared_memory_instance_cache,
730 wasm::WasmCodePosition position) {
731 DCHECK_NOT_NULL(env_); // Wrappers don't get stack checks.
732 if (!FLAG_wasm_stack_checks || !env_->runtime_exception_support) {
733 return;
734 }
735
736 Node* limit_address =
737 LOAD_INSTANCE_FIELD(StackLimitAddress, MachineType::Pointer());
738 Node* limit = gasm_->LoadFromObject(MachineType::Pointer(), limit_address, 0);
739
740 Node* check = SetEffect(graph()->NewNode(
741 mcgraph()->machine()->StackPointerGreaterThan(StackCheckKind::kWasm),
742 limit, effect()));
743
744 Node* if_true;
745 Node* if_false;
746 BranchExpectTrue(check, &if_true, &if_false);
747
748 if (stack_check_call_operator_ == nullptr) {
749 // Build and cache the stack check call operator and the constant
750 // representing the stack check code.
751
752 // A direct call to a wasm runtime stub defined in this module.
753 // Just encode the stub index. This will be patched at relocation.
754 stack_check_code_node_.set(mcgraph()->RelocatableIntPtrConstant(
755 wasm::WasmCode::kWasmStackGuard, RelocInfo::WASM_STUB_CALL));
756
757 constexpr Operator::Properties properties =
758 Operator::kNoThrow | Operator::kNoWrite;
759 // If we ever want to mark this call as kNoDeopt, we'll have to make it
760 // non-eliminatable some other way.
761 STATIC_ASSERT((properties & Operator::kEliminatable) !=
762 Operator::kEliminatable);
763 auto call_descriptor = Linkage::GetStubCallDescriptor(
764 mcgraph()->zone(), // zone
765 NoContextDescriptor{}, // descriptor
766 0, // stack parameter count
767 CallDescriptor::kNoFlags, // flags
768 properties, // properties
769 StubCallMode::kCallWasmRuntimeStub); // stub call mode
770 stack_check_call_operator_ = mcgraph()->common()->Call(call_descriptor);
771 }
772
773 Node* call =
774 graph()->NewNode(stack_check_call_operator_.get(),
775 stack_check_code_node_.get(), effect(), if_false);
776 SetSourcePosition(call, position);
777
778 DCHECK_GT(call->op()->EffectOutputCount(), 0);
779 DCHECK_EQ(call->op()->ControlOutputCount(), 0);
780
781 SetEffectControl(call, if_false);
782
783 Node* merge = Merge(if_true, control());
784 Node* ephi_inputs[] = {check, effect(), merge};
785 Node* ephi = EffectPhi(2, ephi_inputs);
786
787 // We only need to refresh the size of a shared memory, as its start can never
788 // change.
789 if (shared_memory_instance_cache != nullptr) {
790 // We handle caching of the instance cache nodes manually, and we may reload
791 // them in contexts where load elimination would eliminate the reload.
792 // Therefore, we use plain Load nodes which are not subject to load
793 // elimination.
794 Node* new_memory_size =
795 LOAD_INSTANCE_FIELD_NO_ELIMINATION(MemorySize, MachineType::UintPtr());
796 shared_memory_instance_cache->mem_size = CreateOrMergeIntoPhi(
797 MachineType::PointerRepresentation(), merge,
798 shared_memory_instance_cache->mem_size, new_memory_size);
799 }
800
801 SetEffectControl(ephi, merge);
802 }
803
PatchInStackCheckIfNeeded()804 void WasmGraphBuilder::PatchInStackCheckIfNeeded() {
805 if (!needs_stack_check_) return;
806
807 Node* start = graph()->start();
808 // Place a stack check which uses a dummy node as control and effect.
809 Node* dummy = graph()->NewNode(mcgraph()->common()->Dead());
810 SetEffectControl(dummy);
811 // The function-prologue stack check is associated with position 0, which
812 // is never a position of any instruction in the function.
813 // We pass the null instance cache, as we are at the beginning of the function
814 // and do not need to update it.
815 StackCheck(nullptr, 0);
816
817 // In testing, no stack checks were emitted. Nothing to rewire then.
818 if (effect() == dummy) return;
819
820 // Now patch all control uses of {start} to use {control} and all effect uses
821 // to use {effect} instead. We exclude Projection nodes: Projections pointing
822 // to start are floating control, and we want it to point directly to start
823 // because of restrictions later in the pipeline (specifically, loop
824 // unrolling).
825 // Then rewire the dummy node to use start instead.
826 NodeProperties::ReplaceUses(start, start, effect(), control());
827 {
828 // We need an intermediate vector because we are not allowed to modify a use
829 // while traversing uses().
830 std::vector<Node*> projections;
831 for (Node* use : control()->uses()) {
832 if (use->opcode() == IrOpcode::kProjection) projections.emplace_back(use);
833 }
834 for (Node* use : projections) {
835 use->ReplaceInput(NodeProperties::FirstControlIndex(use), start);
836 }
837 }
838 NodeProperties::ReplaceUses(dummy, nullptr, start, start);
839 }
840
Binop(wasm::WasmOpcode opcode,Node * left,Node * right,wasm::WasmCodePosition position)841 Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
842 wasm::WasmCodePosition position) {
843 const Operator* op;
844 MachineOperatorBuilder* m = mcgraph()->machine();
845 switch (opcode) {
846 case wasm::kExprI32Add:
847 op = m->Int32Add();
848 break;
849 case wasm::kExprI32Sub:
850 op = m->Int32Sub();
851 break;
852 case wasm::kExprI32Mul:
853 op = m->Int32Mul();
854 break;
855 case wasm::kExprI32DivS:
856 return BuildI32DivS(left, right, position);
857 case wasm::kExprI32DivU:
858 return BuildI32DivU(left, right, position);
859 case wasm::kExprI32RemS:
860 return BuildI32RemS(left, right, position);
861 case wasm::kExprI32RemU:
862 return BuildI32RemU(left, right, position);
863 case wasm::kExprI32And:
864 op = m->Word32And();
865 break;
866 case wasm::kExprI32Ior:
867 op = m->Word32Or();
868 break;
869 case wasm::kExprI32Xor:
870 op = m->Word32Xor();
871 break;
872 case wasm::kExprI32Shl:
873 op = m->Word32Shl();
874 right = MaskShiftCount32(right);
875 break;
876 case wasm::kExprI32ShrU:
877 op = m->Word32Shr();
878 right = MaskShiftCount32(right);
879 break;
880 case wasm::kExprI32ShrS:
881 op = m->Word32Sar();
882 right = MaskShiftCount32(right);
883 break;
884 case wasm::kExprI32Ror:
885 op = m->Word32Ror();
886 right = MaskShiftCount32(right);
887 break;
888 case wasm::kExprI32Rol:
889 if (m->Word32Rol().IsSupported()) {
890 op = m->Word32Rol().op();
891 right = MaskShiftCount32(right);
892 break;
893 }
894 return BuildI32Rol(left, right);
895 case wasm::kExprI32Eq:
896 op = m->Word32Equal();
897 break;
898 case wasm::kExprI32Ne:
899 return Invert(Binop(wasm::kExprI32Eq, left, right));
900 case wasm::kExprI32LtS:
901 op = m->Int32LessThan();
902 break;
903 case wasm::kExprI32LeS:
904 op = m->Int32LessThanOrEqual();
905 break;
906 case wasm::kExprI32LtU:
907 op = m->Uint32LessThan();
908 break;
909 case wasm::kExprI32LeU:
910 op = m->Uint32LessThanOrEqual();
911 break;
912 case wasm::kExprI32GtS:
913 op = m->Int32LessThan();
914 std::swap(left, right);
915 break;
916 case wasm::kExprI32GeS:
917 op = m->Int32LessThanOrEqual();
918 std::swap(left, right);
919 break;
920 case wasm::kExprI32GtU:
921 op = m->Uint32LessThan();
922 std::swap(left, right);
923 break;
924 case wasm::kExprI32GeU:
925 op = m->Uint32LessThanOrEqual();
926 std::swap(left, right);
927 break;
928 case wasm::kExprI64And:
929 op = m->Word64And();
930 break;
931 case wasm::kExprI64Add:
932 op = m->Int64Add();
933 break;
934 case wasm::kExprI64Sub:
935 op = m->Int64Sub();
936 break;
937 case wasm::kExprI64Mul:
938 op = m->Int64Mul();
939 break;
940 case wasm::kExprI64DivS:
941 return BuildI64DivS(left, right, position);
942 case wasm::kExprI64DivU:
943 return BuildI64DivU(left, right, position);
944 case wasm::kExprI64RemS:
945 return BuildI64RemS(left, right, position);
946 case wasm::kExprI64RemU:
947 return BuildI64RemU(left, right, position);
948 case wasm::kExprI64Ior:
949 op = m->Word64Or();
950 break;
951 case wasm::kExprI64Xor:
952 op = m->Word64Xor();
953 break;
954 case wasm::kExprI64Shl:
955 op = m->Word64Shl();
956 right = MaskShiftCount64(right);
957 break;
958 case wasm::kExprI64ShrU:
959 op = m->Word64Shr();
960 right = MaskShiftCount64(right);
961 break;
962 case wasm::kExprI64ShrS:
963 op = m->Word64Sar();
964 right = MaskShiftCount64(right);
965 break;
966 case wasm::kExprI64Eq:
967 op = m->Word64Equal();
968 break;
969 case wasm::kExprI64Ne:
970 return Invert(Binop(wasm::kExprI64Eq, left, right));
971 case wasm::kExprI64LtS:
972 op = m->Int64LessThan();
973 break;
974 case wasm::kExprI64LeS:
975 op = m->Int64LessThanOrEqual();
976 break;
977 case wasm::kExprI64LtU:
978 op = m->Uint64LessThan();
979 break;
980 case wasm::kExprI64LeU:
981 op = m->Uint64LessThanOrEqual();
982 break;
983 case wasm::kExprI64GtS:
984 op = m->Int64LessThan();
985 std::swap(left, right);
986 break;
987 case wasm::kExprI64GeS:
988 op = m->Int64LessThanOrEqual();
989 std::swap(left, right);
990 break;
991 case wasm::kExprI64GtU:
992 op = m->Uint64LessThan();
993 std::swap(left, right);
994 break;
995 case wasm::kExprI64GeU:
996 op = m->Uint64LessThanOrEqual();
997 std::swap(left, right);
998 break;
999 case wasm::kExprI64Ror:
1000 right = MaskShiftCount64(right);
1001 return m->Is64() ? graph()->NewNode(m->Word64Ror(), left, right)
1002 : graph()->NewNode(m->Word64RorLowerable(), left, right,
1003 control());
1004 case wasm::kExprI64Rol:
1005 if (m->Word64Rol().IsSupported()) {
1006 return m->Is64() ? graph()->NewNode(m->Word64Rol().op(), left,
1007 MaskShiftCount64(right))
1008 : graph()->NewNode(m->Word64RolLowerable().op(), left,
1009 MaskShiftCount64(right), control());
1010 } else if (m->Word32Rol().IsSupported()) {
1011 return graph()->NewNode(m->Word64RolLowerable().placeholder(), left,
1012 right, control());
1013 }
1014 return BuildI64Rol(left, right);
1015 case wasm::kExprF32CopySign:
1016 return BuildF32CopySign(left, right);
1017 case wasm::kExprF64CopySign:
1018 return BuildF64CopySign(left, right);
1019 case wasm::kExprF32Add:
1020 op = m->Float32Add();
1021 break;
1022 case wasm::kExprF32Sub:
1023 op = m->Float32Sub();
1024 break;
1025 case wasm::kExprF32Mul:
1026 op = m->Float32Mul();
1027 break;
1028 case wasm::kExprF32Div:
1029 op = m->Float32Div();
1030 break;
1031 case wasm::kExprF32Eq:
1032 op = m->Float32Equal();
1033 break;
1034 case wasm::kExprF32Ne:
1035 return Invert(Binop(wasm::kExprF32Eq, left, right));
1036 case wasm::kExprF32Lt:
1037 op = m->Float32LessThan();
1038 break;
1039 case wasm::kExprF32Ge:
1040 op = m->Float32LessThanOrEqual();
1041 std::swap(left, right);
1042 break;
1043 case wasm::kExprF32Gt:
1044 op = m->Float32LessThan();
1045 std::swap(left, right);
1046 break;
1047 case wasm::kExprF32Le:
1048 op = m->Float32LessThanOrEqual();
1049 break;
1050 case wasm::kExprF64Add:
1051 op = m->Float64Add();
1052 break;
1053 case wasm::kExprF64Sub:
1054 op = m->Float64Sub();
1055 break;
1056 case wasm::kExprF64Mul:
1057 op = m->Float64Mul();
1058 break;
1059 case wasm::kExprF64Div:
1060 op = m->Float64Div();
1061 break;
1062 case wasm::kExprF64Eq:
1063 op = m->Float64Equal();
1064 break;
1065 case wasm::kExprF64Ne:
1066 return Invert(Binop(wasm::kExprF64Eq, left, right));
1067 case wasm::kExprF64Lt:
1068 op = m->Float64LessThan();
1069 break;
1070 case wasm::kExprF64Le:
1071 op = m->Float64LessThanOrEqual();
1072 break;
1073 case wasm::kExprF64Gt:
1074 op = m->Float64LessThan();
1075 std::swap(left, right);
1076 break;
1077 case wasm::kExprF64Ge:
1078 op = m->Float64LessThanOrEqual();
1079 std::swap(left, right);
1080 break;
1081 case wasm::kExprF32Min:
1082 op = m->Float32Min();
1083 break;
1084 case wasm::kExprF64Min:
1085 op = m->Float64Min();
1086 break;
1087 case wasm::kExprF32Max:
1088 op = m->Float32Max();
1089 break;
1090 case wasm::kExprF64Max:
1091 op = m->Float64Max();
1092 break;
1093 case wasm::kExprF64Pow:
1094 return BuildF64Pow(left, right);
1095 case wasm::kExprF64Atan2:
1096 op = m->Float64Atan2();
1097 break;
1098 case wasm::kExprF64Mod:
1099 return BuildF64Mod(left, right);
1100 case wasm::kExprRefEq:
1101 return gasm_->TaggedEqual(left, right);
1102 case wasm::kExprI32AsmjsDivS:
1103 return BuildI32AsmjsDivS(left, right);
1104 case wasm::kExprI32AsmjsDivU:
1105 return BuildI32AsmjsDivU(left, right);
1106 case wasm::kExprI32AsmjsRemS:
1107 return BuildI32AsmjsRemS(left, right);
1108 case wasm::kExprI32AsmjsRemU:
1109 return BuildI32AsmjsRemU(left, right);
1110 case wasm::kExprI32AsmjsStoreMem8:
1111 return BuildAsmjsStoreMem(MachineType::Int8(), left, right);
1112 case wasm::kExprI32AsmjsStoreMem16:
1113 return BuildAsmjsStoreMem(MachineType::Int16(), left, right);
1114 case wasm::kExprI32AsmjsStoreMem:
1115 return BuildAsmjsStoreMem(MachineType::Int32(), left, right);
1116 case wasm::kExprF32AsmjsStoreMem:
1117 return BuildAsmjsStoreMem(MachineType::Float32(), left, right);
1118 case wasm::kExprF64AsmjsStoreMem:
1119 return BuildAsmjsStoreMem(MachineType::Float64(), left, right);
1120 default:
1121 FATAL_UNSUPPORTED_OPCODE(opcode);
1122 }
1123 return graph()->NewNode(op, left, right);
1124 }
1125
Unop(wasm::WasmOpcode opcode,Node * input,wasm::WasmCodePosition position)1126 Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
1127 wasm::WasmCodePosition position) {
1128 const Operator* op;
1129 MachineOperatorBuilder* m = mcgraph()->machine();
1130 switch (opcode) {
1131 case wasm::kExprI32Eqz:
1132 return gasm_->Word32Equal(input, Int32Constant(0));
1133 case wasm::kExprF32Abs:
1134 op = m->Float32Abs();
1135 break;
1136 case wasm::kExprF32Neg: {
1137 op = m->Float32Neg();
1138 break;
1139 }
1140 case wasm::kExprF32Sqrt:
1141 op = m->Float32Sqrt();
1142 break;
1143 case wasm::kExprF64Abs:
1144 op = m->Float64Abs();
1145 break;
1146 case wasm::kExprF64Neg: {
1147 op = m->Float64Neg();
1148 break;
1149 }
1150 case wasm::kExprF64Sqrt:
1151 op = m->Float64Sqrt();
1152 break;
1153 case wasm::kExprI32SConvertF32:
1154 case wasm::kExprI32UConvertF32:
1155 case wasm::kExprI32SConvertF64:
1156 case wasm::kExprI32UConvertF64:
1157 case wasm::kExprI32SConvertSatF64:
1158 case wasm::kExprI32UConvertSatF64:
1159 case wasm::kExprI32SConvertSatF32:
1160 case wasm::kExprI32UConvertSatF32:
1161 return BuildIntConvertFloat(input, position, opcode);
1162 case wasm::kExprI32AsmjsSConvertF64:
1163 return BuildI32AsmjsSConvertF64(input);
1164 case wasm::kExprI32AsmjsUConvertF64:
1165 return BuildI32AsmjsUConvertF64(input);
1166 case wasm::kExprF32ConvertF64:
1167 op = m->TruncateFloat64ToFloat32();
1168 break;
1169 case wasm::kExprF64SConvertI32:
1170 op = m->ChangeInt32ToFloat64();
1171 break;
1172 case wasm::kExprF64UConvertI32:
1173 op = m->ChangeUint32ToFloat64();
1174 break;
1175 case wasm::kExprF32SConvertI32:
1176 op = m->RoundInt32ToFloat32();
1177 break;
1178 case wasm::kExprF32UConvertI32:
1179 op = m->RoundUint32ToFloat32();
1180 break;
1181 case wasm::kExprI32AsmjsSConvertF32:
1182 return BuildI32AsmjsSConvertF32(input);
1183 case wasm::kExprI32AsmjsUConvertF32:
1184 return BuildI32AsmjsUConvertF32(input);
1185 case wasm::kExprF64ConvertF32:
1186 op = m->ChangeFloat32ToFloat64();
1187 break;
1188 case wasm::kExprF32ReinterpretI32:
1189 op = m->BitcastInt32ToFloat32();
1190 break;
1191 case wasm::kExprI32ReinterpretF32:
1192 op = m->BitcastFloat32ToInt32();
1193 break;
1194 case wasm::kExprI32Clz:
1195 op = m->Word32Clz();
1196 break;
1197 case wasm::kExprI32Ctz: {
1198 if (m->Word32Ctz().IsSupported()) {
1199 op = m->Word32Ctz().op();
1200 break;
1201 } else if (m->Word32ReverseBits().IsSupported()) {
1202 Node* reversed = graph()->NewNode(m->Word32ReverseBits().op(), input);
1203 Node* result = graph()->NewNode(m->Word32Clz(), reversed);
1204 return result;
1205 } else {
1206 return BuildI32Ctz(input);
1207 }
1208 }
1209 case wasm::kExprI32Popcnt: {
1210 if (m->Word32Popcnt().IsSupported()) {
1211 op = m->Word32Popcnt().op();
1212 break;
1213 } else {
1214 return BuildI32Popcnt(input);
1215 }
1216 }
1217 case wasm::kExprF32Floor: {
1218 if (!m->Float32RoundDown().IsSupported()) return BuildF32Floor(input);
1219 op = m->Float32RoundDown().op();
1220 break;
1221 }
1222 case wasm::kExprF32Ceil: {
1223 if (!m->Float32RoundUp().IsSupported()) return BuildF32Ceil(input);
1224 op = m->Float32RoundUp().op();
1225 break;
1226 }
1227 case wasm::kExprF32Trunc: {
1228 if (!m->Float32RoundTruncate().IsSupported()) return BuildF32Trunc(input);
1229 op = m->Float32RoundTruncate().op();
1230 break;
1231 }
1232 case wasm::kExprF32NearestInt: {
1233 if (!m->Float32RoundTiesEven().IsSupported())
1234 return BuildF32NearestInt(input);
1235 op = m->Float32RoundTiesEven().op();
1236 break;
1237 }
1238 case wasm::kExprF64Floor: {
1239 if (!m->Float64RoundDown().IsSupported()) return BuildF64Floor(input);
1240 op = m->Float64RoundDown().op();
1241 break;
1242 }
1243 case wasm::kExprF64Ceil: {
1244 if (!m->Float64RoundUp().IsSupported()) return BuildF64Ceil(input);
1245 op = m->Float64RoundUp().op();
1246 break;
1247 }
1248 case wasm::kExprF64Trunc: {
1249 if (!m->Float64RoundTruncate().IsSupported()) return BuildF64Trunc(input);
1250 op = m->Float64RoundTruncate().op();
1251 break;
1252 }
1253 case wasm::kExprF64NearestInt: {
1254 if (!m->Float64RoundTiesEven().IsSupported())
1255 return BuildF64NearestInt(input);
1256 op = m->Float64RoundTiesEven().op();
1257 break;
1258 }
1259 case wasm::kExprF64Acos: {
1260 return BuildF64Acos(input);
1261 }
1262 case wasm::kExprF64Asin: {
1263 return BuildF64Asin(input);
1264 }
1265 case wasm::kExprF64Atan:
1266 op = m->Float64Atan();
1267 break;
1268 case wasm::kExprF64Cos: {
1269 op = m->Float64Cos();
1270 break;
1271 }
1272 case wasm::kExprF64Sin: {
1273 op = m->Float64Sin();
1274 break;
1275 }
1276 case wasm::kExprF64Tan: {
1277 op = m->Float64Tan();
1278 break;
1279 }
1280 case wasm::kExprF64Exp: {
1281 op = m->Float64Exp();
1282 break;
1283 }
1284 case wasm::kExprF64Log:
1285 op = m->Float64Log();
1286 break;
1287 case wasm::kExprI32ConvertI64:
1288 op = m->TruncateInt64ToInt32();
1289 break;
1290 case wasm::kExprI64SConvertI32:
1291 op = m->ChangeInt32ToInt64();
1292 break;
1293 case wasm::kExprI64UConvertI32:
1294 op = m->ChangeUint32ToUint64();
1295 break;
1296 case wasm::kExprF64ReinterpretI64:
1297 op = m->BitcastInt64ToFloat64();
1298 break;
1299 case wasm::kExprI64ReinterpretF64:
1300 op = m->BitcastFloat64ToInt64();
1301 break;
1302 case wasm::kExprI64Clz:
1303 return m->Is64()
1304 ? graph()->NewNode(m->Word64Clz(), input)
1305 : graph()->NewNode(m->Word64ClzLowerable(), input, control());
1306 case wasm::kExprI64Ctz: {
1307 if (m->Word64Ctz().IsSupported()) {
1308 return m->Is64() ? graph()->NewNode(m->Word64Ctz().op(), input)
1309 : graph()->NewNode(m->Word64CtzLowerable().op(), input,
1310 control());
1311 } else if (m->Is32() && m->Word32Ctz().IsSupported()) {
1312 return graph()->NewNode(m->Word64CtzLowerable().placeholder(), input,
1313 control());
1314 } else if (m->Word64ReverseBits().IsSupported()) {
1315 Node* reversed = graph()->NewNode(m->Word64ReverseBits().op(), input);
1316 Node* result = m->Is64() ? graph()->NewNode(m->Word64Clz(), reversed)
1317 : graph()->NewNode(m->Word64ClzLowerable(),
1318 reversed, control());
1319 return result;
1320 } else {
1321 return BuildI64Ctz(input);
1322 }
1323 }
1324 case wasm::kExprI64Popcnt: {
1325 OptionalOperator popcnt64 = m->Word64Popcnt();
1326 if (popcnt64.IsSupported()) {
1327 op = popcnt64.op();
1328 } else if (m->Is32() && m->Word32Popcnt().IsSupported()) {
1329 op = popcnt64.placeholder();
1330 } else {
1331 return BuildI64Popcnt(input);
1332 }
1333 break;
1334 }
1335 case wasm::kExprI64Eqz:
1336 return gasm_->Word64Equal(input, Int64Constant(0));
1337 case wasm::kExprF32SConvertI64:
1338 if (m->Is32()) {
1339 return BuildF32SConvertI64(input);
1340 }
1341 op = m->RoundInt64ToFloat32();
1342 break;
1343 case wasm::kExprF32UConvertI64:
1344 if (m->Is32()) {
1345 return BuildF32UConvertI64(input);
1346 }
1347 op = m->RoundUint64ToFloat32();
1348 break;
1349 case wasm::kExprF64SConvertI64:
1350 if (m->Is32()) {
1351 return BuildF64SConvertI64(input);
1352 }
1353 op = m->RoundInt64ToFloat64();
1354 break;
1355 case wasm::kExprF64UConvertI64:
1356 if (m->Is32()) {
1357 return BuildF64UConvertI64(input);
1358 }
1359 op = m->RoundUint64ToFloat64();
1360 break;
1361 case wasm::kExprI32SExtendI8:
1362 op = m->SignExtendWord8ToInt32();
1363 break;
1364 case wasm::kExprI32SExtendI16:
1365 op = m->SignExtendWord16ToInt32();
1366 break;
1367 case wasm::kExprI64SExtendI8:
1368 op = m->SignExtendWord8ToInt64();
1369 break;
1370 case wasm::kExprI64SExtendI16:
1371 op = m->SignExtendWord16ToInt64();
1372 break;
1373 case wasm::kExprI64SExtendI32:
1374 op = m->SignExtendWord32ToInt64();
1375 break;
1376 case wasm::kExprI64SConvertF32:
1377 case wasm::kExprI64UConvertF32:
1378 case wasm::kExprI64SConvertF64:
1379 case wasm::kExprI64UConvertF64:
1380 case wasm::kExprI64SConvertSatF32:
1381 case wasm::kExprI64UConvertSatF32:
1382 case wasm::kExprI64SConvertSatF64:
1383 case wasm::kExprI64UConvertSatF64:
1384 return mcgraph()->machine()->Is32()
1385 ? BuildCcallConvertFloat(input, position, opcode)
1386 : BuildIntConvertFloat(input, position, opcode);
1387 case wasm::kExprRefIsNull:
1388 return IsNull(input);
1389 // We abuse ref.as_non_null, which isn't otherwise used in this switch, as
1390 // a sentinel for the negation of ref.is_null.
1391 case wasm::kExprRefAsNonNull:
1392 return gasm_->Int32Sub(gasm_->Int32Constant(1), IsNull(input));
1393 case wasm::kExprI32AsmjsLoadMem8S:
1394 return BuildAsmjsLoadMem(MachineType::Int8(), input);
1395 case wasm::kExprI32AsmjsLoadMem8U:
1396 return BuildAsmjsLoadMem(MachineType::Uint8(), input);
1397 case wasm::kExprI32AsmjsLoadMem16S:
1398 return BuildAsmjsLoadMem(MachineType::Int16(), input);
1399 case wasm::kExprI32AsmjsLoadMem16U:
1400 return BuildAsmjsLoadMem(MachineType::Uint16(), input);
1401 case wasm::kExprI32AsmjsLoadMem:
1402 return BuildAsmjsLoadMem(MachineType::Int32(), input);
1403 case wasm::kExprF32AsmjsLoadMem:
1404 return BuildAsmjsLoadMem(MachineType::Float32(), input);
1405 case wasm::kExprF64AsmjsLoadMem:
1406 return BuildAsmjsLoadMem(MachineType::Float64(), input);
1407 default:
1408 FATAL_UNSUPPORTED_OPCODE(opcode);
1409 }
1410 return graph()->NewNode(op, input);
1411 }
1412
Float32Constant(float value)1413 Node* WasmGraphBuilder::Float32Constant(float value) {
1414 return mcgraph()->Float32Constant(value);
1415 }
1416
Float64Constant(double value)1417 Node* WasmGraphBuilder::Float64Constant(double value) {
1418 return mcgraph()->Float64Constant(value);
1419 }
1420
Simd128Constant(const uint8_t value[16])1421 Node* WasmGraphBuilder::Simd128Constant(const uint8_t value[16]) {
1422 has_simd_ = true;
1423 return graph()->NewNode(mcgraph()->machine()->S128Const(value));
1424 }
1425
BranchNoHint(Node * cond,Node ** true_node,Node ** false_node)1426 Node* WasmGraphBuilder::BranchNoHint(Node* cond, Node** true_node,
1427 Node** false_node) {
1428 return gasm_->Branch(cond, true_node, false_node, BranchHint::kNone);
1429 }
1430
BranchExpectFalse(Node * cond,Node ** true_node,Node ** false_node)1431 Node* WasmGraphBuilder::BranchExpectFalse(Node* cond, Node** true_node,
1432 Node** false_node) {
1433 return gasm_->Branch(cond, true_node, false_node, BranchHint::kFalse);
1434 }
1435
BranchExpectTrue(Node * cond,Node ** true_node,Node ** false_node)1436 Node* WasmGraphBuilder::BranchExpectTrue(Node* cond, Node** true_node,
1437 Node** false_node) {
1438 return gasm_->Branch(cond, true_node, false_node, BranchHint::kTrue);
1439 }
1440
Select(Node * cond,Node * true_node,Node * false_node,wasm::ValueType type)1441 Node* WasmGraphBuilder::Select(Node *cond, Node* true_node,
1442 Node* false_node, wasm::ValueType type) {
1443 MachineOperatorBuilder* m = mcgraph()->machine();
1444 wasm::ValueKind kind = type.kind();
1445 // Lower to select if supported.
1446 if (kind == wasm::kF32 && m->Float32Select().IsSupported()) {
1447 return mcgraph()->graph()->NewNode(m->Float32Select().op(), cond,
1448 true_node, false_node);
1449 }
1450 if (kind == wasm::kF64 && m->Float64Select().IsSupported()) {
1451 return mcgraph()->graph()->NewNode(m->Float64Select().op(), cond,
1452 true_node, false_node);
1453 }
1454 if (kind == wasm::kI32 && m->Word32Select().IsSupported()) {
1455 return mcgraph()->graph()->NewNode(m->Word32Select().op(), cond, true_node,
1456 false_node);
1457 }
1458 if (kind == wasm::kI64 && m->Word64Select().IsSupported()) {
1459 return mcgraph()->graph()->NewNode(m->Word64Select().op(), cond, true_node,
1460 false_node);
1461 }
1462 // Default to control-flow.
1463 Node* controls[2];
1464 BranchNoHint(cond, &controls[0], &controls[1]);
1465 Node* merge = Merge(2, controls);
1466 SetControl(merge);
1467 Node* inputs[] = {true_node, false_node, merge};
1468 return Phi(type, 2, inputs);
1469 }
1470
GetTrapIdForTrap(wasm::TrapReason reason)1471 TrapId WasmGraphBuilder::GetTrapIdForTrap(wasm::TrapReason reason) {
1472 // TODO(wasm): "!env_" should not happen when compiling an actual wasm
1473 // function.
1474 if (!env_ || !env_->runtime_exception_support) {
1475 // We use TrapId::kInvalid as a marker to tell the code generator
1476 // to generate a call to a testing c-function instead of a runtime
1477 // stub. This code should only be called from a cctest.
1478 return TrapId::kInvalid;
1479 }
1480
1481 switch (reason) {
1482 #define TRAPREASON_TO_TRAPID(name) \
1483 case wasm::k##name: \
1484 static_assert( \
1485 static_cast<int>(TrapId::k##name) == wasm::WasmCode::kThrowWasm##name, \
1486 "trap id mismatch"); \
1487 return TrapId::k##name;
1488 FOREACH_WASM_TRAPREASON(TRAPREASON_TO_TRAPID)
1489 #undef TRAPREASON_TO_TRAPID
1490 default:
1491 UNREACHABLE();
1492 }
1493 }
1494
TrapIfTrue(wasm::TrapReason reason,Node * cond,wasm::WasmCodePosition position)1495 void WasmGraphBuilder::TrapIfTrue(wasm::TrapReason reason, Node* cond,
1496 wasm::WasmCodePosition position) {
1497 TrapId trap_id = GetTrapIdForTrap(reason);
1498 Node* node = SetControl(graph()->NewNode(mcgraph()->common()->TrapIf(trap_id),
1499 cond, effect(), control()));
1500 SetSourcePosition(node, position);
1501 }
1502
TrapIfFalse(wasm::TrapReason reason,Node * cond,wasm::WasmCodePosition position)1503 void WasmGraphBuilder::TrapIfFalse(wasm::TrapReason reason, Node* cond,
1504 wasm::WasmCodePosition position) {
1505 TrapId trap_id = GetTrapIdForTrap(reason);
1506 Node* node = SetControl(graph()->NewNode(
1507 mcgraph()->common()->TrapUnless(trap_id), cond, effect(), control()));
1508 SetSourcePosition(node, position);
1509 }
1510
1511 // Add a check that traps if {node} is equal to {val}.
TrapIfEq32(wasm::TrapReason reason,Node * node,int32_t val,wasm::WasmCodePosition position)1512 void WasmGraphBuilder::TrapIfEq32(wasm::TrapReason reason, Node* node,
1513 int32_t val,
1514 wasm::WasmCodePosition position) {
1515 Int32Matcher m(node);
1516 if (m.HasResolvedValue() && !m.Is(val)) return;
1517 if (val == 0) {
1518 TrapIfFalse(reason, node, position);
1519 } else {
1520 TrapIfTrue(reason, gasm_->Word32Equal(node, Int32Constant(val)), position);
1521 }
1522 }
1523
1524 // Add a check that traps if {node} is zero.
ZeroCheck32(wasm::TrapReason reason,Node * node,wasm::WasmCodePosition position)1525 void WasmGraphBuilder::ZeroCheck32(wasm::TrapReason reason, Node* node,
1526 wasm::WasmCodePosition position) {
1527 TrapIfEq32(reason, node, 0, position);
1528 }
1529
1530 // Add a check that traps if {node} is equal to {val}.
TrapIfEq64(wasm::TrapReason reason,Node * node,int64_t val,wasm::WasmCodePosition position)1531 void WasmGraphBuilder::TrapIfEq64(wasm::TrapReason reason, Node* node,
1532 int64_t val,
1533 wasm::WasmCodePosition position) {
1534 Int64Matcher m(node);
1535 if (m.HasResolvedValue() && !m.Is(val)) return;
1536 TrapIfTrue(reason, gasm_->Word64Equal(node, Int64Constant(val)), position);
1537 }
1538
1539 // Add a check that traps if {node} is zero.
ZeroCheck64(wasm::TrapReason reason,Node * node,wasm::WasmCodePosition position)1540 void WasmGraphBuilder::ZeroCheck64(wasm::TrapReason reason, Node* node,
1541 wasm::WasmCodePosition position) {
1542 TrapIfEq64(reason, node, 0, position);
1543 }
1544
Switch(unsigned count,Node * key)1545 Node* WasmGraphBuilder::Switch(unsigned count, Node* key) {
1546 // The instruction selector will use {kArchTableSwitch} for large switches,
1547 // which has limited input count, see {InstructionSelector::EmitTableSwitch}.
1548 DCHECK_LE(count, Instruction::kMaxInputCount - 2); // value_range + 2
1549 DCHECK_LE(count, wasm::kV8MaxWasmFunctionBrTableSize + 1); // plus IfDefault
1550 return graph()->NewNode(mcgraph()->common()->Switch(count), key, control());
1551 }
1552
IfValue(int32_t value,Node * sw)1553 Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) {
1554 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
1555 return graph()->NewNode(mcgraph()->common()->IfValue(value), sw);
1556 }
1557
IfDefault(Node * sw)1558 Node* WasmGraphBuilder::IfDefault(Node* sw) {
1559 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
1560 return graph()->NewNode(mcgraph()->common()->IfDefault(), sw);
1561 }
1562
Return(base::Vector<Node * > vals)1563 Node* WasmGraphBuilder::Return(base::Vector<Node*> vals) {
1564 unsigned count = static_cast<unsigned>(vals.size());
1565 base::SmallVector<Node*, 8> buf(count + 3);
1566
1567 buf[0] = Int32Constant(0);
1568 if (count > 0) {
1569 memcpy(buf.data() + 1, vals.begin(), sizeof(void*) * count);
1570 }
1571 buf[count + 1] = effect();
1572 buf[count + 2] = control();
1573 Node* ret = graph()->NewNode(mcgraph()->common()->Return(count), count + 3,
1574 buf.data());
1575
1576 gasm_->MergeControlToEnd(ret);
1577 return ret;
1578 }
1579
Trap(wasm::TrapReason reason,wasm::WasmCodePosition position)1580 void WasmGraphBuilder::Trap(wasm::TrapReason reason,
1581 wasm::WasmCodePosition position) {
1582 TrapIfFalse(reason, Int32Constant(0), position);
1583 // Connect control to end via a Throw() node.
1584 TerminateThrow(effect(), control());
1585 }
1586
MaskShiftCount32(Node * node)1587 Node* WasmGraphBuilder::MaskShiftCount32(Node* node) {
1588 static const int32_t kMask32 = 0x1F;
1589 if (!mcgraph()->machine()->Word32ShiftIsSafe()) {
1590 // Shifts by constants are so common we pattern-match them here.
1591 Int32Matcher match(node);
1592 if (match.HasResolvedValue()) {
1593 int32_t masked = (match.ResolvedValue() & kMask32);
1594 if (match.ResolvedValue() != masked) node = Int32Constant(masked);
1595 } else {
1596 node = gasm_->Word32And(node, Int32Constant(kMask32));
1597 }
1598 }
1599 return node;
1600 }
1601
MaskShiftCount64(Node * node)1602 Node* WasmGraphBuilder::MaskShiftCount64(Node* node) {
1603 static const int64_t kMask64 = 0x3F;
1604 if (!mcgraph()->machine()->Word32ShiftIsSafe()) {
1605 // Shifts by constants are so common we pattern-match them here.
1606 Int64Matcher match(node);
1607 if (match.HasResolvedValue()) {
1608 int64_t masked = (match.ResolvedValue() & kMask64);
1609 if (match.ResolvedValue() != masked) node = Int64Constant(masked);
1610 } else {
1611 node = gasm_->Word64And(node, Int64Constant(kMask64));
1612 }
1613 }
1614 return node;
1615 }
1616
1617 namespace {
1618
ReverseBytesSupported(MachineOperatorBuilder * m,size_t size_in_bytes)1619 bool ReverseBytesSupported(MachineOperatorBuilder* m, size_t size_in_bytes) {
1620 switch (size_in_bytes) {
1621 case 4:
1622 case 16:
1623 return true;
1624 case 8:
1625 return m->Is64();
1626 default:
1627 break;
1628 }
1629 return false;
1630 }
1631
1632 } // namespace
1633
BuildChangeEndiannessStore(Node * node,MachineRepresentation mem_rep,wasm::ValueType wasmtype)1634 Node* WasmGraphBuilder::BuildChangeEndiannessStore(
1635 Node* node, MachineRepresentation mem_rep, wasm::ValueType wasmtype) {
1636 Node* result;
1637 Node* value = node;
1638 MachineOperatorBuilder* m = mcgraph()->machine();
1639 int valueSizeInBytes = wasmtype.value_kind_size();
1640 int valueSizeInBits = 8 * valueSizeInBytes;
1641 bool isFloat = false;
1642
1643 switch (wasmtype.kind()) {
1644 case wasm::kF64:
1645 value = gasm_->BitcastFloat64ToInt64(node);
1646 isFloat = true;
1647 V8_FALLTHROUGH;
1648 case wasm::kI64:
1649 result = Int64Constant(0);
1650 break;
1651 case wasm::kF32:
1652 value = gasm_->BitcastFloat32ToInt32(node);
1653 isFloat = true;
1654 V8_FALLTHROUGH;
1655 case wasm::kI32:
1656 result = Int32Constant(0);
1657 break;
1658 case wasm::kS128:
1659 DCHECK(ReverseBytesSupported(m, valueSizeInBytes));
1660 break;
1661 default:
1662 UNREACHABLE();
1663 }
1664
1665 if (mem_rep == MachineRepresentation::kWord8) {
1666 // No need to change endianness for byte size, return original node
1667 return node;
1668 }
1669 if (wasmtype == wasm::kWasmI64 && mem_rep < MachineRepresentation::kWord64) {
1670 // In case we store lower part of WasmI64 expression, we can truncate
1671 // upper 32bits
1672 value = gasm_->TruncateInt64ToInt32(value);
1673 valueSizeInBytes = wasm::kWasmI32.value_kind_size();
1674 valueSizeInBits = 8 * valueSizeInBytes;
1675 if (mem_rep == MachineRepresentation::kWord16) {
1676 value = gasm_->Word32Shl(value, Int32Constant(16));
1677 }
1678 } else if (wasmtype == wasm::kWasmI32 &&
1679 mem_rep == MachineRepresentation::kWord16) {
1680 value = gasm_->Word32Shl(value, Int32Constant(16));
1681 }
1682
1683 int i;
1684 uint32_t shiftCount;
1685
1686 if (ReverseBytesSupported(m, valueSizeInBytes)) {
1687 switch (valueSizeInBytes) {
1688 case 4:
1689 result = gasm_->Word32ReverseBytes(value);
1690 break;
1691 case 8:
1692 result = gasm_->Word64ReverseBytes(value);
1693 break;
1694 case 16:
1695 result = graph()->NewNode(m->Simd128ReverseBytes(), value);
1696 break;
1697 default:
1698 UNREACHABLE();
1699 }
1700 } else {
1701 for (i = 0, shiftCount = valueSizeInBits - 8; i < valueSizeInBits / 2;
1702 i += 8, shiftCount -= 16) {
1703 Node* shiftLower;
1704 Node* shiftHigher;
1705 Node* lowerByte;
1706 Node* higherByte;
1707
1708 DCHECK_LT(0, shiftCount);
1709 DCHECK_EQ(0, (shiftCount + 8) % 16);
1710
1711 if (valueSizeInBits > 32) {
1712 shiftLower = gasm_->Word64Shl(value, Int64Constant(shiftCount));
1713 shiftHigher = gasm_->Word64Shr(value, Int64Constant(shiftCount));
1714 lowerByte = gasm_->Word64And(
1715 shiftLower, Int64Constant(static_cast<uint64_t>(0xFF)
1716 << (valueSizeInBits - 8 - i)));
1717 higherByte = gasm_->Word64And(
1718 shiftHigher, Int64Constant(static_cast<uint64_t>(0xFF) << i));
1719 result = gasm_->Word64Or(result, lowerByte);
1720 result = gasm_->Word64Or(result, higherByte);
1721 } else {
1722 shiftLower = gasm_->Word32Shl(value, Int32Constant(shiftCount));
1723 shiftHigher = gasm_->Word32Shr(value, Int32Constant(shiftCount));
1724 lowerByte = gasm_->Word32And(
1725 shiftLower, Int32Constant(static_cast<uint32_t>(0xFF)
1726 << (valueSizeInBits - 8 - i)));
1727 higherByte = gasm_->Word32And(
1728 shiftHigher, Int32Constant(static_cast<uint32_t>(0xFF) << i));
1729 result = gasm_->Word32Or(result, lowerByte);
1730 result = gasm_->Word32Or(result, higherByte);
1731 }
1732 }
1733 }
1734
1735 if (isFloat) {
1736 switch (wasmtype.kind()) {
1737 case wasm::kF64:
1738 result = gasm_->BitcastInt64ToFloat64(result);
1739 break;
1740 case wasm::kF32:
1741 result = gasm_->BitcastInt32ToFloat32(result);
1742 break;
1743 default:
1744 UNREACHABLE();
1745 }
1746 }
1747
1748 return result;
1749 }
1750
BuildChangeEndiannessLoad(Node * node,MachineType memtype,wasm::ValueType wasmtype)1751 Node* WasmGraphBuilder::BuildChangeEndiannessLoad(Node* node,
1752 MachineType memtype,
1753 wasm::ValueType wasmtype) {
1754 Node* result;
1755 Node* value = node;
1756 MachineOperatorBuilder* m = mcgraph()->machine();
1757 int valueSizeInBytes = ElementSizeInBytes(memtype.representation());
1758 int valueSizeInBits = 8 * valueSizeInBytes;
1759 bool isFloat = false;
1760
1761 switch (memtype.representation()) {
1762 case MachineRepresentation::kFloat64:
1763 value = gasm_->BitcastFloat64ToInt64(node);
1764 isFloat = true;
1765 V8_FALLTHROUGH;
1766 case MachineRepresentation::kWord64:
1767 result = Int64Constant(0);
1768 break;
1769 case MachineRepresentation::kFloat32:
1770 value = gasm_->BitcastFloat32ToInt32(node);
1771 isFloat = true;
1772 V8_FALLTHROUGH;
1773 case MachineRepresentation::kWord32:
1774 case MachineRepresentation::kWord16:
1775 result = Int32Constant(0);
1776 break;
1777 case MachineRepresentation::kWord8:
1778 // No need to change endianness for byte size, return original node
1779 return node;
1780 case MachineRepresentation::kSimd128:
1781 DCHECK(ReverseBytesSupported(m, valueSizeInBytes));
1782 break;
1783 default:
1784 UNREACHABLE();
1785 }
1786
1787 int i;
1788 uint32_t shiftCount;
1789
1790 if (ReverseBytesSupported(m, valueSizeInBytes < 4 ? 4 : valueSizeInBytes)) {
1791 switch (valueSizeInBytes) {
1792 case 2:
1793 result = gasm_->Word32ReverseBytes(
1794 gasm_->Word32Shl(value, Int32Constant(16)));
1795 break;
1796 case 4:
1797 result = gasm_->Word32ReverseBytes(value);
1798 break;
1799 case 8:
1800 result = gasm_->Word64ReverseBytes(value);
1801 break;
1802 case 16:
1803 result = graph()->NewNode(m->Simd128ReverseBytes(), value);
1804 break;
1805 default:
1806 UNREACHABLE();
1807 }
1808 } else {
1809 for (i = 0, shiftCount = valueSizeInBits - 8; i < valueSizeInBits / 2;
1810 i += 8, shiftCount -= 16) {
1811 Node* shiftLower;
1812 Node* shiftHigher;
1813 Node* lowerByte;
1814 Node* higherByte;
1815
1816 DCHECK_LT(0, shiftCount);
1817 DCHECK_EQ(0, (shiftCount + 8) % 16);
1818
1819 if (valueSizeInBits > 32) {
1820 shiftLower = gasm_->Word64Shl(value, Int64Constant(shiftCount));
1821 shiftHigher = gasm_->Word64Shr(value, Int64Constant(shiftCount));
1822 lowerByte = gasm_->Word64And(
1823 shiftLower, Int64Constant(static_cast<uint64_t>(0xFF)
1824 << (valueSizeInBits - 8 - i)));
1825 higherByte = gasm_->Word64And(
1826 shiftHigher, Int64Constant(static_cast<uint64_t>(0xFF) << i));
1827 result = gasm_->Word64Or(result, lowerByte);
1828 result = gasm_->Word64Or(result, higherByte);
1829 } else {
1830 shiftLower = gasm_->Word32Shl(value, Int32Constant(shiftCount));
1831 shiftHigher = gasm_->Word32Shr(value, Int32Constant(shiftCount));
1832 lowerByte = gasm_->Word32And(
1833 shiftLower, Int32Constant(static_cast<uint32_t>(0xFF)
1834 << (valueSizeInBits - 8 - i)));
1835 higherByte = gasm_->Word32And(
1836 shiftHigher, Int32Constant(static_cast<uint32_t>(0xFF) << i));
1837 result = gasm_->Word32Or(result, lowerByte);
1838 result = gasm_->Word32Or(result, higherByte);
1839 }
1840 }
1841 }
1842
1843 if (isFloat) {
1844 switch (memtype.representation()) {
1845 case MachineRepresentation::kFloat64:
1846 result = gasm_->BitcastInt64ToFloat64(result);
1847 break;
1848 case MachineRepresentation::kFloat32:
1849 result = gasm_->BitcastInt32ToFloat32(result);
1850 break;
1851 default:
1852 UNREACHABLE();
1853 }
1854 }
1855
1856 // We need to sign or zero extend the value
1857 if (memtype.IsSigned()) {
1858 DCHECK(!isFloat);
1859 if (valueSizeInBits < 32) {
1860 Node* shiftBitCount;
1861 // Perform sign extension using following trick
1862 // result = (x << machine_width - type_width) >> (machine_width -
1863 // type_width)
1864 if (wasmtype == wasm::kWasmI64) {
1865 shiftBitCount = Int32Constant(64 - valueSizeInBits);
1866 result = gasm_->Word64Sar(
1867 gasm_->Word64Shl(gasm_->ChangeInt32ToInt64(result), shiftBitCount),
1868 shiftBitCount);
1869 } else if (wasmtype == wasm::kWasmI32) {
1870 shiftBitCount = Int32Constant(32 - valueSizeInBits);
1871 result = gasm_->Word32Sar(gasm_->Word32Shl(result, shiftBitCount),
1872 shiftBitCount);
1873 }
1874 }
1875 } else if (wasmtype == wasm::kWasmI64 && valueSizeInBits < 64) {
1876 result = gasm_->ChangeUint32ToUint64(result);
1877 }
1878
1879 return result;
1880 }
1881
BuildF32CopySign(Node * left,Node * right)1882 Node* WasmGraphBuilder::BuildF32CopySign(Node* left, Node* right) {
1883 Node* result = Unop(
1884 wasm::kExprF32ReinterpretI32,
1885 Binop(wasm::kExprI32Ior,
1886 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, left),
1887 Int32Constant(0x7FFFFFFF)),
1888 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, right),
1889 Int32Constant(0x80000000))));
1890
1891 return result;
1892 }
1893
BuildF64CopySign(Node * left,Node * right)1894 Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) {
1895 if (mcgraph()->machine()->Is64()) {
1896 return gasm_->BitcastInt64ToFloat64(
1897 gasm_->Word64Or(gasm_->Word64And(gasm_->BitcastFloat64ToInt64(left),
1898 Int64Constant(0x7FFFFFFFFFFFFFFF)),
1899 gasm_->Word64And(gasm_->BitcastFloat64ToInt64(right),
1900 Int64Constant(0x8000000000000000))));
1901 }
1902
1903 DCHECK(mcgraph()->machine()->Is32());
1904
1905 Node* high_word_left = gasm_->Float64ExtractHighWord32(left);
1906 Node* high_word_right = gasm_->Float64ExtractHighWord32(right);
1907
1908 Node* new_high_word = gasm_->Word32Or(
1909 gasm_->Word32And(high_word_left, Int32Constant(0x7FFFFFFF)),
1910 gasm_->Word32And(high_word_right, Int32Constant(0x80000000)));
1911
1912 return gasm_->Float64InsertHighWord32(left, new_high_word);
1913 }
1914
1915 namespace {
1916
IntConvertType(wasm::WasmOpcode opcode)1917 MachineType IntConvertType(wasm::WasmOpcode opcode) {
1918 switch (opcode) {
1919 case wasm::kExprI32SConvertF32:
1920 case wasm::kExprI32SConvertF64:
1921 case wasm::kExprI32SConvertSatF32:
1922 case wasm::kExprI32SConvertSatF64:
1923 return MachineType::Int32();
1924 case wasm::kExprI32UConvertF32:
1925 case wasm::kExprI32UConvertF64:
1926 case wasm::kExprI32UConvertSatF32:
1927 case wasm::kExprI32UConvertSatF64:
1928 return MachineType::Uint32();
1929 case wasm::kExprI64SConvertF32:
1930 case wasm::kExprI64SConvertF64:
1931 case wasm::kExprI64SConvertSatF32:
1932 case wasm::kExprI64SConvertSatF64:
1933 return MachineType::Int64();
1934 case wasm::kExprI64UConvertF32:
1935 case wasm::kExprI64UConvertF64:
1936 case wasm::kExprI64UConvertSatF32:
1937 case wasm::kExprI64UConvertSatF64:
1938 return MachineType::Uint64();
1939 default:
1940 UNREACHABLE();
1941 }
1942 }
1943
FloatConvertType(wasm::WasmOpcode opcode)1944 MachineType FloatConvertType(wasm::WasmOpcode opcode) {
1945 switch (opcode) {
1946 case wasm::kExprI32SConvertF32:
1947 case wasm::kExprI32UConvertF32:
1948 case wasm::kExprI32SConvertSatF32:
1949 case wasm::kExprI64SConvertF32:
1950 case wasm::kExprI64UConvertF32:
1951 case wasm::kExprI32UConvertSatF32:
1952 case wasm::kExprI64SConvertSatF32:
1953 case wasm::kExprI64UConvertSatF32:
1954 return MachineType::Float32();
1955 case wasm::kExprI32SConvertF64:
1956 case wasm::kExprI32UConvertF64:
1957 case wasm::kExprI64SConvertF64:
1958 case wasm::kExprI64UConvertF64:
1959 case wasm::kExprI32SConvertSatF64:
1960 case wasm::kExprI32UConvertSatF64:
1961 case wasm::kExprI64SConvertSatF64:
1962 case wasm::kExprI64UConvertSatF64:
1963 return MachineType::Float64();
1964 default:
1965 UNREACHABLE();
1966 }
1967 }
1968
ConvertOp(WasmGraphBuilder * builder,wasm::WasmOpcode opcode)1969 const Operator* ConvertOp(WasmGraphBuilder* builder, wasm::WasmOpcode opcode) {
1970 switch (opcode) {
1971 case wasm::kExprI32SConvertF32:
1972 return builder->mcgraph()->machine()->TruncateFloat32ToInt32(
1973 TruncateKind::kSetOverflowToMin);
1974 case wasm::kExprI32SConvertSatF32:
1975 return builder->mcgraph()->machine()->TruncateFloat32ToInt32(
1976 TruncateKind::kArchitectureDefault);
1977 case wasm::kExprI32UConvertF32:
1978 return builder->mcgraph()->machine()->TruncateFloat32ToUint32(
1979 TruncateKind::kSetOverflowToMin);
1980 case wasm::kExprI32UConvertSatF32:
1981 return builder->mcgraph()->machine()->TruncateFloat32ToUint32(
1982 TruncateKind::kArchitectureDefault);
1983 case wasm::kExprI32SConvertF64:
1984 case wasm::kExprI32SConvertSatF64:
1985 return builder->mcgraph()->machine()->ChangeFloat64ToInt32();
1986 case wasm::kExprI32UConvertF64:
1987 case wasm::kExprI32UConvertSatF64:
1988 return builder->mcgraph()->machine()->TruncateFloat64ToUint32();
1989 case wasm::kExprI64SConvertF32:
1990 case wasm::kExprI64SConvertSatF32:
1991 return builder->mcgraph()->machine()->TryTruncateFloat32ToInt64();
1992 case wasm::kExprI64UConvertF32:
1993 case wasm::kExprI64UConvertSatF32:
1994 return builder->mcgraph()->machine()->TryTruncateFloat32ToUint64();
1995 case wasm::kExprI64SConvertF64:
1996 case wasm::kExprI64SConvertSatF64:
1997 return builder->mcgraph()->machine()->TryTruncateFloat64ToInt64();
1998 case wasm::kExprI64UConvertF64:
1999 case wasm::kExprI64UConvertSatF64:
2000 return builder->mcgraph()->machine()->TryTruncateFloat64ToUint64();
2001 default:
2002 UNREACHABLE();
2003 }
2004 }
2005
ConvertBackOp(wasm::WasmOpcode opcode)2006 wasm::WasmOpcode ConvertBackOp(wasm::WasmOpcode opcode) {
2007 switch (opcode) {
2008 case wasm::kExprI32SConvertF32:
2009 case wasm::kExprI32SConvertSatF32:
2010 return wasm::kExprF32SConvertI32;
2011 case wasm::kExprI32UConvertF32:
2012 case wasm::kExprI32UConvertSatF32:
2013 return wasm::kExprF32UConvertI32;
2014 case wasm::kExprI32SConvertF64:
2015 case wasm::kExprI32SConvertSatF64:
2016 return wasm::kExprF64SConvertI32;
2017 case wasm::kExprI32UConvertF64:
2018 case wasm::kExprI32UConvertSatF64:
2019 return wasm::kExprF64UConvertI32;
2020 default:
2021 UNREACHABLE();
2022 }
2023 }
2024
IsTrappingConvertOp(wasm::WasmOpcode opcode)2025 bool IsTrappingConvertOp(wasm::WasmOpcode opcode) {
2026 switch (opcode) {
2027 case wasm::kExprI32SConvertF32:
2028 case wasm::kExprI32UConvertF32:
2029 case wasm::kExprI32SConvertF64:
2030 case wasm::kExprI32UConvertF64:
2031 case wasm::kExprI64SConvertF32:
2032 case wasm::kExprI64UConvertF32:
2033 case wasm::kExprI64SConvertF64:
2034 case wasm::kExprI64UConvertF64:
2035 return true;
2036 case wasm::kExprI32SConvertSatF64:
2037 case wasm::kExprI32UConvertSatF64:
2038 case wasm::kExprI32SConvertSatF32:
2039 case wasm::kExprI32UConvertSatF32:
2040 case wasm::kExprI64SConvertSatF32:
2041 case wasm::kExprI64UConvertSatF32:
2042 case wasm::kExprI64SConvertSatF64:
2043 case wasm::kExprI64UConvertSatF64:
2044 return false;
2045 default:
2046 UNREACHABLE();
2047 }
2048 }
2049
Zero(WasmGraphBuilder * builder,const MachineType & ty)2050 Node* Zero(WasmGraphBuilder* builder, const MachineType& ty) {
2051 switch (ty.representation()) {
2052 case MachineRepresentation::kWord32:
2053 return builder->Int32Constant(0);
2054 case MachineRepresentation::kWord64:
2055 return builder->Int64Constant(0);
2056 case MachineRepresentation::kFloat32:
2057 return builder->Float32Constant(0.0);
2058 case MachineRepresentation::kFloat64:
2059 return builder->Float64Constant(0.0);
2060 default:
2061 UNREACHABLE();
2062 }
2063 }
2064
Min(WasmGraphBuilder * builder,const MachineType & ty)2065 Node* Min(WasmGraphBuilder* builder, const MachineType& ty) {
2066 switch (ty.semantic()) {
2067 case MachineSemantic::kInt32:
2068 return builder->Int32Constant(std::numeric_limits<int32_t>::min());
2069 case MachineSemantic::kUint32:
2070 return builder->Int32Constant(std::numeric_limits<uint32_t>::min());
2071 case MachineSemantic::kInt64:
2072 return builder->Int64Constant(std::numeric_limits<int64_t>::min());
2073 case MachineSemantic::kUint64:
2074 return builder->Int64Constant(std::numeric_limits<uint64_t>::min());
2075 default:
2076 UNREACHABLE();
2077 }
2078 }
2079
Max(WasmGraphBuilder * builder,const MachineType & ty)2080 Node* Max(WasmGraphBuilder* builder, const MachineType& ty) {
2081 switch (ty.semantic()) {
2082 case MachineSemantic::kInt32:
2083 return builder->Int32Constant(std::numeric_limits<int32_t>::max());
2084 case MachineSemantic::kUint32:
2085 return builder->Int32Constant(std::numeric_limits<uint32_t>::max());
2086 case MachineSemantic::kInt64:
2087 return builder->Int64Constant(std::numeric_limits<int64_t>::max());
2088 case MachineSemantic::kUint64:
2089 return builder->Int64Constant(std::numeric_limits<uint64_t>::max());
2090 default:
2091 UNREACHABLE();
2092 }
2093 }
2094
TruncOp(const MachineType & ty)2095 wasm::WasmOpcode TruncOp(const MachineType& ty) {
2096 switch (ty.representation()) {
2097 case MachineRepresentation::kFloat32:
2098 return wasm::kExprF32Trunc;
2099 case MachineRepresentation::kFloat64:
2100 return wasm::kExprF64Trunc;
2101 default:
2102 UNREACHABLE();
2103 }
2104 }
2105
NeOp(const MachineType & ty)2106 wasm::WasmOpcode NeOp(const MachineType& ty) {
2107 switch (ty.representation()) {
2108 case MachineRepresentation::kFloat32:
2109 return wasm::kExprF32Ne;
2110 case MachineRepresentation::kFloat64:
2111 return wasm::kExprF64Ne;
2112 default:
2113 UNREACHABLE();
2114 }
2115 }
2116
LtOp(const MachineType & ty)2117 wasm::WasmOpcode LtOp(const MachineType& ty) {
2118 switch (ty.representation()) {
2119 case MachineRepresentation::kFloat32:
2120 return wasm::kExprF32Lt;
2121 case MachineRepresentation::kFloat64:
2122 return wasm::kExprF64Lt;
2123 default:
2124 UNREACHABLE();
2125 }
2126 }
2127
ConvertTrapTest(WasmGraphBuilder * builder,wasm::WasmOpcode opcode,const MachineType & int_ty,const MachineType & float_ty,Node * trunc,Node * converted_value)2128 Node* ConvertTrapTest(WasmGraphBuilder* builder, wasm::WasmOpcode opcode,
2129 const MachineType& int_ty, const MachineType& float_ty,
2130 Node* trunc, Node* converted_value) {
2131 if (int_ty.representation() == MachineRepresentation::kWord32) {
2132 Node* check = builder->Unop(ConvertBackOp(opcode), converted_value);
2133 return builder->Binop(NeOp(float_ty), trunc, check);
2134 }
2135 return builder->graph()->NewNode(builder->mcgraph()->common()->Projection(1),
2136 trunc, builder->graph()->start());
2137 }
2138
ConvertSaturateTest(WasmGraphBuilder * builder,wasm::WasmOpcode opcode,const MachineType & int_ty,const MachineType & float_ty,Node * trunc,Node * converted_value)2139 Node* ConvertSaturateTest(WasmGraphBuilder* builder, wasm::WasmOpcode opcode,
2140 const MachineType& int_ty,
2141 const MachineType& float_ty, Node* trunc,
2142 Node* converted_value) {
2143 Node* test = ConvertTrapTest(builder, opcode, int_ty, float_ty, trunc,
2144 converted_value);
2145 if (int_ty.representation() == MachineRepresentation::kWord64) {
2146 test = builder->Binop(wasm::kExprI64Eq, test, builder->Int64Constant(0));
2147 }
2148 return test;
2149 }
2150
2151 } // namespace
2152
BuildIntConvertFloat(Node * input,wasm::WasmCodePosition position,wasm::WasmOpcode opcode)2153 Node* WasmGraphBuilder::BuildIntConvertFloat(Node* input,
2154 wasm::WasmCodePosition position,
2155 wasm::WasmOpcode opcode) {
2156 const MachineType int_ty = IntConvertType(opcode);
2157 const MachineType float_ty = FloatConvertType(opcode);
2158 const Operator* conv_op = ConvertOp(this, opcode);
2159 Node* trunc = nullptr;
2160 Node* converted_value = nullptr;
2161 const bool is_int32 =
2162 int_ty.representation() == MachineRepresentation::kWord32;
2163 if (is_int32) {
2164 trunc = Unop(TruncOp(float_ty), input);
2165 converted_value = graph()->NewNode(conv_op, trunc);
2166 } else {
2167 trunc = graph()->NewNode(conv_op, input);
2168 converted_value = graph()->NewNode(mcgraph()->common()->Projection(0),
2169 trunc, graph()->start());
2170 }
2171 if (IsTrappingConvertOp(opcode)) {
2172 Node* test =
2173 ConvertTrapTest(this, opcode, int_ty, float_ty, trunc, converted_value);
2174 if (is_int32) {
2175 TrapIfTrue(wasm::kTrapFloatUnrepresentable, test, position);
2176 } else {
2177 ZeroCheck64(wasm::kTrapFloatUnrepresentable, test, position);
2178 }
2179 return converted_value;
2180 }
2181 if (mcgraph()->machine()->SatConversionIsSafe()) {
2182 return converted_value;
2183 }
2184 Node* test = ConvertSaturateTest(this, opcode, int_ty, float_ty, trunc,
2185 converted_value);
2186 Diamond tl_d(graph(), mcgraph()->common(), test, BranchHint::kFalse);
2187 tl_d.Chain(control());
2188 Node* nan_test = Binop(NeOp(float_ty), input, input);
2189 Diamond nan_d(graph(), mcgraph()->common(), nan_test, BranchHint::kFalse);
2190 nan_d.Nest(tl_d, true);
2191 Node* neg_test = Binop(LtOp(float_ty), input, Zero(this, float_ty));
2192 Diamond sat_d(graph(), mcgraph()->common(), neg_test, BranchHint::kNone);
2193 sat_d.Nest(nan_d, false);
2194 Node* sat_val =
2195 sat_d.Phi(int_ty.representation(), Min(this, int_ty), Max(this, int_ty));
2196 Node* nan_val =
2197 nan_d.Phi(int_ty.representation(), Zero(this, int_ty), sat_val);
2198 return tl_d.Phi(int_ty.representation(), nan_val, converted_value);
2199 }
2200
BuildI32AsmjsSConvertF32(Node * input)2201 Node* WasmGraphBuilder::BuildI32AsmjsSConvertF32(Node* input) {
2202 // asm.js must use the wacky JS semantics.
2203 return gasm_->TruncateFloat64ToWord32(gasm_->ChangeFloat32ToFloat64(input));
2204 }
2205
BuildI32AsmjsSConvertF64(Node * input)2206 Node* WasmGraphBuilder::BuildI32AsmjsSConvertF64(Node* input) {
2207 // asm.js must use the wacky JS semantics.
2208 return gasm_->TruncateFloat64ToWord32(input);
2209 }
2210
BuildI32AsmjsUConvertF32(Node * input)2211 Node* WasmGraphBuilder::BuildI32AsmjsUConvertF32(Node* input) {
2212 // asm.js must use the wacky JS semantics.
2213 return gasm_->TruncateFloat64ToWord32(gasm_->ChangeFloat32ToFloat64(input));
2214 }
2215
BuildI32AsmjsUConvertF64(Node * input)2216 Node* WasmGraphBuilder::BuildI32AsmjsUConvertF64(Node* input) {
2217 // asm.js must use the wacky JS semantics.
2218 return gasm_->TruncateFloat64ToWord32(input);
2219 }
2220
BuildBitCountingCall(Node * input,ExternalReference ref,MachineRepresentation input_type)2221 Node* WasmGraphBuilder::BuildBitCountingCall(Node* input, ExternalReference ref,
2222 MachineRepresentation input_type) {
2223 Node* stack_slot_param = StoreArgsInStackSlot({{input_type, input}});
2224
2225 MachineType sig_types[] = {MachineType::Int32(), MachineType::Pointer()};
2226 MachineSignature sig(1, 1, sig_types);
2227
2228 Node* function = gasm_->ExternalConstant(ref);
2229
2230 return BuildCCall(&sig, function, stack_slot_param);
2231 }
2232
BuildI32Ctz(Node * input)2233 Node* WasmGraphBuilder::BuildI32Ctz(Node* input) {
2234 return BuildBitCountingCall(input, ExternalReference::wasm_word32_ctz(),
2235 MachineRepresentation::kWord32);
2236 }
2237
BuildI64Ctz(Node * input)2238 Node* WasmGraphBuilder::BuildI64Ctz(Node* input) {
2239 return Unop(wasm::kExprI64UConvertI32,
2240 BuildBitCountingCall(input, ExternalReference::wasm_word64_ctz(),
2241 MachineRepresentation::kWord64));
2242 }
2243
BuildI32Popcnt(Node * input)2244 Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) {
2245 return BuildBitCountingCall(input, ExternalReference::wasm_word32_popcnt(),
2246 MachineRepresentation::kWord32);
2247 }
2248
BuildI64Popcnt(Node * input)2249 Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) {
2250 return Unop(
2251 wasm::kExprI64UConvertI32,
2252 BuildBitCountingCall(input, ExternalReference::wasm_word64_popcnt(),
2253 MachineRepresentation::kWord64));
2254 }
2255
BuildF32Trunc(Node * input)2256 Node* WasmGraphBuilder::BuildF32Trunc(Node* input) {
2257 MachineType type = MachineType::Float32();
2258 ExternalReference ref = ExternalReference::wasm_f32_trunc();
2259
2260 return BuildCFuncInstruction(ref, type, input);
2261 }
2262
BuildF32Floor(Node * input)2263 Node* WasmGraphBuilder::BuildF32Floor(Node* input) {
2264 MachineType type = MachineType::Float32();
2265 ExternalReference ref = ExternalReference::wasm_f32_floor();
2266 return BuildCFuncInstruction(ref, type, input);
2267 }
2268
BuildF32Ceil(Node * input)2269 Node* WasmGraphBuilder::BuildF32Ceil(Node* input) {
2270 MachineType type = MachineType::Float32();
2271 ExternalReference ref = ExternalReference::wasm_f32_ceil();
2272 return BuildCFuncInstruction(ref, type, input);
2273 }
2274
BuildF32NearestInt(Node * input)2275 Node* WasmGraphBuilder::BuildF32NearestInt(Node* input) {
2276 MachineType type = MachineType::Float32();
2277 ExternalReference ref = ExternalReference::wasm_f32_nearest_int();
2278 return BuildCFuncInstruction(ref, type, input);
2279 }
2280
BuildF64Trunc(Node * input)2281 Node* WasmGraphBuilder::BuildF64Trunc(Node* input) {
2282 MachineType type = MachineType::Float64();
2283 ExternalReference ref = ExternalReference::wasm_f64_trunc();
2284 return BuildCFuncInstruction(ref, type, input);
2285 }
2286
BuildF64Floor(Node * input)2287 Node* WasmGraphBuilder::BuildF64Floor(Node* input) {
2288 MachineType type = MachineType::Float64();
2289 ExternalReference ref = ExternalReference::wasm_f64_floor();
2290 return BuildCFuncInstruction(ref, type, input);
2291 }
2292
BuildF64Ceil(Node * input)2293 Node* WasmGraphBuilder::BuildF64Ceil(Node* input) {
2294 MachineType type = MachineType::Float64();
2295 ExternalReference ref = ExternalReference::wasm_f64_ceil();
2296 return BuildCFuncInstruction(ref, type, input);
2297 }
2298
BuildF64NearestInt(Node * input)2299 Node* WasmGraphBuilder::BuildF64NearestInt(Node* input) {
2300 MachineType type = MachineType::Float64();
2301 ExternalReference ref = ExternalReference::wasm_f64_nearest_int();
2302 return BuildCFuncInstruction(ref, type, input);
2303 }
2304
BuildF64Acos(Node * input)2305 Node* WasmGraphBuilder::BuildF64Acos(Node* input) {
2306 MachineType type = MachineType::Float64();
2307 ExternalReference ref = ExternalReference::f64_acos_wrapper_function();
2308 return BuildCFuncInstruction(ref, type, input);
2309 }
2310
BuildF64Asin(Node * input)2311 Node* WasmGraphBuilder::BuildF64Asin(Node* input) {
2312 MachineType type = MachineType::Float64();
2313 ExternalReference ref = ExternalReference::f64_asin_wrapper_function();
2314 return BuildCFuncInstruction(ref, type, input);
2315 }
2316
BuildF64Pow(Node * left,Node * right)2317 Node* WasmGraphBuilder::BuildF64Pow(Node* left, Node* right) {
2318 MachineType type = MachineType::Float64();
2319 ExternalReference ref = ExternalReference::wasm_float64_pow();
2320 return BuildCFuncInstruction(ref, type, left, right);
2321 }
2322
BuildF64Mod(Node * left,Node * right)2323 Node* WasmGraphBuilder::BuildF64Mod(Node* left, Node* right) {
2324 MachineType type = MachineType::Float64();
2325 ExternalReference ref = ExternalReference::f64_mod_wrapper_function();
2326 return BuildCFuncInstruction(ref, type, left, right);
2327 }
2328
BuildCFuncInstruction(ExternalReference ref,MachineType type,Node * input0,Node * input1)2329 Node* WasmGraphBuilder::BuildCFuncInstruction(ExternalReference ref,
2330 MachineType type, Node* input0,
2331 Node* input1) {
2332 // We do truncation by calling a C function which calculates the result.
2333 // The input is passed to the C function as a byte buffer holding the two
2334 // input doubles. We reserve this byte buffer as a stack slot, store the
2335 // parameters in this buffer slots, pass a pointer to the buffer to the C
2336 // function, and after calling the C function we collect the return value from
2337 // the buffer.
2338 Node* stack_slot;
2339 if (input1) {
2340 stack_slot = StoreArgsInStackSlot(
2341 {{type.representation(), input0}, {type.representation(), input1}});
2342 } else {
2343 stack_slot = StoreArgsInStackSlot({{type.representation(), input0}});
2344 }
2345
2346 MachineType sig_types[] = {MachineType::Pointer()};
2347 MachineSignature sig(0, 1, sig_types);
2348 Node* function = gasm_->ExternalConstant(ref);
2349 BuildCCall(&sig, function, stack_slot);
2350
2351 return gasm_->LoadFromObject(type, stack_slot, 0);
2352 }
2353
BuildF32SConvertI64(Node * input)2354 Node* WasmGraphBuilder::BuildF32SConvertI64(Node* input) {
2355 // TODO(titzer/bradnelson): Check handlng of asm.js case.
2356 return BuildIntToFloatConversionInstruction(
2357 input, ExternalReference::wasm_int64_to_float32(),
2358 MachineRepresentation::kWord64, MachineType::Float32());
2359 }
BuildF32UConvertI64(Node * input)2360 Node* WasmGraphBuilder::BuildF32UConvertI64(Node* input) {
2361 // TODO(titzer/bradnelson): Check handlng of asm.js case.
2362 return BuildIntToFloatConversionInstruction(
2363 input, ExternalReference::wasm_uint64_to_float32(),
2364 MachineRepresentation::kWord64, MachineType::Float32());
2365 }
BuildF64SConvertI64(Node * input)2366 Node* WasmGraphBuilder::BuildF64SConvertI64(Node* input) {
2367 return BuildIntToFloatConversionInstruction(
2368 input, ExternalReference::wasm_int64_to_float64(),
2369 MachineRepresentation::kWord64, MachineType::Float64());
2370 }
BuildF64UConvertI64(Node * input)2371 Node* WasmGraphBuilder::BuildF64UConvertI64(Node* input) {
2372 return BuildIntToFloatConversionInstruction(
2373 input, ExternalReference::wasm_uint64_to_float64(),
2374 MachineRepresentation::kWord64, MachineType::Float64());
2375 }
2376
BuildIntToFloatConversionInstruction(Node * input,ExternalReference ref,MachineRepresentation parameter_representation,const MachineType result_type)2377 Node* WasmGraphBuilder::BuildIntToFloatConversionInstruction(
2378 Node* input, ExternalReference ref,
2379 MachineRepresentation parameter_representation,
2380 const MachineType result_type) {
2381 int stack_slot_size =
2382 std::max(ElementSizeInBytes(parameter_representation),
2383 ElementSizeInBytes(result_type.representation()));
2384 Node* stack_slot =
2385 graph()->NewNode(mcgraph()->machine()->StackSlot(stack_slot_size));
2386 auto store_rep =
2387 StoreRepresentation(parameter_representation, kNoWriteBarrier);
2388 gasm_->Store(store_rep, stack_slot, 0, input);
2389 MachineType sig_types[] = {MachineType::Pointer()};
2390 MachineSignature sig(0, 1, sig_types);
2391 Node* function = gasm_->ExternalConstant(ref);
2392 BuildCCall(&sig, function, stack_slot);
2393 return gasm_->LoadFromObject(result_type, stack_slot, 0);
2394 }
2395
2396 namespace {
2397
convert_ccall_ref(wasm::WasmOpcode opcode)2398 ExternalReference convert_ccall_ref(wasm::WasmOpcode opcode) {
2399 switch (opcode) {
2400 case wasm::kExprI64SConvertF32:
2401 case wasm::kExprI64SConvertSatF32:
2402 return ExternalReference::wasm_float32_to_int64();
2403 case wasm::kExprI64UConvertF32:
2404 case wasm::kExprI64UConvertSatF32:
2405 return ExternalReference::wasm_float32_to_uint64();
2406 case wasm::kExprI64SConvertF64:
2407 case wasm::kExprI64SConvertSatF64:
2408 return ExternalReference::wasm_float64_to_int64();
2409 case wasm::kExprI64UConvertF64:
2410 case wasm::kExprI64UConvertSatF64:
2411 return ExternalReference::wasm_float64_to_uint64();
2412 default:
2413 UNREACHABLE();
2414 }
2415 }
2416
2417 } // namespace
2418
BuildCcallConvertFloat(Node * input,wasm::WasmCodePosition position,wasm::WasmOpcode opcode)2419 Node* WasmGraphBuilder::BuildCcallConvertFloat(Node* input,
2420 wasm::WasmCodePosition position,
2421 wasm::WasmOpcode opcode) {
2422 const MachineType int_ty = IntConvertType(opcode);
2423 const MachineType float_ty = FloatConvertType(opcode);
2424 ExternalReference call_ref = convert_ccall_ref(opcode);
2425 int stack_slot_size = std::max(ElementSizeInBytes(int_ty.representation()),
2426 ElementSizeInBytes(float_ty.representation()));
2427 Node* stack_slot =
2428 graph()->NewNode(mcgraph()->machine()->StackSlot(stack_slot_size));
2429 auto store_rep =
2430 StoreRepresentation(float_ty.representation(), kNoWriteBarrier);
2431 gasm_->Store(store_rep, stack_slot, 0, input);
2432 MachineType sig_types[] = {MachineType::Int32(), MachineType::Pointer()};
2433 MachineSignature sig(1, 1, sig_types);
2434 Node* function = gasm_->ExternalConstant(call_ref);
2435 Node* overflow = BuildCCall(&sig, function, stack_slot);
2436 if (IsTrappingConvertOp(opcode)) {
2437 ZeroCheck32(wasm::kTrapFloatUnrepresentable, overflow, position);
2438 return gasm_->LoadFromObject(int_ty, stack_slot, 0);
2439 }
2440 Node* test = Binop(wasm::kExprI32Eq, overflow, Int32Constant(0), position);
2441 Diamond tl_d(graph(), mcgraph()->common(), test, BranchHint::kFalse);
2442 tl_d.Chain(control());
2443 Node* nan_test = Binop(NeOp(float_ty), input, input);
2444 Diamond nan_d(graph(), mcgraph()->common(), nan_test, BranchHint::kFalse);
2445 nan_d.Nest(tl_d, true);
2446 Node* neg_test = Binop(LtOp(float_ty), input, Zero(this, float_ty));
2447 Diamond sat_d(graph(), mcgraph()->common(), neg_test, BranchHint::kNone);
2448 sat_d.Nest(nan_d, false);
2449 Node* sat_val =
2450 sat_d.Phi(int_ty.representation(), Min(this, int_ty), Max(this, int_ty));
2451 Node* load = gasm_->LoadFromObject(int_ty, stack_slot, 0);
2452 Node* nan_val =
2453 nan_d.Phi(int_ty.representation(), Zero(this, int_ty), sat_val);
2454 return tl_d.Phi(int_ty.representation(), nan_val, load);
2455 }
2456
MemoryGrow(Node * input)2457 Node* WasmGraphBuilder::MemoryGrow(Node* input) {
2458 needs_stack_check_ = true;
2459 if (!env_->module->is_memory64) {
2460 // For 32-bit memories, just call the builtin.
2461 return gasm_->CallRuntimeStub(wasm::WasmCode::kWasmMemoryGrow,
2462 Operator::kNoThrow, input);
2463 }
2464
2465 // If the input is not a positive int32, growing will always fail
2466 // (growing negative or requesting >= 256 TB).
2467 Node* old_effect = effect();
2468 Diamond is_32_bit(graph(), mcgraph()->common(),
2469 gasm_->Uint64LessThanOrEqual(input, Int64Constant(kMaxInt)),
2470 BranchHint::kTrue);
2471 is_32_bit.Chain(control());
2472
2473 SetControl(is_32_bit.if_true);
2474
2475 Node* grow_result = gasm_->ChangeInt32ToInt64(gasm_->CallRuntimeStub(
2476 wasm::WasmCode::kWasmMemoryGrow, Operator::kNoThrow,
2477 gasm_->TruncateInt64ToInt32(input)));
2478
2479 Node* diamond_result = is_32_bit.Phi(MachineRepresentation::kWord64,
2480 grow_result, gasm_->Int64Constant(-1));
2481 SetEffectControl(is_32_bit.EffectPhi(effect(), old_effect), is_32_bit.merge);
2482 return diamond_result;
2483 }
2484
Throw(uint32_t tag_index,const wasm::WasmTag * tag,const base::Vector<Node * > values,wasm::WasmCodePosition position)2485 Node* WasmGraphBuilder::Throw(uint32_t tag_index, const wasm::WasmTag* tag,
2486 const base::Vector<Node*> values,
2487 wasm::WasmCodePosition position) {
2488 needs_stack_check_ = true;
2489 uint32_t encoded_size = WasmExceptionPackage::GetEncodedSize(tag);
2490
2491 Node* values_array = gasm_->CallRuntimeStub(
2492 wasm::WasmCode::kWasmAllocateFixedArray, Operator::kNoThrow,
2493 gasm_->IntPtrConstant(encoded_size));
2494 SetSourcePosition(values_array, position);
2495
2496 uint32_t index = 0;
2497 const wasm::WasmTagSig* sig = tag->sig;
2498 MachineOperatorBuilder* m = mcgraph()->machine();
2499 for (size_t i = 0; i < sig->parameter_count(); ++i) {
2500 Node* value = values[i];
2501 switch (sig->GetParam(i).kind()) {
2502 case wasm::kF32:
2503 value = gasm_->BitcastFloat32ToInt32(value);
2504 V8_FALLTHROUGH;
2505 case wasm::kI32:
2506 BuildEncodeException32BitValue(values_array, &index, value);
2507 break;
2508 case wasm::kF64:
2509 value = gasm_->BitcastFloat64ToInt64(value);
2510 V8_FALLTHROUGH;
2511 case wasm::kI64: {
2512 Node* upper32 = gasm_->TruncateInt64ToInt32(
2513 Binop(wasm::kExprI64ShrU, value, Int64Constant(32)));
2514 BuildEncodeException32BitValue(values_array, &index, upper32);
2515 Node* lower32 = gasm_->TruncateInt64ToInt32(value);
2516 BuildEncodeException32BitValue(values_array, &index, lower32);
2517 break;
2518 }
2519 case wasm::kS128:
2520 BuildEncodeException32BitValue(
2521 values_array, &index,
2522 graph()->NewNode(m->I32x4ExtractLane(0), value));
2523 BuildEncodeException32BitValue(
2524 values_array, &index,
2525 graph()->NewNode(m->I32x4ExtractLane(1), value));
2526 BuildEncodeException32BitValue(
2527 values_array, &index,
2528 graph()->NewNode(m->I32x4ExtractLane(2), value));
2529 BuildEncodeException32BitValue(
2530 values_array, &index,
2531 graph()->NewNode(m->I32x4ExtractLane(3), value));
2532 break;
2533 case wasm::kRef:
2534 case wasm::kOptRef:
2535 case wasm::kRtt:
2536 gasm_->StoreFixedArrayElementAny(values_array, index, value);
2537 ++index;
2538 break;
2539 case wasm::kI8:
2540 case wasm::kI16:
2541 case wasm::kVoid:
2542 case wasm::kBottom:
2543 UNREACHABLE();
2544 }
2545 }
2546 DCHECK_EQ(encoded_size, index);
2547
2548 Node* exception_tag = LoadTagFromTable(tag_index);
2549
2550 Node* throw_call = gasm_->CallRuntimeStub(wasm::WasmCode::kWasmThrow,
2551 Operator::kNoProperties,
2552 exception_tag, values_array);
2553 SetSourcePosition(throw_call, position);
2554 return throw_call;
2555 }
2556
BuildEncodeException32BitValue(Node * values_array,uint32_t * index,Node * value)2557 void WasmGraphBuilder::BuildEncodeException32BitValue(Node* values_array,
2558 uint32_t* index,
2559 Node* value) {
2560 Node* upper_halfword_as_smi =
2561 BuildChangeUint31ToSmi(gasm_->Word32Shr(value, Int32Constant(16)));
2562 gasm_->StoreFixedArrayElementSmi(values_array, *index, upper_halfword_as_smi);
2563 ++(*index);
2564 Node* lower_halfword_as_smi =
2565 BuildChangeUint31ToSmi(gasm_->Word32And(value, Int32Constant(0xFFFFu)));
2566 gasm_->StoreFixedArrayElementSmi(values_array, *index, lower_halfword_as_smi);
2567 ++(*index);
2568 }
2569
BuildDecodeException32BitValue(Node * values_array,uint32_t * index)2570 Node* WasmGraphBuilder::BuildDecodeException32BitValue(Node* values_array,
2571 uint32_t* index) {
2572 Node* upper = BuildChangeSmiToInt32(
2573 gasm_->LoadFixedArrayElementSmi(values_array, *index));
2574 (*index)++;
2575 upper = gasm_->Word32Shl(upper, Int32Constant(16));
2576 Node* lower = BuildChangeSmiToInt32(
2577 gasm_->LoadFixedArrayElementSmi(values_array, *index));
2578 (*index)++;
2579 Node* value = gasm_->Word32Or(upper, lower);
2580 return value;
2581 }
2582
BuildDecodeException64BitValue(Node * values_array,uint32_t * index)2583 Node* WasmGraphBuilder::BuildDecodeException64BitValue(Node* values_array,
2584 uint32_t* index) {
2585 Node* upper = Binop(wasm::kExprI64Shl,
2586 Unop(wasm::kExprI64UConvertI32,
2587 BuildDecodeException32BitValue(values_array, index)),
2588 Int64Constant(32));
2589 Node* lower = Unop(wasm::kExprI64UConvertI32,
2590 BuildDecodeException32BitValue(values_array, index));
2591 return Binop(wasm::kExprI64Ior, upper, lower);
2592 }
2593
Rethrow(Node * except_obj)2594 Node* WasmGraphBuilder::Rethrow(Node* except_obj) {
2595 // TODO(v8:8091): Currently the message of the original exception is not being
2596 // preserved when rethrown to the console. The pending message will need to be
2597 // saved when caught and restored here while being rethrown.
2598 return gasm_->CallRuntimeStub(wasm::WasmCode::kWasmRethrow,
2599 Operator::kNoProperties, except_obj);
2600 }
2601
ExceptionTagEqual(Node * caught_tag,Node * expected_tag)2602 Node* WasmGraphBuilder::ExceptionTagEqual(Node* caught_tag,
2603 Node* expected_tag) {
2604 return gasm_->WordEqual(caught_tag, expected_tag);
2605 }
2606
LoadTagFromTable(uint32_t tag_index)2607 Node* WasmGraphBuilder::LoadTagFromTable(uint32_t tag_index) {
2608 Node* tags_table =
2609 LOAD_INSTANCE_FIELD(TagsTable, MachineType::TaggedPointer());
2610 Node* tag = gasm_->LoadFixedArrayElementPtr(tags_table, tag_index);
2611 return tag;
2612 }
2613
GetExceptionTag(Node * except_obj)2614 Node* WasmGraphBuilder::GetExceptionTag(Node* except_obj) {
2615 return gasm_->CallBuiltin(
2616 Builtin::kWasmGetOwnProperty, Operator::kEliminatable, except_obj,
2617 LOAD_ROOT(wasm_exception_tag_symbol, wasm_exception_tag_symbol),
2618 LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
2619 }
2620
GetExceptionValues(Node * except_obj,const wasm::WasmTag * tag,base::Vector<Node * > values)2621 Node* WasmGraphBuilder::GetExceptionValues(Node* except_obj,
2622 const wasm::WasmTag* tag,
2623 base::Vector<Node*> values) {
2624 Node* values_array = gasm_->CallBuiltin(
2625 Builtin::kWasmGetOwnProperty, Operator::kEliminatable, except_obj,
2626 LOAD_ROOT(wasm_exception_values_symbol, wasm_exception_values_symbol),
2627 LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
2628 uint32_t index = 0;
2629 const wasm::WasmTagSig* sig = tag->sig;
2630 DCHECK_EQ(sig->parameter_count(), values.size());
2631 for (size_t i = 0; i < sig->parameter_count(); ++i) {
2632 Node* value;
2633 switch (sig->GetParam(i).kind()) {
2634 case wasm::kI32:
2635 value = BuildDecodeException32BitValue(values_array, &index);
2636 break;
2637 case wasm::kI64:
2638 value = BuildDecodeException64BitValue(values_array, &index);
2639 break;
2640 case wasm::kF32: {
2641 value = Unop(wasm::kExprF32ReinterpretI32,
2642 BuildDecodeException32BitValue(values_array, &index));
2643 break;
2644 }
2645 case wasm::kF64: {
2646 value = Unop(wasm::kExprF64ReinterpretI64,
2647 BuildDecodeException64BitValue(values_array, &index));
2648 break;
2649 }
2650 case wasm::kS128:
2651 value = graph()->NewNode(
2652 mcgraph()->machine()->I32x4Splat(),
2653 BuildDecodeException32BitValue(values_array, &index));
2654 value = graph()->NewNode(
2655 mcgraph()->machine()->I32x4ReplaceLane(1), value,
2656 BuildDecodeException32BitValue(values_array, &index));
2657 value = graph()->NewNode(
2658 mcgraph()->machine()->I32x4ReplaceLane(2), value,
2659 BuildDecodeException32BitValue(values_array, &index));
2660 value = graph()->NewNode(
2661 mcgraph()->machine()->I32x4ReplaceLane(3), value,
2662 BuildDecodeException32BitValue(values_array, &index));
2663 break;
2664 case wasm::kRef:
2665 case wasm::kOptRef:
2666 case wasm::kRtt:
2667 value = gasm_->LoadFixedArrayElementAny(values_array, index);
2668 ++index;
2669 break;
2670 case wasm::kI8:
2671 case wasm::kI16:
2672 case wasm::kVoid:
2673 case wasm::kBottom:
2674 UNREACHABLE();
2675 }
2676 values[i] = value;
2677 }
2678 DCHECK_EQ(index, WasmExceptionPackage::GetEncodedSize(tag));
2679 return values_array;
2680 }
2681
BuildI32DivS(Node * left,Node * right,wasm::WasmCodePosition position)2682 Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
2683 wasm::WasmCodePosition position) {
2684 ZeroCheck32(wasm::kTrapDivByZero, right, position);
2685 Node* before = control();
2686 Node* denom_is_m1;
2687 Node* denom_is_not_m1;
2688 BranchExpectFalse(gasm_->Word32Equal(right, Int32Constant(-1)), &denom_is_m1,
2689 &denom_is_not_m1);
2690 SetControl(denom_is_m1);
2691 TrapIfEq32(wasm::kTrapDivUnrepresentable, left, kMinInt, position);
2692 if (control() != denom_is_m1) {
2693 SetControl(Merge(denom_is_not_m1, control()));
2694 } else {
2695 SetControl(before);
2696 }
2697 return gasm_->Int32Div(left, right);
2698 }
2699
BuildI32RemS(Node * left,Node * right,wasm::WasmCodePosition position)2700 Node* WasmGraphBuilder::BuildI32RemS(Node* left, Node* right,
2701 wasm::WasmCodePosition position) {
2702 MachineOperatorBuilder* m = mcgraph()->machine();
2703
2704 ZeroCheck32(wasm::kTrapRemByZero, right, position);
2705
2706 Diamond d(graph(), mcgraph()->common(),
2707 gasm_->Word32Equal(right, Int32Constant(-1)), BranchHint::kFalse);
2708 d.Chain(control());
2709
2710 return d.Phi(MachineRepresentation::kWord32, Int32Constant(0),
2711 graph()->NewNode(m->Int32Mod(), left, right, d.if_false));
2712 }
2713
BuildI32DivU(Node * left,Node * right,wasm::WasmCodePosition position)2714 Node* WasmGraphBuilder::BuildI32DivU(Node* left, Node* right,
2715 wasm::WasmCodePosition position) {
2716 ZeroCheck32(wasm::kTrapDivByZero, right, position);
2717 return gasm_->Uint32Div(left, right);
2718 }
2719
BuildI32RemU(Node * left,Node * right,wasm::WasmCodePosition position)2720 Node* WasmGraphBuilder::BuildI32RemU(Node* left, Node* right,
2721 wasm::WasmCodePosition position) {
2722 ZeroCheck32(wasm::kTrapRemByZero, right, position);
2723 return gasm_->Uint32Mod(left, right);
2724 }
2725
BuildI32AsmjsDivS(Node * left,Node * right)2726 Node* WasmGraphBuilder::BuildI32AsmjsDivS(Node* left, Node* right) {
2727 MachineOperatorBuilder* m = mcgraph()->machine();
2728
2729 Int32Matcher mr(right);
2730 if (mr.HasResolvedValue()) {
2731 if (mr.ResolvedValue() == 0) {
2732 return Int32Constant(0);
2733 } else if (mr.ResolvedValue() == -1) {
2734 // The result is the negation of the left input.
2735 return gasm_->Int32Sub(Int32Constant(0), left);
2736 }
2737 return gasm_->Int32Div(left, right);
2738 }
2739
2740 // asm.js semantics return 0 on divide or mod by zero.
2741 if (m->Int32DivIsSafe()) {
2742 // The hardware instruction does the right thing (e.g. arm).
2743 return gasm_->Int32Div(left, right);
2744 }
2745
2746 // Check denominator for zero.
2747 Diamond z(graph(), mcgraph()->common(),
2748 gasm_->Word32Equal(right, Int32Constant(0)), BranchHint::kFalse);
2749 z.Chain(control());
2750
2751 // Check denominator for -1. (avoid minint / -1 case).
2752 Diamond n(graph(), mcgraph()->common(),
2753 gasm_->Word32Equal(right, Int32Constant(-1)), BranchHint::kFalse);
2754 n.Chain(z.if_false);
2755
2756 Node* div = graph()->NewNode(m->Int32Div(), left, right, n.if_false);
2757
2758 Node* neg = gasm_->Int32Sub(Int32Constant(0), left);
2759
2760 return z.Phi(MachineRepresentation::kWord32, Int32Constant(0),
2761 n.Phi(MachineRepresentation::kWord32, neg, div));
2762 }
2763
BuildI32AsmjsRemS(Node * left,Node * right)2764 Node* WasmGraphBuilder::BuildI32AsmjsRemS(Node* left, Node* right) {
2765 CommonOperatorBuilder* c = mcgraph()->common();
2766 MachineOperatorBuilder* m = mcgraph()->machine();
2767 Node* const zero = Int32Constant(0);
2768
2769 Int32Matcher mr(right);
2770 if (mr.HasResolvedValue()) {
2771 if (mr.ResolvedValue() == 0 || mr.ResolvedValue() == -1) {
2772 return zero;
2773 }
2774 return gasm_->Int32Mod(left, right);
2775 }
2776
2777 // General case for signed integer modulus, with optimization for (unknown)
2778 // power of 2 right hand side.
2779 //
2780 // if 0 < right then
2781 // msk = right - 1
2782 // if right & msk != 0 then
2783 // left % right
2784 // else
2785 // if left < 0 then
2786 // -(-left & msk)
2787 // else
2788 // left & msk
2789 // else
2790 // if right < -1 then
2791 // left % right
2792 // else
2793 // zero
2794 //
2795 // Note: We do not use the Diamond helper class here, because it really hurts
2796 // readability with nested diamonds.
2797 Node* const minus_one = Int32Constant(-1);
2798
2799 const Operator* const merge_op = c->Merge(2);
2800 const Operator* const phi_op = c->Phi(MachineRepresentation::kWord32, 2);
2801
2802 Node* check0 = gasm_->Int32LessThan(zero, right);
2803 Node* branch0 =
2804 graph()->NewNode(c->Branch(BranchHint::kTrue), check0, control());
2805
2806 Node* if_true0 = graph()->NewNode(c->IfTrue(), branch0);
2807 Node* true0;
2808 {
2809 Node* msk = graph()->NewNode(m->Int32Add(), right, minus_one);
2810
2811 Node* check1 = graph()->NewNode(m->Word32And(), right, msk);
2812 Node* branch1 = graph()->NewNode(c->Branch(), check1, if_true0);
2813
2814 Node* if_true1 = graph()->NewNode(c->IfTrue(), branch1);
2815 Node* true1 = graph()->NewNode(m->Int32Mod(), left, right, if_true1);
2816
2817 Node* if_false1 = graph()->NewNode(c->IfFalse(), branch1);
2818 Node* false1;
2819 {
2820 Node* check2 = graph()->NewNode(m->Int32LessThan(), left, zero);
2821 Node* branch2 =
2822 graph()->NewNode(c->Branch(BranchHint::kFalse), check2, if_false1);
2823
2824 Node* if_true2 = graph()->NewNode(c->IfTrue(), branch2);
2825 Node* true2 = graph()->NewNode(
2826 m->Int32Sub(), zero,
2827 graph()->NewNode(m->Word32And(),
2828 graph()->NewNode(m->Int32Sub(), zero, left), msk));
2829
2830 Node* if_false2 = graph()->NewNode(c->IfFalse(), branch2);
2831 Node* false2 = graph()->NewNode(m->Word32And(), left, msk);
2832
2833 if_false1 = graph()->NewNode(merge_op, if_true2, if_false2);
2834 false1 = graph()->NewNode(phi_op, true2, false2, if_false1);
2835 }
2836
2837 if_true0 = graph()->NewNode(merge_op, if_true1, if_false1);
2838 true0 = graph()->NewNode(phi_op, true1, false1, if_true0);
2839 }
2840
2841 Node* if_false0 = graph()->NewNode(c->IfFalse(), branch0);
2842 Node* false0;
2843 {
2844 Node* check1 = graph()->NewNode(m->Int32LessThan(), right, minus_one);
2845 Node* branch1 =
2846 graph()->NewNode(c->Branch(BranchHint::kTrue), check1, if_false0);
2847
2848 Node* if_true1 = graph()->NewNode(c->IfTrue(), branch1);
2849 Node* true1 = graph()->NewNode(m->Int32Mod(), left, right, if_true1);
2850
2851 Node* if_false1 = graph()->NewNode(c->IfFalse(), branch1);
2852 Node* false1 = zero;
2853
2854 if_false0 = graph()->NewNode(merge_op, if_true1, if_false1);
2855 false0 = graph()->NewNode(phi_op, true1, false1, if_false0);
2856 }
2857
2858 Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
2859 return graph()->NewNode(phi_op, true0, false0, merge0);
2860 }
2861
BuildI32AsmjsDivU(Node * left,Node * right)2862 Node* WasmGraphBuilder::BuildI32AsmjsDivU(Node* left, Node* right) {
2863 MachineOperatorBuilder* m = mcgraph()->machine();
2864 // asm.js semantics return 0 on divide or mod by zero.
2865 if (m->Uint32DivIsSafe()) {
2866 // The hardware instruction does the right thing (e.g. arm).
2867 return gasm_->Uint32Div(left, right);
2868 }
2869
2870 // Explicit check for x % 0.
2871 Diamond z(graph(), mcgraph()->common(),
2872 gasm_->Word32Equal(right, Int32Constant(0)), BranchHint::kFalse);
2873 z.Chain(control());
2874
2875 return z.Phi(MachineRepresentation::kWord32, Int32Constant(0),
2876 graph()->NewNode(mcgraph()->machine()->Uint32Div(), left, right,
2877 z.if_false));
2878 }
2879
BuildI32AsmjsRemU(Node * left,Node * right)2880 Node* WasmGraphBuilder::BuildI32AsmjsRemU(Node* left, Node* right) {
2881 // asm.js semantics return 0 on divide or mod by zero.
2882 // Explicit check for x % 0.
2883 Diamond z(graph(), mcgraph()->common(),
2884 gasm_->Word32Equal(right, Int32Constant(0)), BranchHint::kFalse);
2885 z.Chain(control());
2886
2887 Node* rem = graph()->NewNode(mcgraph()->machine()->Uint32Mod(), left, right,
2888 z.if_false);
2889 return z.Phi(MachineRepresentation::kWord32, Int32Constant(0), rem);
2890 }
2891
BuildI64DivS(Node * left,Node * right,wasm::WasmCodePosition position)2892 Node* WasmGraphBuilder::BuildI64DivS(Node* left, Node* right,
2893 wasm::WasmCodePosition position) {
2894 if (mcgraph()->machine()->Is32()) {
2895 return BuildDiv64Call(left, right, ExternalReference::wasm_int64_div(),
2896 MachineType::Int64(), wasm::kTrapDivByZero, position);
2897 }
2898 ZeroCheck64(wasm::kTrapDivByZero, right, position);
2899 Node* before = control();
2900 Node* denom_is_m1;
2901 Node* denom_is_not_m1;
2902 BranchExpectFalse(gasm_->Word64Equal(right, Int64Constant(-1)), &denom_is_m1,
2903 &denom_is_not_m1);
2904 SetControl(denom_is_m1);
2905 TrapIfEq64(wasm::kTrapDivUnrepresentable, left,
2906 std::numeric_limits<int64_t>::min(), position);
2907 if (control() != denom_is_m1) {
2908 SetControl(Merge(denom_is_not_m1, control()));
2909 } else {
2910 SetControl(before);
2911 }
2912 return gasm_->Int64Div(left, right);
2913 }
2914
BuildI64RemS(Node * left,Node * right,wasm::WasmCodePosition position)2915 Node* WasmGraphBuilder::BuildI64RemS(Node* left, Node* right,
2916 wasm::WasmCodePosition position) {
2917 if (mcgraph()->machine()->Is32()) {
2918 return BuildDiv64Call(left, right, ExternalReference::wasm_int64_mod(),
2919 MachineType::Int64(), wasm::kTrapRemByZero, position);
2920 }
2921 ZeroCheck64(wasm::kTrapRemByZero, right, position);
2922 Diamond d(mcgraph()->graph(), mcgraph()->common(),
2923 gasm_->Word64Equal(right, Int64Constant(-1)));
2924
2925 d.Chain(control());
2926
2927 Node* rem = graph()->NewNode(mcgraph()->machine()->Int64Mod(), left, right,
2928 d.if_false);
2929
2930 return d.Phi(MachineRepresentation::kWord64, Int64Constant(0), rem);
2931 }
2932
BuildI64DivU(Node * left,Node * right,wasm::WasmCodePosition position)2933 Node* WasmGraphBuilder::BuildI64DivU(Node* left, Node* right,
2934 wasm::WasmCodePosition position) {
2935 if (mcgraph()->machine()->Is32()) {
2936 return BuildDiv64Call(left, right, ExternalReference::wasm_uint64_div(),
2937 MachineType::Int64(), wasm::kTrapDivByZero, position);
2938 }
2939 ZeroCheck64(wasm::kTrapDivByZero, right, position);
2940 return gasm_->Uint64Div(left, right);
2941 }
BuildI64RemU(Node * left,Node * right,wasm::WasmCodePosition position)2942 Node* WasmGraphBuilder::BuildI64RemU(Node* left, Node* right,
2943 wasm::WasmCodePosition position) {
2944 if (mcgraph()->machine()->Is32()) {
2945 return BuildDiv64Call(left, right, ExternalReference::wasm_uint64_mod(),
2946 MachineType::Int64(), wasm::kTrapRemByZero, position);
2947 }
2948 ZeroCheck64(wasm::kTrapRemByZero, right, position);
2949 return gasm_->Uint64Mod(left, right);
2950 }
2951
BuildDiv64Call(Node * left,Node * right,ExternalReference ref,MachineType result_type,wasm::TrapReason trap_zero,wasm::WasmCodePosition position)2952 Node* WasmGraphBuilder::BuildDiv64Call(Node* left, Node* right,
2953 ExternalReference ref,
2954 MachineType result_type,
2955 wasm::TrapReason trap_zero,
2956 wasm::WasmCodePosition position) {
2957 Node* stack_slot =
2958 StoreArgsInStackSlot({{MachineRepresentation::kWord64, left},
2959 {MachineRepresentation::kWord64, right}});
2960
2961 MachineType sig_types[] = {MachineType::Int32(), MachineType::Pointer()};
2962 MachineSignature sig(1, 1, sig_types);
2963
2964 Node* function = gasm_->ExternalConstant(ref);
2965 Node* call = BuildCCall(&sig, function, stack_slot);
2966
2967 ZeroCheck32(trap_zero, call, position);
2968 TrapIfEq32(wasm::kTrapDivUnrepresentable, call, -1, position);
2969 return gasm_->LoadFromObject(result_type, stack_slot, 0);
2970 }
2971
IsNull(Node * object)2972 Node* WasmGraphBuilder::IsNull(Node* object) {
2973 return gasm_->TaggedEqual(object, RefNull());
2974 }
2975
2976 template <typename... Args>
BuildCCall(MachineSignature * sig,Node * function,Args...args)2977 Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node* function,
2978 Args... args) {
2979 DCHECK_LE(sig->return_count(), 1);
2980 DCHECK_EQ(sizeof...(args), sig->parameter_count());
2981 Node* call_args[] = {function, args..., effect(), control()};
2982
2983 auto call_descriptor =
2984 Linkage::GetSimplifiedCDescriptor(mcgraph()->zone(), sig);
2985
2986 return gasm_->Call(call_descriptor, arraysize(call_args), call_args);
2987 }
2988
BuildCallNode(const wasm::FunctionSig * sig,base::Vector<Node * > args,wasm::WasmCodePosition position,Node * instance_node,const Operator * op,Node * frame_state)2989 Node* WasmGraphBuilder::BuildCallNode(const wasm::FunctionSig* sig,
2990 base::Vector<Node*> args,
2991 wasm::WasmCodePosition position,
2992 Node* instance_node, const Operator* op,
2993 Node* frame_state) {
2994 if (instance_node == nullptr) {
2995 instance_node = GetInstance();
2996 }
2997 needs_stack_check_ = true;
2998 const size_t params = sig->parameter_count();
2999 const size_t has_frame_state = frame_state != nullptr ? 1 : 0;
3000 const size_t extra = 3; // instance_node, effect, and control.
3001 const size_t count = 1 + params + extra + has_frame_state;
3002
3003 // Reallocate the buffer to make space for extra inputs.
3004 base::SmallVector<Node*, 16 + extra> inputs(count);
3005 DCHECK_EQ(1 + params, args.size());
3006
3007 // Make room for the instance_node parameter at index 1, just after code.
3008 inputs[0] = args[0]; // code
3009 inputs[1] = instance_node;
3010 if (params > 0) memcpy(&inputs[2], &args[1], params * sizeof(Node*));
3011
3012 // Add effect and control inputs.
3013 if (has_frame_state != 0) inputs[params + 2] = frame_state;
3014 inputs[params + has_frame_state + 2] = effect();
3015 inputs[params + has_frame_state + 3] = control();
3016
3017 Node* call = graph()->NewNode(op, static_cast<int>(count), inputs.begin());
3018 // Return calls have no effect output. Other calls are the new effect node.
3019 if (op->EffectOutputCount() > 0) SetEffect(call);
3020 DCHECK(position == wasm::kNoCodePosition || position > 0);
3021 if (position > 0) SetSourcePosition(call, position);
3022
3023 return call;
3024 }
3025
BuildWasmCall(const wasm::FunctionSig * sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position,Node * instance_node,Node * frame_state)3026 Node* WasmGraphBuilder::BuildWasmCall(const wasm::FunctionSig* sig,
3027 base::Vector<Node*> args,
3028 base::Vector<Node*> rets,
3029 wasm::WasmCodePosition position,
3030 Node* instance_node, Node* frame_state) {
3031 CallDescriptor* call_descriptor = GetWasmCallDescriptor(
3032 mcgraph()->zone(), sig, kWasmFunction, frame_state != nullptr);
3033 const Operator* op = mcgraph()->common()->Call(call_descriptor);
3034 Node* call =
3035 BuildCallNode(sig, args, position, instance_node, op, frame_state);
3036 // TODO(manoskouk): If we have kNoThrow calls, do not set them as control.
3037 DCHECK_GT(call->op()->ControlOutputCount(), 0);
3038 SetControl(call);
3039
3040 size_t ret_count = sig->return_count();
3041 if (ret_count == 0) return call; // No return value.
3042
3043 DCHECK_EQ(ret_count, rets.size());
3044 if (ret_count == 1) {
3045 // Only a single return value.
3046 rets[0] = call;
3047 } else {
3048 // Create projections for all return values.
3049 for (size_t i = 0; i < ret_count; i++) {
3050 rets[i] = graph()->NewNode(mcgraph()->common()->Projection(i), call,
3051 graph()->start());
3052 }
3053 }
3054 return call;
3055 }
3056
BuildWasmReturnCall(const wasm::FunctionSig * sig,base::Vector<Node * > args,wasm::WasmCodePosition position,Node * instance_node)3057 Node* WasmGraphBuilder::BuildWasmReturnCall(const wasm::FunctionSig* sig,
3058 base::Vector<Node*> args,
3059 wasm::WasmCodePosition position,
3060 Node* instance_node) {
3061 CallDescriptor* call_descriptor =
3062 GetWasmCallDescriptor(mcgraph()->zone(), sig);
3063 const Operator* op = mcgraph()->common()->TailCall(call_descriptor);
3064 Node* call = BuildCallNode(sig, args, position, instance_node, op);
3065
3066 // TODO(manoskouk): If we have kNoThrow calls, do not merge them to end.
3067 DCHECK_GT(call->op()->ControlOutputCount(), 0);
3068 gasm_->MergeControlToEnd(call);
3069
3070 return call;
3071 }
3072
BuildImportCall(const wasm::FunctionSig * sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position,int func_index,IsReturnCall continuation)3073 Node* WasmGraphBuilder::BuildImportCall(const wasm::FunctionSig* sig,
3074 base::Vector<Node*> args,
3075 base::Vector<Node*> rets,
3076 wasm::WasmCodePosition position,
3077 int func_index,
3078 IsReturnCall continuation) {
3079 return BuildImportCall(sig, args, rets, position,
3080 gasm_->Uint32Constant(func_index), continuation);
3081 }
3082
BuildImportCall(const wasm::FunctionSig * sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position,Node * func_index,IsReturnCall continuation)3083 Node* WasmGraphBuilder::BuildImportCall(const wasm::FunctionSig* sig,
3084 base::Vector<Node*> args,
3085 base::Vector<Node*> rets,
3086 wasm::WasmCodePosition position,
3087 Node* func_index,
3088 IsReturnCall continuation) {
3089 // Load the imported function refs array from the instance.
3090 Node* imported_function_refs =
3091 LOAD_INSTANCE_FIELD(ImportedFunctionRefs, MachineType::TaggedPointer());
3092 // Access fixed array at {header_size - tag + func_index * kTaggedSize}.
3093 Node* func_index_intptr = BuildChangeUint32ToUintPtr(func_index);
3094 Node* ref_node = gasm_->LoadFixedArrayElement(
3095 imported_function_refs, func_index_intptr, MachineType::TaggedPointer());
3096
3097 // Load the target from the imported_targets array at the offset of
3098 // {func_index}.
3099 Node* func_index_times_pointersize = gasm_->IntMul(
3100 func_index_intptr, gasm_->IntPtrConstant(kSystemPointerSize));
3101 Node* imported_targets =
3102 LOAD_INSTANCE_FIELD(ImportedFunctionTargets, MachineType::Pointer());
3103 Node* target_node = gasm_->LoadImmutableFromObject(
3104 MachineType::Pointer(), imported_targets, func_index_times_pointersize);
3105 args[0] = target_node;
3106
3107 switch (continuation) {
3108 case kCallContinues:
3109 return BuildWasmCall(sig, args, rets, position, ref_node);
3110 case kReturnCall:
3111 DCHECK(rets.empty());
3112 return BuildWasmReturnCall(sig, args, position, ref_node);
3113 }
3114 }
3115
CallDirect(uint32_t index,wasm::FunctionSig * real_sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position)3116 Node* WasmGraphBuilder::CallDirect(uint32_t index, wasm::FunctionSig* real_sig,
3117 base::Vector<Node*> args,
3118 base::Vector<Node*> rets,
3119 wasm::WasmCodePosition position) {
3120 DCHECK_NULL(args[0]);
3121
3122 if (env_ && index < env_->module->num_imported_functions) {
3123 // Call to an imported function.
3124 return BuildImportCall(real_sig, args, rets, position, index,
3125 kCallContinues);
3126 }
3127
3128 // A direct call to a wasm function defined in this module.
3129 // Just encode the function index. This will be patched at instantiation.
3130 Address code = static_cast<Address>(index);
3131 args[0] = mcgraph()->RelocatableIntPtrConstant(code, RelocInfo::WASM_CALL);
3132
3133 return BuildWasmCall(real_sig, args, rets, position, nullptr);
3134 }
3135
CallIndirect(uint32_t table_index,uint32_t sig_index,wasm::FunctionSig * sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position)3136 Node* WasmGraphBuilder::CallIndirect(uint32_t table_index, uint32_t sig_index,
3137 wasm::FunctionSig* sig,
3138 base::Vector<Node*> args,
3139 base::Vector<Node*> rets,
3140 wasm::WasmCodePosition position) {
3141 return BuildIndirectCall(table_index, sig_index, sig, args, rets, position,
3142 kCallContinues);
3143 }
3144
LoadIndirectFunctionTable(uint32_t table_index,Node ** ift_size,Node ** ift_sig_ids,Node ** ift_targets,Node ** ift_instances)3145 void WasmGraphBuilder::LoadIndirectFunctionTable(uint32_t table_index,
3146 Node** ift_size,
3147 Node** ift_sig_ids,
3148 Node** ift_targets,
3149 Node** ift_instances) {
3150 bool needs_dynamic_size = true;
3151 const wasm::WasmTable& table = env_->module->tables[table_index];
3152 if (table.has_maximum_size && table.maximum_size == table.initial_size) {
3153 *ift_size = Int32Constant(table.initial_size);
3154 needs_dynamic_size = false;
3155 }
3156
3157 if (table_index == 0) {
3158 if (needs_dynamic_size) {
3159 *ift_size = LOAD_MUTABLE_INSTANCE_FIELD(IndirectFunctionTableSize,
3160 MachineType::Uint32());
3161 }
3162 *ift_sig_ids = LOAD_MUTABLE_INSTANCE_FIELD(IndirectFunctionTableSigIds,
3163 MachineType::Pointer());
3164 *ift_targets = LOAD_MUTABLE_INSTANCE_FIELD(IndirectFunctionTableTargets,
3165 MachineType::Pointer());
3166 *ift_instances = LOAD_MUTABLE_INSTANCE_FIELD(IndirectFunctionTableRefs,
3167 MachineType::TaggedPointer());
3168 return;
3169 }
3170
3171 Node* ift_tables = LOAD_MUTABLE_INSTANCE_FIELD(IndirectFunctionTables,
3172 MachineType::TaggedPointer());
3173 Node* ift_table = gasm_->LoadFixedArrayElementAny(ift_tables, table_index);
3174
3175 if (needs_dynamic_size) {
3176 *ift_size = gasm_->LoadFromObject(
3177 MachineType::Int32(), ift_table,
3178 wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kSizeOffset));
3179 }
3180
3181 *ift_sig_ids = gasm_->LoadFromObject(
3182 MachineType::Pointer(), ift_table,
3183 wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kSigIdsOffset));
3184
3185 *ift_targets = gasm_->LoadFromObject(
3186 MachineType::Pointer(), ift_table,
3187 wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kTargetsOffset));
3188
3189 *ift_instances = gasm_->LoadFromObject(
3190 MachineType::TaggedPointer(), ift_table,
3191 wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kRefsOffset));
3192 }
3193
BuildIndirectCall(uint32_t table_index,uint32_t sig_index,wasm::FunctionSig * real_sig,base::Vector<Node * > args,base::Vector<Node * > rets,wasm::WasmCodePosition position,IsReturnCall continuation)3194 Node* WasmGraphBuilder::BuildIndirectCall(
3195 uint32_t table_index, uint32_t sig_index, wasm::FunctionSig* real_sig,
3196 base::Vector<Node*> args, base::Vector<Node*> rets,
3197 wasm::WasmCodePosition position, IsReturnCall continuation) {
3198 DCHECK_NOT_NULL(args[0]);
3199 DCHECK_NOT_NULL(env_);
3200
3201 // First we have to load the table.
3202 Node* ift_size;
3203 Node* ift_sig_ids;
3204 Node* ift_targets;
3205 Node* ift_instances;
3206 LoadIndirectFunctionTable(table_index, &ift_size, &ift_sig_ids, &ift_targets,
3207 &ift_instances);
3208
3209 Node* key = args[0];
3210
3211 // Bounds check against the table size.
3212 Node* in_bounds = gasm_->Uint32LessThan(key, ift_size);
3213 TrapIfFalse(wasm::kTrapTableOutOfBounds, in_bounds, position);
3214
3215 const wasm::ValueType table_type = env_->module->tables[table_index].type;
3216 // Check that the table entry is not null and that the type of the function is
3217 // **identical with** the function type declared at the call site (no
3218 // subtyping of functions is allowed).
3219 // Note: Since null entries are identified by having ift_sig_id (-1), we only
3220 // need one comparison.
3221 // TODO(9495): Change this if we should do full function subtyping instead.
3222 const bool needs_signature_check =
3223 FLAG_experimental_wasm_gc ||
3224 table_type.is_reference_to(wasm::HeapType::kFunc) ||
3225 table_type.is_nullable();
3226 if (needs_signature_check) {
3227 Node* int32_scaled_key =
3228 BuildChangeUint32ToUintPtr(gasm_->Word32Shl(key, Int32Constant(2)));
3229
3230 Node* loaded_sig = gasm_->LoadFromObject(MachineType::Int32(), ift_sig_ids,
3231 int32_scaled_key);
3232 int32_t expected_sig_id = env_->module->canonicalized_type_ids[sig_index];
3233 Node* sig_match =
3234 gasm_->Word32Equal(loaded_sig, Int32Constant(expected_sig_id));
3235 TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
3236 }
3237
3238 Node* key_intptr = BuildChangeUint32ToUintPtr(key);
3239
3240 Node* target_instance = gasm_->LoadFixedArrayElement(
3241 ift_instances, key_intptr, MachineType::TaggedPointer());
3242
3243 Node* intptr_scaled_key =
3244 gasm_->IntMul(key_intptr, gasm_->IntPtrConstant(kSystemPointerSize));
3245
3246 Node* target = gasm_->LoadFromObject(MachineType::Pointer(), ift_targets,
3247 intptr_scaled_key);
3248
3249 args[0] = target;
3250
3251 switch (continuation) {
3252 case kCallContinues:
3253 return BuildWasmCall(real_sig, args, rets, position, target_instance);
3254 case kReturnCall:
3255 return BuildWasmReturnCall(real_sig, args, position, target_instance);
3256 }
3257 }
3258
BuildLoadExternalPointerFromObject(Node * object,int offset,ExternalPointerTag tag)3259 Node* WasmGraphBuilder::BuildLoadExternalPointerFromObject(
3260 Node* object, int offset, ExternalPointerTag tag) {
3261 #ifdef V8_SANDBOXED_EXTERNAL_POINTERS
3262 Node* external_pointer = gasm_->LoadFromObject(
3263 MachineType::Uint32(), object, wasm::ObjectAccess::ToTagged(offset));
3264 STATIC_ASSERT(kExternalPointerIndexShift > kSystemPointerSizeLog2);
3265 Node* shift_amount =
3266 gasm_->Int32Constant(kExternalPointerIndexShift - kSystemPointerSizeLog2);
3267 Node* scaled_index = gasm_->Word32Shr(external_pointer, shift_amount);
3268 Node* isolate_root = BuildLoadIsolateRoot();
3269 Node* table =
3270 gasm_->LoadFromObject(MachineType::Pointer(), isolate_root,
3271 IsolateData::external_pointer_table_offset() +
3272 Internals::kExternalPointerTableBufferOffset);
3273 Node* decoded_ptr = gasm_->Load(MachineType::Pointer(), table, scaled_index);
3274 return gasm_->WordAnd(decoded_ptr, gasm_->IntPtrConstant(~tag));
3275 #else
3276 return gasm_->LoadFromObject(MachineType::Pointer(), object,
3277 wasm::ObjectAccess::ToTagged(offset));
3278 #endif
3279 }
3280
BuildLoadCallTargetFromExportedFunctionData(Node * function)3281 Node* WasmGraphBuilder::BuildLoadCallTargetFromExportedFunctionData(
3282 Node* function) {
3283 Node* internal = gasm_->LoadFromObject(
3284 MachineType::TaggedPointer(), function,
3285 wasm::ObjectAccess::ToTagged(WasmExportedFunctionData::kInternalOffset));
3286 return BuildLoadExternalPointerFromObject(
3287 internal, WasmInternalFunction::kForeignAddressOffset);
3288 }
3289
3290 // TODO(9495): Support CAPI function refs.
BuildCallRef(const wasm::FunctionSig * real_sig,base::Vector<Node * > args,base::Vector<Node * > rets,CheckForNull null_check,IsReturnCall continuation,wasm::WasmCodePosition position)3291 Node* WasmGraphBuilder::BuildCallRef(const wasm::FunctionSig* real_sig,
3292 base::Vector<Node*> args,
3293 base::Vector<Node*> rets,
3294 CheckForNull null_check,
3295 IsReturnCall continuation,
3296 wasm::WasmCodePosition position) {
3297 if (null_check == kWithNullCheck) {
3298 TrapIfTrue(wasm::kTrapNullDereference, IsNull(args[0]), position);
3299 }
3300
3301 Node* function = args[0];
3302
3303 auto load_target = gasm_->MakeLabel();
3304 auto end_label = gasm_->MakeLabel(MachineType::PointerRepresentation());
3305
3306 Node* ref_node = gasm_->LoadImmutableFromObject(
3307 MachineType::TaggedPointer(), function,
3308 wasm::ObjectAccess::ToTagged(WasmInternalFunction::kRefOffset));
3309
3310 Node* target = BuildLoadExternalPointerFromObject(
3311 function, WasmInternalFunction::kForeignAddressOffset);
3312 Node* is_null_target = gasm_->WordEqual(target, gasm_->IntPtrConstant(0));
3313 gasm_->GotoIfNot(is_null_target, &end_label, target);
3314 {
3315 // Compute the call target from the (on-heap) wrapper code. The cached
3316 // target can only be null for WasmJSFunctions.
3317 Node* wrapper_code = gasm_->LoadImmutableFromObject(
3318 MachineType::TaggedPointer(), function,
3319 wasm::ObjectAccess::ToTagged(WasmInternalFunction::kCodeOffset));
3320 Node* call_target;
3321 if (V8_EXTERNAL_CODE_SPACE_BOOL) {
3322 call_target = BuildLoadExternalPointerFromObject(
3323 wrapper_code, CodeDataContainer::kCodeEntryPointOffset,
3324 kCodeEntryPointTag);
3325
3326 } else {
3327 call_target = gasm_->IntAdd(
3328 wrapper_code, gasm_->IntPtrConstant(
3329 wasm::ObjectAccess::ToTagged(Code::kHeaderSize)));
3330 }
3331 gasm_->Goto(&end_label, call_target);
3332 }
3333
3334 gasm_->Bind(&end_label);
3335
3336 args[0] = end_label.PhiAt(0);
3337
3338 Node* call = continuation == kCallContinues
3339 ? BuildWasmCall(real_sig, args, rets, position, ref_node)
3340 : BuildWasmReturnCall(real_sig, args, position, ref_node);
3341 return call;
3342 }
3343
CompareToInternalFunctionAtIndex(Node * func_ref,uint32_t function_index,Node ** success_control,Node ** failure_control)3344 void WasmGraphBuilder::CompareToInternalFunctionAtIndex(
3345 Node* func_ref, uint32_t function_index, Node** success_control,
3346 Node** failure_control) {
3347 // Since we are comparing to a function reference, it is guaranteed that
3348 // instance->wasm_internal_functions() has been initialized.
3349 Node* internal_functions = gasm_->LoadImmutable(
3350 MachineType::TaggedPointer(), GetInstance(),
3351 wasm::ObjectAccess::ToTagged(
3352 WasmInstanceObject::kWasmInternalFunctionsOffset));
3353 Node* function_ref_at_index = gasm_->LoadFixedArrayElement(
3354 internal_functions, gasm_->IntPtrConstant(function_index),
3355 MachineType::AnyTagged());
3356 gasm_->Branch(gasm_->TaggedEqual(function_ref_at_index, func_ref),
3357 success_control, failure_control, BranchHint::kTrue);
3358 }
3359
CallRef(const wasm::FunctionSig * real_sig,base::Vector<Node * > args,base::Vector<Node * > rets,WasmGraphBuilder::CheckForNull null_check,wasm::WasmCodePosition position)3360 Node* WasmGraphBuilder::CallRef(const wasm::FunctionSig* real_sig,
3361 base::Vector<Node*> args,
3362 base::Vector<Node*> rets,
3363 WasmGraphBuilder::CheckForNull null_check,
3364 wasm::WasmCodePosition position) {
3365 return BuildCallRef(real_sig, args, rets, null_check,
3366 IsReturnCall::kCallContinues, position);
3367 }
3368
ReturnCallRef(const wasm::FunctionSig * real_sig,base::Vector<Node * > args,WasmGraphBuilder::CheckForNull null_check,wasm::WasmCodePosition position)3369 Node* WasmGraphBuilder::ReturnCallRef(const wasm::FunctionSig* real_sig,
3370 base::Vector<Node*> args,
3371 WasmGraphBuilder::CheckForNull null_check,
3372 wasm::WasmCodePosition position) {
3373 return BuildCallRef(real_sig, args, {}, null_check, IsReturnCall::kReturnCall,
3374 position);
3375 }
3376
ReturnCall(uint32_t index,const wasm::FunctionSig * real_sig,base::Vector<Node * > args,wasm::WasmCodePosition position)3377 Node* WasmGraphBuilder::ReturnCall(uint32_t index,
3378 const wasm::FunctionSig* real_sig,
3379 base::Vector<Node*> args,
3380 wasm::WasmCodePosition position) {
3381 DCHECK_NULL(args[0]);
3382
3383 if (env_ && index < env_->module->num_imported_functions) {
3384 // Return Call to an imported function.
3385 return BuildImportCall(real_sig, args, {}, position, index, kReturnCall);
3386 }
3387
3388 // A direct tail call to a wasm function defined in this module.
3389 // Just encode the function index. This will be patched during code
3390 // generation.
3391 Address code = static_cast<Address>(index);
3392 args[0] = mcgraph()->RelocatableIntPtrConstant(code, RelocInfo::WASM_CALL);
3393
3394 return BuildWasmReturnCall(real_sig, args, position, nullptr);
3395 }
3396
ReturnCallIndirect(uint32_t table_index,uint32_t sig_index,wasm::FunctionSig * real_sig,base::Vector<Node * > args,wasm::WasmCodePosition position)3397 Node* WasmGraphBuilder::ReturnCallIndirect(uint32_t table_index,
3398 uint32_t sig_index,
3399 wasm::FunctionSig* real_sig,
3400 base::Vector<Node*> args,
3401 wasm::WasmCodePosition position) {
3402 return BuildIndirectCall(table_index, sig_index, real_sig, args, {}, position,
3403 kReturnCall);
3404 }
3405
BrOnNull(Node * ref_object,Node ** null_node,Node ** non_null_node)3406 void WasmGraphBuilder::BrOnNull(Node* ref_object, Node** null_node,
3407 Node** non_null_node) {
3408 BranchExpectFalse(IsNull(ref_object), null_node, non_null_node);
3409 }
3410
BuildI32Rol(Node * left,Node * right)3411 Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) {
3412 // Implement Rol by Ror since TurboFan does not have Rol opcode.
3413 // TODO(weiliang): support Word32Rol opcode in TurboFan.
3414 Int32Matcher m(right);
3415 if (m.HasResolvedValue()) {
3416 return Binop(wasm::kExprI32Ror, left,
3417 Int32Constant(32 - (m.ResolvedValue() & 0x1F)));
3418 } else {
3419 return Binop(wasm::kExprI32Ror, left,
3420 Binop(wasm::kExprI32Sub, Int32Constant(32), right));
3421 }
3422 }
3423
BuildI64Rol(Node * left,Node * right)3424 Node* WasmGraphBuilder::BuildI64Rol(Node* left, Node* right) {
3425 // Implement Rol by Ror since TurboFan does not have Rol opcode.
3426 // TODO(weiliang): support Word64Rol opcode in TurboFan.
3427 Int64Matcher m(right);
3428 Node* inv_right = m.HasResolvedValue()
3429 ? Int64Constant(64 - (m.ResolvedValue() & 0x3F))
3430 : Binop(wasm::kExprI64Sub, Int64Constant(64), right);
3431 return Binop(wasm::kExprI64Ror, left, inv_right);
3432 }
3433
Invert(Node * node)3434 Node* WasmGraphBuilder::Invert(Node* node) {
3435 return Unop(wasm::kExprI32Eqz, node);
3436 }
3437
BuildTruncateIntPtrToInt32(Node * value)3438 Node* WasmGraphBuilder::BuildTruncateIntPtrToInt32(Node* value) {
3439 return mcgraph()->machine()->Is64() ? gasm_->TruncateInt64ToInt32(value)
3440 : value;
3441 }
3442
BuildChangeInt32ToIntPtr(Node * value)3443 Node* WasmGraphBuilder::BuildChangeInt32ToIntPtr(Node* value) {
3444 return mcgraph()->machine()->Is64() ? gasm_->ChangeInt32ToInt64(value)
3445 : value;
3446 }
3447
BuildChangeIntPtrToInt64(Node * value)3448 Node* WasmGraphBuilder::BuildChangeIntPtrToInt64(Node* value) {
3449 return mcgraph()->machine()->Is32() ? gasm_->ChangeInt32ToInt64(value)
3450 : value;
3451 }
3452
BuildChangeUint32ToUintPtr(Node * node)3453 Node* WasmGraphBuilder::BuildChangeUint32ToUintPtr(Node* node) {
3454 if (mcgraph()->machine()->Is32()) return node;
3455 // Fold instances of ChangeUint32ToUint64(IntConstant) directly.
3456 Uint32Matcher matcher(node);
3457 if (matcher.HasResolvedValue()) {
3458 uintptr_t value = matcher.ResolvedValue();
3459 return mcgraph()->IntPtrConstant(bit_cast<intptr_t>(value));
3460 }
3461 return gasm_->ChangeUint32ToUint64(node);
3462 }
3463
BuildSmiShiftBitsConstant()3464 Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() {
3465 return gasm_->IntPtrConstant(kSmiShiftSize + kSmiTagSize);
3466 }
3467
BuildSmiShiftBitsConstant32()3468 Node* WasmGraphBuilder::BuildSmiShiftBitsConstant32() {
3469 return Int32Constant(kSmiShiftSize + kSmiTagSize);
3470 }
3471
BuildChangeInt32ToSmi(Node * value)3472 Node* WasmGraphBuilder::BuildChangeInt32ToSmi(Node* value) {
3473 // With pointer compression, only the lower 32 bits are used.
3474 return COMPRESS_POINTERS_BOOL
3475 ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32())
3476 : gasm_->WordShl(BuildChangeInt32ToIntPtr(value),
3477 BuildSmiShiftBitsConstant());
3478 }
3479
BuildChangeUint31ToSmi(Node * value)3480 Node* WasmGraphBuilder::BuildChangeUint31ToSmi(Node* value) {
3481 return COMPRESS_POINTERS_BOOL
3482 ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32())
3483 : gasm_->WordShl(BuildChangeUint32ToUintPtr(value),
3484 BuildSmiShiftBitsConstant());
3485 }
3486
BuildChangeSmiToInt32(Node * value)3487 Node* WasmGraphBuilder::BuildChangeSmiToInt32(Node* value) {
3488 return COMPRESS_POINTERS_BOOL
3489 ? gasm_->Word32Sar(value, BuildSmiShiftBitsConstant32())
3490 : BuildTruncateIntPtrToInt32(
3491 gasm_->WordSar(value, BuildSmiShiftBitsConstant()));
3492 }
3493
BuildChangeSmiToIntPtr(Node * value)3494 Node* WasmGraphBuilder::BuildChangeSmiToIntPtr(Node* value) {
3495 return COMPRESS_POINTERS_BOOL
3496 ? BuildChangeInt32ToIntPtr(
3497 gasm_->Word32Sar(value, BuildSmiShiftBitsConstant32()))
3498 : gasm_->WordSar(value, BuildSmiShiftBitsConstant());
3499 }
3500
BuildConvertUint32ToSmiWithSaturation(Node * value,uint32_t maxval)3501 Node* WasmGraphBuilder::BuildConvertUint32ToSmiWithSaturation(Node* value,
3502 uint32_t maxval) {
3503 DCHECK(Smi::IsValid(maxval));
3504 Node* max = mcgraph()->Uint32Constant(maxval);
3505 Node* check = gasm_->Uint32LessThanOrEqual(value, max);
3506 Node* valsmi = BuildChangeUint31ToSmi(value);
3507 Node* maxsmi = gasm_->NumberConstant(maxval);
3508 Diamond d(graph(), mcgraph()->common(), check, BranchHint::kTrue);
3509 d.Chain(control());
3510 return d.Phi(MachineRepresentation::kTagged, valsmi, maxsmi);
3511 }
3512
InitInstanceCache(WasmInstanceCacheNodes * instance_cache)3513 void WasmGraphBuilder::InitInstanceCache(
3514 WasmInstanceCacheNodes* instance_cache) {
3515 // We handle caching of the instance cache nodes manually, and we may reload
3516 // them in contexts where load elimination would eliminate the reload.
3517 // Therefore, we use plain Load nodes which are not subject to load
3518 // elimination.
3519
3520 // Load the memory start.
3521 #ifdef V8_SANDBOXED_POINTERS
3522 instance_cache->mem_start = LOAD_INSTANCE_FIELD_NO_ELIMINATION(
3523 MemoryStart, MachineType::SandboxedPointer());
3524 #else
3525 instance_cache->mem_start =
3526 LOAD_INSTANCE_FIELD_NO_ELIMINATION(MemoryStart, MachineType::UintPtr());
3527 #endif
3528
3529 // Load the memory size.
3530 instance_cache->mem_size =
3531 LOAD_INSTANCE_FIELD_NO_ELIMINATION(MemorySize, MachineType::UintPtr());
3532 }
3533
PrepareInstanceCacheForLoop(WasmInstanceCacheNodes * instance_cache,Node * control)3534 void WasmGraphBuilder::PrepareInstanceCacheForLoop(
3535 WasmInstanceCacheNodes* instance_cache, Node* control) {
3536 #define INTRODUCE_PHI(field, rep) \
3537 instance_cache->field = graph()->NewNode(mcgraph()->common()->Phi(rep, 1), \
3538 instance_cache->field, control);
3539
3540 INTRODUCE_PHI(mem_start, MachineType::PointerRepresentation());
3541 INTRODUCE_PHI(mem_size, MachineType::PointerRepresentation());
3542 #undef INTRODUCE_PHI
3543 }
3544
NewInstanceCacheMerge(WasmInstanceCacheNodes * to,WasmInstanceCacheNodes * from,Node * merge)3545 void WasmGraphBuilder::NewInstanceCacheMerge(WasmInstanceCacheNodes* to,
3546 WasmInstanceCacheNodes* from,
3547 Node* merge) {
3548 #define INTRODUCE_PHI(field, rep) \
3549 if (to->field != from->field) { \
3550 Node* vals[] = {to->field, from->field, merge}; \
3551 to->field = graph()->NewNode(mcgraph()->common()->Phi(rep, 2), 3, vals); \
3552 }
3553
3554 INTRODUCE_PHI(mem_start, MachineType::PointerRepresentation());
3555 INTRODUCE_PHI(mem_size, MachineRepresentation::kWord32);
3556 #undef INTRODUCE_PHI
3557 }
3558
MergeInstanceCacheInto(WasmInstanceCacheNodes * to,WasmInstanceCacheNodes * from,Node * merge)3559 void WasmGraphBuilder::MergeInstanceCacheInto(WasmInstanceCacheNodes* to,
3560 WasmInstanceCacheNodes* from,
3561 Node* merge) {
3562 to->mem_size = CreateOrMergeIntoPhi(MachineType::PointerRepresentation(),
3563 merge, to->mem_size, from->mem_size);
3564 to->mem_start = CreateOrMergeIntoPhi(MachineType::PointerRepresentation(),
3565 merge, to->mem_start, from->mem_start);
3566 }
3567
CreateOrMergeIntoPhi(MachineRepresentation rep,Node * merge,Node * tnode,Node * fnode)3568 Node* WasmGraphBuilder::CreateOrMergeIntoPhi(MachineRepresentation rep,
3569 Node* merge, Node* tnode,
3570 Node* fnode) {
3571 if (IsPhiWithMerge(tnode, merge)) {
3572 AppendToPhi(tnode, fnode);
3573 } else if (tnode != fnode) {
3574 // Note that it is not safe to use {Buffer} here since this method is used
3575 // via {CheckForException} while the {Buffer} is in use by another method.
3576 uint32_t count = merge->InputCount();
3577 // + 1 for the merge node.
3578 base::SmallVector<Node*, 9> inputs(count + 1);
3579 for (uint32_t j = 0; j < count - 1; j++) inputs[j] = tnode;
3580 inputs[count - 1] = fnode;
3581 inputs[count] = merge;
3582 tnode = graph()->NewNode(mcgraph()->common()->Phi(rep, count), count + 1,
3583 inputs.begin());
3584 }
3585 return tnode;
3586 }
3587
CreateOrMergeIntoEffectPhi(Node * merge,Node * tnode,Node * fnode)3588 Node* WasmGraphBuilder::CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode,
3589 Node* fnode) {
3590 if (IsPhiWithMerge(tnode, merge)) {
3591 AppendToPhi(tnode, fnode);
3592 } else if (tnode != fnode) {
3593 // Note that it is not safe to use {Buffer} here since this method is used
3594 // via {CheckForException} while the {Buffer} is in use by another method.
3595 uint32_t count = merge->InputCount();
3596 // + 1 for the merge node.
3597 base::SmallVector<Node*, 9> inputs(count + 1);
3598 for (uint32_t j = 0; j < count - 1; j++) {
3599 inputs[j] = tnode;
3600 }
3601 inputs[count - 1] = fnode;
3602 inputs[count] = merge;
3603 tnode = graph()->NewNode(mcgraph()->common()->EffectPhi(count), count + 1,
3604 inputs.begin());
3605 }
3606 return tnode;
3607 }
3608
effect()3609 Node* WasmGraphBuilder::effect() { return gasm_->effect(); }
3610
control()3611 Node* WasmGraphBuilder::control() { return gasm_->control(); }
3612
SetEffect(Node * node)3613 Node* WasmGraphBuilder::SetEffect(Node* node) {
3614 SetEffectControl(node, control());
3615 return node;
3616 }
3617
SetControl(Node * node)3618 Node* WasmGraphBuilder::SetControl(Node* node) {
3619 SetEffectControl(effect(), node);
3620 return node;
3621 }
3622
SetEffectControl(Node * effect,Node * control)3623 void WasmGraphBuilder::SetEffectControl(Node* effect, Node* control) {
3624 gasm_->InitializeEffectControl(effect, control);
3625 }
3626
MemBuffer(uintptr_t offset)3627 Node* WasmGraphBuilder::MemBuffer(uintptr_t offset) {
3628 DCHECK_NOT_NULL(instance_cache_);
3629 Node* mem_start = instance_cache_->mem_start;
3630 DCHECK_NOT_NULL(mem_start);
3631 if (offset == 0) return mem_start;
3632 return gasm_->IntAdd(mem_start, gasm_->UintPtrConstant(offset));
3633 }
3634
CurrentMemoryPages()3635 Node* WasmGraphBuilder::CurrentMemoryPages() {
3636 // CurrentMemoryPages can not be called from asm.js.
3637 DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin);
3638 DCHECK_NOT_NULL(instance_cache_);
3639 Node* mem_size = instance_cache_->mem_size;
3640 DCHECK_NOT_NULL(mem_size);
3641 Node* result =
3642 gasm_->WordShr(mem_size, Int32Constant(wasm::kWasmPageSizeLog2));
3643 result = env_->module->is_memory64 ? BuildChangeIntPtrToInt64(result)
3644 : BuildTruncateIntPtrToInt32(result);
3645 return result;
3646 }
3647
3648 // Only call this function for code which is not reused across instantiations,
3649 // as we do not patch the embedded js_context.
BuildCallToRuntimeWithContext(Runtime::FunctionId f,Node * js_context,Node ** parameters,int parameter_count)3650 Node* WasmGraphBuilder::BuildCallToRuntimeWithContext(Runtime::FunctionId f,
3651 Node* js_context,
3652 Node** parameters,
3653 int parameter_count) {
3654 const Runtime::Function* fun = Runtime::FunctionForId(f);
3655 auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
3656 mcgraph()->zone(), f, fun->nargs, Operator::kNoProperties,
3657 CallDescriptor::kNoFlags);
3658 // The CEntryStub is loaded from the IsolateRoot so that generated code is
3659 // Isolate independent. At the moment this is only done for CEntryStub(1).
3660 Node* isolate_root = BuildLoadIsolateRoot();
3661 DCHECK_EQ(1, fun->result_size);
3662 auto centry_id =
3663 Builtin::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit;
3664 int builtin_slot_offset = IsolateData::BuiltinSlotOffset(centry_id);
3665 Node* centry_stub = gasm_->LoadFromObject(MachineType::Pointer(),
3666 isolate_root, builtin_slot_offset);
3667 // TODO(titzer): allow arbitrary number of runtime arguments
3668 // At the moment we only allow 5 parameters. If more parameters are needed,
3669 // increase this constant accordingly.
3670 static const int kMaxParams = 5;
3671 DCHECK_GE(kMaxParams, parameter_count);
3672 Node* inputs[kMaxParams + 6];
3673 int count = 0;
3674 inputs[count++] = centry_stub;
3675 for (int i = 0; i < parameter_count; i++) {
3676 inputs[count++] = parameters[i];
3677 }
3678 inputs[count++] =
3679 mcgraph()->ExternalConstant(ExternalReference::Create(f)); // ref
3680 inputs[count++] = Int32Constant(fun->nargs); // arity
3681 inputs[count++] = js_context; // js_context
3682 inputs[count++] = effect();
3683 inputs[count++] = control();
3684
3685 return gasm_->Call(call_descriptor, count, inputs);
3686 }
3687
BuildCallToRuntime(Runtime::FunctionId f,Node ** parameters,int parameter_count)3688 Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f,
3689 Node** parameters,
3690 int parameter_count) {
3691 return BuildCallToRuntimeWithContext(f, NoContextConstant(), parameters,
3692 parameter_count);
3693 }
3694
GetGlobalBaseAndOffset(const wasm::WasmGlobal & global,Node ** base,Node ** offset)3695 void WasmGraphBuilder::GetGlobalBaseAndOffset(const wasm::WasmGlobal& global,
3696 Node** base, Node** offset) {
3697 if (global.mutability && global.imported) {
3698 Node* base_or_index = gasm_->LoadFromObject(
3699 MachineType::UintPtr(),
3700 LOAD_INSTANCE_FIELD(ImportedMutableGlobals, MachineType::UintPtr()),
3701 Int32Constant(global.index * kSystemPointerSize));
3702 if (global.type.is_reference()) {
3703 // Load the base from the ImportedMutableGlobalsBuffer of the instance.
3704 Node* buffers = LOAD_INSTANCE_FIELD(ImportedMutableGlobalsBuffers,
3705 MachineType::TaggedPointer());
3706 *base = gasm_->LoadFixedArrayElementAny(buffers, global.index);
3707
3708 // For this case, {base_or_index} gives the index of the global in the
3709 // buffer. From the index, calculate the actual offset in the FixedArray.
3710 // This is kHeaderSize + (index * kTaggedSize).
3711 *offset = gasm_->IntAdd(
3712 gasm_->IntMul(base_or_index, gasm_->IntPtrConstant(kTaggedSize)),
3713 gasm_->IntPtrConstant(
3714 wasm::ObjectAccess::ToTagged(FixedArray::kObjectsOffset)));
3715 } else {
3716 *base = base_or_index;
3717 *offset = gasm_->IntPtrConstant(0);
3718 }
3719 } else if (global.type.is_reference()) {
3720 *base =
3721 LOAD_INSTANCE_FIELD(TaggedGlobalsBuffer, MachineType::TaggedPointer());
3722 *offset = gasm_->IntPtrConstant(
3723 wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(global.offset));
3724 } else {
3725 *base = LOAD_INSTANCE_FIELD(GlobalsStart, MachineType::UintPtr());
3726 *offset = gasm_->IntPtrConstant(global.offset);
3727 }
3728 }
3729
GlobalGet(uint32_t index)3730 Node* WasmGraphBuilder::GlobalGet(uint32_t index) {
3731 const wasm::WasmGlobal& global = env_->module->globals[index];
3732 if (global.type == wasm::kWasmS128) has_simd_ = true;
3733 Node* base = nullptr;
3734 Node* offset = nullptr;
3735 GetGlobalBaseAndOffset(global, &base, &offset);
3736 MachineType mem_type = global.type.machine_type();
3737 return global.mutability ? gasm_->LoadFromObject(mem_type, base, offset)
3738 : gasm_->LoadImmutable(mem_type, base, offset);
3739 }
3740
GlobalSet(uint32_t index,Node * val)3741 void WasmGraphBuilder::GlobalSet(uint32_t index, Node* val) {
3742 const wasm::WasmGlobal& global = env_->module->globals[index];
3743 if (global.type == wasm::kWasmS128) has_simd_ = true;
3744 Node* base = nullptr;
3745 Node* offset = nullptr;
3746 GetGlobalBaseAndOffset(global, &base, &offset);
3747 ObjectAccess access(global.type.machine_type(), global.type.is_reference()
3748 ? kFullWriteBarrier
3749 : kNoWriteBarrier);
3750 gasm_->StoreToObject(access, base, offset, val);
3751 }
3752
TableGet(uint32_t table_index,Node * index,wasm::WasmCodePosition position)3753 Node* WasmGraphBuilder::TableGet(uint32_t table_index, Node* index,
3754 wasm::WasmCodePosition position) {
3755 return gasm_->CallRuntimeStub(wasm::WasmCode::kWasmTableGet,
3756 Operator::kNoThrow,
3757 gasm_->IntPtrConstant(table_index), index);
3758 }
3759
TableSet(uint32_t table_index,Node * index,Node * val,wasm::WasmCodePosition position)3760 void WasmGraphBuilder::TableSet(uint32_t table_index, Node* index, Node* val,
3761 wasm::WasmCodePosition position) {
3762 gasm_->CallRuntimeStub(wasm::WasmCode::kWasmTableSet, Operator::kNoThrow,
3763 gasm_->IntPtrConstant(table_index), index, val);
3764 }
3765
CheckBoundsAndAlignment(int8_t access_size,Node * index,uint64_t offset,wasm::WasmCodePosition position)3766 Node* WasmGraphBuilder::CheckBoundsAndAlignment(
3767 int8_t access_size, Node* index, uint64_t offset,
3768 wasm::WasmCodePosition position) {
3769 // Atomic operations need bounds checks until the backend can emit protected
3770 // loads.
3771 index =
3772 BoundsCheckMem(access_size, index, offset, position, kNeedsBoundsCheck)
3773 .first;
3774
3775 const uintptr_t align_mask = access_size - 1;
3776
3777 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
3778 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
3779 // Don't emit an alignment check if the index is a constant.
3780 // TODO(wasm): a constant match is also done above in {BoundsCheckMem}.
3781 UintPtrMatcher match(index);
3782 if (match.HasResolvedValue()) {
3783 uintptr_t effective_offset = match.ResolvedValue() + capped_offset;
3784 if ((effective_offset & align_mask) != 0) {
3785 // statically known to be unaligned; trap.
3786 TrapIfEq32(wasm::kTrapUnalignedAccess, Int32Constant(0), 0, position);
3787 }
3788 return index;
3789 }
3790
3791 // Unlike regular memory accesses, atomic memory accesses should trap if
3792 // the effective offset is misaligned.
3793 // TODO(wasm): this addition is redundant with one inserted by {MemBuffer}.
3794 Node* effective_offset = gasm_->IntAdd(MemBuffer(capped_offset), index);
3795
3796 Node* cond =
3797 gasm_->WordAnd(effective_offset, gasm_->IntPtrConstant(align_mask));
3798 TrapIfFalse(wasm::kTrapUnalignedAccess,
3799 gasm_->Word32Equal(cond, Int32Constant(0)), position);
3800 return index;
3801 }
3802
3803 // Insert code to bounds check a memory access if necessary. Return the
3804 // bounds-checked index, which is guaranteed to have (the equivalent of)
3805 // {uintptr_t} representation.
3806 std::pair<Node*, WasmGraphBuilder::BoundsCheckResult>
BoundsCheckMem(uint8_t access_size,Node * index,uint64_t offset,wasm::WasmCodePosition position,EnforceBoundsCheck enforce_check)3807 WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
3808 uint64_t offset,
3809 wasm::WasmCodePosition position,
3810 EnforceBoundsCheck enforce_check) {
3811 DCHECK_LE(1, access_size);
3812
3813 // If the offset does not fit in a uintptr_t, this can never succeed on this
3814 // machine.
3815 if (offset > std::numeric_limits<uintptr_t>::max() ||
3816 !base::IsInBounds<uintptr_t>(offset, access_size,
3817 env_->max_memory_size)) {
3818 // The access will be out of bounds, even for the largest memory.
3819 TrapIfEq32(wasm::kTrapMemOutOfBounds, Int32Constant(0), 0, position);
3820 return {gasm_->UintPtrConstant(0), kOutOfBounds};
3821 }
3822
3823 // Convert the index to uintptr.
3824 if (!env_->module->is_memory64) {
3825 index = BuildChangeUint32ToUintPtr(index);
3826 } else if (kSystemPointerSize == kInt32Size) {
3827 // In memory64 mode on 32-bit systems, the upper 32 bits need to be zero to
3828 // succeed the bounds check.
3829 DCHECK_NE(wasm::kTrapHandler, env_->bounds_checks);
3830 if (env_->bounds_checks == wasm::kExplicitBoundsChecks) {
3831 Node* high_word = gasm_->TruncateInt64ToInt32(
3832 gasm_->Word64Shr(index, Int32Constant(32)));
3833 TrapIfTrue(wasm::kTrapMemOutOfBounds, high_word, position);
3834 }
3835 // Only use the low word for the following bounds check.
3836 index = gasm_->TruncateInt64ToInt32(index);
3837 }
3838
3839 // If no bounds checks should be performed (for testing), just return the
3840 // converted index and assume it to be in-bounds.
3841 if (env_->bounds_checks == wasm::kNoBoundsChecks) return {index, kInBounds};
3842
3843 // The accessed memory is [index + offset, index + end_offset].
3844 // Check that the last read byte (at {index + end_offset}) is in bounds.
3845 // 1) Check that {end_offset < mem_size}. This also ensures that we can safely
3846 // compute {effective_size} as {mem_size - end_offset)}.
3847 // {effective_size} is >= 1 if condition 1) holds.
3848 // 2) Check that {index + end_offset < mem_size} by
3849 // - computing {effective_size} as {mem_size - end_offset} and
3850 // - checking that {index < effective_size}.
3851
3852 uintptr_t end_offset = offset + access_size - 1u;
3853
3854 UintPtrMatcher match(index);
3855 if (match.HasResolvedValue() && end_offset <= env_->min_memory_size &&
3856 match.ResolvedValue() < env_->min_memory_size - end_offset) {
3857 // The input index is a constant and everything is statically within
3858 // bounds of the smallest possible memory.
3859 return {index, kInBounds};
3860 }
3861
3862 if (env_->bounds_checks == wasm::kTrapHandler &&
3863 enforce_check == kCanOmitBoundsCheck) {
3864 return {index, kTrapHandler};
3865 }
3866
3867 Node* mem_size = instance_cache_->mem_size;
3868 Node* end_offset_node = mcgraph_->UintPtrConstant(end_offset);
3869 if (end_offset > env_->min_memory_size) {
3870 // The end offset is larger than the smallest memory.
3871 // Dynamically check the end offset against the dynamic memory size.
3872 Node* cond = gasm_->UintLessThan(end_offset_node, mem_size);
3873 TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
3874 }
3875
3876 // This produces a positive number since {end_offset <= min_size <= mem_size}.
3877 Node* effective_size = gasm_->IntSub(mem_size, end_offset_node);
3878
3879 // Introduce the actual bounds check.
3880 Node* cond = gasm_->UintLessThan(index, effective_size);
3881 TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
3882 return {index, kDynamicallyChecked};
3883 }
3884
GetSafeLoadOperator(int offset,wasm::ValueType type)3885 const Operator* WasmGraphBuilder::GetSafeLoadOperator(int offset,
3886 wasm::ValueType type) {
3887 int alignment = offset % type.value_kind_size();
3888 MachineType mach_type = type.machine_type();
3889 if (COMPRESS_POINTERS_BOOL && mach_type.IsTagged()) {
3890 // We are loading tagged value from off-heap location, so we need to load
3891 // it as a full word otherwise we will not be able to decompress it.
3892 mach_type = MachineType::Pointer();
3893 }
3894 if (alignment == 0 || mcgraph()->machine()->UnalignedLoadSupported(
3895 type.machine_representation())) {
3896 return mcgraph()->machine()->Load(mach_type);
3897 }
3898 return mcgraph()->machine()->UnalignedLoad(mach_type);
3899 }
3900
GetSafeStoreOperator(int offset,wasm::ValueType type)3901 const Operator* WasmGraphBuilder::GetSafeStoreOperator(int offset,
3902 wasm::ValueType type) {
3903 int alignment = offset % type.value_kind_size();
3904 MachineRepresentation rep = type.machine_representation();
3905 if (COMPRESS_POINTERS_BOOL && IsAnyTagged(rep)) {
3906 // We are storing tagged value to off-heap location, so we need to store
3907 // it as a full word otherwise we will not be able to decompress it.
3908 rep = MachineType::PointerRepresentation();
3909 }
3910 if (alignment == 0 || mcgraph()->machine()->UnalignedStoreSupported(rep)) {
3911 StoreRepresentation store_rep(rep, WriteBarrierKind::kNoWriteBarrier);
3912 return mcgraph()->machine()->Store(store_rep);
3913 }
3914 UnalignedStoreRepresentation store_rep(rep);
3915 return mcgraph()->machine()->UnalignedStore(store_rep);
3916 }
3917
TraceFunctionEntry(wasm::WasmCodePosition position)3918 void WasmGraphBuilder::TraceFunctionEntry(wasm::WasmCodePosition position) {
3919 Node* call = BuildCallToRuntime(Runtime::kWasmTraceEnter, nullptr, 0);
3920 SetSourcePosition(call, position);
3921 }
3922
TraceFunctionExit(base::Vector<Node * > vals,wasm::WasmCodePosition position)3923 void WasmGraphBuilder::TraceFunctionExit(base::Vector<Node*> vals,
3924 wasm::WasmCodePosition position) {
3925 Node* info = gasm_->IntPtrConstant(0);
3926 size_t num_returns = vals.size();
3927 if (num_returns == 1) {
3928 wasm::ValueType return_type = sig_->GetReturn(0);
3929 MachineRepresentation rep = return_type.machine_representation();
3930 int size = ElementSizeInBytes(rep);
3931 info = gasm_->StackSlot(size, size);
3932
3933 gasm_->Store(StoreRepresentation(rep, kNoWriteBarrier), info,
3934 Int32Constant(0), vals[0]);
3935 }
3936
3937 Node* call = BuildCallToRuntime(Runtime::kWasmTraceExit, &info, 1);
3938 SetSourcePosition(call, position);
3939 }
3940
TraceMemoryOperation(bool is_store,MachineRepresentation rep,Node * index,uintptr_t offset,wasm::WasmCodePosition position)3941 void WasmGraphBuilder::TraceMemoryOperation(bool is_store,
3942 MachineRepresentation rep,
3943 Node* index, uintptr_t offset,
3944 wasm::WasmCodePosition position) {
3945 int kAlign = 4; // Ensure that the LSB is 0, such that this looks like a Smi.
3946 TNode<RawPtrT> info =
3947 gasm_->StackSlot(sizeof(wasm::MemoryTracingInfo), kAlign);
3948
3949 Node* effective_offset = gasm_->IntAdd(gasm_->UintPtrConstant(offset), index);
3950 auto store = [&](int field_offset, MachineRepresentation rep, Node* data) {
3951 gasm_->Store(StoreRepresentation(rep, kNoWriteBarrier), info,
3952 Int32Constant(field_offset), data);
3953 };
3954 // Store effective_offset, is_store, and mem_rep.
3955 store(offsetof(wasm::MemoryTracingInfo, offset),
3956 MachineType::PointerRepresentation(), effective_offset);
3957 store(offsetof(wasm::MemoryTracingInfo, is_store),
3958 MachineRepresentation::kWord8, Int32Constant(is_store ? 1 : 0));
3959 store(offsetof(wasm::MemoryTracingInfo, mem_rep),
3960 MachineRepresentation::kWord8, Int32Constant(static_cast<int>(rep)));
3961
3962 Node* args[] = {info};
3963 Node* call =
3964 BuildCallToRuntime(Runtime::kWasmTraceMemory, args, arraysize(args));
3965 SetSourcePosition(call, position);
3966 }
3967
3968 namespace {
GetLoadTransformation(MachineType memtype,wasm::LoadTransformationKind transform)3969 LoadTransformation GetLoadTransformation(
3970 MachineType memtype, wasm::LoadTransformationKind transform) {
3971 switch (transform) {
3972 case wasm::LoadTransformationKind::kSplat: {
3973 if (memtype == MachineType::Int8()) {
3974 return LoadTransformation::kS128Load8Splat;
3975 } else if (memtype == MachineType::Int16()) {
3976 return LoadTransformation::kS128Load16Splat;
3977 } else if (memtype == MachineType::Int32()) {
3978 return LoadTransformation::kS128Load32Splat;
3979 } else if (memtype == MachineType::Int64()) {
3980 return LoadTransformation::kS128Load64Splat;
3981 }
3982 break;
3983 }
3984 case wasm::LoadTransformationKind::kExtend: {
3985 if (memtype == MachineType::Int8()) {
3986 return LoadTransformation::kS128Load8x8S;
3987 } else if (memtype == MachineType::Uint8()) {
3988 return LoadTransformation::kS128Load8x8U;
3989 } else if (memtype == MachineType::Int16()) {
3990 return LoadTransformation::kS128Load16x4S;
3991 } else if (memtype == MachineType::Uint16()) {
3992 return LoadTransformation::kS128Load16x4U;
3993 } else if (memtype == MachineType::Int32()) {
3994 return LoadTransformation::kS128Load32x2S;
3995 } else if (memtype == MachineType::Uint32()) {
3996 return LoadTransformation::kS128Load32x2U;
3997 }
3998 break;
3999 }
4000 case wasm::LoadTransformationKind::kZeroExtend: {
4001 if (memtype == MachineType::Int32()) {
4002 return LoadTransformation::kS128Load32Zero;
4003 } else if (memtype == MachineType::Int64()) {
4004 return LoadTransformation::kS128Load64Zero;
4005 }
4006 break;
4007 }
4008 }
4009 UNREACHABLE();
4010 }
4011
GetMemoryAccessKind(MachineGraph * mcgraph,MachineRepresentation memrep,WasmGraphBuilder::BoundsCheckResult bounds_check_result)4012 MemoryAccessKind GetMemoryAccessKind(
4013 MachineGraph* mcgraph, MachineRepresentation memrep,
4014 WasmGraphBuilder::BoundsCheckResult bounds_check_result) {
4015 if (bounds_check_result == WasmGraphBuilder::kTrapHandler) {
4016 // Protected instructions do not come in an 'unaligned' flavor, so the trap
4017 // handler can currently only be used on systems where all memory accesses
4018 // are allowed to be unaligned.
4019 DCHECK(memrep == MachineRepresentation::kWord8 ||
4020 mcgraph->machine()->UnalignedLoadSupported(memrep));
4021 return MemoryAccessKind::kProtected;
4022 }
4023 if (memrep != MachineRepresentation::kWord8 &&
4024 !mcgraph->machine()->UnalignedLoadSupported(memrep)) {
4025 return MemoryAccessKind::kUnaligned;
4026 }
4027 return MemoryAccessKind::kNormal;
4028 }
4029 } // namespace
4030
LoadLane(wasm::ValueType type,MachineType memtype,Node * value,Node * index,uint64_t offset,uint32_t alignment,uint8_t laneidx,wasm::WasmCodePosition position)4031 Node* WasmGraphBuilder::LoadLane(wasm::ValueType type, MachineType memtype,
4032 Node* value, Node* index, uint64_t offset,
4033 uint32_t alignment, uint8_t laneidx,
4034 wasm::WasmCodePosition position) {
4035 has_simd_ = true;
4036 Node* load;
4037 uint8_t access_size = memtype.MemSize();
4038 BoundsCheckResult bounds_check_result;
4039 std::tie(index, bounds_check_result) =
4040 BoundsCheckMem(access_size, index, offset, position, kCanOmitBoundsCheck);
4041
4042 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
4043 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
4044 MemoryAccessKind load_kind = GetMemoryAccessKind(
4045 mcgraph_, memtype.representation(), bounds_check_result);
4046
4047 load = SetEffect(graph()->NewNode(
4048 mcgraph()->machine()->LoadLane(load_kind, memtype, laneidx),
4049 MemBuffer(capped_offset), index, value, effect(), control()));
4050
4051 if (load_kind == MemoryAccessKind::kProtected) {
4052 SetSourcePosition(load, position);
4053 }
4054 if (FLAG_trace_wasm_memory) {
4055 TraceMemoryOperation(false, memtype.representation(), index, capped_offset,
4056 position);
4057 }
4058
4059 return load;
4060 }
4061
LoadTransform(wasm::ValueType type,MachineType memtype,wasm::LoadTransformationKind transform,Node * index,uint64_t offset,uint32_t alignment,wasm::WasmCodePosition position)4062 Node* WasmGraphBuilder::LoadTransform(wasm::ValueType type, MachineType memtype,
4063 wasm::LoadTransformationKind transform,
4064 Node* index, uint64_t offset,
4065 uint32_t alignment,
4066 wasm::WasmCodePosition position) {
4067 has_simd_ = true;
4068
4069 Node* load;
4070 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
4071 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
4072
4073 // Wasm semantics throw on OOB. Introduce explicit bounds check and
4074 // conditioning when not using the trap handler.
4075
4076 // Load extends always load 8 bytes.
4077 uint8_t access_size = transform == wasm::LoadTransformationKind::kExtend
4078 ? 8
4079 : memtype.MemSize();
4080 BoundsCheckResult bounds_check_result;
4081 std::tie(index, bounds_check_result) =
4082 BoundsCheckMem(access_size, index, offset, position, kCanOmitBoundsCheck);
4083
4084 LoadTransformation transformation = GetLoadTransformation(memtype, transform);
4085 MemoryAccessKind load_kind = GetMemoryAccessKind(
4086 mcgraph_, memtype.representation(), bounds_check_result);
4087
4088 load = SetEffect(graph()->NewNode(
4089 mcgraph()->machine()->LoadTransform(load_kind, transformation),
4090 MemBuffer(capped_offset), index, effect(), control()));
4091
4092 if (load_kind == MemoryAccessKind::kProtected) {
4093 SetSourcePosition(load, position);
4094 }
4095
4096 if (FLAG_trace_wasm_memory) {
4097 TraceMemoryOperation(false, memtype.representation(), index, capped_offset,
4098 position);
4099 }
4100 return load;
4101 }
4102
LoadMem(wasm::ValueType type,MachineType memtype,Node * index,uint64_t offset,uint32_t alignment,wasm::WasmCodePosition position)4103 Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
4104 Node* index, uint64_t offset,
4105 uint32_t alignment,
4106 wasm::WasmCodePosition position) {
4107 Node* load;
4108
4109 if (memtype.representation() == MachineRepresentation::kSimd128) {
4110 has_simd_ = true;
4111 }
4112
4113 // Wasm semantics throw on OOB. Introduce explicit bounds check and
4114 // conditioning when not using the trap handler.
4115 BoundsCheckResult bounds_check_result;
4116 std::tie(index, bounds_check_result) = BoundsCheckMem(
4117 memtype.MemSize(), index, offset, position, kCanOmitBoundsCheck);
4118
4119 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
4120 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
4121
4122 switch (GetMemoryAccessKind(mcgraph_, memtype.representation(),
4123 bounds_check_result)) {
4124 case MemoryAccessKind::kUnaligned:
4125 load = gasm_->LoadUnaligned(memtype, MemBuffer(capped_offset), index);
4126 break;
4127 case MemoryAccessKind::kProtected:
4128 load = gasm_->ProtectedLoad(memtype, MemBuffer(capped_offset), index);
4129 SetSourcePosition(load, position);
4130 break;
4131 case MemoryAccessKind::kNormal:
4132 load = gasm_->Load(memtype, MemBuffer(capped_offset), index);
4133 break;
4134 }
4135
4136 #if defined(V8_TARGET_BIG_ENDIAN)
4137 load = BuildChangeEndiannessLoad(load, memtype, type);
4138 #endif
4139
4140 if (type == wasm::kWasmI64 &&
4141 ElementSizeInBytes(memtype.representation()) < 8) {
4142 // TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes.
4143 load = memtype.IsSigned()
4144 ? gasm_->ChangeInt32ToInt64(load) // sign extend
4145 : gasm_->ChangeUint32ToUint64(load); // zero extend
4146 }
4147
4148 if (FLAG_trace_wasm_memory) {
4149 TraceMemoryOperation(false, memtype.representation(), index, capped_offset,
4150 position);
4151 }
4152
4153 return load;
4154 }
4155
StoreLane(MachineRepresentation mem_rep,Node * index,uint64_t offset,uint32_t alignment,Node * val,uint8_t laneidx,wasm::WasmCodePosition position,wasm::ValueType type)4156 void WasmGraphBuilder::StoreLane(MachineRepresentation mem_rep, Node* index,
4157 uint64_t offset, uint32_t alignment, Node* val,
4158 uint8_t laneidx,
4159 wasm::WasmCodePosition position,
4160 wasm::ValueType type) {
4161 has_simd_ = true;
4162 BoundsCheckResult bounds_check_result;
4163 std::tie(index, bounds_check_result) =
4164 BoundsCheckMem(i::ElementSizeInBytes(mem_rep), index, offset, position,
4165 kCanOmitBoundsCheck);
4166
4167 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
4168 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
4169 MemoryAccessKind load_kind =
4170 GetMemoryAccessKind(mcgraph_, mem_rep, bounds_check_result);
4171
4172 Node* store = SetEffect(graph()->NewNode(
4173 mcgraph()->machine()->StoreLane(load_kind, mem_rep, laneidx),
4174 MemBuffer(capped_offset), index, val, effect(), control()));
4175
4176 if (load_kind == MemoryAccessKind::kProtected) {
4177 SetSourcePosition(store, position);
4178 }
4179 if (FLAG_trace_wasm_memory) {
4180 TraceMemoryOperation(true, mem_rep, index, capped_offset, position);
4181 }
4182 }
4183
StoreMem(MachineRepresentation mem_rep,Node * index,uint64_t offset,uint32_t alignment,Node * val,wasm::WasmCodePosition position,wasm::ValueType type)4184 void WasmGraphBuilder::StoreMem(MachineRepresentation mem_rep, Node* index,
4185 uint64_t offset, uint32_t alignment, Node* val,
4186 wasm::WasmCodePosition position,
4187 wasm::ValueType type) {
4188 if (mem_rep == MachineRepresentation::kSimd128) {
4189 has_simd_ = true;
4190 }
4191
4192 BoundsCheckResult bounds_check_result;
4193 std::tie(index, bounds_check_result) =
4194 BoundsCheckMem(i::ElementSizeInBytes(mem_rep), index, offset, position,
4195 kCanOmitBoundsCheck);
4196
4197 #if defined(V8_TARGET_BIG_ENDIAN)
4198 val = BuildChangeEndiannessStore(val, mem_rep, type);
4199 #endif
4200
4201 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
4202 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
4203
4204 switch (GetMemoryAccessKind(mcgraph_, mem_rep, bounds_check_result)) {
4205 case MemoryAccessKind::kUnaligned:
4206 gasm_->StoreUnaligned(UnalignedStoreRepresentation{mem_rep},
4207 MemBuffer(capped_offset), index, val);
4208 break;
4209 case MemoryAccessKind::kProtected:
4210 SetSourcePosition(
4211 gasm_->ProtectedStore(mem_rep, MemBuffer(capped_offset), index, val),
4212 position);
4213 break;
4214 case MemoryAccessKind::kNormal:
4215 gasm_->Store(StoreRepresentation{mem_rep, kNoWriteBarrier},
4216 MemBuffer(capped_offset), index, val);
4217 break;
4218 }
4219
4220 if (FLAG_trace_wasm_memory) {
4221 TraceMemoryOperation(true, mem_rep, index, capped_offset, position);
4222 }
4223 }
4224
BuildAsmjsLoadMem(MachineType type,Node * index)4225 Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) {
4226 DCHECK_NOT_NULL(instance_cache_);
4227 Node* mem_start = instance_cache_->mem_start;
4228 Node* mem_size = instance_cache_->mem_size;
4229 DCHECK_NOT_NULL(mem_start);
4230 DCHECK_NOT_NULL(mem_size);
4231
4232 // Asm.js semantics are defined in terms of typed arrays, hence OOB
4233 // reads return {undefined} coerced to the result type (0 for integers, NaN
4234 // for float and double).
4235 // Note that we check against the memory size ignoring the size of the
4236 // stored value, which is conservative if misaligned. Technically, asm.js
4237 // should never have misaligned accesses.
4238 index = BuildChangeUint32ToUintPtr(index);
4239 Diamond bounds_check(graph(), mcgraph()->common(),
4240 gasm_->UintLessThan(index, mem_size), BranchHint::kTrue);
4241 bounds_check.Chain(control());
4242
4243 Node* load = graph()->NewNode(mcgraph()->machine()->Load(type), mem_start,
4244 index, effect(), bounds_check.if_true);
4245 SetEffectControl(bounds_check.EffectPhi(load, effect()), bounds_check.merge);
4246
4247 Node* oob_value;
4248 switch (type.representation()) {
4249 case MachineRepresentation::kWord8:
4250 case MachineRepresentation::kWord16:
4251 case MachineRepresentation::kWord32:
4252 oob_value = Int32Constant(0);
4253 break;
4254 case MachineRepresentation::kWord64:
4255 oob_value = Int64Constant(0);
4256 break;
4257 case MachineRepresentation::kFloat32:
4258 oob_value = Float32Constant(std::numeric_limits<float>::quiet_NaN());
4259 break;
4260 case MachineRepresentation::kFloat64:
4261 oob_value = Float64Constant(std::numeric_limits<double>::quiet_NaN());
4262 break;
4263 default:
4264 UNREACHABLE();
4265 }
4266
4267 return bounds_check.Phi(type.representation(), load, oob_value);
4268 }
4269
BuildAsmjsStoreMem(MachineType type,Node * index,Node * val)4270 Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index,
4271 Node* val) {
4272 DCHECK_NOT_NULL(instance_cache_);
4273 Node* mem_start = instance_cache_->mem_start;
4274 Node* mem_size = instance_cache_->mem_size;
4275 DCHECK_NOT_NULL(mem_start);
4276 DCHECK_NOT_NULL(mem_size);
4277
4278 // Asm.js semantics are to ignore OOB writes.
4279 // Note that we check against the memory size ignoring the size of the
4280 // stored value, which is conservative if misaligned. Technically, asm.js
4281 // should never have misaligned accesses.
4282 Diamond bounds_check(graph(), mcgraph()->common(),
4283 gasm_->Uint32LessThan(index, mem_size),
4284 BranchHint::kTrue);
4285 bounds_check.Chain(control());
4286
4287 index = BuildChangeUint32ToUintPtr(index);
4288 const Operator* store_op = mcgraph()->machine()->Store(StoreRepresentation(
4289 type.representation(), WriteBarrierKind::kNoWriteBarrier));
4290 Node* store = graph()->NewNode(store_op, mem_start, index, val, effect(),
4291 bounds_check.if_true);
4292 SetEffectControl(bounds_check.EffectPhi(store, effect()), bounds_check.merge);
4293 return val;
4294 }
4295
BuildF64x2Ceil(Node * input)4296 Node* WasmGraphBuilder::BuildF64x2Ceil(Node* input) {
4297 MachineType type = MachineType::Simd128();
4298 ExternalReference ref = ExternalReference::wasm_f64x2_ceil();
4299 return BuildCFuncInstruction(ref, type, input);
4300 }
4301
BuildF64x2Floor(Node * input)4302 Node* WasmGraphBuilder::BuildF64x2Floor(Node* input) {
4303 MachineType type = MachineType::Simd128();
4304 ExternalReference ref = ExternalReference::wasm_f64x2_floor();
4305 return BuildCFuncInstruction(ref, type, input);
4306 }
4307
BuildF64x2Trunc(Node * input)4308 Node* WasmGraphBuilder::BuildF64x2Trunc(Node* input) {
4309 MachineType type = MachineType::Simd128();
4310 ExternalReference ref = ExternalReference::wasm_f64x2_trunc();
4311 return BuildCFuncInstruction(ref, type, input);
4312 }
4313
BuildF64x2NearestInt(Node * input)4314 Node* WasmGraphBuilder::BuildF64x2NearestInt(Node* input) {
4315 MachineType type = MachineType::Simd128();
4316 ExternalReference ref = ExternalReference::wasm_f64x2_nearest_int();
4317 return BuildCFuncInstruction(ref, type, input);
4318 }
4319
BuildF32x4Ceil(Node * input)4320 Node* WasmGraphBuilder::BuildF32x4Ceil(Node* input) {
4321 MachineType type = MachineType::Simd128();
4322 ExternalReference ref = ExternalReference::wasm_f32x4_ceil();
4323 return BuildCFuncInstruction(ref, type, input);
4324 }
4325
BuildF32x4Floor(Node * input)4326 Node* WasmGraphBuilder::BuildF32x4Floor(Node* input) {
4327 MachineType type = MachineType::Simd128();
4328 ExternalReference ref = ExternalReference::wasm_f32x4_floor();
4329 return BuildCFuncInstruction(ref, type, input);
4330 }
4331
BuildF32x4Trunc(Node * input)4332 Node* WasmGraphBuilder::BuildF32x4Trunc(Node* input) {
4333 MachineType type = MachineType::Simd128();
4334 ExternalReference ref = ExternalReference::wasm_f32x4_trunc();
4335 return BuildCFuncInstruction(ref, type, input);
4336 }
4337
BuildF32x4NearestInt(Node * input)4338 Node* WasmGraphBuilder::BuildF32x4NearestInt(Node* input) {
4339 MachineType type = MachineType::Simd128();
4340 ExternalReference ref = ExternalReference::wasm_f32x4_nearest_int();
4341 return BuildCFuncInstruction(ref, type, input);
4342 }
4343
PrintDebugName(Node * node)4344 void WasmGraphBuilder::PrintDebugName(Node* node) {
4345 PrintF("#%d:%s", node->id(), node->op()->mnemonic());
4346 }
4347
graph()4348 Graph* WasmGraphBuilder::graph() { return mcgraph()->graph(); }
4349
graph_zone()4350 Zone* WasmGraphBuilder::graph_zone() { return graph()->zone(); }
4351
4352 namespace {
CreateMachineSignature(Zone * zone,const wasm::FunctionSig * sig,WasmGraphBuilder::CallOrigin origin)4353 Signature<MachineRepresentation>* CreateMachineSignature(
4354 Zone* zone, const wasm::FunctionSig* sig,
4355 WasmGraphBuilder::CallOrigin origin) {
4356 Signature<MachineRepresentation>::Builder builder(zone, sig->return_count(),
4357 sig->parameter_count());
4358 for (auto ret : sig->returns()) {
4359 if (origin == WasmGraphBuilder::kCalledFromJS) {
4360 builder.AddReturn(MachineRepresentation::kTagged);
4361 } else {
4362 builder.AddReturn(ret.machine_representation());
4363 }
4364 }
4365
4366 for (auto param : sig->parameters()) {
4367 if (origin == WasmGraphBuilder::kCalledFromJS) {
4368 // Parameters coming from JavaScript are always tagged values. Especially
4369 // when the signature says that it's an I64 value, then a BigInt object is
4370 // provided by JavaScript, and not two 32-bit parameters.
4371 builder.AddParam(MachineRepresentation::kTagged);
4372 } else {
4373 builder.AddParam(param.machine_representation());
4374 }
4375 }
4376 return builder.Build();
4377 }
4378
4379 } // namespace
4380
AddInt64LoweringReplacement(CallDescriptor * original,CallDescriptor * replacement)4381 void WasmGraphBuilder::AddInt64LoweringReplacement(
4382 CallDescriptor* original, CallDescriptor* replacement) {
4383 if (!lowering_special_case_) {
4384 lowering_special_case_ = std::make_unique<Int64LoweringSpecialCase>();
4385 }
4386 lowering_special_case_->replacements.insert({original, replacement});
4387 }
4388
GetI32AtomicWaitCallDescriptor()4389 CallDescriptor* WasmGraphBuilder::GetI32AtomicWaitCallDescriptor() {
4390 if (i32_atomic_wait_descriptor_) return i32_atomic_wait_descriptor_;
4391
4392 i32_atomic_wait_descriptor_ = GetBuiltinCallDescriptor(
4393 Builtin::kWasmI32AtomicWait64, zone_, StubCallMode::kCallWasmRuntimeStub);
4394
4395 AddInt64LoweringReplacement(
4396 i32_atomic_wait_descriptor_,
4397 GetBuiltinCallDescriptor(Builtin::kWasmI32AtomicWait32, zone_,
4398 StubCallMode::kCallWasmRuntimeStub));
4399
4400 return i32_atomic_wait_descriptor_;
4401 }
4402
GetI64AtomicWaitCallDescriptor()4403 CallDescriptor* WasmGraphBuilder::GetI64AtomicWaitCallDescriptor() {
4404 if (i64_atomic_wait_descriptor_) return i64_atomic_wait_descriptor_;
4405
4406 i64_atomic_wait_descriptor_ = GetBuiltinCallDescriptor(
4407 Builtin::kWasmI64AtomicWait64, zone_, StubCallMode::kCallWasmRuntimeStub);
4408
4409 AddInt64LoweringReplacement(
4410 i64_atomic_wait_descriptor_,
4411 GetBuiltinCallDescriptor(Builtin::kWasmI64AtomicWait32, zone_,
4412 StubCallMode::kCallWasmRuntimeStub));
4413
4414 return i64_atomic_wait_descriptor_;
4415 }
4416
LowerInt64(Signature<MachineRepresentation> * sig)4417 void WasmGraphBuilder::LowerInt64(Signature<MachineRepresentation>* sig) {
4418 if (mcgraph()->machine()->Is64()) return;
4419 Int64Lowering r(mcgraph()->graph(), mcgraph()->machine(), mcgraph()->common(),
4420 gasm_->simplified(), mcgraph()->zone(), sig,
4421 std::move(lowering_special_case_));
4422 r.LowerGraph();
4423 }
4424
LowerInt64(CallOrigin origin)4425 void WasmGraphBuilder::LowerInt64(CallOrigin origin) {
4426 LowerInt64(CreateMachineSignature(mcgraph()->zone(), sig_, origin));
4427 }
4428
SetSourcePosition(Node * node,wasm::WasmCodePosition position)4429 void WasmGraphBuilder::SetSourcePosition(Node* node,
4430 wasm::WasmCodePosition position) {
4431 DCHECK_NE(position, wasm::kNoCodePosition);
4432 if (source_position_table_) {
4433 source_position_table_->SetSourcePosition(node, SourcePosition(position));
4434 }
4435 }
4436
S128Zero()4437 Node* WasmGraphBuilder::S128Zero() {
4438 has_simd_ = true;
4439 return graph()->NewNode(mcgraph()->machine()->S128Zero());
4440 }
4441
SimdOp(wasm::WasmOpcode opcode,Node * const * inputs)4442 Node* WasmGraphBuilder::SimdOp(wasm::WasmOpcode opcode, Node* const* inputs) {
4443 has_simd_ = true;
4444 switch (opcode) {
4445 case wasm::kExprF64x2Splat:
4446 return graph()->NewNode(mcgraph()->machine()->F64x2Splat(), inputs[0]);
4447 case wasm::kExprF64x2Abs:
4448 return graph()->NewNode(mcgraph()->machine()->F64x2Abs(), inputs[0]);
4449 case wasm::kExprF64x2Neg:
4450 return graph()->NewNode(mcgraph()->machine()->F64x2Neg(), inputs[0]);
4451 case wasm::kExprF64x2Sqrt:
4452 return graph()->NewNode(mcgraph()->machine()->F64x2Sqrt(), inputs[0]);
4453 case wasm::kExprF64x2Add:
4454 return graph()->NewNode(mcgraph()->machine()->F64x2Add(), inputs[0],
4455 inputs[1]);
4456 case wasm::kExprF64x2Sub:
4457 return graph()->NewNode(mcgraph()->machine()->F64x2Sub(), inputs[0],
4458 inputs[1]);
4459 case wasm::kExprF64x2Mul:
4460 return graph()->NewNode(mcgraph()->machine()->F64x2Mul(), inputs[0],
4461 inputs[1]);
4462 case wasm::kExprF64x2Div:
4463 return graph()->NewNode(mcgraph()->machine()->F64x2Div(), inputs[0],
4464 inputs[1]);
4465 case wasm::kExprF64x2Min:
4466 return graph()->NewNode(mcgraph()->machine()->F64x2Min(), inputs[0],
4467 inputs[1]);
4468 case wasm::kExprF64x2Max:
4469 return graph()->NewNode(mcgraph()->machine()->F64x2Max(), inputs[0],
4470 inputs[1]);
4471 case wasm::kExprF64x2Eq:
4472 return graph()->NewNode(mcgraph()->machine()->F64x2Eq(), inputs[0],
4473 inputs[1]);
4474 case wasm::kExprF64x2Ne:
4475 return graph()->NewNode(mcgraph()->machine()->F64x2Ne(), inputs[0],
4476 inputs[1]);
4477 case wasm::kExprF64x2Lt:
4478 return graph()->NewNode(mcgraph()->machine()->F64x2Lt(), inputs[0],
4479 inputs[1]);
4480 case wasm::kExprF64x2Le:
4481 return graph()->NewNode(mcgraph()->machine()->F64x2Le(), inputs[0],
4482 inputs[1]);
4483 case wasm::kExprF64x2Gt:
4484 return graph()->NewNode(mcgraph()->machine()->F64x2Lt(), inputs[1],
4485 inputs[0]);
4486 case wasm::kExprF64x2Ge:
4487 return graph()->NewNode(mcgraph()->machine()->F64x2Le(), inputs[1],
4488 inputs[0]);
4489 case wasm::kExprF64x2Qfma:
4490 return graph()->NewNode(mcgraph()->machine()->F64x2Qfma(), inputs[0],
4491 inputs[1], inputs[2]);
4492 case wasm::kExprF64x2Qfms:
4493 return graph()->NewNode(mcgraph()->machine()->F64x2Qfms(), inputs[0],
4494 inputs[1], inputs[2]);
4495 case wasm::kExprF64x2Pmin:
4496 return graph()->NewNode(mcgraph()->machine()->F64x2Pmin(), inputs[0],
4497 inputs[1]);
4498 case wasm::kExprF64x2Pmax:
4499 return graph()->NewNode(mcgraph()->machine()->F64x2Pmax(), inputs[0],
4500 inputs[1]);
4501 case wasm::kExprF64x2Ceil:
4502 // Architecture support for F64x2Ceil and Float64RoundUp is the same.
4503 if (!mcgraph()->machine()->Float64RoundUp().IsSupported())
4504 return BuildF64x2Ceil(inputs[0]);
4505 return graph()->NewNode(mcgraph()->machine()->F64x2Ceil(), inputs[0]);
4506 case wasm::kExprF64x2Floor:
4507 // Architecture support for F64x2Floor and Float64RoundDown is the same.
4508 if (!mcgraph()->machine()->Float64RoundDown().IsSupported())
4509 return BuildF64x2Floor(inputs[0]);
4510 return graph()->NewNode(mcgraph()->machine()->F64x2Floor(), inputs[0]);
4511 case wasm::kExprF64x2Trunc:
4512 // Architecture support for F64x2Trunc and Float64RoundTruncate is the
4513 // same.
4514 if (!mcgraph()->machine()->Float64RoundTruncate().IsSupported())
4515 return BuildF64x2Trunc(inputs[0]);
4516 return graph()->NewNode(mcgraph()->machine()->F64x2Trunc(), inputs[0]);
4517 case wasm::kExprF64x2NearestInt:
4518 // Architecture support for F64x2NearestInt and Float64RoundTiesEven is
4519 // the same.
4520 if (!mcgraph()->machine()->Float64RoundTiesEven().IsSupported())
4521 return BuildF64x2NearestInt(inputs[0]);
4522 return graph()->NewNode(mcgraph()->machine()->F64x2NearestInt(),
4523 inputs[0]);
4524 case wasm::kExprF64x2ConvertLowI32x4S:
4525 return graph()->NewNode(mcgraph()->machine()->F64x2ConvertLowI32x4S(),
4526 inputs[0]);
4527 case wasm::kExprF64x2ConvertLowI32x4U:
4528 return graph()->NewNode(mcgraph()->machine()->F64x2ConvertLowI32x4U(),
4529 inputs[0]);
4530 case wasm::kExprF64x2PromoteLowF32x4:
4531 return graph()->NewNode(mcgraph()->machine()->F64x2PromoteLowF32x4(),
4532 inputs[0]);
4533 case wasm::kExprF32x4Splat:
4534 return graph()->NewNode(mcgraph()->machine()->F32x4Splat(), inputs[0]);
4535 case wasm::kExprF32x4SConvertI32x4:
4536 return graph()->NewNode(mcgraph()->machine()->F32x4SConvertI32x4(),
4537 inputs[0]);
4538 case wasm::kExprF32x4UConvertI32x4:
4539 return graph()->NewNode(mcgraph()->machine()->F32x4UConvertI32x4(),
4540 inputs[0]);
4541 case wasm::kExprF32x4Abs:
4542 return graph()->NewNode(mcgraph()->machine()->F32x4Abs(), inputs[0]);
4543 case wasm::kExprF32x4Neg:
4544 return graph()->NewNode(mcgraph()->machine()->F32x4Neg(), inputs[0]);
4545 case wasm::kExprF32x4Sqrt:
4546 return graph()->NewNode(mcgraph()->machine()->F32x4Sqrt(), inputs[0]);
4547 case wasm::kExprF32x4RecipApprox:
4548 return graph()->NewNode(mcgraph()->machine()->F32x4RecipApprox(),
4549 inputs[0]);
4550 case wasm::kExprF32x4RecipSqrtApprox:
4551 return graph()->NewNode(mcgraph()->machine()->F32x4RecipSqrtApprox(),
4552 inputs[0]);
4553 case wasm::kExprF32x4Add:
4554 return graph()->NewNode(mcgraph()->machine()->F32x4Add(), inputs[0],
4555 inputs[1]);
4556 case wasm::kExprF32x4Sub:
4557 return graph()->NewNode(mcgraph()->machine()->F32x4Sub(), inputs[0],
4558 inputs[1]);
4559 case wasm::kExprF32x4Mul:
4560 return graph()->NewNode(mcgraph()->machine()->F32x4Mul(), inputs[0],
4561 inputs[1]);
4562 case wasm::kExprF32x4Div:
4563 return graph()->NewNode(mcgraph()->machine()->F32x4Div(), inputs[0],
4564 inputs[1]);
4565 case wasm::kExprF32x4Min:
4566 return graph()->NewNode(mcgraph()->machine()->F32x4Min(), inputs[0],
4567 inputs[1]);
4568 case wasm::kExprF32x4Max:
4569 return graph()->NewNode(mcgraph()->machine()->F32x4Max(), inputs[0],
4570 inputs[1]);
4571 case wasm::kExprF32x4Eq:
4572 return graph()->NewNode(mcgraph()->machine()->F32x4Eq(), inputs[0],
4573 inputs[1]);
4574 case wasm::kExprF32x4Ne:
4575 return graph()->NewNode(mcgraph()->machine()->F32x4Ne(), inputs[0],
4576 inputs[1]);
4577 case wasm::kExprF32x4Lt:
4578 return graph()->NewNode(mcgraph()->machine()->F32x4Lt(), inputs[0],
4579 inputs[1]);
4580 case wasm::kExprF32x4Le:
4581 return graph()->NewNode(mcgraph()->machine()->F32x4Le(), inputs[0],
4582 inputs[1]);
4583 case wasm::kExprF32x4Gt:
4584 return graph()->NewNode(mcgraph()->machine()->F32x4Lt(), inputs[1],
4585 inputs[0]);
4586 case wasm::kExprF32x4Ge:
4587 return graph()->NewNode(mcgraph()->machine()->F32x4Le(), inputs[1],
4588 inputs[0]);
4589 case wasm::kExprF32x4Qfma:
4590 return graph()->NewNode(mcgraph()->machine()->F32x4Qfma(), inputs[0],
4591 inputs[1], inputs[2]);
4592 case wasm::kExprF32x4Qfms:
4593 return graph()->NewNode(mcgraph()->machine()->F32x4Qfms(), inputs[0],
4594 inputs[1], inputs[2]);
4595 case wasm::kExprF32x4Pmin:
4596 return graph()->NewNode(mcgraph()->machine()->F32x4Pmin(), inputs[0],
4597 inputs[1]);
4598 case wasm::kExprF32x4Pmax:
4599 return graph()->NewNode(mcgraph()->machine()->F32x4Pmax(), inputs[0],
4600 inputs[1]);
4601 case wasm::kExprF32x4Ceil:
4602 // Architecture support for F32x4Ceil and Float32RoundUp is the same.
4603 if (!mcgraph()->machine()->Float32RoundUp().IsSupported())
4604 return BuildF32x4Ceil(inputs[0]);
4605 return graph()->NewNode(mcgraph()->machine()->F32x4Ceil(), inputs[0]);
4606 case wasm::kExprF32x4Floor:
4607 // Architecture support for F32x4Floor and Float32RoundDown is the same.
4608 if (!mcgraph()->machine()->Float32RoundDown().IsSupported())
4609 return BuildF32x4Floor(inputs[0]);
4610 return graph()->NewNode(mcgraph()->machine()->F32x4Floor(), inputs[0]);
4611 case wasm::kExprF32x4Trunc:
4612 // Architecture support for F32x4Trunc and Float32RoundTruncate is the
4613 // same.
4614 if (!mcgraph()->machine()->Float32RoundTruncate().IsSupported())
4615 return BuildF32x4Trunc(inputs[0]);
4616 return graph()->NewNode(mcgraph()->machine()->F32x4Trunc(), inputs[0]);
4617 case wasm::kExprF32x4NearestInt:
4618 // Architecture support for F32x4NearestInt and Float32RoundTiesEven is
4619 // the same.
4620 if (!mcgraph()->machine()->Float32RoundTiesEven().IsSupported())
4621 return BuildF32x4NearestInt(inputs[0]);
4622 return graph()->NewNode(mcgraph()->machine()->F32x4NearestInt(),
4623 inputs[0]);
4624 case wasm::kExprF32x4DemoteF64x2Zero:
4625 return graph()->NewNode(mcgraph()->machine()->F32x4DemoteF64x2Zero(),
4626 inputs[0]);
4627 case wasm::kExprI64x2Splat:
4628 return graph()->NewNode(mcgraph()->machine()->I64x2Splat(), inputs[0]);
4629 case wasm::kExprI64x2Abs:
4630 return graph()->NewNode(mcgraph()->machine()->I64x2Abs(), inputs[0]);
4631 case wasm::kExprI64x2Neg:
4632 return graph()->NewNode(mcgraph()->machine()->I64x2Neg(), inputs[0]);
4633 case wasm::kExprI64x2SConvertI32x4Low:
4634 return graph()->NewNode(mcgraph()->machine()->I64x2SConvertI32x4Low(),
4635 inputs[0]);
4636 case wasm::kExprI64x2SConvertI32x4High:
4637 return graph()->NewNode(mcgraph()->machine()->I64x2SConvertI32x4High(),
4638 inputs[0]);
4639 case wasm::kExprI64x2UConvertI32x4Low:
4640 return graph()->NewNode(mcgraph()->machine()->I64x2UConvertI32x4Low(),
4641 inputs[0]);
4642 case wasm::kExprI64x2UConvertI32x4High:
4643 return graph()->NewNode(mcgraph()->machine()->I64x2UConvertI32x4High(),
4644 inputs[0]);
4645 case wasm::kExprI64x2BitMask:
4646 return graph()->NewNode(mcgraph()->machine()->I64x2BitMask(), inputs[0]);
4647 case wasm::kExprI64x2Shl:
4648 return graph()->NewNode(mcgraph()->machine()->I64x2Shl(), inputs[0],
4649 inputs[1]);
4650 case wasm::kExprI64x2ShrS:
4651 return graph()->NewNode(mcgraph()->machine()->I64x2ShrS(), inputs[0],
4652 inputs[1]);
4653 case wasm::kExprI64x2Add:
4654 return graph()->NewNode(mcgraph()->machine()->I64x2Add(), inputs[0],
4655 inputs[1]);
4656 case wasm::kExprI64x2Sub:
4657 return graph()->NewNode(mcgraph()->machine()->I64x2Sub(), inputs[0],
4658 inputs[1]);
4659 case wasm::kExprI64x2Mul:
4660 return graph()->NewNode(mcgraph()->machine()->I64x2Mul(), inputs[0],
4661 inputs[1]);
4662 case wasm::kExprI64x2Eq:
4663 return graph()->NewNode(mcgraph()->machine()->I64x2Eq(), inputs[0],
4664 inputs[1]);
4665 case wasm::kExprI64x2Ne:
4666 return graph()->NewNode(mcgraph()->machine()->I64x2Ne(), inputs[0],
4667 inputs[1]);
4668 case wasm::kExprI64x2LtS:
4669 return graph()->NewNode(mcgraph()->machine()->I64x2GtS(), inputs[1],
4670 inputs[0]);
4671 case wasm::kExprI64x2LeS:
4672 return graph()->NewNode(mcgraph()->machine()->I64x2GeS(), inputs[1],
4673 inputs[0]);
4674 case wasm::kExprI64x2GtS:
4675 return graph()->NewNode(mcgraph()->machine()->I64x2GtS(), inputs[0],
4676 inputs[1]);
4677 case wasm::kExprI64x2GeS:
4678 return graph()->NewNode(mcgraph()->machine()->I64x2GeS(), inputs[0],
4679 inputs[1]);
4680 case wasm::kExprI64x2ShrU:
4681 return graph()->NewNode(mcgraph()->machine()->I64x2ShrU(), inputs[0],
4682 inputs[1]);
4683 case wasm::kExprI64x2ExtMulLowI32x4S:
4684 return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulLowI32x4S(),
4685 inputs[0], inputs[1]);
4686 case wasm::kExprI64x2ExtMulHighI32x4S:
4687 return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulHighI32x4S(),
4688 inputs[0], inputs[1]);
4689 case wasm::kExprI64x2ExtMulLowI32x4U:
4690 return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulLowI32x4U(),
4691 inputs[0], inputs[1]);
4692 case wasm::kExprI64x2ExtMulHighI32x4U:
4693 return graph()->NewNode(mcgraph()->machine()->I64x2ExtMulHighI32x4U(),
4694 inputs[0], inputs[1]);
4695 case wasm::kExprI32x4Splat:
4696 return graph()->NewNode(mcgraph()->machine()->I32x4Splat(), inputs[0]);
4697 case wasm::kExprI32x4SConvertF32x4:
4698 return graph()->NewNode(mcgraph()->machine()->I32x4SConvertF32x4(),
4699 inputs[0]);
4700 case wasm::kExprI32x4UConvertF32x4:
4701 return graph()->NewNode(mcgraph()->machine()->I32x4UConvertF32x4(),
4702 inputs[0]);
4703 case wasm::kExprI32x4SConvertI16x8Low:
4704 return graph()->NewNode(mcgraph()->machine()->I32x4SConvertI16x8Low(),
4705 inputs[0]);
4706 case wasm::kExprI32x4SConvertI16x8High:
4707 return graph()->NewNode(mcgraph()->machine()->I32x4SConvertI16x8High(),
4708 inputs[0]);
4709 case wasm::kExprI32x4Neg:
4710 return graph()->NewNode(mcgraph()->machine()->I32x4Neg(), inputs[0]);
4711 case wasm::kExprI32x4Shl:
4712 return graph()->NewNode(mcgraph()->machine()->I32x4Shl(), inputs[0],
4713 inputs[1]);
4714 case wasm::kExprI32x4ShrS:
4715 return graph()->NewNode(mcgraph()->machine()->I32x4ShrS(), inputs[0],
4716 inputs[1]);
4717 case wasm::kExprI32x4Add:
4718 return graph()->NewNode(mcgraph()->machine()->I32x4Add(), inputs[0],
4719 inputs[1]);
4720 case wasm::kExprI32x4Sub:
4721 return graph()->NewNode(mcgraph()->machine()->I32x4Sub(), inputs[0],
4722 inputs[1]);
4723 case wasm::kExprI32x4Mul:
4724 return graph()->NewNode(mcgraph()->machine()->I32x4Mul(), inputs[0],
4725 inputs[1]);
4726 case wasm::kExprI32x4MinS:
4727 return graph()->NewNode(mcgraph()->machine()->I32x4MinS(), inputs[0],
4728 inputs[1]);
4729 case wasm::kExprI32x4MaxS:
4730 return graph()->NewNode(mcgraph()->machine()->I32x4MaxS(), inputs[0],
4731 inputs[1]);
4732 case wasm::kExprI32x4Eq:
4733 return graph()->NewNode(mcgraph()->machine()->I32x4Eq(), inputs[0],
4734 inputs[1]);
4735 case wasm::kExprI32x4Ne:
4736 return graph()->NewNode(mcgraph()->machine()->I32x4Ne(), inputs[0],
4737 inputs[1]);
4738 case wasm::kExprI32x4LtS:
4739 return graph()->NewNode(mcgraph()->machine()->I32x4GtS(), inputs[1],
4740 inputs[0]);
4741 case wasm::kExprI32x4LeS:
4742 return graph()->NewNode(mcgraph()->machine()->I32x4GeS(), inputs[1],
4743 inputs[0]);
4744 case wasm::kExprI32x4GtS:
4745 return graph()->NewNode(mcgraph()->machine()->I32x4GtS(), inputs[0],
4746 inputs[1]);
4747 case wasm::kExprI32x4GeS:
4748 return graph()->NewNode(mcgraph()->machine()->I32x4GeS(), inputs[0],
4749 inputs[1]);
4750 case wasm::kExprI32x4UConvertI16x8Low:
4751 return graph()->NewNode(mcgraph()->machine()->I32x4UConvertI16x8Low(),
4752 inputs[0]);
4753 case wasm::kExprI32x4UConvertI16x8High:
4754 return graph()->NewNode(mcgraph()->machine()->I32x4UConvertI16x8High(),
4755 inputs[0]);
4756 case wasm::kExprI32x4ShrU:
4757 return graph()->NewNode(mcgraph()->machine()->I32x4ShrU(), inputs[0],
4758 inputs[1]);
4759 case wasm::kExprI32x4MinU:
4760 return graph()->NewNode(mcgraph()->machine()->I32x4MinU(), inputs[0],
4761 inputs[1]);
4762 case wasm::kExprI32x4MaxU:
4763 return graph()->NewNode(mcgraph()->machine()->I32x4MaxU(), inputs[0],
4764 inputs[1]);
4765 case wasm::kExprI32x4LtU:
4766 return graph()->NewNode(mcgraph()->machine()->I32x4GtU(), inputs[1],
4767 inputs[0]);
4768 case wasm::kExprI32x4LeU:
4769 return graph()->NewNode(mcgraph()->machine()->I32x4GeU(), inputs[1],
4770 inputs[0]);
4771 case wasm::kExprI32x4GtU:
4772 return graph()->NewNode(mcgraph()->machine()->I32x4GtU(), inputs[0],
4773 inputs[1]);
4774 case wasm::kExprI32x4GeU:
4775 return graph()->NewNode(mcgraph()->machine()->I32x4GeU(), inputs[0],
4776 inputs[1]);
4777 case wasm::kExprI32x4Abs:
4778 return graph()->NewNode(mcgraph()->machine()->I32x4Abs(), inputs[0]);
4779 case wasm::kExprI32x4BitMask:
4780 return graph()->NewNode(mcgraph()->machine()->I32x4BitMask(), inputs[0]);
4781 case wasm::kExprI32x4DotI16x8S:
4782 return graph()->NewNode(mcgraph()->machine()->I32x4DotI16x8S(), inputs[0],
4783 inputs[1]);
4784 case wasm::kExprI32x4ExtMulLowI16x8S:
4785 return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulLowI16x8S(),
4786 inputs[0], inputs[1]);
4787 case wasm::kExprI32x4ExtMulHighI16x8S:
4788 return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulHighI16x8S(),
4789 inputs[0], inputs[1]);
4790 case wasm::kExprI32x4ExtMulLowI16x8U:
4791 return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulLowI16x8U(),
4792 inputs[0], inputs[1]);
4793 case wasm::kExprI32x4ExtMulHighI16x8U:
4794 return graph()->NewNode(mcgraph()->machine()->I32x4ExtMulHighI16x8U(),
4795 inputs[0], inputs[1]);
4796 case wasm::kExprI32x4ExtAddPairwiseI16x8S:
4797 return graph()->NewNode(mcgraph()->machine()->I32x4ExtAddPairwiseI16x8S(),
4798 inputs[0]);
4799 case wasm::kExprI32x4ExtAddPairwiseI16x8U:
4800 return graph()->NewNode(mcgraph()->machine()->I32x4ExtAddPairwiseI16x8U(),
4801 inputs[0]);
4802 case wasm::kExprI32x4TruncSatF64x2SZero:
4803 return graph()->NewNode(mcgraph()->machine()->I32x4TruncSatF64x2SZero(),
4804 inputs[0]);
4805 case wasm::kExprI32x4TruncSatF64x2UZero:
4806 return graph()->NewNode(mcgraph()->machine()->I32x4TruncSatF64x2UZero(),
4807 inputs[0]);
4808 case wasm::kExprI16x8Splat:
4809 return graph()->NewNode(mcgraph()->machine()->I16x8Splat(), inputs[0]);
4810 case wasm::kExprI16x8SConvertI8x16Low:
4811 return graph()->NewNode(mcgraph()->machine()->I16x8SConvertI8x16Low(),
4812 inputs[0]);
4813 case wasm::kExprI16x8SConvertI8x16High:
4814 return graph()->NewNode(mcgraph()->machine()->I16x8SConvertI8x16High(),
4815 inputs[0]);
4816 case wasm::kExprI16x8Shl:
4817 return graph()->NewNode(mcgraph()->machine()->I16x8Shl(), inputs[0],
4818 inputs[1]);
4819 case wasm::kExprI16x8ShrS:
4820 return graph()->NewNode(mcgraph()->machine()->I16x8ShrS(), inputs[0],
4821 inputs[1]);
4822 case wasm::kExprI16x8Neg:
4823 return graph()->NewNode(mcgraph()->machine()->I16x8Neg(), inputs[0]);
4824 case wasm::kExprI16x8SConvertI32x4:
4825 return graph()->NewNode(mcgraph()->machine()->I16x8SConvertI32x4(),
4826 inputs[0], inputs[1]);
4827 case wasm::kExprI16x8Add:
4828 return graph()->NewNode(mcgraph()->machine()->I16x8Add(), inputs[0],
4829 inputs[1]);
4830 case wasm::kExprI16x8AddSatS:
4831 return graph()->NewNode(mcgraph()->machine()->I16x8AddSatS(), inputs[0],
4832 inputs[1]);
4833 case wasm::kExprI16x8Sub:
4834 return graph()->NewNode(mcgraph()->machine()->I16x8Sub(), inputs[0],
4835 inputs[1]);
4836 case wasm::kExprI16x8SubSatS:
4837 return graph()->NewNode(mcgraph()->machine()->I16x8SubSatS(), inputs[0],
4838 inputs[1]);
4839 case wasm::kExprI16x8Mul:
4840 return graph()->NewNode(mcgraph()->machine()->I16x8Mul(), inputs[0],
4841 inputs[1]);
4842 case wasm::kExprI16x8MinS:
4843 return graph()->NewNode(mcgraph()->machine()->I16x8MinS(), inputs[0],
4844 inputs[1]);
4845 case wasm::kExprI16x8MaxS:
4846 return graph()->NewNode(mcgraph()->machine()->I16x8MaxS(), inputs[0],
4847 inputs[1]);
4848 case wasm::kExprI16x8Eq:
4849 return graph()->NewNode(mcgraph()->machine()->I16x8Eq(), inputs[0],
4850 inputs[1]);
4851 case wasm::kExprI16x8Ne:
4852 return graph()->NewNode(mcgraph()->machine()->I16x8Ne(), inputs[0],
4853 inputs[1]);
4854 case wasm::kExprI16x8LtS:
4855 return graph()->NewNode(mcgraph()->machine()->I16x8GtS(), inputs[1],
4856 inputs[0]);
4857 case wasm::kExprI16x8LeS:
4858 return graph()->NewNode(mcgraph()->machine()->I16x8GeS(), inputs[1],
4859 inputs[0]);
4860 case wasm::kExprI16x8GtS:
4861 return graph()->NewNode(mcgraph()->machine()->I16x8GtS(), inputs[0],
4862 inputs[1]);
4863 case wasm::kExprI16x8GeS:
4864 return graph()->NewNode(mcgraph()->machine()->I16x8GeS(), inputs[0],
4865 inputs[1]);
4866 case wasm::kExprI16x8UConvertI8x16Low:
4867 return graph()->NewNode(mcgraph()->machine()->I16x8UConvertI8x16Low(),
4868 inputs[0]);
4869 case wasm::kExprI16x8UConvertI8x16High:
4870 return graph()->NewNode(mcgraph()->machine()->I16x8UConvertI8x16High(),
4871 inputs[0]);
4872 case wasm::kExprI16x8UConvertI32x4:
4873 return graph()->NewNode(mcgraph()->machine()->I16x8UConvertI32x4(),
4874 inputs[0], inputs[1]);
4875 case wasm::kExprI16x8ShrU:
4876 return graph()->NewNode(mcgraph()->machine()->I16x8ShrU(), inputs[0],
4877 inputs[1]);
4878 case wasm::kExprI16x8AddSatU:
4879 return graph()->NewNode(mcgraph()->machine()->I16x8AddSatU(), inputs[0],
4880 inputs[1]);
4881 case wasm::kExprI16x8SubSatU:
4882 return graph()->NewNode(mcgraph()->machine()->I16x8SubSatU(), inputs[0],
4883 inputs[1]);
4884 case wasm::kExprI16x8MinU:
4885 return graph()->NewNode(mcgraph()->machine()->I16x8MinU(), inputs[0],
4886 inputs[1]);
4887 case wasm::kExprI16x8MaxU:
4888 return graph()->NewNode(mcgraph()->machine()->I16x8MaxU(), inputs[0],
4889 inputs[1]);
4890 case wasm::kExprI16x8LtU:
4891 return graph()->NewNode(mcgraph()->machine()->I16x8GtU(), inputs[1],
4892 inputs[0]);
4893 case wasm::kExprI16x8LeU:
4894 return graph()->NewNode(mcgraph()->machine()->I16x8GeU(), inputs[1],
4895 inputs[0]);
4896 case wasm::kExprI16x8GtU:
4897 return graph()->NewNode(mcgraph()->machine()->I16x8GtU(), inputs[0],
4898 inputs[1]);
4899 case wasm::kExprI16x8GeU:
4900 return graph()->NewNode(mcgraph()->machine()->I16x8GeU(), inputs[0],
4901 inputs[1]);
4902 case wasm::kExprI16x8RoundingAverageU:
4903 return graph()->NewNode(mcgraph()->machine()->I16x8RoundingAverageU(),
4904 inputs[0], inputs[1]);
4905 case wasm::kExprI16x8Q15MulRSatS:
4906 return graph()->NewNode(mcgraph()->machine()->I16x8Q15MulRSatS(),
4907 inputs[0], inputs[1]);
4908 case wasm::kExprI16x8Abs:
4909 return graph()->NewNode(mcgraph()->machine()->I16x8Abs(), inputs[0]);
4910 case wasm::kExprI16x8BitMask:
4911 return graph()->NewNode(mcgraph()->machine()->I16x8BitMask(), inputs[0]);
4912 case wasm::kExprI16x8ExtMulLowI8x16S:
4913 return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulLowI8x16S(),
4914 inputs[0], inputs[1]);
4915 case wasm::kExprI16x8ExtMulHighI8x16S:
4916 return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulHighI8x16S(),
4917 inputs[0], inputs[1]);
4918 case wasm::kExprI16x8ExtMulLowI8x16U:
4919 return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulLowI8x16U(),
4920 inputs[0], inputs[1]);
4921 case wasm::kExprI16x8ExtMulHighI8x16U:
4922 return graph()->NewNode(mcgraph()->machine()->I16x8ExtMulHighI8x16U(),
4923 inputs[0], inputs[1]);
4924 case wasm::kExprI16x8ExtAddPairwiseI8x16S:
4925 return graph()->NewNode(mcgraph()->machine()->I16x8ExtAddPairwiseI8x16S(),
4926 inputs[0]);
4927 case wasm::kExprI16x8ExtAddPairwiseI8x16U:
4928 return graph()->NewNode(mcgraph()->machine()->I16x8ExtAddPairwiseI8x16U(),
4929 inputs[0]);
4930 case wasm::kExprI8x16Splat:
4931 return graph()->NewNode(mcgraph()->machine()->I8x16Splat(), inputs[0]);
4932 case wasm::kExprI8x16Neg:
4933 return graph()->NewNode(mcgraph()->machine()->I8x16Neg(), inputs[0]);
4934 case wasm::kExprI8x16Shl:
4935 return graph()->NewNode(mcgraph()->machine()->I8x16Shl(), inputs[0],
4936 inputs[1]);
4937 case wasm::kExprI8x16ShrS:
4938 return graph()->NewNode(mcgraph()->machine()->I8x16ShrS(), inputs[0],
4939 inputs[1]);
4940 case wasm::kExprI8x16SConvertI16x8:
4941 return graph()->NewNode(mcgraph()->machine()->I8x16SConvertI16x8(),
4942 inputs[0], inputs[1]);
4943 case wasm::kExprI8x16Add:
4944 return graph()->NewNode(mcgraph()->machine()->I8x16Add(), inputs[0],
4945 inputs[1]);
4946 case wasm::kExprI8x16AddSatS:
4947 return graph()->NewNode(mcgraph()->machine()->I8x16AddSatS(), inputs[0],
4948 inputs[1]);
4949 case wasm::kExprI8x16Sub:
4950 return graph()->NewNode(mcgraph()->machine()->I8x16Sub(), inputs[0],
4951 inputs[1]);
4952 case wasm::kExprI8x16SubSatS:
4953 return graph()->NewNode(mcgraph()->machine()->I8x16SubSatS(), inputs[0],
4954 inputs[1]);
4955 case wasm::kExprI8x16MinS:
4956 return graph()->NewNode(mcgraph()->machine()->I8x16MinS(), inputs[0],
4957 inputs[1]);
4958 case wasm::kExprI8x16MaxS:
4959 return graph()->NewNode(mcgraph()->machine()->I8x16MaxS(), inputs[0],
4960 inputs[1]);
4961 case wasm::kExprI8x16Eq:
4962 return graph()->NewNode(mcgraph()->machine()->I8x16Eq(), inputs[0],
4963 inputs[1]);
4964 case wasm::kExprI8x16Ne:
4965 return graph()->NewNode(mcgraph()->machine()->I8x16Ne(), inputs[0],
4966 inputs[1]);
4967 case wasm::kExprI8x16LtS:
4968 return graph()->NewNode(mcgraph()->machine()->I8x16GtS(), inputs[1],
4969 inputs[0]);
4970 case wasm::kExprI8x16LeS:
4971 return graph()->NewNode(mcgraph()->machine()->I8x16GeS(), inputs[1],
4972 inputs[0]);
4973 case wasm::kExprI8x16GtS:
4974 return graph()->NewNode(mcgraph()->machine()->I8x16GtS(), inputs[0],
4975 inputs[1]);
4976 case wasm::kExprI8x16GeS:
4977 return graph()->NewNode(mcgraph()->machine()->I8x16GeS(), inputs[0],
4978 inputs[1]);
4979 case wasm::kExprI8x16ShrU:
4980 return graph()->NewNode(mcgraph()->machine()->I8x16ShrU(), inputs[0],
4981 inputs[1]);
4982 case wasm::kExprI8x16UConvertI16x8:
4983 return graph()->NewNode(mcgraph()->machine()->I8x16UConvertI16x8(),
4984 inputs[0], inputs[1]);
4985 case wasm::kExprI8x16AddSatU:
4986 return graph()->NewNode(mcgraph()->machine()->I8x16AddSatU(), inputs[0],
4987 inputs[1]);
4988 case wasm::kExprI8x16SubSatU:
4989 return graph()->NewNode(mcgraph()->machine()->I8x16SubSatU(), inputs[0],
4990 inputs[1]);
4991 case wasm::kExprI8x16MinU:
4992 return graph()->NewNode(mcgraph()->machine()->I8x16MinU(), inputs[0],
4993 inputs[1]);
4994 case wasm::kExprI8x16MaxU:
4995 return graph()->NewNode(mcgraph()->machine()->I8x16MaxU(), inputs[0],
4996 inputs[1]);
4997 case wasm::kExprI8x16LtU:
4998 return graph()->NewNode(mcgraph()->machine()->I8x16GtU(), inputs[1],
4999 inputs[0]);
5000 case wasm::kExprI8x16LeU:
5001 return graph()->NewNode(mcgraph()->machine()->I8x16GeU(), inputs[1],
5002 inputs[0]);
5003 case wasm::kExprI8x16GtU:
5004 return graph()->NewNode(mcgraph()->machine()->I8x16GtU(), inputs[0],
5005 inputs[1]);
5006 case wasm::kExprI8x16GeU:
5007 return graph()->NewNode(mcgraph()->machine()->I8x16GeU(), inputs[0],
5008 inputs[1]);
5009 case wasm::kExprI8x16RoundingAverageU:
5010 return graph()->NewNode(mcgraph()->machine()->I8x16RoundingAverageU(),
5011 inputs[0], inputs[1]);
5012 case wasm::kExprI8x16Popcnt:
5013 return graph()->NewNode(mcgraph()->machine()->I8x16Popcnt(), inputs[0]);
5014 case wasm::kExprI8x16Abs:
5015 return graph()->NewNode(mcgraph()->machine()->I8x16Abs(), inputs[0]);
5016 case wasm::kExprI8x16BitMask:
5017 return graph()->NewNode(mcgraph()->machine()->I8x16BitMask(), inputs[0]);
5018 case wasm::kExprS128And:
5019 return graph()->NewNode(mcgraph()->machine()->S128And(), inputs[0],
5020 inputs[1]);
5021 case wasm::kExprS128Or:
5022 return graph()->NewNode(mcgraph()->machine()->S128Or(), inputs[0],
5023 inputs[1]);
5024 case wasm::kExprS128Xor:
5025 return graph()->NewNode(mcgraph()->machine()->S128Xor(), inputs[0],
5026 inputs[1]);
5027 case wasm::kExprS128Not:
5028 return graph()->NewNode(mcgraph()->machine()->S128Not(), inputs[0]);
5029 case wasm::kExprS128Select:
5030 return graph()->NewNode(mcgraph()->machine()->S128Select(), inputs[2],
5031 inputs[0], inputs[1]);
5032 case wasm::kExprS128AndNot:
5033 return graph()->NewNode(mcgraph()->machine()->S128AndNot(), inputs[0],
5034 inputs[1]);
5035 case wasm::kExprI64x2AllTrue:
5036 return graph()->NewNode(mcgraph()->machine()->I64x2AllTrue(), inputs[0]);
5037 case wasm::kExprI32x4AllTrue:
5038 return graph()->NewNode(mcgraph()->machine()->I32x4AllTrue(), inputs[0]);
5039 case wasm::kExprI16x8AllTrue:
5040 return graph()->NewNode(mcgraph()->machine()->I16x8AllTrue(), inputs[0]);
5041 case wasm::kExprV128AnyTrue:
5042 return graph()->NewNode(mcgraph()->machine()->V128AnyTrue(), inputs[0]);
5043 case wasm::kExprI8x16AllTrue:
5044 return graph()->NewNode(mcgraph()->machine()->I8x16AllTrue(), inputs[0]);
5045 case wasm::kExprI8x16Swizzle:
5046 return graph()->NewNode(mcgraph()->machine()->I8x16Swizzle(false),
5047 inputs[0], inputs[1]);
5048 case wasm::kExprI8x16RelaxedSwizzle:
5049 return graph()->NewNode(mcgraph()->machine()->I8x16Swizzle(true),
5050 inputs[0], inputs[1]);
5051 case wasm::kExprI8x16RelaxedLaneSelect:
5052 // Relaxed lane select puts the mask as first input (same as S128Select).
5053 return graph()->NewNode(mcgraph()->machine()->I8x16RelaxedLaneSelect(),
5054 inputs[2], inputs[0], inputs[1]);
5055 case wasm::kExprI16x8RelaxedLaneSelect:
5056 return graph()->NewNode(mcgraph()->machine()->I16x8RelaxedLaneSelect(),
5057 inputs[2], inputs[0], inputs[1]);
5058 case wasm::kExprI32x4RelaxedLaneSelect:
5059 return graph()->NewNode(mcgraph()->machine()->I32x4RelaxedLaneSelect(),
5060 inputs[2], inputs[0], inputs[1]);
5061 case wasm::kExprI64x2RelaxedLaneSelect:
5062 return graph()->NewNode(mcgraph()->machine()->I64x2RelaxedLaneSelect(),
5063 inputs[2], inputs[0], inputs[1]);
5064 case wasm::kExprF32x4RelaxedMin:
5065 return graph()->NewNode(mcgraph()->machine()->F32x4RelaxedMin(),
5066 inputs[0], inputs[1]);
5067 case wasm::kExprF32x4RelaxedMax:
5068 return graph()->NewNode(mcgraph()->machine()->F32x4RelaxedMax(),
5069 inputs[0], inputs[1]);
5070 case wasm::kExprF64x2RelaxedMin:
5071 return graph()->NewNode(mcgraph()->machine()->F64x2RelaxedMin(),
5072 inputs[0], inputs[1]);
5073 case wasm::kExprF64x2RelaxedMax:
5074 return graph()->NewNode(mcgraph()->machine()->F64x2RelaxedMax(),
5075 inputs[0], inputs[1]);
5076 case wasm::kExprI32x4RelaxedTruncF64x2SZero:
5077 return graph()->NewNode(
5078 mcgraph()->machine()->I32x4RelaxedTruncF64x2SZero(), inputs[0]);
5079 case wasm::kExprI32x4RelaxedTruncF64x2UZero:
5080 return graph()->NewNode(
5081 mcgraph()->machine()->I32x4RelaxedTruncF64x2UZero(), inputs[0]);
5082 case wasm::kExprI32x4RelaxedTruncF32x4S:
5083 return graph()->NewNode(mcgraph()->machine()->I32x4RelaxedTruncF32x4S(),
5084 inputs[0]);
5085 case wasm::kExprI32x4RelaxedTruncF32x4U:
5086 return graph()->NewNode(mcgraph()->machine()->I32x4RelaxedTruncF32x4U(),
5087 inputs[0]);
5088 default:
5089 FATAL_UNSUPPORTED_OPCODE(opcode);
5090 }
5091 }
5092
SimdLaneOp(wasm::WasmOpcode opcode,uint8_t lane,Node * const * inputs)5093 Node* WasmGraphBuilder::SimdLaneOp(wasm::WasmOpcode opcode, uint8_t lane,
5094 Node* const* inputs) {
5095 has_simd_ = true;
5096 switch (opcode) {
5097 case wasm::kExprF64x2ExtractLane:
5098 return graph()->NewNode(mcgraph()->machine()->F64x2ExtractLane(lane),
5099 inputs[0]);
5100 case wasm::kExprF64x2ReplaceLane:
5101 return graph()->NewNode(mcgraph()->machine()->F64x2ReplaceLane(lane),
5102 inputs[0], inputs[1]);
5103 case wasm::kExprF32x4ExtractLane:
5104 return graph()->NewNode(mcgraph()->machine()->F32x4ExtractLane(lane),
5105 inputs[0]);
5106 case wasm::kExprF32x4ReplaceLane:
5107 return graph()->NewNode(mcgraph()->machine()->F32x4ReplaceLane(lane),
5108 inputs[0], inputs[1]);
5109 case wasm::kExprI64x2ExtractLane:
5110 return graph()->NewNode(mcgraph()->machine()->I64x2ExtractLane(lane),
5111 inputs[0]);
5112 case wasm::kExprI64x2ReplaceLane:
5113 return graph()->NewNode(mcgraph()->machine()->I64x2ReplaceLane(lane),
5114 inputs[0], inputs[1]);
5115 case wasm::kExprI32x4ExtractLane:
5116 return graph()->NewNode(mcgraph()->machine()->I32x4ExtractLane(lane),
5117 inputs[0]);
5118 case wasm::kExprI32x4ReplaceLane:
5119 return graph()->NewNode(mcgraph()->machine()->I32x4ReplaceLane(lane),
5120 inputs[0], inputs[1]);
5121 case wasm::kExprI16x8ExtractLaneS:
5122 return graph()->NewNode(mcgraph()->machine()->I16x8ExtractLaneS(lane),
5123 inputs[0]);
5124 case wasm::kExprI16x8ExtractLaneU:
5125 return graph()->NewNode(mcgraph()->machine()->I16x8ExtractLaneU(lane),
5126 inputs[0]);
5127 case wasm::kExprI16x8ReplaceLane:
5128 return graph()->NewNode(mcgraph()->machine()->I16x8ReplaceLane(lane),
5129 inputs[0], inputs[1]);
5130 case wasm::kExprI8x16ExtractLaneS:
5131 return graph()->NewNode(mcgraph()->machine()->I8x16ExtractLaneS(lane),
5132 inputs[0]);
5133 case wasm::kExprI8x16ExtractLaneU:
5134 return graph()->NewNode(mcgraph()->machine()->I8x16ExtractLaneU(lane),
5135 inputs[0]);
5136 case wasm::kExprI8x16ReplaceLane:
5137 return graph()->NewNode(mcgraph()->machine()->I8x16ReplaceLane(lane),
5138 inputs[0], inputs[1]);
5139 default:
5140 FATAL_UNSUPPORTED_OPCODE(opcode);
5141 }
5142 }
5143
Simd8x16ShuffleOp(const uint8_t shuffle[16],Node * const * inputs)5144 Node* WasmGraphBuilder::Simd8x16ShuffleOp(const uint8_t shuffle[16],
5145 Node* const* inputs) {
5146 has_simd_ = true;
5147 return graph()->NewNode(mcgraph()->machine()->I8x16Shuffle(shuffle),
5148 inputs[0], inputs[1]);
5149 }
5150
AtomicOp(wasm::WasmOpcode opcode,Node * const * inputs,uint32_t alignment,uint64_t offset,wasm::WasmCodePosition position)5151 Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
5152 uint32_t alignment, uint64_t offset,
5153 wasm::WasmCodePosition position) {
5154 struct AtomicOpInfo {
5155 enum Type : int8_t {
5156 kNoInput = 0,
5157 kOneInput = 1,
5158 kTwoInputs = 2,
5159 kSpecial
5160 };
5161
5162 using OperatorByType =
5163 const Operator* (MachineOperatorBuilder::*)(MachineType);
5164 using OperatorByRep =
5165 const Operator* (MachineOperatorBuilder::*)(MachineRepresentation);
5166 using OperatorByAtomicLoadRep =
5167 const Operator* (MachineOperatorBuilder::*)(AtomicLoadParameters);
5168 using OperatorByAtomicStoreRep =
5169 const Operator* (MachineOperatorBuilder::*)(AtomicStoreParameters);
5170
5171 const Type type;
5172 const MachineType machine_type;
5173 const OperatorByType operator_by_type = nullptr;
5174 const OperatorByRep operator_by_rep = nullptr;
5175 const OperatorByAtomicLoadRep operator_by_atomic_load_params = nullptr;
5176 const OperatorByAtomicStoreRep operator_by_atomic_store_rep = nullptr;
5177 const wasm::ValueType wasm_type;
5178
5179 constexpr AtomicOpInfo(Type t, MachineType m, OperatorByType o)
5180 : type(t), machine_type(m), operator_by_type(o) {}
5181 constexpr AtomicOpInfo(Type t, MachineType m, OperatorByRep o)
5182 : type(t), machine_type(m), operator_by_rep(o) {}
5183 constexpr AtomicOpInfo(Type t, MachineType m, OperatorByAtomicLoadRep o,
5184 wasm::ValueType v)
5185 : type(t),
5186 machine_type(m),
5187 operator_by_atomic_load_params(o),
5188 wasm_type(v) {}
5189 constexpr AtomicOpInfo(Type t, MachineType m, OperatorByAtomicStoreRep o,
5190 wasm::ValueType v)
5191 : type(t),
5192 machine_type(m),
5193 operator_by_atomic_store_rep(o),
5194 wasm_type(v) {}
5195
5196 // Constexpr, hence just a table lookup in most compilers.
5197 static constexpr AtomicOpInfo Get(wasm::WasmOpcode opcode) {
5198 switch (opcode) {
5199 #define CASE(Name, Type, MachType, Op) \
5200 case wasm::kExpr##Name: \
5201 return {Type, MachineType::MachType(), &MachineOperatorBuilder::Op};
5202 #define CASE_LOAD_STORE(Name, Type, MachType, Op, WasmType) \
5203 case wasm::kExpr##Name: \
5204 return {Type, MachineType::MachType(), &MachineOperatorBuilder::Op, \
5205 WasmType};
5206
5207 // Binops.
5208 CASE(I32AtomicAdd, kOneInput, Uint32, Word32AtomicAdd)
5209 CASE(I64AtomicAdd, kOneInput, Uint64, Word64AtomicAdd)
5210 CASE(I32AtomicAdd8U, kOneInput, Uint8, Word32AtomicAdd)
5211 CASE(I32AtomicAdd16U, kOneInput, Uint16, Word32AtomicAdd)
5212 CASE(I64AtomicAdd8U, kOneInput, Uint8, Word64AtomicAdd)
5213 CASE(I64AtomicAdd16U, kOneInput, Uint16, Word64AtomicAdd)
5214 CASE(I64AtomicAdd32U, kOneInput, Uint32, Word64AtomicAdd)
5215 CASE(I32AtomicSub, kOneInput, Uint32, Word32AtomicSub)
5216 CASE(I64AtomicSub, kOneInput, Uint64, Word64AtomicSub)
5217 CASE(I32AtomicSub8U, kOneInput, Uint8, Word32AtomicSub)
5218 CASE(I32AtomicSub16U, kOneInput, Uint16, Word32AtomicSub)
5219 CASE(I64AtomicSub8U, kOneInput, Uint8, Word64AtomicSub)
5220 CASE(I64AtomicSub16U, kOneInput, Uint16, Word64AtomicSub)
5221 CASE(I64AtomicSub32U, kOneInput, Uint32, Word64AtomicSub)
5222 CASE(I32AtomicAnd, kOneInput, Uint32, Word32AtomicAnd)
5223 CASE(I64AtomicAnd, kOneInput, Uint64, Word64AtomicAnd)
5224 CASE(I32AtomicAnd8U, kOneInput, Uint8, Word32AtomicAnd)
5225 CASE(I32AtomicAnd16U, kOneInput, Uint16, Word32AtomicAnd)
5226 CASE(I64AtomicAnd8U, kOneInput, Uint8, Word64AtomicAnd)
5227 CASE(I64AtomicAnd16U, kOneInput, Uint16, Word64AtomicAnd)
5228 CASE(I64AtomicAnd32U, kOneInput, Uint32, Word64AtomicAnd)
5229 CASE(I32AtomicOr, kOneInput, Uint32, Word32AtomicOr)
5230 CASE(I64AtomicOr, kOneInput, Uint64, Word64AtomicOr)
5231 CASE(I32AtomicOr8U, kOneInput, Uint8, Word32AtomicOr)
5232 CASE(I32AtomicOr16U, kOneInput, Uint16, Word32AtomicOr)
5233 CASE(I64AtomicOr8U, kOneInput, Uint8, Word64AtomicOr)
5234 CASE(I64AtomicOr16U, kOneInput, Uint16, Word64AtomicOr)
5235 CASE(I64AtomicOr32U, kOneInput, Uint32, Word64AtomicOr)
5236 CASE(I32AtomicXor, kOneInput, Uint32, Word32AtomicXor)
5237 CASE(I64AtomicXor, kOneInput, Uint64, Word64AtomicXor)
5238 CASE(I32AtomicXor8U, kOneInput, Uint8, Word32AtomicXor)
5239 CASE(I32AtomicXor16U, kOneInput, Uint16, Word32AtomicXor)
5240 CASE(I64AtomicXor8U, kOneInput, Uint8, Word64AtomicXor)
5241 CASE(I64AtomicXor16U, kOneInput, Uint16, Word64AtomicXor)
5242 CASE(I64AtomicXor32U, kOneInput, Uint32, Word64AtomicXor)
5243 CASE(I32AtomicExchange, kOneInput, Uint32, Word32AtomicExchange)
5244 CASE(I64AtomicExchange, kOneInput, Uint64, Word64AtomicExchange)
5245 CASE(I32AtomicExchange8U, kOneInput, Uint8, Word32AtomicExchange)
5246 CASE(I32AtomicExchange16U, kOneInput, Uint16, Word32AtomicExchange)
5247 CASE(I64AtomicExchange8U, kOneInput, Uint8, Word64AtomicExchange)
5248 CASE(I64AtomicExchange16U, kOneInput, Uint16, Word64AtomicExchange)
5249 CASE(I64AtomicExchange32U, kOneInput, Uint32, Word64AtomicExchange)
5250
5251 // Compare-exchange.
5252 CASE(I32AtomicCompareExchange, kTwoInputs, Uint32,
5253 Word32AtomicCompareExchange)
5254 CASE(I64AtomicCompareExchange, kTwoInputs, Uint64,
5255 Word64AtomicCompareExchange)
5256 CASE(I32AtomicCompareExchange8U, kTwoInputs, Uint8,
5257 Word32AtomicCompareExchange)
5258 CASE(I32AtomicCompareExchange16U, kTwoInputs, Uint16,
5259 Word32AtomicCompareExchange)
5260 CASE(I64AtomicCompareExchange8U, kTwoInputs, Uint8,
5261 Word64AtomicCompareExchange)
5262 CASE(I64AtomicCompareExchange16U, kTwoInputs, Uint16,
5263 Word64AtomicCompareExchange)
5264 CASE(I64AtomicCompareExchange32U, kTwoInputs, Uint32,
5265 Word64AtomicCompareExchange)
5266
5267 // Load.
5268 CASE_LOAD_STORE(I32AtomicLoad, kNoInput, Uint32, Word32AtomicLoad,
5269 wasm::kWasmI32)
5270 CASE_LOAD_STORE(I64AtomicLoad, kNoInput, Uint64, Word64AtomicLoad,
5271 wasm::kWasmI64)
5272 CASE_LOAD_STORE(I32AtomicLoad8U, kNoInput, Uint8, Word32AtomicLoad,
5273 wasm::kWasmI32)
5274 CASE_LOAD_STORE(I32AtomicLoad16U, kNoInput, Uint16, Word32AtomicLoad,
5275 wasm::kWasmI32)
5276 CASE_LOAD_STORE(I64AtomicLoad8U, kNoInput, Uint8, Word64AtomicLoad,
5277 wasm::kWasmI64)
5278 CASE_LOAD_STORE(I64AtomicLoad16U, kNoInput, Uint16, Word64AtomicLoad,
5279 wasm::kWasmI64)
5280 CASE_LOAD_STORE(I64AtomicLoad32U, kNoInput, Uint32, Word64AtomicLoad,
5281 wasm::kWasmI64)
5282
5283 // Store.
5284 CASE_LOAD_STORE(I32AtomicStore, kOneInput, Uint32, Word32AtomicStore,
5285 wasm::kWasmI32)
5286 CASE_LOAD_STORE(I64AtomicStore, kOneInput, Uint64, Word64AtomicStore,
5287 wasm::kWasmI64)
5288 CASE_LOAD_STORE(I32AtomicStore8U, kOneInput, Uint8, Word32AtomicStore,
5289 wasm::kWasmI32)
5290 CASE_LOAD_STORE(I32AtomicStore16U, kOneInput, Uint16, Word32AtomicStore,
5291 wasm::kWasmI32)
5292 CASE_LOAD_STORE(I64AtomicStore8U, kOneInput, Uint8, Word64AtomicStore,
5293 wasm::kWasmI64)
5294 CASE_LOAD_STORE(I64AtomicStore16U, kOneInput, Uint16, Word64AtomicStore,
5295 wasm::kWasmI64)
5296 CASE_LOAD_STORE(I64AtomicStore32U, kOneInput, Uint32, Word64AtomicStore,
5297 wasm::kWasmI64)
5298
5299 #undef CASE
5300 #undef CASE_LOAD_STORE
5301
5302 case wasm::kExprAtomicNotify:
5303 return {kSpecial, MachineType::Int32(), OperatorByType{nullptr}};
5304 case wasm::kExprI32AtomicWait:
5305 return {kSpecial, MachineType::Int32(), OperatorByType{nullptr}};
5306 case wasm::kExprI64AtomicWait:
5307 return {kSpecial, MachineType::Int64(), OperatorByType{nullptr}};
5308 default:
5309 UNREACHABLE();
5310 }
5311 }
5312 };
5313
5314 AtomicOpInfo info = AtomicOpInfo::Get(opcode);
5315
5316 Node* index = CheckBoundsAndAlignment(info.machine_type.MemSize(), inputs[0],
5317 offset, position);
5318
5319 // {offset} is validated to be within uintptr_t range in {BoundsCheckMem}.
5320 uintptr_t capped_offset = static_cast<uintptr_t>(offset);
5321 if (info.type != AtomicOpInfo::kSpecial) {
5322 const Operator* op;
5323 if (info.operator_by_type) {
5324 op = (mcgraph()->machine()->*info.operator_by_type)(info.machine_type);
5325 } else if (info.operator_by_rep) {
5326 op = (mcgraph()->machine()->*info.operator_by_rep)(
5327 info.machine_type.representation());
5328 } else if (info.operator_by_atomic_load_params) {
5329 op = (mcgraph()->machine()->*info.operator_by_atomic_load_params)(
5330 AtomicLoadParameters(info.machine_type, AtomicMemoryOrder::kSeqCst));
5331 } else {
5332 op = (mcgraph()->machine()->*info.operator_by_atomic_store_rep)(
5333 AtomicStoreParameters(info.machine_type.representation(),
5334 WriteBarrierKind::kNoWriteBarrier,
5335 AtomicMemoryOrder::kSeqCst));
5336 }
5337
5338 Node* input_nodes[6] = {MemBuffer(capped_offset), index};
5339 int num_actual_inputs = info.type;
5340 std::copy_n(inputs + 1, num_actual_inputs, input_nodes + 2);
5341 input_nodes[num_actual_inputs + 2] = effect();
5342 input_nodes[num_actual_inputs + 3] = control();
5343
5344 #ifdef V8_TARGET_BIG_ENDIAN
5345 // Reverse the value bytes before storing.
5346 if (info.operator_by_atomic_store_rep) {
5347 input_nodes[num_actual_inputs + 1] = BuildChangeEndiannessStore(
5348 input_nodes[num_actual_inputs + 1],
5349 info.machine_type.representation(), info.wasm_type);
5350 }
5351 #endif
5352
5353 Node* result = gasm_->AddNode(
5354 graph()->NewNode(op, num_actual_inputs + 4, input_nodes));
5355
5356 #ifdef V8_TARGET_BIG_ENDIAN
5357 // Reverse the value bytes after load.
5358 if (info.operator_by_atomic_load_params) {
5359 result =
5360 BuildChangeEndiannessLoad(result, info.machine_type, info.wasm_type);
5361 }
5362 #endif
5363
5364 return result;
5365 }
5366
5367 // After we've bounds-checked, compute the effective offset.
5368 Node* effective_offset =
5369 gasm_->IntAdd(gasm_->UintPtrConstant(capped_offset), index);
5370
5371 switch (opcode) {
5372 case wasm::kExprAtomicNotify:
5373 return gasm_->CallRuntimeStub(wasm::WasmCode::kWasmAtomicNotify,
5374 Operator::kNoThrow, effective_offset,
5375 inputs[1]);
5376
5377 case wasm::kExprI32AtomicWait: {
5378 auto* call_descriptor = GetI32AtomicWaitCallDescriptor();
5379
5380 intptr_t target = mcgraph()->machine()->Is64()
5381 ? wasm::WasmCode::kWasmI32AtomicWait64
5382 : wasm::WasmCode::kWasmI32AtomicWait32;
5383 Node* call_target = mcgraph()->RelocatableIntPtrConstant(
5384 target, RelocInfo::WASM_STUB_CALL);
5385
5386 return gasm_->Call(call_descriptor, call_target, effective_offset,
5387 inputs[1], inputs[2]);
5388 }
5389
5390 case wasm::kExprI64AtomicWait: {
5391 auto* call_descriptor = GetI64AtomicWaitCallDescriptor();
5392
5393 intptr_t target = mcgraph()->machine()->Is64()
5394 ? wasm::WasmCode::kWasmI64AtomicWait64
5395 : wasm::WasmCode::kWasmI64AtomicWait32;
5396 Node* call_target = mcgraph()->RelocatableIntPtrConstant(
5397 target, RelocInfo::WASM_STUB_CALL);
5398
5399 return gasm_->Call(call_descriptor, call_target, effective_offset,
5400 inputs[1], inputs[2]);
5401 }
5402
5403 default:
5404 FATAL_UNSUPPORTED_OPCODE(opcode);
5405 }
5406 }
5407
AtomicFence()5408 void WasmGraphBuilder::AtomicFence() {
5409 SetEffect(graph()->NewNode(mcgraph()->machine()->MemBarrier(), effect(),
5410 control()));
5411 }
5412
MemoryInit(uint32_t data_segment_index,Node * dst,Node * src,Node * size,wasm::WasmCodePosition position)5413 void WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst,
5414 Node* src, Node* size,
5415 wasm::WasmCodePosition position) {
5416 // The data segment index must be in bounds since it is required by
5417 // validation.
5418 DCHECK_LT(data_segment_index, env_->module->num_declared_data_segments);
5419
5420 Node* function =
5421 gasm_->ExternalConstant(ExternalReference::wasm_memory_init());
5422
5423 MemTypeToUintPtrOrOOBTrap({&dst}, position);
5424
5425 Node* stack_slot = StoreArgsInStackSlot(
5426 {{MachineType::PointerRepresentation(), GetInstance()},
5427 {MachineType::PointerRepresentation(), dst},
5428 {MachineRepresentation::kWord32, src},
5429 {MachineRepresentation::kWord32,
5430 gasm_->Uint32Constant(data_segment_index)},
5431 {MachineRepresentation::kWord32, size}});
5432
5433 auto sig = FixedSizeSignature<MachineType>::Returns(MachineType::Int32())
5434 .Params(MachineType::Pointer());
5435 Node* call = BuildCCall(&sig, function, stack_slot);
5436 // TODO(manoskouk): Also throw kDataSegmentOutOfBounds.
5437 TrapIfFalse(wasm::kTrapMemOutOfBounds, call, position);
5438 }
5439
DataDrop(uint32_t data_segment_index,wasm::WasmCodePosition position)5440 void WasmGraphBuilder::DataDrop(uint32_t data_segment_index,
5441 wasm::WasmCodePosition position) {
5442 DCHECK_LT(data_segment_index, env_->module->num_declared_data_segments);
5443
5444 Node* seg_size_array =
5445 LOAD_INSTANCE_FIELD(DataSegmentSizes, MachineType::Pointer());
5446 STATIC_ASSERT(wasm::kV8MaxWasmDataSegments <= kMaxUInt32 >> 2);
5447 auto access = ObjectAccess(MachineType::Int32(), kNoWriteBarrier);
5448 gasm_->StoreToObject(access, seg_size_array, data_segment_index << 2,
5449 Int32Constant(0));
5450 }
5451
StoreArgsInStackSlot(std::initializer_list<std::pair<MachineRepresentation,Node * >> args)5452 Node* WasmGraphBuilder::StoreArgsInStackSlot(
5453 std::initializer_list<std::pair<MachineRepresentation, Node*>> args) {
5454 int slot_size = 0;
5455 for (auto arg : args) {
5456 slot_size += ElementSizeInBytes(arg.first);
5457 }
5458 DCHECK_LT(0, slot_size);
5459 Node* stack_slot =
5460 graph()->NewNode(mcgraph()->machine()->StackSlot(slot_size));
5461
5462 int offset = 0;
5463 for (auto arg : args) {
5464 MachineRepresentation type = arg.first;
5465 Node* value = arg.second;
5466 gasm_->StoreUnaligned(type, stack_slot, Int32Constant(offset), value);
5467 offset += ElementSizeInBytes(type);
5468 }
5469 return stack_slot;
5470 }
5471
MemTypeToUintPtrOrOOBTrap(std::initializer_list<Node ** > nodes,wasm::WasmCodePosition position)5472 void WasmGraphBuilder::MemTypeToUintPtrOrOOBTrap(
5473 std::initializer_list<Node**> nodes, wasm::WasmCodePosition position) {
5474 if (!env_->module->is_memory64) {
5475 for (Node** node : nodes) {
5476 *node = BuildChangeUint32ToUintPtr(*node);
5477 }
5478 return;
5479 }
5480 if (kSystemPointerSize == kInt64Size) return; // memory64 on 64-bit
5481 Node* any_high_word = nullptr;
5482 for (Node** node : nodes) {
5483 Node* high_word =
5484 gasm_->TruncateInt64ToInt32(gasm_->Word64Shr(*node, Int32Constant(32)));
5485 any_high_word =
5486 any_high_word ? gasm_->Word32Or(any_high_word, high_word) : high_word;
5487 // Only keep the low word as uintptr_t.
5488 *node = gasm_->TruncateInt64ToInt32(*node);
5489 }
5490 TrapIfTrue(wasm::kTrapMemOutOfBounds, any_high_word, position);
5491 }
5492
MemoryCopy(Node * dst,Node * src,Node * size,wasm::WasmCodePosition position)5493 void WasmGraphBuilder::MemoryCopy(Node* dst, Node* src, Node* size,
5494 wasm::WasmCodePosition position) {
5495 Node* function =
5496 gasm_->ExternalConstant(ExternalReference::wasm_memory_copy());
5497
5498 MemTypeToUintPtrOrOOBTrap({&dst, &src, &size}, position);
5499
5500 Node* stack_slot = StoreArgsInStackSlot(
5501 {{MachineType::PointerRepresentation(), GetInstance()},
5502 {MachineType::PointerRepresentation(), dst},
5503 {MachineType::PointerRepresentation(), src},
5504 {MachineType::PointerRepresentation(), size}});
5505
5506 auto sig = FixedSizeSignature<MachineType>::Returns(MachineType::Int32())
5507 .Params(MachineType::Pointer());
5508 Node* call = BuildCCall(&sig, function, stack_slot);
5509 TrapIfFalse(wasm::kTrapMemOutOfBounds, call, position);
5510 }
5511
MemoryFill(Node * dst,Node * value,Node * size,wasm::WasmCodePosition position)5512 void WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size,
5513 wasm::WasmCodePosition position) {
5514 Node* function =
5515 gasm_->ExternalConstant(ExternalReference::wasm_memory_fill());
5516
5517 MemTypeToUintPtrOrOOBTrap({&dst, &size}, position);
5518
5519 Node* stack_slot = StoreArgsInStackSlot(
5520 {{MachineType::PointerRepresentation(), GetInstance()},
5521 {MachineType::PointerRepresentation(), dst},
5522 {MachineRepresentation::kWord32, value},
5523 {MachineType::PointerRepresentation(), size}});
5524
5525 auto sig = FixedSizeSignature<MachineType>::Returns(MachineType::Int32())
5526 .Params(MachineType::Pointer());
5527 Node* call = BuildCCall(&sig, function, stack_slot);
5528 TrapIfFalse(wasm::kTrapMemOutOfBounds, call, position);
5529 }
5530
TableInit(uint32_t table_index,uint32_t elem_segment_index,Node * dst,Node * src,Node * size,wasm::WasmCodePosition position)5531 void WasmGraphBuilder::TableInit(uint32_t table_index,
5532 uint32_t elem_segment_index, Node* dst,
5533 Node* src, Node* size,
5534 wasm::WasmCodePosition position) {
5535 gasm_->CallRuntimeStub(wasm::WasmCode::kWasmTableInit, Operator::kNoThrow,
5536 dst, src, size, gasm_->NumberConstant(table_index),
5537 gasm_->NumberConstant(elem_segment_index));
5538 }
5539
ElemDrop(uint32_t elem_segment_index,wasm::WasmCodePosition position)5540 void WasmGraphBuilder::ElemDrop(uint32_t elem_segment_index,
5541 wasm::WasmCodePosition position) {
5542 // The elem segment index must be in bounds since it is required by
5543 // validation.
5544 DCHECK_LT(elem_segment_index, env_->module->elem_segments.size());
5545
5546 Node* dropped_elem_segments =
5547 LOAD_INSTANCE_FIELD(DroppedElemSegments, MachineType::Pointer());
5548 auto store_rep =
5549 StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier);
5550 gasm_->Store(store_rep, dropped_elem_segments, elem_segment_index,
5551 Int32Constant(1));
5552 }
5553
TableCopy(uint32_t table_dst_index,uint32_t table_src_index,Node * dst,Node * src,Node * size,wasm::WasmCodePosition position)5554 void WasmGraphBuilder::TableCopy(uint32_t table_dst_index,
5555 uint32_t table_src_index, Node* dst, Node* src,
5556 Node* size, wasm::WasmCodePosition position) {
5557 gasm_->CallRuntimeStub(wasm::WasmCode::kWasmTableCopy, Operator::kNoThrow,
5558 dst, src, size, gasm_->NumberConstant(table_dst_index),
5559 gasm_->NumberConstant(table_src_index));
5560 }
5561
TableGrow(uint32_t table_index,Node * value,Node * delta)5562 Node* WasmGraphBuilder::TableGrow(uint32_t table_index, Node* value,
5563 Node* delta) {
5564 return BuildChangeSmiToInt32(gasm_->CallRuntimeStub(
5565 wasm::WasmCode::kWasmTableGrow, Operator::kNoThrow,
5566 graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)), delta,
5567 value));
5568 }
5569
TableSize(uint32_t table_index)5570 Node* WasmGraphBuilder::TableSize(uint32_t table_index) {
5571 Node* tables = LOAD_INSTANCE_FIELD(Tables, MachineType::TaggedPointer());
5572 Node* table = gasm_->LoadFixedArrayElementAny(tables, table_index);
5573
5574 int length_field_size = WasmTableObject::kCurrentLengthOffsetEnd -
5575 WasmTableObject::kCurrentLengthOffset + 1;
5576 Node* length_smi = gasm_->LoadFromObject(
5577 assert_size(length_field_size, MachineType::TaggedSigned()), table,
5578 wasm::ObjectAccess::ToTagged(WasmTableObject::kCurrentLengthOffset));
5579
5580 return BuildChangeSmiToInt32(length_smi);
5581 }
5582
TableFill(uint32_t table_index,Node * start,Node * value,Node * count)5583 void WasmGraphBuilder::TableFill(uint32_t table_index, Node* start, Node* value,
5584 Node* count) {
5585 gasm_->CallRuntimeStub(
5586 wasm::WasmCode::kWasmTableFill, Operator::kNoThrow,
5587 graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)), start,
5588 count, value);
5589 }
5590
StructNewWithRtt(uint32_t struct_index,const wasm::StructType * type,Node * rtt,base::Vector<Node * > fields)5591 Node* WasmGraphBuilder::StructNewWithRtt(uint32_t struct_index,
5592 const wasm::StructType* type,
5593 Node* rtt,
5594 base::Vector<Node*> fields) {
5595 int size = WasmStruct::Size(type);
5596 Node* s = gasm_->Allocate(size);
5597 gasm_->StoreMap(s, rtt);
5598 gasm_->InitializeImmutableInObject(
5599 ObjectAccess(MachineType::TaggedPointer(), kNoWriteBarrier), s,
5600 wasm::ObjectAccess::ToTagged(JSReceiver::kPropertiesOrHashOffset),
5601 LOAD_ROOT(EmptyFixedArray, empty_fixed_array));
5602 for (uint32_t i = 0; i < type->field_count(); i++) {
5603 gasm_->StoreStructField(s, type, i, fields[i]);
5604 }
5605 // If this assert fails then initialization of padding field might be
5606 // necessary.
5607 static_assert(Heap::kMinObjectSizeInTaggedWords == 2 &&
5608 WasmStruct::kHeaderSize == 2 * kTaggedSize,
5609 "empty struct might require initialization of padding field");
5610 return s;
5611 }
5612
ChooseArrayAllocationBuiltin(wasm::ValueType element_type,Node * initial_value)5613 Builtin ChooseArrayAllocationBuiltin(wasm::ValueType element_type,
5614 Node* initial_value) {
5615 if (initial_value != nullptr) {
5616 // {initial_value} will be used for initialization after allocation.
5617 return Builtin::kWasmAllocateArray_Uninitialized;
5618 }
5619 if (element_type.is_reference()) {
5620 return Builtin::kWasmAllocateArray_InitNull;
5621 }
5622 return Builtin::kWasmAllocateArray_InitZero;
5623 }
5624
ArrayNewWithRtt(uint32_t array_index,const wasm::ArrayType * type,Node * length,Node * initial_value,Node * rtt,wasm::WasmCodePosition position)5625 Node* WasmGraphBuilder::ArrayNewWithRtt(uint32_t array_index,
5626 const wasm::ArrayType* type,
5627 Node* length, Node* initial_value,
5628 Node* rtt,
5629 wasm::WasmCodePosition position) {
5630 TrapIfFalse(wasm::kTrapArrayTooLarge,
5631 gasm_->Uint32LessThanOrEqual(
5632 length, gasm_->Uint32Constant(WasmArray::MaxLength(type))),
5633 position);
5634 wasm::ValueType element_type = type->element_type();
5635 // TODO(7748): Consider using gasm_->Allocate().
5636 Builtin stub = ChooseArrayAllocationBuiltin(element_type, initial_value);
5637 // Do NOT mark this as Operator::kEliminatable, because that would cause the
5638 // Call node to have no control inputs, which means it could get scheduled
5639 // before the check/trap above.
5640 Node* a =
5641 gasm_->CallBuiltin(stub, Operator::kNoDeopt | Operator::kNoThrow, rtt,
5642 length, Int32Constant(element_type.value_kind_size()));
5643 if (initial_value != nullptr) {
5644 // TODO(manoskouk): If the loop is ever removed here, we have to update
5645 // ArrayNewWithRtt() in graph-builder-interface.cc to not mark the current
5646 // loop as non-innermost.
5647 auto loop = gasm_->MakeLoopLabel(MachineRepresentation::kWord32);
5648 auto done = gasm_->MakeLabel();
5649 Node* start_offset =
5650 Int32Constant(wasm::ObjectAccess::ToTagged(WasmArray::kHeaderSize));
5651 Node* element_size = Int32Constant(element_type.value_kind_size());
5652 Node* end_offset =
5653 gasm_->Int32Add(start_offset, gasm_->Int32Mul(element_size, length));
5654 gasm_->Goto(&loop, start_offset);
5655 gasm_->Bind(&loop);
5656 {
5657 Node* offset = loop.PhiAt(0);
5658 Node* check = gasm_->Uint32LessThan(offset, end_offset);
5659 gasm_->GotoIfNot(check, &done);
5660 gasm_->StoreToObject(ObjectAccessForGCStores(type->element_type()), a,
5661 offset, initial_value);
5662 offset = gasm_->Int32Add(offset, element_size);
5663 gasm_->Goto(&loop, offset);
5664 }
5665 gasm_->Bind(&done);
5666 }
5667 return a;
5668 }
5669
ArrayInit(const wasm::ArrayType * type,Node * rtt,base::Vector<Node * > elements)5670 Node* WasmGraphBuilder::ArrayInit(const wasm::ArrayType* type, Node* rtt,
5671 base::Vector<Node*> elements) {
5672 wasm::ValueType element_type = type->element_type();
5673 // TODO(7748): Consider using gasm_->Allocate().
5674 Node* array =
5675 gasm_->CallBuiltin(Builtin::kWasmAllocateArray_Uninitialized,
5676 Operator::kNoDeopt | Operator::kNoThrow, rtt,
5677 Int32Constant(static_cast<int32_t>(elements.size())),
5678 Int32Constant(element_type.value_kind_size()));
5679 for (int i = 0; i < static_cast<int>(elements.size()); i++) {
5680 Node* offset =
5681 gasm_->WasmArrayElementOffset(Int32Constant(i), element_type);
5682 if (type->mutability()) {
5683 gasm_->StoreToObject(ObjectAccessForGCStores(element_type), array, offset,
5684 elements[i]);
5685 } else {
5686 gasm_->InitializeImmutableInObject(ObjectAccessForGCStores(element_type),
5687 array, offset, elements[i]);
5688 }
5689 }
5690 return array;
5691 }
5692
ArrayInitFromData(const wasm::ArrayType * type,uint32_t data_segment,Node * offset,Node * length,Node * rtt,wasm::WasmCodePosition position)5693 Node* WasmGraphBuilder::ArrayInitFromData(const wasm::ArrayType* type,
5694 uint32_t data_segment, Node* offset,
5695 Node* length, Node* rtt,
5696 wasm::WasmCodePosition position) {
5697 Node* array = gasm_->CallBuiltin(
5698 Builtin::kWasmArrayInitFromData, Operator::kNoDeopt | Operator::kNoThrow,
5699 gasm_->Uint32Constant(data_segment), offset, length, rtt);
5700 TrapIfTrue(wasm::kTrapArrayTooLarge,
5701 gasm_->TaggedEqual(
5702 array, gasm_->NumberConstant(
5703 wasm::kArrayInitFromDataArrayTooLargeErrorCode)),
5704 position);
5705 TrapIfTrue(
5706 wasm::kTrapDataSegmentOutOfBounds,
5707 gasm_->TaggedEqual(
5708 array, gasm_->NumberConstant(
5709 wasm::kArrayInitFromDataSegmentOutOfBoundsErrorCode)),
5710 position);
5711 return array;
5712 }
5713
RttCanon(uint32_t type_index)5714 Node* WasmGraphBuilder::RttCanon(uint32_t type_index) {
5715 Node* maps_list =
5716 LOAD_INSTANCE_FIELD(ManagedObjectMaps, MachineType::TaggedPointer());
5717 return gasm_->LoadImmutable(
5718 MachineType::TaggedPointer(), maps_list,
5719 wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(type_index));
5720 }
5721
TestCallbacks(GraphAssemblerLabel<1> * label)5722 WasmGraphBuilder::Callbacks WasmGraphBuilder::TestCallbacks(
5723 GraphAssemblerLabel<1>* label) {
5724 return {// succeed_if
5725 [=](Node* condition, BranchHint hint) -> void {
5726 gasm_->GotoIf(condition, label, hint, Int32Constant(1));
5727 },
5728 // fail_if
5729 [=](Node* condition, BranchHint hint) -> void {
5730 gasm_->GotoIf(condition, label, hint, Int32Constant(0));
5731 },
5732 // fail_if_not
5733 [=](Node* condition, BranchHint hint) -> void {
5734 gasm_->GotoIfNot(condition, label, hint, Int32Constant(0));
5735 }};
5736 }
5737
CastCallbacks(GraphAssemblerLabel<0> * label,wasm::WasmCodePosition position)5738 WasmGraphBuilder::Callbacks WasmGraphBuilder::CastCallbacks(
5739 GraphAssemblerLabel<0>* label, wasm::WasmCodePosition position) {
5740 return {// succeed_if
5741 [=](Node* condition, BranchHint hint) -> void {
5742 gasm_->GotoIf(condition, label, hint);
5743 },
5744 // fail_if
5745 [=](Node* condition, BranchHint hint) -> void {
5746 TrapIfTrue(wasm::kTrapIllegalCast, condition, position);
5747 },
5748 // fail_if_not
5749 [=](Node* condition, BranchHint hint) -> void {
5750 TrapIfFalse(wasm::kTrapIllegalCast, condition, position);
5751 }};
5752 }
5753
BranchCallbacks(SmallNodeVector & no_match_controls,SmallNodeVector & no_match_effects,SmallNodeVector & match_controls,SmallNodeVector & match_effects)5754 WasmGraphBuilder::Callbacks WasmGraphBuilder::BranchCallbacks(
5755 SmallNodeVector& no_match_controls, SmallNodeVector& no_match_effects,
5756 SmallNodeVector& match_controls, SmallNodeVector& match_effects) {
5757 return {
5758 // succeed_if
5759 [&](Node* condition, BranchHint hint) -> void {
5760 Node* branch = graph()->NewNode(mcgraph()->common()->Branch(hint),
5761 condition, control());
5762 match_controls.emplace_back(
5763 graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
5764 match_effects.emplace_back(effect());
5765 SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
5766 },
5767 // fail_if
5768 [&](Node* condition, BranchHint hint) -> void {
5769 Node* branch = graph()->NewNode(mcgraph()->common()->Branch(hint),
5770 condition, control());
5771 no_match_controls.emplace_back(
5772 graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
5773 no_match_effects.emplace_back(effect());
5774 SetControl(graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
5775 },
5776 // fail_if_not
5777 [&](Node* condition, BranchHint hint) -> void {
5778 Node* branch = graph()->NewNode(mcgraph()->common()->Branch(hint),
5779 condition, control());
5780 no_match_controls.emplace_back(
5781 graph()->NewNode(mcgraph()->common()->IfFalse(), branch));
5782 no_match_effects.emplace_back(effect());
5783 SetControl(graph()->NewNode(mcgraph()->common()->IfTrue(), branch));
5784 }};
5785 }
5786
TypeCheck(Node * object,Node * rtt,WasmGraphBuilder::ObjectReferenceKnowledge config,bool null_succeeds,Callbacks callbacks)5787 void WasmGraphBuilder::TypeCheck(
5788 Node* object, Node* rtt, WasmGraphBuilder::ObjectReferenceKnowledge config,
5789 bool null_succeeds, Callbacks callbacks) {
5790 if (config.object_can_be_null) {
5791 (null_succeeds ? callbacks.succeed_if : callbacks.fail_if)(
5792 IsNull(object), BranchHint::kFalse);
5793 }
5794
5795 Node* map = gasm_->LoadMap(object);
5796
5797 // First, check if types happen to be equal. This has been shown to give large
5798 // speedups.
5799 callbacks.succeed_if(gasm_->TaggedEqual(map, rtt), BranchHint::kTrue);
5800
5801 Node* type_info = gasm_->LoadWasmTypeInfo(map);
5802 Node* supertypes = gasm_->LoadSupertypes(type_info);
5803 Node* rtt_depth = gasm_->UintPtrConstant(config.rtt_depth);
5804
5805 // If the depth of the rtt is known to be less that the minimum supertype
5806 // array length, we can access the supertype without bounds-checking the
5807 // supertype array.
5808 if (config.rtt_depth >= wasm::kMinimumSupertypeArraySize) {
5809 Node* supertypes_length =
5810 BuildChangeSmiToIntPtr(gasm_->LoadFixedArrayLengthAsSmi(supertypes));
5811 callbacks.fail_if_not(gasm_->UintLessThan(rtt_depth, supertypes_length),
5812 BranchHint::kTrue);
5813 }
5814 Node* maybe_match = gasm_->LoadImmutableFixedArrayElement(
5815 supertypes, rtt_depth, MachineType::TaggedPointer());
5816
5817 callbacks.fail_if_not(gasm_->TaggedEqual(maybe_match, rtt),
5818 BranchHint::kTrue);
5819 }
5820
DataCheck(Node * object,bool object_can_be_null,Callbacks callbacks)5821 void WasmGraphBuilder::DataCheck(Node* object, bool object_can_be_null,
5822 Callbacks callbacks) {
5823 if (object_can_be_null) {
5824 callbacks.fail_if(IsNull(object), BranchHint::kFalse);
5825 }
5826 callbacks.fail_if(gasm_->IsI31(object), BranchHint::kFalse);
5827 Node* map = gasm_->LoadMap(object);
5828 callbacks.fail_if_not(gasm_->IsDataRefMap(map), BranchHint::kTrue);
5829 }
5830
ManagedObjectInstanceCheck(Node * object,bool object_can_be_null,InstanceType instance_type,Callbacks callbacks)5831 void WasmGraphBuilder::ManagedObjectInstanceCheck(Node* object,
5832 bool object_can_be_null,
5833 InstanceType instance_type,
5834 Callbacks callbacks) {
5835 if (object_can_be_null) {
5836 callbacks.fail_if(IsNull(object), BranchHint::kFalse);
5837 }
5838 callbacks.fail_if(gasm_->IsI31(object), BranchHint::kFalse);
5839 callbacks.fail_if_not(gasm_->HasInstanceType(object, instance_type),
5840 BranchHint::kTrue);
5841 }
5842
BrOnCastAbs(Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect,std::function<void (Callbacks)> type_checker)5843 void WasmGraphBuilder::BrOnCastAbs(
5844 Node** match_control, Node** match_effect, Node** no_match_control,
5845 Node** no_match_effect, std::function<void(Callbacks)> type_checker) {
5846 SmallNodeVector no_match_controls, no_match_effects, match_controls,
5847 match_effects;
5848
5849 type_checker(BranchCallbacks(no_match_controls, no_match_effects,
5850 match_controls, match_effects));
5851
5852 match_controls.emplace_back(control());
5853 match_effects.emplace_back(effect());
5854
5855 // Wire up the control/effect nodes.
5856 unsigned count = static_cast<unsigned>(match_controls.size());
5857 DCHECK_EQ(match_controls.size(), match_effects.size());
5858 *match_control = Merge(count, match_controls.data());
5859 // EffectPhis need their control dependency as an additional input.
5860 match_effects.emplace_back(*match_control);
5861 *match_effect = EffectPhi(count, match_effects.data());
5862 DCHECK_EQ(no_match_controls.size(), no_match_effects.size());
5863 // Range is 2..4, so casting to unsigned is safe.
5864 count = static_cast<unsigned>(no_match_controls.size());
5865 *no_match_control = Merge(count, no_match_controls.data());
5866 // EffectPhis need their control dependency as an additional input.
5867 no_match_effects.emplace_back(*no_match_control);
5868 *no_match_effect = EffectPhi(count, no_match_effects.data());
5869 }
5870
RefTest(Node * object,Node * rtt,ObjectReferenceKnowledge config)5871 Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
5872 ObjectReferenceKnowledge config) {
5873 auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
5874 TypeCheck(object, rtt, config, false, TestCallbacks(&done));
5875 gasm_->Goto(&done, Int32Constant(1));
5876 gasm_->Bind(&done);
5877 return done.PhiAt(0);
5878 }
5879
RefCast(Node * object,Node * rtt,ObjectReferenceKnowledge config,wasm::WasmCodePosition position)5880 Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
5881 ObjectReferenceKnowledge config,
5882 wasm::WasmCodePosition position) {
5883 if (!FLAG_experimental_wasm_assume_ref_cast_succeeds) {
5884 auto done = gasm_->MakeLabel();
5885 TypeCheck(object, rtt, config, true, CastCallbacks(&done, position));
5886 gasm_->Goto(&done);
5887 gasm_->Bind(&done);
5888 }
5889 return object;
5890 }
5891
BrOnCast(Node * object,Node * rtt,ObjectReferenceKnowledge config,Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect)5892 void WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
5893 ObjectReferenceKnowledge config,
5894 Node** match_control, Node** match_effect,
5895 Node** no_match_control,
5896 Node** no_match_effect) {
5897 BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
5898 [=](Callbacks callbacks) -> void {
5899 return TypeCheck(object, rtt, config, false, callbacks);
5900 });
5901 }
5902
RefIsData(Node * object,bool object_can_be_null)5903 Node* WasmGraphBuilder::RefIsData(Node* object, bool object_can_be_null) {
5904 auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
5905 DataCheck(object, object_can_be_null, TestCallbacks(&done));
5906 gasm_->Goto(&done, Int32Constant(1));
5907 gasm_->Bind(&done);
5908 return done.PhiAt(0);
5909 }
5910
RefAsData(Node * object,bool object_can_be_null,wasm::WasmCodePosition position)5911 Node* WasmGraphBuilder::RefAsData(Node* object, bool object_can_be_null,
5912 wasm::WasmCodePosition position) {
5913 auto done = gasm_->MakeLabel();
5914 DataCheck(object, object_can_be_null, CastCallbacks(&done, position));
5915 gasm_->Goto(&done);
5916 gasm_->Bind(&done);
5917 return object;
5918 }
5919
BrOnData(Node * object,Node *,ObjectReferenceKnowledge config,Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect)5920 void WasmGraphBuilder::BrOnData(Node* object, Node* /*rtt*/,
5921 ObjectReferenceKnowledge config,
5922 Node** match_control, Node** match_effect,
5923 Node** no_match_control,
5924 Node** no_match_effect) {
5925 BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
5926 [=](Callbacks callbacks) -> void {
5927 return DataCheck(object, config.object_can_be_null, callbacks);
5928 });
5929 }
5930
RefIsFunc(Node * object,bool object_can_be_null)5931 Node* WasmGraphBuilder::RefIsFunc(Node* object, bool object_can_be_null) {
5932 auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
5933 ManagedObjectInstanceCheck(object, object_can_be_null,
5934 WASM_INTERNAL_FUNCTION_TYPE, TestCallbacks(&done));
5935 gasm_->Goto(&done, Int32Constant(1));
5936 gasm_->Bind(&done);
5937 return done.PhiAt(0);
5938 }
5939
RefAsFunc(Node * object,bool object_can_be_null,wasm::WasmCodePosition position)5940 Node* WasmGraphBuilder::RefAsFunc(Node* object, bool object_can_be_null,
5941 wasm::WasmCodePosition position) {
5942 auto done = gasm_->MakeLabel();
5943 ManagedObjectInstanceCheck(object, object_can_be_null,
5944 WASM_INTERNAL_FUNCTION_TYPE,
5945 CastCallbacks(&done, position));
5946 gasm_->Goto(&done);
5947 gasm_->Bind(&done);
5948 return object;
5949 }
5950
BrOnFunc(Node * object,Node *,ObjectReferenceKnowledge config,Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect)5951 void WasmGraphBuilder::BrOnFunc(Node* object, Node* /*rtt*/,
5952 ObjectReferenceKnowledge config,
5953 Node** match_control, Node** match_effect,
5954 Node** no_match_control,
5955 Node** no_match_effect) {
5956 BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
5957 [=](Callbacks callbacks) -> void {
5958 return ManagedObjectInstanceCheck(
5959 object, config.object_can_be_null,
5960 WASM_INTERNAL_FUNCTION_TYPE, callbacks);
5961 });
5962 }
5963
RefIsArray(Node * object,bool object_can_be_null)5964 Node* WasmGraphBuilder::RefIsArray(Node* object, bool object_can_be_null) {
5965 auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
5966 ManagedObjectInstanceCheck(object, object_can_be_null, WASM_ARRAY_TYPE,
5967 TestCallbacks(&done));
5968 gasm_->Goto(&done, Int32Constant(1));
5969 gasm_->Bind(&done);
5970 return done.PhiAt(0);
5971 }
5972
RefAsArray(Node * object,bool object_can_be_null,wasm::WasmCodePosition position)5973 Node* WasmGraphBuilder::RefAsArray(Node* object, bool object_can_be_null,
5974 wasm::WasmCodePosition position) {
5975 auto done = gasm_->MakeLabel();
5976 ManagedObjectInstanceCheck(object, object_can_be_null, WASM_ARRAY_TYPE,
5977 CastCallbacks(&done, position));
5978 gasm_->Goto(&done);
5979 gasm_->Bind(&done);
5980 return object;
5981 }
5982
BrOnArray(Node * object,Node *,ObjectReferenceKnowledge config,Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect)5983 void WasmGraphBuilder::BrOnArray(Node* object, Node* /*rtt*/,
5984 ObjectReferenceKnowledge config,
5985 Node** match_control, Node** match_effect,
5986 Node** no_match_control,
5987 Node** no_match_effect) {
5988 BrOnCastAbs(match_control, match_effect, no_match_control, no_match_effect,
5989 [=](Callbacks callbacks) -> void {
5990 return ManagedObjectInstanceCheck(object,
5991 config.object_can_be_null,
5992 WASM_ARRAY_TYPE, callbacks);
5993 });
5994 }
5995
RefIsI31(Node * object)5996 Node* WasmGraphBuilder::RefIsI31(Node* object) { return gasm_->IsI31(object); }
5997
RefAsI31(Node * object,wasm::WasmCodePosition position)5998 Node* WasmGraphBuilder::RefAsI31(Node* object,
5999 wasm::WasmCodePosition position) {
6000 TrapIfFalse(wasm::kTrapIllegalCast, gasm_->IsI31(object), position);
6001 return object;
6002 }
6003
BrOnI31(Node * object,Node *,ObjectReferenceKnowledge,Node ** match_control,Node ** match_effect,Node ** no_match_control,Node ** no_match_effect)6004 void WasmGraphBuilder::BrOnI31(Node* object, Node* /* rtt */,
6005 ObjectReferenceKnowledge /* config */,
6006 Node** match_control, Node** match_effect,
6007 Node** no_match_control,
6008 Node** no_match_effect) {
6009 gasm_->Branch(gasm_->IsI31(object), match_control, no_match_control,
6010 BranchHint::kTrue);
6011
6012 SetControl(*no_match_control);
6013 *match_effect = effect();
6014 *no_match_effect = effect();
6015 }
6016
StructGet(Node * struct_object,const wasm::StructType * struct_type,uint32_t field_index,CheckForNull null_check,bool is_signed,wasm::WasmCodePosition position)6017 Node* WasmGraphBuilder::StructGet(Node* struct_object,
6018 const wasm::StructType* struct_type,
6019 uint32_t field_index, CheckForNull null_check,
6020 bool is_signed,
6021 wasm::WasmCodePosition position) {
6022 if (null_check == kWithNullCheck) {
6023 TrapIfTrue(wasm::kTrapNullDereference, IsNull(struct_object), position);
6024 }
6025 // It is not enough to invoke ValueType::machine_type(), because the
6026 // signedness has to be determined by {is_signed}.
6027 MachineType machine_type = MachineType::TypeForRepresentation(
6028 struct_type->field(field_index).machine_representation(), is_signed);
6029 Node* offset = gasm_->FieldOffset(struct_type, field_index);
6030 return struct_type->mutability(field_index)
6031 ? gasm_->LoadFromObject(machine_type, struct_object, offset)
6032 : gasm_->LoadImmutableFromObject(machine_type, struct_object,
6033 offset);
6034 }
6035
StructSet(Node * struct_object,const wasm::StructType * struct_type,uint32_t field_index,Node * field_value,CheckForNull null_check,wasm::WasmCodePosition position)6036 void WasmGraphBuilder::StructSet(Node* struct_object,
6037 const wasm::StructType* struct_type,
6038 uint32_t field_index, Node* field_value,
6039 CheckForNull null_check,
6040 wasm::WasmCodePosition position) {
6041 if (null_check == kWithNullCheck) {
6042 TrapIfTrue(wasm::kTrapNullDereference, IsNull(struct_object), position);
6043 }
6044 gasm_->StoreStructField(struct_object, struct_type, field_index, field_value);
6045 }
6046
BoundsCheckArray(Node * array,Node * index,wasm::WasmCodePosition position)6047 void WasmGraphBuilder::BoundsCheckArray(Node* array, Node* index,
6048 wasm::WasmCodePosition position) {
6049 if (V8_UNLIKELY(FLAG_experimental_wasm_skip_bounds_checks)) return;
6050 Node* length = gasm_->LoadWasmArrayLength(array);
6051 TrapIfFalse(wasm::kTrapArrayOutOfBounds, gasm_->Uint32LessThan(index, length),
6052 position);
6053 }
6054
BoundsCheckArrayCopy(Node * array,Node * index,Node * length,wasm::WasmCodePosition position)6055 void WasmGraphBuilder::BoundsCheckArrayCopy(Node* array, Node* index,
6056 Node* length,
6057 wasm::WasmCodePosition position) {
6058 if (V8_UNLIKELY(FLAG_experimental_wasm_skip_bounds_checks)) return;
6059 Node* array_length = gasm_->LoadWasmArrayLength(array);
6060 Node* range_end = gasm_->Int32Add(index, length);
6061 Node* range_valid = gasm_->Word32And(
6062 gasm_->Uint32LessThanOrEqual(range_end, array_length),
6063 gasm_->Uint32LessThanOrEqual(index, range_end)); // No overflow
6064 TrapIfFalse(wasm::kTrapArrayOutOfBounds, range_valid, position);
6065 }
6066
ArrayGet(Node * array_object,const wasm::ArrayType * type,Node * index,CheckForNull null_check,bool is_signed,wasm::WasmCodePosition position)6067 Node* WasmGraphBuilder::ArrayGet(Node* array_object,
6068 const wasm::ArrayType* type, Node* index,
6069 CheckForNull null_check, bool is_signed,
6070 wasm::WasmCodePosition position) {
6071 if (null_check == kWithNullCheck) {
6072 TrapIfTrue(wasm::kTrapNullDereference, IsNull(array_object), position);
6073 }
6074 BoundsCheckArray(array_object, index, position);
6075 MachineType machine_type = MachineType::TypeForRepresentation(
6076 type->element_type().machine_representation(), is_signed);
6077 Node* offset = gasm_->WasmArrayElementOffset(index, type->element_type());
6078 return type->mutability()
6079 ? gasm_->LoadFromObject(machine_type, array_object, offset)
6080 : gasm_->LoadImmutableFromObject(machine_type, array_object,
6081 offset);
6082 }
6083
ArraySet(Node * array_object,const wasm::ArrayType * type,Node * index,Node * value,CheckForNull null_check,wasm::WasmCodePosition position)6084 void WasmGraphBuilder::ArraySet(Node* array_object, const wasm::ArrayType* type,
6085 Node* index, Node* value,
6086 CheckForNull null_check,
6087 wasm::WasmCodePosition position) {
6088 if (null_check == kWithNullCheck) {
6089 TrapIfTrue(wasm::kTrapNullDereference, IsNull(array_object), position);
6090 }
6091 BoundsCheckArray(array_object, index, position);
6092 Node* offset = gasm_->WasmArrayElementOffset(index, type->element_type());
6093 gasm_->StoreToObject(ObjectAccessForGCStores(type->element_type()),
6094 array_object, offset, value);
6095 }
6096
ArrayLen(Node * array_object,CheckForNull null_check,wasm::WasmCodePosition position)6097 Node* WasmGraphBuilder::ArrayLen(Node* array_object, CheckForNull null_check,
6098 wasm::WasmCodePosition position) {
6099 if (null_check == kWithNullCheck) {
6100 TrapIfTrue(wasm::kTrapNullDereference, IsNull(array_object), position);
6101 }
6102 return gasm_->LoadWasmArrayLength(array_object);
6103 }
6104
6105 // TODO(7748): Add an option to copy in a loop for small array sizes. To find
6106 // the length limit, run test/mjsunit/wasm/array-copy-benchmark.js.
ArrayCopy(Node * dst_array,Node * dst_index,CheckForNull dst_null_check,Node * src_array,Node * src_index,CheckForNull src_null_check,Node * length,wasm::WasmCodePosition position)6107 void WasmGraphBuilder::ArrayCopy(Node* dst_array, Node* dst_index,
6108 CheckForNull dst_null_check, Node* src_array,
6109 Node* src_index, CheckForNull src_null_check,
6110 Node* length,
6111 wasm::WasmCodePosition position) {
6112 if (dst_null_check == kWithNullCheck) {
6113 TrapIfTrue(wasm::kTrapNullDereference, IsNull(dst_array), position);
6114 }
6115 if (src_null_check == kWithNullCheck) {
6116 TrapIfTrue(wasm::kTrapNullDereference, IsNull(src_array), position);
6117 }
6118 BoundsCheckArrayCopy(dst_array, dst_index, length, position);
6119 BoundsCheckArrayCopy(src_array, src_index, length, position);
6120
6121 auto skip = gasm_->MakeLabel();
6122
6123 gasm_->GotoIf(gasm_->Word32Equal(length, Int32Constant(0)), &skip,
6124 BranchHint::kFalse);
6125
6126 Node* function =
6127 gasm_->ExternalConstant(ExternalReference::wasm_array_copy());
6128 MachineType arg_types[]{
6129 MachineType::TaggedPointer(), MachineType::TaggedPointer(),
6130 MachineType::Uint32(), MachineType::TaggedPointer(),
6131 MachineType::Uint32(), MachineType::Uint32()};
6132 MachineSignature sig(0, 6, arg_types);
6133 BuildCCall(&sig, function, GetInstance(), dst_array, dst_index, src_array,
6134 src_index, length);
6135 gasm_->Goto(&skip);
6136 gasm_->Bind(&skip);
6137 }
6138
6139 // 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
6140 constexpr int kI31To32BitSmiShift = 33;
6141
I31New(Node * input)6142 Node* WasmGraphBuilder::I31New(Node* input) {
6143 if (SmiValuesAre31Bits()) {
6144 return gasm_->Word32Shl(input, BuildSmiShiftBitsConstant32());
6145 }
6146 DCHECK(SmiValuesAre32Bits());
6147 input = BuildChangeInt32ToIntPtr(input);
6148 return gasm_->WordShl(input, gasm_->IntPtrConstant(kI31To32BitSmiShift));
6149 }
6150
I31GetS(Node * input)6151 Node* WasmGraphBuilder::I31GetS(Node* input) {
6152 if (SmiValuesAre31Bits()) {
6153 input = BuildTruncateIntPtrToInt32(input);
6154 return gasm_->Word32SarShiftOutZeros(input, BuildSmiShiftBitsConstant32());
6155 }
6156 DCHECK(SmiValuesAre32Bits());
6157 return BuildTruncateIntPtrToInt32(
6158 gasm_->WordSar(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)));
6159 }
6160
I31GetU(Node * input)6161 Node* WasmGraphBuilder::I31GetU(Node* input) {
6162 if (SmiValuesAre31Bits()) {
6163 input = BuildTruncateIntPtrToInt32(input);
6164 return gasm_->Word32Shr(input, BuildSmiShiftBitsConstant32());
6165 }
6166 DCHECK(SmiValuesAre32Bits());
6167 return BuildTruncateIntPtrToInt32(
6168 gasm_->WordShr(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)));
6169 }
6170
6171 class WasmDecorator final : public GraphDecorator {
6172 public:
WasmDecorator(NodeOriginTable * origins,wasm::Decoder * decoder)6173 explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
6174 : origins_(origins), decoder_(decoder) {}
6175
Decorate(Node * node)6176 void Decorate(Node* node) final {
6177 origins_->SetNodeOrigin(
6178 node, NodeOrigin("wasm graph creation", "n/a",
6179 NodeOrigin::kWasmBytecode, decoder_->position()));
6180 }
6181
6182 private:
6183 compiler::NodeOriginTable* origins_;
6184 wasm::Decoder* decoder_;
6185 };
6186
AddBytecodePositionDecorator(NodeOriginTable * node_origins,wasm::Decoder * decoder)6187 void WasmGraphBuilder::AddBytecodePositionDecorator(
6188 NodeOriginTable* node_origins, wasm::Decoder* decoder) {
6189 DCHECK_NULL(decorator_);
6190 decorator_ = graph()->zone()->New<WasmDecorator>(node_origins, decoder);
6191 graph()->AddDecorator(decorator_);
6192 }
6193
RemoveBytecodePositionDecorator()6194 void WasmGraphBuilder::RemoveBytecodePositionDecorator() {
6195 DCHECK_NOT_NULL(decorator_);
6196 graph()->RemoveDecorator(decorator_);
6197 decorator_ = nullptr;
6198 }
6199
6200 namespace {
6201
6202 // A non-null {isolate} signifies that the generated code is treated as being in
6203 // a JS frame for functions like BuildIsolateRoot().
6204 class WasmWrapperGraphBuilder : public WasmGraphBuilder {
6205 public:
WasmWrapperGraphBuilder(Zone * zone,MachineGraph * mcgraph,const wasm::FunctionSig * sig,const wasm::WasmModule * module,Parameter0Mode parameter_mode,Isolate * isolate,compiler::SourcePositionTable * spt,StubCallMode stub_mode,wasm::WasmFeatures features)6206 WasmWrapperGraphBuilder(Zone* zone, MachineGraph* mcgraph,
6207 const wasm::FunctionSig* sig,
6208 const wasm::WasmModule* module,
6209 Parameter0Mode parameter_mode, Isolate* isolate,
6210 compiler::SourcePositionTable* spt,
6211 StubCallMode stub_mode, wasm::WasmFeatures features)
6212 : WasmGraphBuilder(nullptr, zone, mcgraph, sig, spt, parameter_mode,
6213 isolate),
6214 module_(module),
6215 stub_mode_(stub_mode),
6216 enabled_features_(features) {}
6217
GetI64ToBigIntCallDescriptor()6218 CallDescriptor* GetI64ToBigIntCallDescriptor() {
6219 if (i64_to_bigint_descriptor_) return i64_to_bigint_descriptor_;
6220
6221 i64_to_bigint_descriptor_ =
6222 GetBuiltinCallDescriptor(Builtin::kI64ToBigInt, zone_, stub_mode_);
6223
6224 AddInt64LoweringReplacement(
6225 i64_to_bigint_descriptor_,
6226 GetBuiltinCallDescriptor(Builtin::kI32PairToBigInt, zone_, stub_mode_));
6227 return i64_to_bigint_descriptor_;
6228 }
6229
GetBigIntToI64CallDescriptor(bool needs_frame_state)6230 CallDescriptor* GetBigIntToI64CallDescriptor(bool needs_frame_state) {
6231 if (bigint_to_i64_descriptor_) return bigint_to_i64_descriptor_;
6232
6233 bigint_to_i64_descriptor_ = GetBuiltinCallDescriptor(
6234 Builtin::kBigIntToI64, zone_, stub_mode_, needs_frame_state);
6235
6236 AddInt64LoweringReplacement(
6237 bigint_to_i64_descriptor_,
6238 GetBuiltinCallDescriptor(Builtin::kBigIntToI32Pair, zone_, stub_mode_));
6239 return bigint_to_i64_descriptor_;
6240 }
6241
GetTargetForBuiltinCall(wasm::WasmCode::RuntimeStubId wasm_stub,Builtin builtin)6242 Node* GetTargetForBuiltinCall(wasm::WasmCode::RuntimeStubId wasm_stub,
6243 Builtin builtin) {
6244 return (stub_mode_ == StubCallMode::kCallWasmRuntimeStub)
6245 ? mcgraph()->RelocatableIntPtrConstant(wasm_stub,
6246 RelocInfo::WASM_STUB_CALL)
6247 : gasm_->GetBuiltinPointerTarget(builtin);
6248 }
6249
BuildChangeInt32ToNumber(Node * value)6250 Node* BuildChangeInt32ToNumber(Node* value) {
6251 // We expect most integers at runtime to be Smis, so it is important for
6252 // wrapper performance that Smi conversion be inlined.
6253 if (SmiValuesAre32Bits()) {
6254 return BuildChangeInt32ToSmi(value);
6255 }
6256 DCHECK(SmiValuesAre31Bits());
6257
6258 auto builtin = gasm_->MakeDeferredLabel();
6259 auto done = gasm_->MakeLabel(MachineRepresentation::kTagged);
6260
6261 // Double value to test if value can be a Smi, and if so, to convert it.
6262 Node* add = gasm_->Int32AddWithOverflow(value, value);
6263 Node* ovf = gasm_->Projection(1, add);
6264 gasm_->GotoIf(ovf, &builtin);
6265
6266 // If it didn't overflow, the result is {2 * value} as pointer-sized value.
6267 Node* smi_tagged = BuildChangeInt32ToIntPtr(gasm_->Projection(0, add));
6268 gasm_->Goto(&done, smi_tagged);
6269
6270 // Otherwise, call builtin, to convert to a HeapNumber.
6271 gasm_->Bind(&builtin);
6272 CommonOperatorBuilder* common = mcgraph()->common();
6273 Node* target =
6274 GetTargetForBuiltinCall(wasm::WasmCode::kWasmInt32ToHeapNumber,
6275 Builtin::kWasmInt32ToHeapNumber);
6276 if (!int32_to_heapnumber_operator_.is_set()) {
6277 auto call_descriptor = Linkage::GetStubCallDescriptor(
6278 mcgraph()->zone(), WasmInt32ToHeapNumberDescriptor(), 0,
6279 CallDescriptor::kNoFlags, Operator::kNoProperties, stub_mode_);
6280 int32_to_heapnumber_operator_.set(common->Call(call_descriptor));
6281 }
6282 Node* call =
6283 gasm_->Call(int32_to_heapnumber_operator_.get(), target, value);
6284 gasm_->Goto(&done, call);
6285 gasm_->Bind(&done);
6286 return done.PhiAt(0);
6287 }
6288
BuildChangeTaggedToInt32(Node * value,Node * context,Node * frame_state)6289 Node* BuildChangeTaggedToInt32(Node* value, Node* context,
6290 Node* frame_state) {
6291 // We expect most integers at runtime to be Smis, so it is important for
6292 // wrapper performance that Smi conversion be inlined.
6293 auto builtin = gasm_->MakeDeferredLabel();
6294 auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
6295
6296 gasm_->GotoIfNot(IsSmi(value), &builtin);
6297
6298 // If Smi, convert to int32.
6299 Node* smi = BuildChangeSmiToInt32(value);
6300 gasm_->Goto(&done, smi);
6301
6302 // Otherwise, call builtin which changes non-Smi to Int32.
6303 gasm_->Bind(&builtin);
6304 CommonOperatorBuilder* common = mcgraph()->common();
6305 Node* target =
6306 GetTargetForBuiltinCall(wasm::WasmCode::kWasmTaggedNonSmiToInt32,
6307 Builtin::kWasmTaggedNonSmiToInt32);
6308 if (!tagged_non_smi_to_int32_operator_.is_set()) {
6309 auto call_descriptor = Linkage::GetStubCallDescriptor(
6310 mcgraph()->zone(), WasmTaggedNonSmiToInt32Descriptor(), 0,
6311 frame_state ? CallDescriptor::kNeedsFrameState
6312 : CallDescriptor::kNoFlags,
6313 Operator::kNoProperties, stub_mode_);
6314 tagged_non_smi_to_int32_operator_.set(common->Call(call_descriptor));
6315 }
6316 Node* call = frame_state
6317 ? gasm_->Call(tagged_non_smi_to_int32_operator_.get(),
6318 target, value, context, frame_state)
6319 : gasm_->Call(tagged_non_smi_to_int32_operator_.get(),
6320 target, value, context);
6321 SetSourcePosition(call, 1);
6322 gasm_->Goto(&done, call);
6323 gasm_->Bind(&done);
6324 return done.PhiAt(0);
6325 }
6326
BuildChangeFloat32ToNumber(Node * value)6327 Node* BuildChangeFloat32ToNumber(Node* value) {
6328 CommonOperatorBuilder* common = mcgraph()->common();
6329 Node* target = GetTargetForBuiltinCall(wasm::WasmCode::kWasmFloat32ToNumber,
6330 Builtin::kWasmFloat32ToNumber);
6331 if (!float32_to_number_operator_.is_set()) {
6332 auto call_descriptor = Linkage::GetStubCallDescriptor(
6333 mcgraph()->zone(), WasmFloat32ToNumberDescriptor(), 0,
6334 CallDescriptor::kNoFlags, Operator::kNoProperties, stub_mode_);
6335 float32_to_number_operator_.set(common->Call(call_descriptor));
6336 }
6337 return gasm_->Call(float32_to_number_operator_.get(), target, value);
6338 }
6339
BuildChangeFloat64ToNumber(Node * value)6340 Node* BuildChangeFloat64ToNumber(Node* value) {
6341 CommonOperatorBuilder* common = mcgraph()->common();
6342 Node* target = GetTargetForBuiltinCall(wasm::WasmCode::kWasmFloat64ToNumber,
6343 Builtin::kWasmFloat64ToNumber);
6344 if (!float64_to_number_operator_.is_set()) {
6345 auto call_descriptor = Linkage::GetStubCallDescriptor(
6346 mcgraph()->zone(), WasmFloat64ToNumberDescriptor(), 0,
6347 CallDescriptor::kNoFlags, Operator::kNoProperties, stub_mode_);
6348 float64_to_number_operator_.set(common->Call(call_descriptor));
6349 }
6350 return gasm_->Call(float64_to_number_operator_.get(), target, value);
6351 }
6352
BuildChangeTaggedToFloat64(Node * value,Node * context,Node * frame_state)6353 Node* BuildChangeTaggedToFloat64(Node* value, Node* context,
6354 Node* frame_state) {
6355 CommonOperatorBuilder* common = mcgraph()->common();
6356 Node* target = GetTargetForBuiltinCall(wasm::WasmCode::kWasmTaggedToFloat64,
6357 Builtin::kWasmTaggedToFloat64);
6358 bool needs_frame_state = frame_state != nullptr;
6359 if (!tagged_to_float64_operator_.is_set()) {
6360 auto call_descriptor = Linkage::GetStubCallDescriptor(
6361 mcgraph()->zone(), WasmTaggedToFloat64Descriptor(), 0,
6362 frame_state ? CallDescriptor::kNeedsFrameState
6363 : CallDescriptor::kNoFlags,
6364 Operator::kNoProperties, stub_mode_);
6365 tagged_to_float64_operator_.set(common->Call(call_descriptor));
6366 }
6367 Node* call = needs_frame_state
6368 ? gasm_->Call(tagged_to_float64_operator_.get(), target,
6369 value, context, frame_state)
6370 : gasm_->Call(tagged_to_float64_operator_.get(), target,
6371 value, context);
6372 SetSourcePosition(call, 1);
6373 return call;
6374 }
6375
AddArgumentNodes(base::Vector<Node * > args,int pos,int param_count,const wasm::FunctionSig * sig,Node * context)6376 int AddArgumentNodes(base::Vector<Node*> args, int pos, int param_count,
6377 const wasm::FunctionSig* sig, Node* context) {
6378 // Convert wasm numbers to JS values.
6379 for (int i = 0; i < param_count; ++i) {
6380 Node* param =
6381 Param(i + 1); // Start from index 1 to drop the instance_node.
6382 args[pos++] = ToJS(param, sig->GetParam(i), context);
6383 }
6384 return pos;
6385 }
6386
ToJS(Node * node,wasm::ValueType type,Node * context)6387 Node* ToJS(Node* node, wasm::ValueType type, Node* context) {
6388 switch (type.kind()) {
6389 case wasm::kI32:
6390 return BuildChangeInt32ToNumber(node);
6391 case wasm::kI64:
6392 return BuildChangeInt64ToBigInt(node);
6393 case wasm::kF32:
6394 return BuildChangeFloat32ToNumber(node);
6395 case wasm::kF64:
6396 return BuildChangeFloat64ToNumber(node);
6397 case wasm::kRef:
6398 case wasm::kOptRef:
6399 switch (type.heap_representation()) {
6400 case wasm::HeapType::kFunc: {
6401 if (type.kind() == wasm::kOptRef) {
6402 auto done =
6403 gasm_->MakeLabel(MachineRepresentation::kTaggedPointer);
6404 // Do not wrap {null}.
6405 gasm_->GotoIf(IsNull(node), &done, node);
6406 gasm_->Goto(&done,
6407 gasm_->LoadFromObject(
6408 MachineType::TaggedPointer(), node,
6409 wasm::ObjectAccess::ToTagged(
6410 WasmInternalFunction::kExternalOffset)));
6411 gasm_->Bind(&done);
6412 return done.PhiAt(0);
6413 } else {
6414 return gasm_->LoadFromObject(
6415 MachineType::TaggedPointer(), node,
6416 wasm::ObjectAccess::ToTagged(
6417 WasmInternalFunction::kExternalOffset));
6418 }
6419 }
6420 case wasm::HeapType::kEq:
6421 case wasm::HeapType::kData:
6422 case wasm::HeapType::kArray:
6423 case wasm::HeapType::kI31:
6424 // TODO(7748): Update this when JS interop is settled.
6425 if (type.kind() == wasm::kOptRef) {
6426 auto done =
6427 gasm_->MakeLabel(MachineRepresentation::kTaggedPointer);
6428 // Do not wrap {null}.
6429 gasm_->GotoIf(IsNull(node), &done, node);
6430 gasm_->Goto(&done, BuildAllocateObjectWrapper(node, context));
6431 gasm_->Bind(&done);
6432 return done.PhiAt(0);
6433 } else {
6434 return BuildAllocateObjectWrapper(node, context);
6435 }
6436 case wasm::HeapType::kAny: {
6437 if (!enabled_features_.has_gc()) return node;
6438 // Wrap {node} in object wrapper if it is an array/struct.
6439 // Extract external function if this is a WasmInternalFunction.
6440 // Otherwise (i.e. null and external refs), return input.
6441 // Treat i31 as externref because they are indistinguishable from
6442 // Smis.
6443 // TODO(7748): Update this when JS interop is settled.
6444 auto wrap = gasm_->MakeLabel();
6445 auto function = gasm_->MakeLabel();
6446 auto done = gasm_->MakeLabel(MachineRepresentation::kTaggedPointer);
6447 gasm_->GotoIf(IsSmi(node), &done, node);
6448 gasm_->GotoIf(gasm_->IsDataRefMap(gasm_->LoadMap(node)), &wrap);
6449 gasm_->GotoIf(
6450 gasm_->HasInstanceType(node, WASM_INTERNAL_FUNCTION_TYPE),
6451 &function);
6452 // This includes the case where {node == null}.
6453 gasm_->Goto(&done, node);
6454
6455 gasm_->Bind(&wrap);
6456 gasm_->Goto(&done, BuildAllocateObjectWrapper(node, context));
6457
6458 gasm_->Bind(&function);
6459 gasm_->Goto(&done, gasm_->LoadFromObject(
6460 MachineType::TaggedPointer(), node,
6461 wasm::ObjectAccess::ToTagged(
6462 WasmInternalFunction::kExternalOffset)));
6463
6464 gasm_->Bind(&done);
6465 return done.PhiAt(0);
6466 }
6467 default:
6468 DCHECK(type.has_index());
6469 if (module_->has_signature(type.ref_index())) {
6470 // Typed function. Extract the external function.
6471 return gasm_->LoadFromObject(
6472 MachineType::TaggedPointer(), node,
6473 wasm::ObjectAccess::ToTagged(
6474 WasmInternalFunction::kExternalOffset));
6475 }
6476 // If this is reached, then IsJSCompatibleSignature() is too
6477 // permissive.
6478 // TODO(7748): Figure out a JS interop story for arrays and structs.
6479 UNREACHABLE();
6480 }
6481 case wasm::kRtt:
6482 case wasm::kI8:
6483 case wasm::kI16:
6484 case wasm::kS128:
6485 case wasm::kVoid:
6486 case wasm::kBottom:
6487 // If this is reached, then IsJSCompatibleSignature() is too permissive.
6488 // TODO(7748): Figure out what to do for RTTs.
6489 UNREACHABLE();
6490 }
6491 }
6492
6493 // TODO(7748): Temporary solution to allow round-tripping of Wasm objects
6494 // through JavaScript, where they show up as opaque boxes. This will disappear
6495 // once we have a proper WasmGC <-> JS interaction story.
BuildAllocateObjectWrapper(Node * input,Node * context)6496 Node* BuildAllocateObjectWrapper(Node* input, Node* context) {
6497 if (FLAG_wasm_gc_js_interop) return input;
6498 return gasm_->CallBuiltin(Builtin::kWasmAllocateObjectWrapper,
6499 Operator::kEliminatable, input, context);
6500 }
6501
6502 // Assumes {input} has been checked for validity against the target wasm type.
6503 // If {input} is a function, returns the WasmInternalFunction associated with
6504 // it. If {input} has the {wasm_wrapped_object_symbol} property, returns the
6505 // value of that property. Otherwise, returns {input}.
BuildUnpackObjectWrapper(Node * input,Node * context)6506 Node* BuildUnpackObjectWrapper(Node* input, Node* context) {
6507 auto not_a_function = gasm_->MakeLabel();
6508 auto end = gasm_->MakeLabel(MachineRepresentation::kTaggedPointer);
6509
6510 gasm_->GotoIfNot(gasm_->HasInstanceType(input, JS_FUNCTION_TYPE),
6511 ¬_a_function);
6512
6513 Node* function_data = gasm_->LoadFunctionDataFromJSFunction(input);
6514
6515 // Due to type checking, {function_data} will be a WasmFunctionData.
6516 Node* internal = gasm_->LoadFromObject(
6517 MachineType::TaggedPointer(), function_data,
6518 wasm::ObjectAccess::ToTagged(WasmFunctionData::kInternalOffset));
6519 gasm_->Goto(&end, internal);
6520
6521 gasm_->Bind(¬_a_function);
6522 if (!FLAG_wasm_gc_js_interop) {
6523 Node* obj = gasm_->CallBuiltin(
6524 Builtin::kWasmGetOwnProperty, Operator::kEliminatable, input,
6525 LOAD_ROOT(wasm_wrapped_object_symbol, wasm_wrapped_object_symbol),
6526 context);
6527 // Invalid object wrappers (i.e. any other JS object that doesn't have the
6528 // magic hidden property) will return {undefined}. Map that to {input}.
6529 Node* is_undefined = gasm_->TaggedEqual(obj, UndefinedValue());
6530 gasm_->GotoIf(is_undefined, &end, input);
6531
6532 gasm_->Goto(&end, obj);
6533 } else {
6534 gasm_->Goto(&end, input);
6535 }
6536
6537 gasm_->Bind(&end);
6538
6539 return end.PhiAt(0);
6540 }
6541
BuildChangeInt64ToBigInt(Node * input)6542 Node* BuildChangeInt64ToBigInt(Node* input) {
6543 Node* target;
6544 if (mcgraph()->machine()->Is64()) {
6545 target = GetTargetForBuiltinCall(wasm::WasmCode::kI64ToBigInt,
6546 Builtin::kI64ToBigInt);
6547 } else {
6548 DCHECK(mcgraph()->machine()->Is32());
6549 // On 32-bit platforms we already set the target to the
6550 // I32PairToBigInt builtin here, so that we don't have to replace the
6551 // target in the int64-lowering.
6552 target = GetTargetForBuiltinCall(wasm::WasmCode::kI32PairToBigInt,
6553 Builtin::kI32PairToBigInt);
6554 }
6555 return gasm_->Call(GetI64ToBigIntCallDescriptor(), target, input);
6556 }
6557
BuildChangeBigIntToInt64(Node * input,Node * context,Node * frame_state)6558 Node* BuildChangeBigIntToInt64(Node* input, Node* context,
6559 Node* frame_state) {
6560 Node* target;
6561 if (mcgraph()->machine()->Is64()) {
6562 target = GetTargetForBuiltinCall(wasm::WasmCode::kBigIntToI64,
6563 Builtin::kBigIntToI64);
6564 } else {
6565 DCHECK(mcgraph()->machine()->Is32());
6566 // On 32-bit platforms we already set the target to the
6567 // BigIntToI32Pair builtin here, so that we don't have to replace the
6568 // target in the int64-lowering.
6569 target = GetTargetForBuiltinCall(wasm::WasmCode::kBigIntToI32Pair,
6570 Builtin::kBigIntToI32Pair);
6571 }
6572
6573 return frame_state ? gasm_->Call(GetBigIntToI64CallDescriptor(true), target,
6574 input, context, frame_state)
6575 : gasm_->Call(GetBigIntToI64CallDescriptor(false),
6576 target, input, context);
6577 }
6578
BuildCheckValidRefValue(Node * input,Node * js_context,wasm::ValueType type)6579 void BuildCheckValidRefValue(Node* input, Node* js_context,
6580 wasm::ValueType type) {
6581 // Make sure ValueType fits in a Smi.
6582 STATIC_ASSERT(wasm::ValueType::kLastUsedBit + 1 <= kSmiValueSize);
6583 // The instance node is always defined: if an instance is not available, it
6584 // is the undefined value.
6585 Node* inputs[] = {GetInstance(), input,
6586 mcgraph()->IntPtrConstant(
6587 IntToSmi(static_cast<int>(type.raw_bit_field())))};
6588
6589 Node* check = BuildChangeSmiToInt32(BuildCallToRuntimeWithContext(
6590 Runtime::kWasmIsValidRefValue, js_context, inputs, 3));
6591
6592 Diamond type_check(graph(), mcgraph()->common(), check, BranchHint::kTrue);
6593 type_check.Chain(control());
6594 SetControl(type_check.if_false);
6595
6596 Node* old_effect = effect();
6597 BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, js_context,
6598 nullptr, 0);
6599
6600 SetEffectControl(type_check.EffectPhi(old_effect, effect()),
6601 type_check.merge);
6602 }
6603
FromJS(Node * input,Node * js_context,wasm::ValueType type,Node * frame_state=nullptr)6604 Node* FromJS(Node* input, Node* js_context, wasm::ValueType type,
6605 Node* frame_state = nullptr) {
6606 switch (type.kind()) {
6607 case wasm::kRef:
6608 case wasm::kOptRef: {
6609 switch (type.heap_representation()) {
6610 case wasm::HeapType::kAny:
6611 if (!enabled_features_.has_gc()) return input;
6612 // If this is a wrapper for arrays/structs/i31s, unpack it.
6613 // TODO(7748): Update this when JS interop has settled.
6614 return BuildUnpackObjectWrapper(input, js_context);
6615 case wasm::HeapType::kFunc:
6616 BuildCheckValidRefValue(input, js_context, type);
6617 return BuildUnpackObjectWrapper(input, js_context);
6618 case wasm::HeapType::kData:
6619 case wasm::HeapType::kArray:
6620 case wasm::HeapType::kEq:
6621 case wasm::HeapType::kI31:
6622 // TODO(7748): Update this when JS interop has settled.
6623 BuildCheckValidRefValue(input, js_context, type);
6624 // This will just return {input} if the object is not wrapped, i.e.
6625 // if it is null (given the check just above).
6626 return BuildUnpackObjectWrapper(input, js_context);
6627 default:
6628 if (module_->has_signature(type.ref_index())) {
6629 BuildCheckValidRefValue(input, js_context, type);
6630 return BuildUnpackObjectWrapper(input, js_context);
6631 }
6632 // If this is reached, then IsJSCompatibleSignature() is too
6633 // permissive.
6634 UNREACHABLE();
6635 }
6636 }
6637 case wasm::kF32:
6638 return gasm_->TruncateFloat64ToFloat32(
6639 BuildChangeTaggedToFloat64(input, js_context, frame_state));
6640
6641 case wasm::kF64:
6642 return BuildChangeTaggedToFloat64(input, js_context, frame_state);
6643
6644 case wasm::kI32:
6645 return BuildChangeTaggedToInt32(input, js_context, frame_state);
6646
6647 case wasm::kI64:
6648 // i64 values can only come from BigInt.
6649 return BuildChangeBigIntToInt64(input, js_context, frame_state);
6650
6651 case wasm::kRtt:
6652 case wasm::kS128:
6653 case wasm::kI8:
6654 case wasm::kI16:
6655 case wasm::kBottom:
6656 case wasm::kVoid:
6657 // If this is reached, then IsJSCompatibleSignature() is too permissive.
6658 // TODO(7748): Figure out what to do for RTTs.
6659 UNREACHABLE();
6660 }
6661 }
6662
SmiToFloat32(Node * input)6663 Node* SmiToFloat32(Node* input) {
6664 return gasm_->RoundInt32ToFloat32(BuildChangeSmiToInt32(input));
6665 }
6666
SmiToFloat64(Node * input)6667 Node* SmiToFloat64(Node* input) {
6668 return gasm_->ChangeInt32ToFloat64(BuildChangeSmiToInt32(input));
6669 }
6670
HeapNumberToFloat64(Node * input)6671 Node* HeapNumberToFloat64(Node* input) {
6672 return gasm_->LoadFromObject(
6673 MachineType::Float64(), input,
6674 wasm::ObjectAccess::ToTagged(HeapNumber::kValueOffset));
6675 }
6676
FromJSFast(Node * input,wasm::ValueType type)6677 Node* FromJSFast(Node* input, wasm::ValueType type) {
6678 switch (type.kind()) {
6679 case wasm::kI32:
6680 return BuildChangeSmiToInt32(input);
6681 case wasm::kF32: {
6682 auto done = gasm_->MakeLabel(MachineRepresentation::kFloat32);
6683 auto heap_number = gasm_->MakeLabel();
6684 gasm_->GotoIfNot(IsSmi(input), &heap_number);
6685 gasm_->Goto(&done, SmiToFloat32(input));
6686 gasm_->Bind(&heap_number);
6687 Node* value =
6688 gasm_->TruncateFloat64ToFloat32(HeapNumberToFloat64(input));
6689 gasm_->Goto(&done, value);
6690 gasm_->Bind(&done);
6691 return done.PhiAt(0);
6692 }
6693 case wasm::kF64: {
6694 auto done = gasm_->MakeLabel(MachineRepresentation::kFloat64);
6695 auto heap_number = gasm_->MakeLabel();
6696 gasm_->GotoIfNot(IsSmi(input), &heap_number);
6697 gasm_->Goto(&done, SmiToFloat64(input));
6698 gasm_->Bind(&heap_number);
6699 gasm_->Goto(&done, HeapNumberToFloat64(input));
6700 gasm_->Bind(&done);
6701 return done.PhiAt(0);
6702 }
6703 case wasm::kRef:
6704 case wasm::kOptRef:
6705 case wasm::kI64:
6706 case wasm::kRtt:
6707 case wasm::kS128:
6708 case wasm::kI8:
6709 case wasm::kI16:
6710 case wasm::kBottom:
6711 case wasm::kVoid:
6712 UNREACHABLE();
6713 }
6714 }
6715
BuildModifyThreadInWasmFlagHelper(Node * thread_in_wasm_flag_address,bool new_value)6716 void BuildModifyThreadInWasmFlagHelper(Node* thread_in_wasm_flag_address,
6717 bool new_value) {
6718 if (FLAG_debug_code) {
6719 Node* flag_value = gasm_->LoadFromObject(MachineType::Pointer(),
6720 thread_in_wasm_flag_address, 0);
6721 Node* check =
6722 gasm_->Word32Equal(flag_value, Int32Constant(new_value ? 0 : 1));
6723
6724 Diamond flag_check(graph(), mcgraph()->common(), check,
6725 BranchHint::kTrue);
6726 flag_check.Chain(control());
6727 SetControl(flag_check.if_false);
6728 Node* message_id = gasm_->NumberConstant(static_cast<int32_t>(
6729 new_value ? AbortReason::kUnexpectedThreadInWasmSet
6730 : AbortReason::kUnexpectedThreadInWasmUnset));
6731
6732 Node* old_effect = effect();
6733 Node* call = BuildCallToRuntimeWithContext(
6734 Runtime::kAbort, NoContextConstant(), &message_id, 1);
6735 flag_check.merge->ReplaceInput(1, call);
6736 SetEffectControl(flag_check.EffectPhi(old_effect, effect()),
6737 flag_check.merge);
6738 }
6739
6740 gasm_->StoreToObject(ObjectAccess(MachineType::Int32(), kNoWriteBarrier),
6741 thread_in_wasm_flag_address, 0,
6742 Int32Constant(new_value ? 1 : 0));
6743 }
6744
BuildModifyThreadInWasmFlag(bool new_value)6745 void BuildModifyThreadInWasmFlag(bool new_value) {
6746 if (!trap_handler::IsTrapHandlerEnabled()) return;
6747 Node* isolate_root = BuildLoadIsolateRoot();
6748
6749 Node* thread_in_wasm_flag_address =
6750 gasm_->LoadFromObject(MachineType::Pointer(), isolate_root,
6751 Isolate::thread_in_wasm_flag_address_offset());
6752
6753 BuildModifyThreadInWasmFlagHelper(thread_in_wasm_flag_address, new_value);
6754 }
6755
6756 class ModifyThreadInWasmFlagScope {
6757 public:
ModifyThreadInWasmFlagScope(WasmWrapperGraphBuilder * wasm_wrapper_graph_builder,WasmGraphAssembler * gasm)6758 ModifyThreadInWasmFlagScope(
6759 WasmWrapperGraphBuilder* wasm_wrapper_graph_builder,
6760 WasmGraphAssembler* gasm)
6761 : wasm_wrapper_graph_builder_(wasm_wrapper_graph_builder) {
6762 if (!trap_handler::IsTrapHandlerEnabled()) return;
6763 Node* isolate_root = wasm_wrapper_graph_builder_->BuildLoadIsolateRoot();
6764
6765 thread_in_wasm_flag_address_ =
6766 gasm->LoadFromObject(MachineType::Pointer(), isolate_root,
6767 Isolate::thread_in_wasm_flag_address_offset());
6768
6769 wasm_wrapper_graph_builder_->BuildModifyThreadInWasmFlagHelper(
6770 thread_in_wasm_flag_address_, true);
6771 }
6772
~ModifyThreadInWasmFlagScope()6773 ~ModifyThreadInWasmFlagScope() {
6774 if (!trap_handler::IsTrapHandlerEnabled()) return;
6775
6776 wasm_wrapper_graph_builder_->BuildModifyThreadInWasmFlagHelper(
6777 thread_in_wasm_flag_address_, false);
6778 }
6779
6780 private:
6781 WasmWrapperGraphBuilder* wasm_wrapper_graph_builder_;
6782 Node* thread_in_wasm_flag_address_;
6783 };
6784
BuildMultiReturnFixedArrayFromIterable(const wasm::FunctionSig * sig,Node * iterable,Node * context)6785 Node* BuildMultiReturnFixedArrayFromIterable(const wasm::FunctionSig* sig,
6786 Node* iterable, Node* context) {
6787 Node* length = BuildChangeUint31ToSmi(
6788 mcgraph()->Uint32Constant(static_cast<uint32_t>(sig->return_count())));
6789 return gasm_->CallBuiltin(Builtin::kIterableToFixedArrayForWasm,
6790 Operator::kEliminatable, iterable, length,
6791 context);
6792 }
6793
6794 // Generate a call to the AllocateJSArray builtin.
BuildCallAllocateJSArray(Node * array_length,Node * context)6795 Node* BuildCallAllocateJSArray(Node* array_length, Node* context) {
6796 // Since we don't check that args will fit in an array,
6797 // we make sure this is true based on statically known limits.
6798 STATIC_ASSERT(wasm::kV8MaxWasmFunctionReturns <=
6799 JSArray::kInitialMaxFastElementArray);
6800 return gasm_->CallBuiltin(Builtin::kWasmAllocateJSArray,
6801 Operator::kEliminatable, array_length, context);
6802 }
6803
BuildCallAndReturn(bool is_import,Node * js_context,Node * function_data,base::SmallVector<Node *,16> args,const JSWasmCallData * js_wasm_call_data,Node * frame_state)6804 Node* BuildCallAndReturn(bool is_import, Node* js_context,
6805 Node* function_data,
6806 base::SmallVector<Node*, 16> args,
6807 const JSWasmCallData* js_wasm_call_data,
6808 Node* frame_state) {
6809 const int rets_count = static_cast<int>(sig_->return_count());
6810 base::SmallVector<Node*, 1> rets(rets_count);
6811
6812 // Set the ThreadInWasm flag before we do the actual call.
6813 {
6814 ModifyThreadInWasmFlagScope modify_thread_in_wasm_flag_builder(
6815 this, gasm_.get());
6816
6817 if (is_import) {
6818 // Call to an imported function.
6819 // Load function index from {WasmExportedFunctionData}.
6820 Node* function_index = BuildChangeSmiToInt32(
6821 gasm_->LoadExportedFunctionIndexAsSmi(function_data));
6822 BuildImportCall(sig_, base::VectorOf(args), base::VectorOf(rets),
6823 wasm::kNoCodePosition, function_index, kCallContinues);
6824 } else {
6825 // Call to a wasm function defined in this module.
6826 // The (cached) call target is the jump table slot for that function.
6827 Node* internal = gasm_->LoadFromObject(
6828 MachineType::TaggedPointer(), function_data,
6829 wasm::ObjectAccess::ToTagged(WasmFunctionData::kInternalOffset));
6830 args[0] = BuildLoadExternalPointerFromObject(
6831 internal, WasmInternalFunction::kForeignAddressOffset);
6832 Node* instance_node = gasm_->LoadFromObject(
6833 MachineType::TaggedPointer(), internal,
6834 wasm::ObjectAccess::ToTagged(WasmInternalFunction::kRefOffset));
6835 BuildWasmCall(sig_, base::VectorOf(args), base::VectorOf(rets),
6836 wasm::kNoCodePosition, instance_node, frame_state);
6837 }
6838 }
6839
6840 Node* jsval;
6841 if (sig_->return_count() == 0) {
6842 jsval = UndefinedValue();
6843 } else if (sig_->return_count() == 1) {
6844 jsval = js_wasm_call_data && !js_wasm_call_data->result_needs_conversion()
6845 ? rets[0]
6846 : ToJS(rets[0], sig_->GetReturn(), js_context);
6847 } else {
6848 int32_t return_count = static_cast<int32_t>(sig_->return_count());
6849 Node* size = gasm_->NumberConstant(return_count);
6850
6851 jsval = BuildCallAllocateJSArray(size, js_context);
6852
6853 Node* fixed_array = gasm_->LoadJSArrayElements(jsval);
6854
6855 for (int i = 0; i < return_count; ++i) {
6856 Node* value = ToJS(rets[i], sig_->GetReturn(i), js_context);
6857 gasm_->StoreFixedArrayElementAny(fixed_array, i, value);
6858 }
6859 }
6860 return jsval;
6861 }
6862
QualifiesForFastTransform(const wasm::FunctionSig *)6863 bool QualifiesForFastTransform(const wasm::FunctionSig*) {
6864 const int wasm_count = static_cast<int>(sig_->parameter_count());
6865 for (int i = 0; i < wasm_count; ++i) {
6866 wasm::ValueType type = sig_->GetParam(i);
6867 switch (type.kind()) {
6868 case wasm::kRef:
6869 case wasm::kOptRef:
6870 case wasm::kI64:
6871 case wasm::kRtt:
6872 case wasm::kS128:
6873 case wasm::kI8:
6874 case wasm::kI16:
6875 case wasm::kBottom:
6876 case wasm::kVoid:
6877 return false;
6878 case wasm::kI32:
6879 case wasm::kF32:
6880 case wasm::kF64:
6881 break;
6882 }
6883 }
6884 return true;
6885 }
6886
IsSmi(Node * input)6887 Node* IsSmi(Node* input) {
6888 return gasm_->Word32Equal(
6889 gasm_->Word32And(BuildTruncateIntPtrToInt32(input),
6890 Int32Constant(kSmiTagMask)),
6891 Int32Constant(kSmiTag));
6892 }
6893
CanTransformFast(Node * input,wasm::ValueType type,v8::internal::compiler::GraphAssemblerLabel<0> * slow_path)6894 void CanTransformFast(
6895 Node* input, wasm::ValueType type,
6896 v8::internal::compiler::GraphAssemblerLabel<0>* slow_path) {
6897 switch (type.kind()) {
6898 case wasm::kI32: {
6899 gasm_->GotoIfNot(IsSmi(input), slow_path);
6900 return;
6901 }
6902 case wasm::kF32:
6903 case wasm::kF64: {
6904 auto done = gasm_->MakeLabel();
6905 gasm_->GotoIf(IsSmi(input), &done);
6906 Node* map = gasm_->LoadMap(input);
6907 Node* heap_number_map = LOAD_ROOT(HeapNumberMap, heap_number_map);
6908 #if V8_MAP_PACKING
6909 Node* is_heap_number = gasm_->WordEqual(heap_number_map, map);
6910 #else
6911 Node* is_heap_number = gasm_->TaggedEqual(heap_number_map, map);
6912 #endif
6913 gasm_->GotoIf(is_heap_number, &done);
6914 gasm_->Goto(slow_path);
6915 gasm_->Bind(&done);
6916 return;
6917 }
6918 case wasm::kRef:
6919 case wasm::kOptRef:
6920 case wasm::kI64:
6921 case wasm::kRtt:
6922 case wasm::kS128:
6923 case wasm::kI8:
6924 case wasm::kI16:
6925 case wasm::kBottom:
6926 case wasm::kVoid:
6927 UNREACHABLE();
6928 }
6929 }
6930
BuildJSToWasmWrapper(bool is_import,const JSWasmCallData * js_wasm_call_data=nullptr,Node * frame_state=nullptr)6931 void BuildJSToWasmWrapper(bool is_import,
6932 const JSWasmCallData* js_wasm_call_data = nullptr,
6933 Node* frame_state = nullptr) {
6934 const int wasm_param_count = static_cast<int>(sig_->parameter_count());
6935
6936 // Build the start and the JS parameter nodes.
6937 Start(wasm_param_count + 5);
6938
6939 // Create the js_closure and js_context parameters.
6940 Node* js_closure = Param(Linkage::kJSCallClosureParamIndex, "%closure");
6941 Node* js_context = Param(
6942 Linkage::GetJSCallContextParamIndex(wasm_param_count + 1), "%context");
6943 Node* function_data = gasm_->LoadFunctionDataFromJSFunction(js_closure);
6944
6945 if (!wasm::IsJSCompatibleSignature(sig_, module_, enabled_features_)) {
6946 // Throw a TypeError. Use the js_context of the calling javascript
6947 // function (passed as a parameter), such that the generated code is
6948 // js_context independent.
6949 BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, js_context,
6950 nullptr, 0);
6951 TerminateThrow(effect(), control());
6952 return;
6953 }
6954
6955 const int args_count = wasm_param_count + 1; // +1 for wasm_code.
6956
6957 // Check whether the signature of the function allows for a fast
6958 // transformation (if any params exist that need transformation).
6959 // Create a fast transformation path, only if it does.
6960 bool include_fast_path = !js_wasm_call_data && wasm_param_count > 0 &&
6961 QualifiesForFastTransform(sig_);
6962
6963 // Prepare Param() nodes. Param() nodes can only be created once,
6964 // so we need to use the same nodes along all possible transformation paths.
6965 base::SmallVector<Node*, 16> params(args_count);
6966 for (int i = 0; i < wasm_param_count; ++i) params[i + 1] = Param(i + 1);
6967
6968 auto done = gasm_->MakeLabel(MachineRepresentation::kTagged);
6969 if (include_fast_path) {
6970 auto slow_path = gasm_->MakeDeferredLabel();
6971 // Check if the params received on runtime can be actually transformed
6972 // using the fast transformation. When a param that cannot be transformed
6973 // fast is encountered, skip checking the rest and fall back to the slow
6974 // path.
6975 for (int i = 0; i < wasm_param_count; ++i) {
6976 CanTransformFast(params[i + 1], sig_->GetParam(i), &slow_path);
6977 }
6978 // Convert JS parameters to wasm numbers using the fast transformation
6979 // and build the call.
6980 base::SmallVector<Node*, 16> args(args_count);
6981 for (int i = 0; i < wasm_param_count; ++i) {
6982 Node* wasm_param = FromJSFast(params[i + 1], sig_->GetParam(i));
6983 args[i + 1] = wasm_param;
6984 }
6985 Node* jsval = BuildCallAndReturn(is_import, js_context, function_data,
6986 args, js_wasm_call_data, frame_state);
6987 gasm_->Goto(&done, jsval);
6988 gasm_->Bind(&slow_path);
6989 }
6990 // Convert JS parameters to wasm numbers using the default transformation
6991 // and build the call.
6992 base::SmallVector<Node*, 16> args(args_count);
6993 for (int i = 0; i < wasm_param_count; ++i) {
6994 bool do_conversion =
6995 !js_wasm_call_data || js_wasm_call_data->arg_needs_conversion(i);
6996 if (do_conversion) {
6997 args[i + 1] =
6998 FromJS(params[i + 1], js_context, sig_->GetParam(i), frame_state);
6999 } else {
7000 Node* wasm_param = params[i + 1];
7001
7002 // For Float32 parameters
7003 // we set UseInfo::CheckedNumberOrOddballAsFloat64 in
7004 // simplified-lowering and we need to add here a conversion from Float64
7005 // to Float32.
7006 if (sig_->GetParam(i).kind() == wasm::kF32) {
7007 wasm_param = gasm_->TruncateFloat64ToFloat32(wasm_param);
7008 }
7009
7010 args[i + 1] = wasm_param;
7011 }
7012 }
7013 Node* jsval = BuildCallAndReturn(is_import, js_context, function_data, args,
7014 js_wasm_call_data, frame_state);
7015 // If both the default and a fast transformation paths are present,
7016 // get the return value based on the path used.
7017 if (include_fast_path) {
7018 gasm_->Goto(&done, jsval);
7019 gasm_->Bind(&done);
7020 Return(done.PhiAt(0));
7021 } else {
7022 Return(jsval);
7023 }
7024 if (ContainsInt64(sig_)) LowerInt64(kCalledFromJS);
7025 }
7026
BuildReceiverNode(Node * callable_node,Node * native_context,Node * undefined_node)7027 Node* BuildReceiverNode(Node* callable_node, Node* native_context,
7028 Node* undefined_node) {
7029 // Check function strict bit.
7030 Node* shared_function_info = gasm_->LoadSharedFunctionInfo(callable_node);
7031 Node* flags = gasm_->LoadFromObject(
7032 MachineType::Int32(), shared_function_info,
7033 wasm::ObjectAccess::FlagsOffsetInSharedFunctionInfo());
7034 Node* strict_check =
7035 Binop(wasm::kExprI32And, flags,
7036 Int32Constant(SharedFunctionInfo::IsNativeBit::kMask |
7037 SharedFunctionInfo::IsStrictBit::kMask));
7038
7039 // Load global receiver if sloppy else use undefined.
7040 Diamond strict_d(graph(), mcgraph()->common(), strict_check,
7041 BranchHint::kNone);
7042 Node* old_effect = effect();
7043 SetControl(strict_d.if_false);
7044 Node* global_proxy = gasm_->LoadFixedArrayElementPtr(
7045 native_context, Context::GLOBAL_PROXY_INDEX);
7046 SetEffectControl(strict_d.EffectPhi(old_effect, global_proxy),
7047 strict_d.merge);
7048 return strict_d.Phi(MachineRepresentation::kTagged, undefined_node,
7049 global_proxy);
7050 }
7051
BuildSuspend(Node * value,Node * api_function_ref,MachineRepresentation rep)7052 Node* BuildSuspend(Node* value, Node* api_function_ref,
7053 MachineRepresentation rep) {
7054 // If value is a promise, suspend to the js-to-wasm prompt, and resume later
7055 // with the promise's resolved value.
7056 auto resume = gasm_->MakeLabel(rep);
7057 gasm_->GotoIf(IsSmi(value), &resume, value);
7058 gasm_->GotoIfNot(gasm_->HasInstanceType(value, JS_PROMISE_TYPE), &resume,
7059 BranchHint::kTrue, value);
7060 Node* suspender = gasm_->Load(
7061 MachineType::TaggedPointer(), api_function_ref,
7062 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kSuspenderOffset));
7063 Node* native_context = gasm_->Load(
7064 MachineType::TaggedPointer(), api_function_ref,
7065 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kNativeContextOffset));
7066 auto* call_descriptor = GetBuiltinCallDescriptor(
7067 Builtin::kWasmSuspend, zone_, StubCallMode::kCallWasmRuntimeStub);
7068 Node* call_target = mcgraph()->RelocatableIntPtrConstant(
7069 wasm::WasmCode::kWasmSuspend, RelocInfo::WASM_STUB_CALL);
7070 Node* args[] = {value, suspender};
7071 Node* chained_promise = BuildCallToRuntimeWithContext(
7072 Runtime::kWasmCreateResumePromise, native_context, args, 2);
7073 Node* resolved =
7074 gasm_->Call(call_descriptor, call_target, chained_promise, suspender);
7075 gasm_->Goto(&resume, resolved);
7076 gasm_->Bind(&resume);
7077 return resume.PhiAt(0);
7078 }
7079
7080 // For wasm-to-js wrappers, parameter 0 is a WasmApiFunctionRef.
BuildWasmToJSWrapper(WasmImportCallKind kind,int expected_arity,wasm::Suspend suspend)7081 bool BuildWasmToJSWrapper(WasmImportCallKind kind, int expected_arity,
7082 wasm::Suspend suspend) {
7083 int wasm_count = static_cast<int>(sig_->parameter_count());
7084
7085 // Build the start and the parameter nodes.
7086 Start(wasm_count + 3);
7087
7088 Node* native_context = gasm_->Load(
7089 MachineType::TaggedPointer(), Param(0),
7090 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kNativeContextOffset));
7091
7092 if (kind == WasmImportCallKind::kRuntimeTypeError) {
7093 // =======================================================================
7094 // === Runtime TypeError =================================================
7095 // =======================================================================
7096 BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError,
7097 native_context, nullptr, 0);
7098 TerminateThrow(effect(), control());
7099 return false;
7100 }
7101
7102 Node* callable_node = gasm_->Load(
7103 MachineType::TaggedPointer(), Param(0),
7104 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kCallableOffset));
7105
7106 Node* undefined_node = UndefinedValue();
7107
7108 Node* call = nullptr;
7109
7110 // Clear the ThreadInWasm flag.
7111 BuildModifyThreadInWasmFlag(false);
7112
7113 switch (kind) {
7114 // =======================================================================
7115 // === JS Functions with matching arity ==================================
7116 // =======================================================================
7117 case WasmImportCallKind::kJSFunctionArityMatch: {
7118 base::SmallVector<Node*, 16> args(wasm_count + 7);
7119 int pos = 0;
7120 Node* function_context =
7121 gasm_->LoadContextFromJSFunction(callable_node);
7122 args[pos++] = callable_node; // target callable.
7123
7124 // Determine receiver at runtime.
7125 args[pos++] =
7126 BuildReceiverNode(callable_node, native_context, undefined_node);
7127
7128 auto call_descriptor = Linkage::GetJSCallDescriptor(
7129 graph()->zone(), false, wasm_count + 1, CallDescriptor::kNoFlags);
7130
7131 // Convert wasm numbers to JS values.
7132 pos = AddArgumentNodes(base::VectorOf(args), pos, wasm_count, sig_,
7133 native_context);
7134
7135 args[pos++] = undefined_node; // new target
7136 args[pos++] =
7137 Int32Constant(JSParameterCount(wasm_count)); // argument count
7138 args[pos++] = function_context;
7139 args[pos++] = effect();
7140 args[pos++] = control();
7141
7142 DCHECK_EQ(pos, args.size());
7143 call = gasm_->Call(call_descriptor, pos, args.begin());
7144 if (suspend == wasm::kSuspend) {
7145 MachineRepresentation rep =
7146 sig_->return_count() >= 1
7147 ? sig_->GetReturn(0).machine_representation()
7148 : MachineRepresentation::kNone;
7149 call = BuildSuspend(call, Param(0), rep);
7150 }
7151 break;
7152 }
7153 // =======================================================================
7154 // === JS Functions with mismatching arity ===============================
7155 // =======================================================================
7156 case WasmImportCallKind::kJSFunctionArityMismatch: {
7157 int pushed_count = std::max(expected_arity, wasm_count);
7158 base::SmallVector<Node*, 16> args(pushed_count + 7);
7159 int pos = 0;
7160
7161 args[pos++] = callable_node; // target callable.
7162 // Determine receiver at runtime.
7163 args[pos++] =
7164 BuildReceiverNode(callable_node, native_context, undefined_node);
7165
7166 // Convert wasm numbers to JS values.
7167 pos = AddArgumentNodes(base::VectorOf(args), pos, wasm_count, sig_,
7168 native_context);
7169 for (int i = wasm_count; i < expected_arity; ++i) {
7170 args[pos++] = undefined_node;
7171 }
7172 args[pos++] = undefined_node; // new target
7173 args[pos++] =
7174 Int32Constant(JSParameterCount(wasm_count)); // argument count
7175
7176 Node* function_context =
7177 gasm_->LoadContextFromJSFunction(callable_node);
7178 args[pos++] = function_context;
7179 args[pos++] = effect();
7180 args[pos++] = control();
7181 DCHECK_EQ(pos, args.size());
7182
7183 auto call_descriptor = Linkage::GetJSCallDescriptor(
7184 graph()->zone(), false, pushed_count + 1, CallDescriptor::kNoFlags);
7185 call = gasm_->Call(call_descriptor, pos, args.begin());
7186 // TODO(12191): Handle suspending wrapper.
7187 break;
7188 }
7189 // =======================================================================
7190 // === General case of unknown callable ==================================
7191 // =======================================================================
7192 case WasmImportCallKind::kUseCallBuiltin: {
7193 base::SmallVector<Node*, 16> args(wasm_count + 7);
7194 int pos = 0;
7195 args[pos++] =
7196 gasm_->GetBuiltinPointerTarget(Builtin::kCall_ReceiverIsAny);
7197 args[pos++] = callable_node;
7198 args[pos++] =
7199 Int32Constant(JSParameterCount(wasm_count)); // argument count
7200 args[pos++] = undefined_node; // receiver
7201
7202 auto call_descriptor = Linkage::GetStubCallDescriptor(
7203 graph()->zone(), CallTrampolineDescriptor{}, wasm_count + 1,
7204 CallDescriptor::kNoFlags, Operator::kNoProperties,
7205 StubCallMode::kCallBuiltinPointer);
7206
7207 // Convert wasm numbers to JS values.
7208 pos = AddArgumentNodes(base::VectorOf(args), pos, wasm_count, sig_,
7209 native_context);
7210
7211 // The native_context is sufficient here, because all kind of callables
7212 // which depend on the context provide their own context. The context
7213 // here is only needed if the target is a constructor to throw a
7214 // TypeError, if the target is a native function, or if the target is a
7215 // callable JSObject, which can only be constructed by the runtime.
7216 args[pos++] = native_context;
7217 args[pos++] = effect();
7218 args[pos++] = control();
7219
7220 DCHECK_EQ(pos, args.size());
7221 call = gasm_->Call(call_descriptor, pos, args.begin());
7222 // TODO(12191): Handle suspending wrapper.
7223 break;
7224 }
7225 default:
7226 UNREACHABLE();
7227 }
7228 DCHECK_NOT_NULL(call);
7229
7230 SetSourcePosition(call, 0);
7231
7232 // Convert the return value(s) back.
7233 if (sig_->return_count() <= 1) {
7234 Node* val = sig_->return_count() == 0
7235 ? Int32Constant(0)
7236 : FromJS(call, native_context, sig_->GetReturn());
7237 BuildModifyThreadInWasmFlag(true);
7238 Return(val);
7239 } else {
7240 Node* fixed_array =
7241 BuildMultiReturnFixedArrayFromIterable(sig_, call, native_context);
7242 base::SmallVector<Node*, 8> wasm_values(sig_->return_count());
7243 for (unsigned i = 0; i < sig_->return_count(); ++i) {
7244 wasm_values[i] = FromJS(gasm_->LoadFixedArrayElementAny(fixed_array, i),
7245 native_context, sig_->GetReturn(i));
7246 }
7247 BuildModifyThreadInWasmFlag(true);
7248 Return(base::VectorOf(wasm_values));
7249 }
7250
7251 if (ContainsInt64(sig_)) LowerInt64(kCalledFromWasm);
7252 return true;
7253 }
7254
BuildCapiCallWrapper()7255 void BuildCapiCallWrapper() {
7256 // Set up the graph start.
7257 Start(static_cast<int>(sig_->parameter_count()) +
7258 1 /* offset for first parameter index being -1 */ +
7259 1 /* WasmApiFunctionRef */);
7260 // Store arguments on our stack, then align the stack for calling to C.
7261 int param_bytes = 0;
7262 for (wasm::ValueType type : sig_->parameters()) {
7263 param_bytes += type.value_kind_size();
7264 }
7265 int return_bytes = 0;
7266 for (wasm::ValueType type : sig_->returns()) {
7267 return_bytes += type.value_kind_size();
7268 }
7269
7270 int stack_slot_bytes = std::max(param_bytes, return_bytes);
7271 Node* values = stack_slot_bytes == 0
7272 ? mcgraph()->IntPtrConstant(0)
7273 : graph()->NewNode(mcgraph()->machine()->StackSlot(
7274 stack_slot_bytes, kDoubleAlignment));
7275
7276 int offset = 0;
7277 int param_count = static_cast<int>(sig_->parameter_count());
7278 for (int i = 0; i < param_count; ++i) {
7279 wasm::ValueType type = sig_->GetParam(i);
7280 // Start from the parameter with index 1 to drop the instance_node.
7281 // TODO(jkummerow): When a values is a reference type, we should pass it
7282 // in a GC-safe way, not just as a raw pointer.
7283 SetEffect(graph()->NewNode(GetSafeStoreOperator(offset, type), values,
7284 Int32Constant(offset), Param(i + 1), effect(),
7285 control()));
7286 offset += type.value_kind_size();
7287 }
7288
7289 Node* function_node = gasm_->Load(
7290 MachineType::TaggedPointer(), Param(0),
7291 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kCallableOffset));
7292 Node* sfi_data = gasm_->LoadFunctionDataFromJSFunction(function_node);
7293 Node* host_data_foreign =
7294 gasm_->Load(MachineType::AnyTagged(), sfi_data,
7295 wasm::ObjectAccess::ToTagged(
7296 WasmCapiFunctionData::kEmbedderDataOffset));
7297
7298 BuildModifyThreadInWasmFlag(false);
7299 Node* isolate_root = BuildLoadIsolateRoot();
7300 Node* fp_value = graph()->NewNode(mcgraph()->machine()->LoadFramePointer());
7301 gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
7302 kNoWriteBarrier),
7303 isolate_root, Isolate::c_entry_fp_offset(), fp_value);
7304
7305 Node* function = BuildLoadCallTargetFromExportedFunctionData(sfi_data);
7306
7307 // Parameters: Address host_data_foreign, Address arguments.
7308 MachineType host_sig_types[] = {
7309 MachineType::Pointer(), MachineType::Pointer(), MachineType::Pointer()};
7310 MachineSignature host_sig(1, 2, host_sig_types);
7311 Node* return_value =
7312 BuildCCall(&host_sig, function, host_data_foreign, values);
7313
7314 BuildModifyThreadInWasmFlag(true);
7315
7316 Node* old_effect = effect();
7317 Node* exception_branch = graph()->NewNode(
7318 mcgraph()->common()->Branch(BranchHint::kTrue),
7319 gasm_->WordEqual(return_value, mcgraph()->IntPtrConstant(0)),
7320 control());
7321 SetControl(
7322 graph()->NewNode(mcgraph()->common()->IfFalse(), exception_branch));
7323 WasmRethrowExplicitContextDescriptor interface_descriptor;
7324 auto call_descriptor = Linkage::GetStubCallDescriptor(
7325 mcgraph()->zone(), interface_descriptor,
7326 interface_descriptor.GetStackParameterCount(), CallDescriptor::kNoFlags,
7327 Operator::kNoProperties, StubCallMode::kCallWasmRuntimeStub);
7328 Node* call_target = mcgraph()->RelocatableIntPtrConstant(
7329 wasm::WasmCode::kWasmRethrowExplicitContext, RelocInfo::WASM_STUB_CALL);
7330 Node* context = gasm_->Load(
7331 MachineType::TaggedPointer(), Param(0),
7332 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kNativeContextOffset));
7333 gasm_->Call(call_descriptor, call_target, return_value, context);
7334 TerminateThrow(effect(), control());
7335
7336 SetEffectControl(old_effect, graph()->NewNode(mcgraph()->common()->IfTrue(),
7337 exception_branch));
7338 DCHECK_LT(sig_->return_count(), wasm::kV8MaxWasmFunctionReturns);
7339 size_t return_count = sig_->return_count();
7340 if (return_count == 0) {
7341 Return(Int32Constant(0));
7342 } else {
7343 base::SmallVector<Node*, 8> returns(return_count);
7344 offset = 0;
7345 for (size_t i = 0; i < return_count; ++i) {
7346 wasm::ValueType type = sig_->GetReturn(i);
7347 Node* val = SetEffect(
7348 graph()->NewNode(GetSafeLoadOperator(offset, type), values,
7349 Int32Constant(offset), effect(), control()));
7350 returns[i] = val;
7351 offset += type.value_kind_size();
7352 }
7353 Return(base::VectorOf(returns));
7354 }
7355
7356 if (ContainsInt64(sig_)) LowerInt64(kCalledFromWasm);
7357 }
7358
BuildJSFastApiCallWrapper(Handle<JSFunction> target)7359 void BuildJSFastApiCallWrapper(Handle<JSFunction> target) {
7360 // Here 'callable_node' must be equal to 'target' but we cannot pass a
7361 // HeapConstant(target) because WasmCode::Validate() fails with
7362 // Unexpected mode: FULL_EMBEDDED_OBJECT.
7363 Node* callable_node = gasm_->Load(
7364 MachineType::TaggedPointer(), Param(0),
7365 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kCallableOffset));
7366 Node* native_context = gasm_->Load(
7367 MachineType::TaggedPointer(), Param(0),
7368 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kNativeContextOffset));
7369 Node* undefined_node = UndefinedValue();
7370 Node* receiver_node =
7371 BuildReceiverNode(callable_node, native_context, undefined_node);
7372
7373 SharedFunctionInfo shared = target->shared();
7374 FunctionTemplateInfo api_func_data = shared.get_api_func_data();
7375 const Address c_address = api_func_data.GetCFunction(0);
7376 const v8::CFunctionInfo* c_signature = api_func_data.GetCSignature(0);
7377 int c_arg_count = c_signature->ArgumentCount();
7378
7379 BuildModifyThreadInWasmFlag(false);
7380
7381 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
7382 Address c_functions[] = {c_address};
7383 const v8::CFunctionInfo* const c_signatures[] = {c_signature};
7384 target->GetIsolate()->simulator_data()->RegisterFunctionsAndSignatures(
7385 c_functions, c_signatures, 1);
7386 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
7387
7388 MachineSignature::Builder builder(graph()->zone(), 1, c_arg_count);
7389 builder.AddReturn(MachineType::TypeForCType(c_signature->ReturnInfo()));
7390 for (int i = 0; i < c_arg_count; i += 1) {
7391 builder.AddParam(MachineType::TypeForCType(c_signature->ArgumentInfo(i)));
7392 }
7393
7394 base::SmallVector<Node*, 16> args(c_arg_count + 3);
7395 int pos = 0;
7396
7397 args[pos++] = mcgraph()->ExternalConstant(
7398 ExternalReference::Create(c_address, ExternalReference::FAST_C_CALL));
7399
7400 auto store_stack = [this](Node* node) -> Node* {
7401 constexpr int kAlign = alignof(uintptr_t);
7402 constexpr int kSize = sizeof(uintptr_t);
7403 Node* stack_slot = gasm_->StackSlot(kSize, kAlign);
7404
7405 gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
7406 kNoWriteBarrier),
7407 stack_slot, 0, node);
7408 return stack_slot;
7409 };
7410
7411 // Set receiver.
7412 args[pos++] = store_stack(receiver_node);
7413
7414 for (int i = 1; i < c_arg_count; i += 1) {
7415 switch (c_signature->ArgumentInfo(i).GetType()) {
7416 case CTypeInfo::Type::kV8Value:
7417 args[pos++] = store_stack(Param(i));
7418 break;
7419 default:
7420 args[pos++] = Param(i);
7421 break;
7422 }
7423 }
7424 DCHECK(!c_signature->HasOptions());
7425 args[pos++] = effect();
7426 args[pos++] = control();
7427
7428 auto call_descriptor =
7429 Linkage::GetSimplifiedCDescriptor(graph()->zone(), builder.Build());
7430
7431 // CPU profiler support.
7432 Node* target_address = gasm_->ExternalConstant(
7433 ExternalReference::fast_api_call_target_address(target->GetIsolate()));
7434 gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
7435 kNoWriteBarrier),
7436 target_address, 0, gasm_->IntPtrConstant(c_address));
7437
7438 // Disable JS execution.
7439 Node* javascript_execution_assert = gasm_->ExternalConstant(
7440 ExternalReference::javascript_execution_assert(target->GetIsolate()));
7441 gasm_->Store(
7442 StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier),
7443 javascript_execution_assert, 0, gasm_->Int32Constant(0));
7444
7445 // Execute the fast call API.
7446 Node* call = gasm_->Call(call_descriptor, pos, args.begin());
7447
7448 // Reenable JS execution.
7449 gasm_->Store(
7450 StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier),
7451 javascript_execution_assert, 0, gasm_->Int32Constant(1));
7452
7453 // Reset the CPU profiler target address.
7454 gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
7455 kNoWriteBarrier),
7456 target_address, 0, gasm_->IntPtrConstant(0));
7457
7458 BuildModifyThreadInWasmFlag(true);
7459
7460 Return(call);
7461 }
7462
BuildJSToJSWrapper()7463 void BuildJSToJSWrapper() {
7464 int wasm_count = static_cast<int>(sig_->parameter_count());
7465
7466 // Build the start and the parameter nodes.
7467 int param_count = 1 /* closure */ + 1 /* receiver */ + wasm_count +
7468 1 /* new.target */ + 1 /* #arg */ + 1 /* context */;
7469 Start(param_count);
7470 Node* closure = Param(Linkage::kJSCallClosureParamIndex);
7471 Node* context = Param(Linkage::GetJSCallContextParamIndex(wasm_count + 1));
7472
7473 // Throw a TypeError if the signature is incompatible with JavaScript.
7474 if (!wasm::IsJSCompatibleSignature(sig_, module_, enabled_features_)) {
7475 BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, context,
7476 nullptr, 0);
7477 TerminateThrow(effect(), control());
7478 return;
7479 }
7480
7481 // Load the original callable from the closure.
7482 Node* func_data = gasm_->LoadFunctionDataFromJSFunction(closure);
7483 Node* internal = gasm_->LoadFromObject(
7484 MachineType::AnyTagged(), func_data,
7485 wasm::ObjectAccess::ToTagged(WasmFunctionData::kInternalOffset));
7486 Node* ref = gasm_->LoadFromObject(
7487 MachineType::AnyTagged(), internal,
7488 wasm::ObjectAccess::ToTagged(WasmInternalFunction::kRefOffset));
7489 Node* callable = gasm_->LoadFromObject(
7490 MachineType::AnyTagged(), ref,
7491 wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kCallableOffset));
7492
7493 // Call the underlying closure.
7494 base::SmallVector<Node*, 16> args(wasm_count + 7);
7495 int pos = 0;
7496 args[pos++] = gasm_->GetBuiltinPointerTarget(Builtin::kCall_ReceiverIsAny);
7497 args[pos++] = callable;
7498 args[pos++] =
7499 Int32Constant(JSParameterCount(wasm_count)); // argument count
7500 args[pos++] = UndefinedValue(); // receiver
7501
7502 auto call_descriptor = Linkage::GetStubCallDescriptor(
7503 graph()->zone(), CallTrampolineDescriptor{}, wasm_count + 1,
7504 CallDescriptor::kNoFlags, Operator::kNoProperties,
7505 StubCallMode::kCallBuiltinPointer);
7506
7507 // Convert parameter JS values to wasm numbers and back to JS values.
7508 for (int i = 0; i < wasm_count; ++i) {
7509 Node* param = Param(i + 1); // Start from index 1 to skip receiver.
7510 args[pos++] = ToJS(FromJS(param, context, sig_->GetParam(i)),
7511 sig_->GetParam(i), context);
7512 }
7513
7514 args[pos++] = context;
7515 args[pos++] = effect();
7516 args[pos++] = control();
7517
7518 DCHECK_EQ(pos, args.size());
7519 Node* call = gasm_->Call(call_descriptor, pos, args.begin());
7520
7521 // Convert return JS values to wasm numbers and back to JS values.
7522 Node* jsval;
7523 if (sig_->return_count() == 0) {
7524 jsval = UndefinedValue();
7525 } else if (sig_->return_count() == 1) {
7526 jsval = ToJS(FromJS(call, context, sig_->GetReturn()), sig_->GetReturn(),
7527 context);
7528 } else {
7529 Node* fixed_array =
7530 BuildMultiReturnFixedArrayFromIterable(sig_, call, context);
7531 int32_t return_count = static_cast<int32_t>(sig_->return_count());
7532 Node* size = gasm_->NumberConstant(return_count);
7533 jsval = BuildCallAllocateJSArray(size, context);
7534 Node* result_fixed_array = gasm_->LoadJSArrayElements(jsval);
7535 for (unsigned i = 0; i < sig_->return_count(); ++i) {
7536 const auto& type = sig_->GetReturn(i);
7537 Node* elem = gasm_->LoadFixedArrayElementAny(fixed_array, i);
7538 Node* cast = ToJS(FromJS(elem, context, type), type, context);
7539 gasm_->StoreFixedArrayElementAny(result_fixed_array, i, cast);
7540 }
7541 }
7542 Return(jsval);
7543 }
7544
BuildCWasmEntry()7545 void BuildCWasmEntry() {
7546 // +1 offset for first parameter index being -1.
7547 Start(CWasmEntryParameters::kNumParameters + 1);
7548
7549 Node* code_entry = Param(CWasmEntryParameters::kCodeEntry);
7550 Node* object_ref = Param(CWasmEntryParameters::kObjectRef);
7551 Node* arg_buffer = Param(CWasmEntryParameters::kArgumentsBuffer);
7552 Node* c_entry_fp = Param(CWasmEntryParameters::kCEntryFp);
7553
7554 Node* fp_value = graph()->NewNode(mcgraph()->machine()->LoadFramePointer());
7555 gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
7556 kNoWriteBarrier),
7557 fp_value, TypedFrameConstants::kFirstPushedFrameValueOffset,
7558 c_entry_fp);
7559
7560 int wasm_arg_count = static_cast<int>(sig_->parameter_count());
7561 base::SmallVector<Node*, 16> args(wasm_arg_count + 4);
7562
7563 int pos = 0;
7564 args[pos++] = code_entry;
7565 args[pos++] = object_ref;
7566
7567 int offset = 0;
7568 for (wasm::ValueType type : sig_->parameters()) {
7569 Node* arg_load = SetEffect(
7570 graph()->NewNode(GetSafeLoadOperator(offset, type), arg_buffer,
7571 Int32Constant(offset), effect(), control()));
7572 args[pos++] = arg_load;
7573 offset += type.value_kind_size();
7574 }
7575
7576 args[pos++] = effect();
7577 args[pos++] = control();
7578
7579 // Call the wasm code.
7580 auto call_descriptor = GetWasmCallDescriptor(mcgraph()->zone(), sig_);
7581
7582 DCHECK_EQ(pos, args.size());
7583 Node* call = gasm_->Call(call_descriptor, pos, args.begin());
7584
7585 Node* if_success = graph()->NewNode(mcgraph()->common()->IfSuccess(), call);
7586 Node* if_exception =
7587 graph()->NewNode(mcgraph()->common()->IfException(), call, call);
7588
7589 // Handle exception: return it.
7590 SetControl(if_exception);
7591 Return(if_exception);
7592
7593 // Handle success: store the return value(s).
7594 SetControl(if_success);
7595 pos = 0;
7596 offset = 0;
7597 for (wasm::ValueType type : sig_->returns()) {
7598 Node* value = sig_->return_count() == 1
7599 ? call
7600 : graph()->NewNode(mcgraph()->common()->Projection(pos),
7601 call, control());
7602 SetEffect(graph()->NewNode(GetSafeStoreOperator(offset, type), arg_buffer,
7603 Int32Constant(offset), value, effect(),
7604 control()));
7605 offset += type.value_kind_size();
7606 pos++;
7607 }
7608
7609 Return(mcgraph()->IntPtrConstant(0));
7610
7611 if (mcgraph()->machine()->Is32() && ContainsInt64(sig_)) {
7612 // No special lowering should be requested in the C entry.
7613 DCHECK_NULL(lowering_special_case_);
7614
7615 MachineRepresentation sig_reps[] = {
7616 MachineType::PointerRepresentation(), // return value
7617 MachineType::PointerRepresentation(), // target
7618 MachineRepresentation::kTagged, // object_ref
7619 MachineType::PointerRepresentation(), // argv
7620 MachineType::PointerRepresentation() // c_entry_fp
7621 };
7622 Signature<MachineRepresentation> c_entry_sig(1, 4, sig_reps);
7623 Int64Lowering r(mcgraph()->graph(), mcgraph()->machine(),
7624 mcgraph()->common(), gasm_->simplified(),
7625 mcgraph()->zone(), &c_entry_sig);
7626 r.LowerGraph();
7627 }
7628 }
7629
7630 private:
7631 const wasm::WasmModule* module_;
7632 StubCallMode stub_mode_;
7633 SetOncePointer<const Operator> int32_to_heapnumber_operator_;
7634 SetOncePointer<const Operator> tagged_non_smi_to_int32_operator_;
7635 SetOncePointer<const Operator> float32_to_number_operator_;
7636 SetOncePointer<const Operator> float64_to_number_operator_;
7637 SetOncePointer<const Operator> tagged_to_float64_operator_;
7638 wasm::WasmFeatures enabled_features_;
7639 CallDescriptor* bigint_to_i64_descriptor_ = nullptr;
7640 CallDescriptor* i64_to_bigint_descriptor_ = nullptr;
7641 };
7642
7643 } // namespace
7644
BuildInlinedJSToWasmWrapper(Zone * zone,MachineGraph * mcgraph,const wasm::FunctionSig * signature,const wasm::WasmModule * module,Isolate * isolate,compiler::SourcePositionTable * spt,StubCallMode stub_mode,wasm::WasmFeatures features,const JSWasmCallData * js_wasm_call_data,Node * frame_state)7645 void BuildInlinedJSToWasmWrapper(
7646 Zone* zone, MachineGraph* mcgraph, const wasm::FunctionSig* signature,
7647 const wasm::WasmModule* module, Isolate* isolate,
7648 compiler::SourcePositionTable* spt, StubCallMode stub_mode,
7649 wasm::WasmFeatures features, const JSWasmCallData* js_wasm_call_data,
7650 Node* frame_state) {
7651 WasmWrapperGraphBuilder builder(zone, mcgraph, signature, module,
7652 WasmGraphBuilder::kNoSpecialParameterMode,
7653 isolate, spt, stub_mode, features);
7654 builder.BuildJSToWasmWrapper(false, js_wasm_call_data, frame_state);
7655 }
7656
NewJSToWasmCompilationJob(Isolate * isolate,const wasm::FunctionSig * sig,const wasm::WasmModule * module,bool is_import,const wasm::WasmFeatures & enabled_features)7657 std::unique_ptr<TurbofanCompilationJob> NewJSToWasmCompilationJob(
7658 Isolate* isolate, const wasm::FunctionSig* sig,
7659 const wasm::WasmModule* module, bool is_import,
7660 const wasm::WasmFeatures& enabled_features) {
7661 //----------------------------------------------------------------------------
7662 // Create the Graph.
7663 //----------------------------------------------------------------------------
7664 std::unique_ptr<Zone> zone = std::make_unique<Zone>(
7665 wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
7666 Graph* graph = zone->New<Graph>(zone.get());
7667 CommonOperatorBuilder* common = zone->New<CommonOperatorBuilder>(zone.get());
7668 MachineOperatorBuilder* machine = zone->New<MachineOperatorBuilder>(
7669 zone.get(), MachineType::PointerRepresentation(),
7670 InstructionSelector::SupportedMachineOperatorFlags(),
7671 InstructionSelector::AlignmentRequirements());
7672 MachineGraph* mcgraph = zone->New<MachineGraph>(graph, common, machine);
7673
7674 WasmWrapperGraphBuilder builder(
7675 zone.get(), mcgraph, sig, module,
7676 WasmGraphBuilder::kNoSpecialParameterMode, isolate, nullptr,
7677 StubCallMode::kCallBuiltinPointer, enabled_features);
7678 builder.BuildJSToWasmWrapper(is_import);
7679
7680 //----------------------------------------------------------------------------
7681 // Create the compilation job.
7682 //----------------------------------------------------------------------------
7683 std::unique_ptr<char[]> debug_name = WasmExportedFunction::GetDebugName(sig);
7684
7685 int params = static_cast<int>(sig->parameter_count());
7686 CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
7687 zone.get(), false, params + 1, CallDescriptor::kNoFlags);
7688
7689 return Pipeline::NewWasmHeapStubCompilationJob(
7690 isolate, incoming, std::move(zone), graph, CodeKind::JS_TO_WASM_FUNCTION,
7691 std::move(debug_name), WasmAssemblerOptions());
7692 }
7693
NormalizeFastApiRepresentation(const CTypeInfo & info)7694 static MachineRepresentation NormalizeFastApiRepresentation(
7695 const CTypeInfo& info) {
7696 MachineType t = MachineType::TypeForCType(info);
7697 // Wasm representation of bool is i32 instead of i1.
7698 if (t.semantic() == MachineSemantic::kBool) {
7699 return MachineRepresentation::kWord32;
7700 }
7701 return t.representation();
7702 }
7703
IsSupportedWasmFastApiFunction(const wasm::FunctionSig * expected_sig,Handle<SharedFunctionInfo> shared)7704 static bool IsSupportedWasmFastApiFunction(
7705 const wasm::FunctionSig* expected_sig, Handle<SharedFunctionInfo> shared) {
7706 if (!shared->IsApiFunction()) {
7707 return false;
7708 }
7709 if (shared->get_api_func_data().GetCFunctionsCount() == 0) {
7710 return false;
7711 }
7712 if (!shared->get_api_func_data().accept_any_receiver()) {
7713 return false;
7714 }
7715 if (!shared->get_api_func_data().signature().IsUndefined()) {
7716 // TODO(wasm): CFunctionInfo* signature check.
7717 return false;
7718 }
7719 const CFunctionInfo* info = shared->get_api_func_data().GetCSignature(0);
7720 if (!fast_api_call::CanOptimizeFastSignature(info)) {
7721 return false;
7722 }
7723 // Options are not supported yet.
7724 if (info->HasOptions()) {
7725 return false;
7726 }
7727
7728 const auto log_imported_function_mismatch = [&shared]() {
7729 if (FLAG_trace_opt) {
7730 CodeTracer::Scope scope(shared->GetIsolate()->GetCodeTracer());
7731 PrintF(scope.file(), "[disabled optimization for ");
7732 shared->ShortPrint(scope.file());
7733 PrintF(scope.file(),
7734 ", reason: the signature of the imported function in the Wasm "
7735 "module doesn't match that of the Fast API function]\n");
7736 }
7737 };
7738
7739 // C functions only have one return value.
7740 if (expected_sig->return_count() > 1) {
7741 // Here and below, we log when the function we call is declared as an Api
7742 // function but we cannot optimize the call, which might be unxepected. In
7743 // that case we use the "slow" path making a normal Wasm->JS call and
7744 // calling the "slow" callback specified in FunctionTemplate::New().
7745 log_imported_function_mismatch();
7746 return false;
7747 }
7748 CTypeInfo return_info = info->ReturnInfo();
7749 // Unsupported if return type doesn't match.
7750 if (expected_sig->return_count() == 0 &&
7751 return_info.GetType() != CTypeInfo::Type::kVoid) {
7752 log_imported_function_mismatch();
7753 return false;
7754 }
7755 // Unsupported if return type doesn't match.
7756 if (expected_sig->return_count() == 1) {
7757 if (return_info.GetType() == CTypeInfo::Type::kVoid) {
7758 log_imported_function_mismatch();
7759 return false;
7760 }
7761 if (NormalizeFastApiRepresentation(return_info) !=
7762 expected_sig->GetReturn(0).machine_type().representation()) {
7763 log_imported_function_mismatch();
7764 return false;
7765 }
7766 }
7767 // Unsupported if arity doesn't match.
7768 if (expected_sig->parameter_count() != info->ArgumentCount() - 1) {
7769 log_imported_function_mismatch();
7770 return false;
7771 }
7772 // Unsupported if any argument types don't match.
7773 for (unsigned int i = 0; i < expected_sig->parameter_count(); i += 1) {
7774 // Arg 0 is the receiver, skip over it since wasm doesn't
7775 // have a concept of receivers.
7776 CTypeInfo arg = info->ArgumentInfo(i + 1);
7777 if (NormalizeFastApiRepresentation(arg) !=
7778 expected_sig->GetParam(i).machine_type().representation()) {
7779 log_imported_function_mismatch();
7780 return false;
7781 }
7782 }
7783 return true;
7784 }
7785
ResolveWasmImportCall(Handle<JSReceiver> callable,const wasm::FunctionSig * expected_sig,const wasm::WasmModule * module,const wasm::WasmFeatures & enabled_features)7786 WasmImportData ResolveWasmImportCall(
7787 Handle<JSReceiver> callable, const wasm::FunctionSig* expected_sig,
7788 const wasm::WasmModule* module,
7789 const wasm::WasmFeatures& enabled_features) {
7790 Isolate* isolate = callable->GetIsolate();
7791 Handle<HeapObject> no_suspender;
7792 if (WasmExportedFunction::IsWasmExportedFunction(*callable)) {
7793 auto imported_function = Handle<WasmExportedFunction>::cast(callable);
7794 if (!imported_function->MatchesSignature(module, expected_sig)) {
7795 return {WasmImportCallKind::kLinkError, callable, no_suspender};
7796 }
7797 uint32_t func_index =
7798 static_cast<uint32_t>(imported_function->function_index());
7799 if (func_index >=
7800 imported_function->instance().module()->num_imported_functions) {
7801 return {WasmImportCallKind::kWasmToWasm, callable, no_suspender};
7802 }
7803 // Resolve the shortcut to the underlying callable and continue.
7804 Handle<WasmInstanceObject> instance(imported_function->instance(), isolate);
7805 ImportedFunctionEntry entry(instance, func_index);
7806 callable = handle(entry.callable(), isolate);
7807 }
7808 Handle<HeapObject> suspender = isolate->factory()->undefined_value();
7809 if (WasmJSFunction::IsWasmJSFunction(*callable)) {
7810 auto js_function = Handle<WasmJSFunction>::cast(callable);
7811 suspender = handle(js_function->GetSuspender(), isolate);
7812 if ((!suspender->IsUndefined() &&
7813 !js_function->MatchesSignatureForSuspend(expected_sig)) ||
7814 (suspender->IsUndefined() &&
7815 !js_function->MatchesSignature(expected_sig))) {
7816 return {WasmImportCallKind::kLinkError, callable, no_suspender};
7817 }
7818 // Resolve the short-cut to the underlying callable and continue.
7819 callable = handle(js_function->GetCallable(), isolate);
7820 }
7821 if (WasmCapiFunction::IsWasmCapiFunction(*callable)) {
7822 auto capi_function = Handle<WasmCapiFunction>::cast(callable);
7823 if (!capi_function->MatchesSignature(expected_sig)) {
7824 return {WasmImportCallKind::kLinkError, callable, no_suspender};
7825 }
7826 return {WasmImportCallKind::kWasmToCapi, callable, no_suspender};
7827 }
7828 // Assuming we are calling to JS, check whether this would be a runtime error.
7829 if (!wasm::IsJSCompatibleSignature(expected_sig, module, enabled_features)) {
7830 return {WasmImportCallKind::kRuntimeTypeError, callable, no_suspender};
7831 }
7832 // Check if this can be a JS fast API call.
7833 if (FLAG_turbo_fast_api_calls &&
7834 (callable->IsJSFunction() || callable->IsJSBoundFunction())) {
7835 Handle<JSFunction> target;
7836 if (callable->IsJSBoundFunction()) {
7837 Handle<JSBoundFunction> bound_target =
7838 Handle<JSBoundFunction>::cast(callable);
7839 // Nested bound functions and arguments not supported yet.
7840 if (bound_target->bound_arguments().length() == 0 &&
7841 !bound_target->bound_target_function().IsJSBoundFunction()) {
7842 Handle<JSReceiver> bound_target_function = handle(
7843 bound_target->bound_target_function(), callable->GetIsolate());
7844 if (bound_target_function->IsJSFunction()) {
7845 target = Handle<JSFunction>::cast(bound_target_function);
7846 }
7847 }
7848 } else {
7849 DCHECK(callable->IsJSFunction());
7850 target = Handle<JSFunction>::cast(callable);
7851 }
7852
7853 if (!target.is_null()) {
7854 Handle<SharedFunctionInfo> shared(target->shared(), target->GetIsolate());
7855
7856 if (IsSupportedWasmFastApiFunction(expected_sig, shared)) {
7857 return {WasmImportCallKind::kWasmToJSFastApi, target, no_suspender};
7858 }
7859 }
7860 }
7861 // For JavaScript calls, determine whether the target has an arity match.
7862 if (callable->IsJSFunction()) {
7863 Handle<JSFunction> function = Handle<JSFunction>::cast(callable);
7864 Handle<SharedFunctionInfo> shared(function->shared(),
7865 function->GetIsolate());
7866
7867 // Check for math intrinsics.
7868 #define COMPARE_SIG_FOR_BUILTIN(name) \
7869 { \
7870 const wasm::FunctionSig* sig = \
7871 wasm::WasmOpcodes::Signature(wasm::kExpr##name); \
7872 if (!sig) sig = wasm::WasmOpcodes::AsmjsSignature(wasm::kExpr##name); \
7873 DCHECK_NOT_NULL(sig); \
7874 if (*expected_sig == *sig) { \
7875 return {WasmImportCallKind::k##name, callable, no_suspender}; \
7876 } \
7877 }
7878 #define COMPARE_SIG_FOR_BUILTIN_F64(name) \
7879 case Builtin::kMath##name: \
7880 COMPARE_SIG_FOR_BUILTIN(F64##name); \
7881 break;
7882 #define COMPARE_SIG_FOR_BUILTIN_F32_F64(name) \
7883 case Builtin::kMath##name: \
7884 COMPARE_SIG_FOR_BUILTIN(F64##name); \
7885 COMPARE_SIG_FOR_BUILTIN(F32##name); \
7886 break;
7887
7888 if (FLAG_wasm_math_intrinsics && shared->HasBuiltinId()) {
7889 switch (shared->builtin_id()) {
7890 COMPARE_SIG_FOR_BUILTIN_F64(Acos);
7891 COMPARE_SIG_FOR_BUILTIN_F64(Asin);
7892 COMPARE_SIG_FOR_BUILTIN_F64(Atan);
7893 COMPARE_SIG_FOR_BUILTIN_F64(Cos);
7894 COMPARE_SIG_FOR_BUILTIN_F64(Sin);
7895 COMPARE_SIG_FOR_BUILTIN_F64(Tan);
7896 COMPARE_SIG_FOR_BUILTIN_F64(Exp);
7897 COMPARE_SIG_FOR_BUILTIN_F64(Log);
7898 COMPARE_SIG_FOR_BUILTIN_F64(Atan2);
7899 COMPARE_SIG_FOR_BUILTIN_F64(Pow);
7900 COMPARE_SIG_FOR_BUILTIN_F32_F64(Min);
7901 COMPARE_SIG_FOR_BUILTIN_F32_F64(Max);
7902 COMPARE_SIG_FOR_BUILTIN_F32_F64(Abs);
7903 COMPARE_SIG_FOR_BUILTIN_F32_F64(Ceil);
7904 COMPARE_SIG_FOR_BUILTIN_F32_F64(Floor);
7905 COMPARE_SIG_FOR_BUILTIN_F32_F64(Sqrt);
7906 case Builtin::kMathFround:
7907 COMPARE_SIG_FOR_BUILTIN(F32ConvertF64);
7908 break;
7909 default:
7910 break;
7911 }
7912 }
7913
7914 #undef COMPARE_SIG_FOR_BUILTIN
7915 #undef COMPARE_SIG_FOR_BUILTIN_F64
7916 #undef COMPARE_SIG_FOR_BUILTIN_F32_F64
7917
7918 if (IsClassConstructor(shared->kind())) {
7919 // Class constructor will throw anyway.
7920 return {WasmImportCallKind::kUseCallBuiltin, callable, suspender};
7921 }
7922
7923 if (shared->internal_formal_parameter_count_without_receiver() ==
7924 expected_sig->parameter_count()) {
7925 return {WasmImportCallKind::kJSFunctionArityMatch, callable, suspender};
7926 }
7927
7928 // If function isn't compiled, compile it now.
7929 Isolate* isolate = callable->GetIsolate();
7930 IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
7931 if (!is_compiled_scope.is_compiled()) {
7932 Compiler::Compile(isolate, function, Compiler::CLEAR_EXCEPTION,
7933 &is_compiled_scope);
7934 }
7935
7936 return {WasmImportCallKind::kJSFunctionArityMismatch, callable, suspender};
7937 }
7938 // Unknown case. Use the call builtin.
7939 return {WasmImportCallKind::kUseCallBuiltin, callable, suspender};
7940 }
7941
7942 namespace {
7943
GetMathIntrinsicOpcode(WasmImportCallKind kind,const char ** name_ptr)7944 wasm::WasmOpcode GetMathIntrinsicOpcode(WasmImportCallKind kind,
7945 const char** name_ptr) {
7946 #define CASE(name) \
7947 case WasmImportCallKind::k##name: \
7948 *name_ptr = "WasmMathIntrinsic:" #name; \
7949 return wasm::kExpr##name
7950 switch (kind) {
7951 CASE(F64Acos);
7952 CASE(F64Asin);
7953 CASE(F64Atan);
7954 CASE(F64Cos);
7955 CASE(F64Sin);
7956 CASE(F64Tan);
7957 CASE(F64Exp);
7958 CASE(F64Log);
7959 CASE(F64Atan2);
7960 CASE(F64Pow);
7961 CASE(F64Ceil);
7962 CASE(F64Floor);
7963 CASE(F64Sqrt);
7964 CASE(F64Min);
7965 CASE(F64Max);
7966 CASE(F64Abs);
7967 CASE(F32Min);
7968 CASE(F32Max);
7969 CASE(F32Abs);
7970 CASE(F32Ceil);
7971 CASE(F32Floor);
7972 CASE(F32Sqrt);
7973 CASE(F32ConvertF64);
7974 default:
7975 UNREACHABLE();
7976 }
7977 #undef CASE
7978 }
7979
CompileWasmMathIntrinsic(WasmImportCallKind kind,const wasm::FunctionSig * sig)7980 wasm::WasmCompilationResult CompileWasmMathIntrinsic(
7981 WasmImportCallKind kind, const wasm::FunctionSig* sig) {
7982 DCHECK_EQ(1, sig->return_count());
7983
7984 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
7985 "wasm.CompileWasmMathIntrinsic");
7986
7987 Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
7988
7989 // Compile a Wasm function with a single bytecode and let TurboFan
7990 // generate either inlined machine code or a call to a helper.
7991 SourcePositionTable* source_positions = nullptr;
7992 MachineGraph* mcgraph = zone.New<MachineGraph>(
7993 zone.New<Graph>(&zone), zone.New<CommonOperatorBuilder>(&zone),
7994 zone.New<MachineOperatorBuilder>(
7995 &zone, MachineType::PointerRepresentation(),
7996 InstructionSelector::SupportedMachineOperatorFlags(),
7997 InstructionSelector::AlignmentRequirements()));
7998
7999 wasm::CompilationEnv env(
8000 nullptr, wasm::kNoBoundsChecks,
8001 wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport,
8002 wasm::WasmFeatures::All(), wasm::DynamicTiering::kDisabled);
8003
8004 WasmGraphBuilder builder(&env, mcgraph->zone(), mcgraph, sig,
8005 source_positions);
8006
8007 // Set up the graph start.
8008 builder.Start(static_cast<int>(sig->parameter_count() + 1 + 1));
8009
8010 // Generate either a unop or a binop.
8011 Node* node = nullptr;
8012 const char* debug_name = "WasmMathIntrinsic";
8013 auto opcode = GetMathIntrinsicOpcode(kind, &debug_name);
8014 switch (sig->parameter_count()) {
8015 case 1:
8016 node = builder.Unop(opcode, builder.Param(1));
8017 break;
8018 case 2:
8019 node = builder.Binop(opcode, builder.Param(1), builder.Param(2));
8020 break;
8021 default:
8022 UNREACHABLE();
8023 }
8024
8025 builder.Return(node);
8026
8027 // Run the compiler pipeline to generate machine code.
8028 auto call_descriptor = GetWasmCallDescriptor(&zone, sig);
8029 if (mcgraph->machine()->Is32()) {
8030 call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
8031 }
8032
8033 // The code does not call to JS, but conceptually it is an import wrapper,
8034 // hence use {WASM_TO_JS_FUNCTION} here.
8035 // TODO(wasm): Rename this to {WASM_IMPORT_CALL}?
8036 return Pipeline::GenerateCodeForWasmNativeStub(
8037 call_descriptor, mcgraph, CodeKind::WASM_TO_JS_FUNCTION, debug_name,
8038 WasmStubAssemblerOptions(), source_positions);
8039 }
8040
8041 } // namespace
8042
CompileWasmImportCallWrapper(wasm::CompilationEnv * env,WasmImportCallKind kind,const wasm::FunctionSig * sig,bool source_positions,int expected_arity,wasm::Suspend suspend)8043 wasm::WasmCompilationResult CompileWasmImportCallWrapper(
8044 wasm::CompilationEnv* env, WasmImportCallKind kind,
8045 const wasm::FunctionSig* sig, bool source_positions, int expected_arity,
8046 wasm::Suspend suspend) {
8047 DCHECK_NE(WasmImportCallKind::kLinkError, kind);
8048 DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
8049 DCHECK_NE(WasmImportCallKind::kWasmToJSFastApi, kind);
8050
8051 // Check for math intrinsics first.
8052 if (FLAG_wasm_math_intrinsics &&
8053 kind >= WasmImportCallKind::kFirstMathIntrinsic &&
8054 kind <= WasmImportCallKind::kLastMathIntrinsic) {
8055 return CompileWasmMathIntrinsic(kind, sig);
8056 }
8057
8058 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
8059 "wasm.CompileWasmImportCallWrapper");
8060 base::TimeTicks start_time;
8061 if (V8_UNLIKELY(FLAG_trace_wasm_compilation_times)) {
8062 start_time = base::TimeTicks::Now();
8063 }
8064
8065 //----------------------------------------------------------------------------
8066 // Create the Graph
8067 //----------------------------------------------------------------------------
8068 Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
8069 Graph* graph = zone.New<Graph>(&zone);
8070 CommonOperatorBuilder* common = zone.New<CommonOperatorBuilder>(&zone);
8071 MachineOperatorBuilder* machine = zone.New<MachineOperatorBuilder>(
8072 &zone, MachineType::PointerRepresentation(),
8073 InstructionSelector::SupportedMachineOperatorFlags(),
8074 InstructionSelector::AlignmentRequirements());
8075 MachineGraph* mcgraph = zone.New<MachineGraph>(graph, common, machine);
8076
8077 SourcePositionTable* source_position_table =
8078 source_positions ? zone.New<SourcePositionTable>(graph) : nullptr;
8079
8080 WasmWrapperGraphBuilder builder(
8081 &zone, mcgraph, sig, env->module,
8082 WasmGraphBuilder::kWasmApiFunctionRefMode, nullptr, source_position_table,
8083 StubCallMode::kCallWasmRuntimeStub, env->enabled_features);
8084 builder.BuildWasmToJSWrapper(kind, expected_arity, suspend);
8085
8086 // Build a name in the form "wasm-to-js-<kind>-<signature>".
8087 constexpr size_t kMaxNameLen = 128;
8088 char func_name[kMaxNameLen];
8089 int name_prefix_len = SNPrintF(base::VectorOf(func_name, kMaxNameLen),
8090 "wasm-to-js-%d-", static_cast<int>(kind));
8091 PrintSignature(base::VectorOf(func_name, kMaxNameLen) + name_prefix_len, sig,
8092 '-');
8093
8094 // Schedule and compile to machine code.
8095 CallDescriptor* incoming =
8096 GetWasmCallDescriptor(&zone, sig, WasmCallKind::kWasmImportWrapper);
8097 if (machine->Is32()) {
8098 incoming = GetI32WasmCallDescriptor(&zone, incoming);
8099 }
8100 wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
8101 incoming, mcgraph, CodeKind::WASM_TO_JS_FUNCTION, func_name,
8102 WasmStubAssemblerOptions(), source_position_table);
8103
8104 if (V8_UNLIKELY(FLAG_trace_wasm_compilation_times)) {
8105 base::TimeDelta time = base::TimeTicks::Now() - start_time;
8106 int codesize = result.code_desc.body_size();
8107 StdoutStream{} << "Compiled WasmToJS wrapper " << func_name << ", took "
8108 << time.InMilliseconds() << " ms; codesize " << codesize
8109 << std::endl;
8110 }
8111
8112 return result;
8113 }
8114
CompileWasmCapiCallWrapper(wasm::NativeModule * native_module,const wasm::FunctionSig * sig)8115 wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::NativeModule* native_module,
8116 const wasm::FunctionSig* sig) {
8117 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
8118 "wasm.CompileWasmCapiFunction");
8119
8120 Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
8121
8122 // TODO(jkummerow): Extract common code into helper method.
8123 SourcePositionTable* source_positions = nullptr;
8124 MachineGraph* mcgraph = zone.New<MachineGraph>(
8125 zone.New<Graph>(&zone), zone.New<CommonOperatorBuilder>(&zone),
8126 zone.New<MachineOperatorBuilder>(
8127 &zone, MachineType::PointerRepresentation(),
8128 InstructionSelector::SupportedMachineOperatorFlags(),
8129 InstructionSelector::AlignmentRequirements()));
8130
8131 WasmWrapperGraphBuilder builder(
8132 &zone, mcgraph, sig, native_module->module(),
8133 WasmGraphBuilder::kWasmApiFunctionRefMode, nullptr, source_positions,
8134 StubCallMode::kCallWasmRuntimeStub, native_module->enabled_features());
8135
8136 builder.BuildCapiCallWrapper();
8137
8138 // Run the compiler pipeline to generate machine code.
8139 CallDescriptor* call_descriptor =
8140 GetWasmCallDescriptor(&zone, sig, WasmCallKind::kWasmCapiFunction);
8141 if (mcgraph->machine()->Is32()) {
8142 call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
8143 }
8144
8145 const char* debug_name = "WasmCapiCall";
8146 wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
8147 call_descriptor, mcgraph, CodeKind::WASM_TO_CAPI_FUNCTION, debug_name,
8148 WasmStubAssemblerOptions(), source_positions);
8149 wasm::WasmCode* published_code;
8150 {
8151 wasm::CodeSpaceWriteScope code_space_write_scope(native_module);
8152 std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
8153 wasm::kAnonymousFuncIndex, result.code_desc, result.frame_slot_count,
8154 result.tagged_parameter_slots,
8155 result.protected_instructions_data.as_vector(),
8156 result.source_positions.as_vector(), wasm::WasmCode::kWasmToCapiWrapper,
8157 wasm::ExecutionTier::kNone, wasm::kNoDebugging);
8158 published_code = native_module->PublishCode(std::move(wasm_code));
8159 }
8160 return published_code;
8161 }
8162
CompileWasmJSFastCallWrapper(wasm::NativeModule * native_module,const wasm::FunctionSig * sig,Handle<JSFunction> target)8163 wasm::WasmCode* CompileWasmJSFastCallWrapper(wasm::NativeModule* native_module,
8164 const wasm::FunctionSig* sig,
8165 Handle<JSFunction> target) {
8166 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
8167 "wasm.CompileWasmJSFastCallWrapper");
8168
8169 Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
8170
8171 // TODO(jkummerow): Extract common code into helper method.
8172 SourcePositionTable* source_positions = nullptr;
8173 MachineGraph* mcgraph = zone.New<MachineGraph>(
8174 zone.New<Graph>(&zone), zone.New<CommonOperatorBuilder>(&zone),
8175 zone.New<MachineOperatorBuilder>(
8176 &zone, MachineType::PointerRepresentation(),
8177 InstructionSelector::SupportedMachineOperatorFlags(),
8178 InstructionSelector::AlignmentRequirements()));
8179
8180 WasmWrapperGraphBuilder builder(
8181 &zone, mcgraph, sig, native_module->module(),
8182 WasmGraphBuilder::kWasmApiFunctionRefMode, nullptr, source_positions,
8183 StubCallMode::kCallWasmRuntimeStub, native_module->enabled_features());
8184
8185 // Set up the graph start.
8186 int param_count = static_cast<int>(sig->parameter_count()) +
8187 1 /* offset for first parameter index being -1 */ +
8188 1 /* Wasm instance */ + 1 /* kExtraCallableParam */;
8189 builder.Start(param_count);
8190 builder.BuildJSFastApiCallWrapper(target);
8191
8192 // Run the compiler pipeline to generate machine code.
8193 CallDescriptor* call_descriptor =
8194 GetWasmCallDescriptor(&zone, sig, WasmCallKind::kWasmImportWrapper);
8195 if (mcgraph->machine()->Is32()) {
8196 call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
8197 }
8198
8199 const char* debug_name = "WasmJSFastApiCall";
8200 wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
8201 call_descriptor, mcgraph, CodeKind::WASM_TO_JS_FUNCTION, debug_name,
8202 WasmStubAssemblerOptions(), source_positions);
8203 {
8204 wasm::CodeSpaceWriteScope code_space_write_scope(native_module);
8205 std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
8206 wasm::kAnonymousFuncIndex, result.code_desc, result.frame_slot_count,
8207 result.tagged_parameter_slots,
8208 result.protected_instructions_data.as_vector(),
8209 result.source_positions.as_vector(), wasm::WasmCode::kWasmToJsWrapper,
8210 wasm::ExecutionTier::kNone, wasm::kNoDebugging);
8211 return native_module->PublishCode(std::move(wasm_code));
8212 }
8213 }
8214
CompileWasmToJSWrapper(Isolate * isolate,const wasm::FunctionSig * sig,WasmImportCallKind kind,int expected_arity,wasm::Suspend suspend)8215 MaybeHandle<Code> CompileWasmToJSWrapper(Isolate* isolate,
8216 const wasm::FunctionSig* sig,
8217 WasmImportCallKind kind,
8218 int expected_arity,
8219 wasm::Suspend suspend) {
8220 std::unique_ptr<Zone> zone = std::make_unique<Zone>(
8221 isolate->allocator(), ZONE_NAME, kCompressGraphZone);
8222
8223 // Create the Graph
8224 Graph* graph = zone->New<Graph>(zone.get());
8225 CommonOperatorBuilder* common = zone->New<CommonOperatorBuilder>(zone.get());
8226 MachineOperatorBuilder* machine = zone->New<MachineOperatorBuilder>(
8227 zone.get(), MachineType::PointerRepresentation(),
8228 InstructionSelector::SupportedMachineOperatorFlags(),
8229 InstructionSelector::AlignmentRequirements());
8230 MachineGraph* mcgraph = zone->New<MachineGraph>(graph, common, machine);
8231
8232 WasmWrapperGraphBuilder builder(zone.get(), mcgraph, sig, nullptr,
8233 WasmGraphBuilder::kWasmApiFunctionRefMode,
8234 nullptr, nullptr,
8235 StubCallMode::kCallBuiltinPointer,
8236 wasm::WasmFeatures::FromIsolate(isolate));
8237 builder.BuildWasmToJSWrapper(kind, expected_arity, suspend);
8238
8239 // Build a name in the form "wasm-to-js-<kind>-<signature>".
8240 constexpr size_t kMaxNameLen = 128;
8241 constexpr size_t kNamePrefixLen = 11;
8242 auto name_buffer = std::unique_ptr<char[]>(new char[kMaxNameLen]);
8243 memcpy(name_buffer.get(), "wasm-to-js:", kNamePrefixLen);
8244 PrintSignature(
8245 base::VectorOf(name_buffer.get(), kMaxNameLen) + kNamePrefixLen, sig);
8246
8247 // Generate the call descriptor.
8248 CallDescriptor* incoming =
8249 GetWasmCallDescriptor(zone.get(), sig, WasmCallKind::kWasmImportWrapper);
8250
8251 // Run the compilation job synchronously.
8252 std::unique_ptr<TurbofanCompilationJob> job(
8253 Pipeline::NewWasmHeapStubCompilationJob(
8254 isolate, incoming, std::move(zone), graph,
8255 CodeKind::WASM_TO_JS_FUNCTION, std::move(name_buffer),
8256 AssemblerOptions::Default(isolate)));
8257
8258 // Compile the wrapper
8259 if (job->ExecuteJob(isolate->counters()->runtime_call_stats()) ==
8260 CompilationJob::FAILED ||
8261 job->FinalizeJob(isolate) == CompilationJob::FAILED) {
8262 return Handle<Code>();
8263 }
8264 Handle<Code> code = job->compilation_info()->code();
8265 return code;
8266 }
8267
CompileJSToJSWrapper(Isolate * isolate,const wasm::FunctionSig * sig,const wasm::WasmModule * module)8268 MaybeHandle<Code> CompileJSToJSWrapper(Isolate* isolate,
8269 const wasm::FunctionSig* sig,
8270 const wasm::WasmModule* module) {
8271 std::unique_ptr<Zone> zone = std::make_unique<Zone>(
8272 isolate->allocator(), ZONE_NAME, kCompressGraphZone);
8273 Graph* graph = zone->New<Graph>(zone.get());
8274 CommonOperatorBuilder* common = zone->New<CommonOperatorBuilder>(zone.get());
8275 MachineOperatorBuilder* machine = zone->New<MachineOperatorBuilder>(
8276 zone.get(), MachineType::PointerRepresentation(),
8277 InstructionSelector::SupportedMachineOperatorFlags(),
8278 InstructionSelector::AlignmentRequirements());
8279 MachineGraph* mcgraph = zone->New<MachineGraph>(graph, common, machine);
8280
8281 WasmWrapperGraphBuilder builder(zone.get(), mcgraph, sig, module,
8282 WasmGraphBuilder::kNoSpecialParameterMode,
8283 isolate, nullptr,
8284 StubCallMode::kCallBuiltinPointer,
8285 wasm::WasmFeatures::FromIsolate(isolate));
8286 builder.BuildJSToJSWrapper();
8287
8288 int wasm_count = static_cast<int>(sig->parameter_count());
8289 CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
8290 zone.get(), false, wasm_count + 1, CallDescriptor::kNoFlags);
8291
8292 // Build a name in the form "js-to-js:<params>:<returns>".
8293 constexpr size_t kMaxNameLen = 128;
8294 constexpr size_t kNamePrefixLen = 9;
8295 auto name_buffer = std::unique_ptr<char[]>(new char[kMaxNameLen]);
8296 memcpy(name_buffer.get(), "js-to-js:", kNamePrefixLen);
8297 PrintSignature(
8298 base::VectorOf(name_buffer.get(), kMaxNameLen) + kNamePrefixLen, sig);
8299
8300 // Run the compilation job synchronously.
8301 std::unique_ptr<TurbofanCompilationJob> job(
8302 Pipeline::NewWasmHeapStubCompilationJob(
8303 isolate, incoming, std::move(zone), graph,
8304 CodeKind::JS_TO_JS_FUNCTION, std::move(name_buffer),
8305 AssemblerOptions::Default(isolate)));
8306
8307 if (job->ExecuteJob(isolate->counters()->runtime_call_stats()) ==
8308 CompilationJob::FAILED ||
8309 job->FinalizeJob(isolate) == CompilationJob::FAILED) {
8310 return {};
8311 }
8312 Handle<Code> code = job->compilation_info()->code();
8313
8314 return code;
8315 }
8316
CompileCWasmEntry(Isolate * isolate,const wasm::FunctionSig * sig,const wasm::WasmModule * module)8317 Handle<CodeT> CompileCWasmEntry(Isolate* isolate, const wasm::FunctionSig* sig,
8318 const wasm::WasmModule* module) {
8319 std::unique_ptr<Zone> zone = std::make_unique<Zone>(
8320 isolate->allocator(), ZONE_NAME, kCompressGraphZone);
8321 Graph* graph = zone->New<Graph>(zone.get());
8322 CommonOperatorBuilder* common = zone->New<CommonOperatorBuilder>(zone.get());
8323 MachineOperatorBuilder* machine = zone->New<MachineOperatorBuilder>(
8324 zone.get(), MachineType::PointerRepresentation(),
8325 InstructionSelector::SupportedMachineOperatorFlags(),
8326 InstructionSelector::AlignmentRequirements());
8327 MachineGraph* mcgraph = zone->New<MachineGraph>(graph, common, machine);
8328
8329 WasmWrapperGraphBuilder builder(zone.get(), mcgraph, sig, module,
8330 WasmGraphBuilder::kWasmApiFunctionRefMode,
8331 nullptr, nullptr,
8332 StubCallMode::kCallBuiltinPointer,
8333 wasm::WasmFeatures::FromIsolate(isolate));
8334 builder.BuildCWasmEntry();
8335
8336 // Schedule and compile to machine code.
8337 MachineType sig_types[] = {MachineType::Pointer(), // return
8338 MachineType::Pointer(), // target
8339 MachineType::AnyTagged(), // object_ref
8340 MachineType::Pointer(), // argv
8341 MachineType::Pointer()}; // c_entry_fp
8342 MachineSignature incoming_sig(1, 4, sig_types);
8343 // Traps need the root register, for TailCallRuntime to call
8344 // Runtime::kThrowWasmError.
8345 CallDescriptor::Flags flags = CallDescriptor::kInitializeRootRegister;
8346 CallDescriptor* incoming =
8347 Linkage::GetSimplifiedCDescriptor(zone.get(), &incoming_sig, flags);
8348
8349 // Build a name in the form "c-wasm-entry:<params>:<returns>".
8350 constexpr size_t kMaxNameLen = 128;
8351 constexpr size_t kNamePrefixLen = 13;
8352 auto name_buffer = std::unique_ptr<char[]>(new char[kMaxNameLen]);
8353 memcpy(name_buffer.get(), "c-wasm-entry:", kNamePrefixLen);
8354 PrintSignature(
8355 base::VectorOf(name_buffer.get(), kMaxNameLen) + kNamePrefixLen, sig);
8356
8357 // Run the compilation job synchronously.
8358 std::unique_ptr<TurbofanCompilationJob> job(
8359 Pipeline::NewWasmHeapStubCompilationJob(
8360 isolate, incoming, std::move(zone), graph, CodeKind::C_WASM_ENTRY,
8361 std::move(name_buffer), AssemblerOptions::Default(isolate)));
8362
8363 CHECK_NE(job->ExecuteJob(isolate->counters()->runtime_call_stats(), nullptr),
8364 CompilationJob::FAILED);
8365 CHECK_NE(job->FinalizeJob(isolate), CompilationJob::FAILED);
8366
8367 return ToCodeT(job->compilation_info()->code(), isolate);
8368 }
8369
8370 namespace {
8371
BuildGraphForWasmFunction(wasm::CompilationEnv * env,const wasm::FunctionBody & func_body,int func_index,wasm::WasmFeatures * detected,MachineGraph * mcgraph,std::vector<compiler::WasmLoopInfo> * loop_infos,NodeOriginTable * node_origins,SourcePositionTable * source_positions)8372 bool BuildGraphForWasmFunction(wasm::CompilationEnv* env,
8373 const wasm::FunctionBody& func_body,
8374 int func_index, wasm::WasmFeatures* detected,
8375 MachineGraph* mcgraph,
8376 std::vector<compiler::WasmLoopInfo>* loop_infos,
8377 NodeOriginTable* node_origins,
8378 SourcePositionTable* source_positions) {
8379 // Create a TF graph during decoding.
8380 WasmGraphBuilder builder(env, mcgraph->zone(), mcgraph, func_body.sig,
8381 source_positions);
8382 auto* allocator = wasm::GetWasmEngine()->allocator();
8383 wasm::VoidResult graph_construction_result = wasm::BuildTFGraph(
8384 allocator, env->enabled_features, env->module, &builder, detected,
8385 func_body, loop_infos, node_origins, func_index, wasm::kRegularFunction);
8386 if (graph_construction_result.failed()) {
8387 if (FLAG_trace_wasm_compiler) {
8388 StdoutStream{} << "Compilation failed: "
8389 << graph_construction_result.error().message()
8390 << std::endl;
8391 }
8392 return false;
8393 }
8394
8395 auto sig = CreateMachineSignature(mcgraph->zone(), func_body.sig,
8396 WasmGraphBuilder::kCalledFromWasm);
8397 builder.LowerInt64(sig);
8398
8399 return true;
8400 }
8401
GetDebugName(Zone * zone,const wasm::WasmModule * module,const wasm::WireBytesStorage * wire_bytes,int index)8402 base::Vector<const char> GetDebugName(Zone* zone,
8403 const wasm::WasmModule* module,
8404 const wasm::WireBytesStorage* wire_bytes,
8405 int index) {
8406 base::Optional<wasm::ModuleWireBytes> module_bytes =
8407 wire_bytes->GetModuleBytes();
8408 if (module_bytes.has_value() &&
8409 (FLAG_trace_turbo || FLAG_trace_turbo_scheduled ||
8410 FLAG_trace_turbo_graph || FLAG_print_wasm_code)) {
8411 wasm::WireBytesRef name = module->lazily_generated_names.LookupFunctionName(
8412 module_bytes.value(), index);
8413 if (!name.is_empty()) {
8414 int name_len = name.length();
8415 char* index_name = zone->NewArray<char>(name_len);
8416 memcpy(index_name, module_bytes->start() + name.offset(), name_len);
8417 return base::Vector<const char>(index_name, name_len);
8418 }
8419 }
8420
8421 constexpr int kBufferLength = 24;
8422
8423 base::EmbeddedVector<char, kBufferLength> name_vector;
8424 int name_len = SNPrintF(name_vector, "wasm-function#%d", index);
8425 DCHECK(name_len > 0 && name_len < name_vector.length());
8426
8427 char* index_name = zone->NewArray<char>(name_len);
8428 memcpy(index_name, name_vector.begin(), name_len);
8429 return base::Vector<const char>(index_name, name_len);
8430 }
8431
8432 } // namespace
8433
ExecuteTurbofanWasmCompilation(wasm::CompilationEnv * env,const wasm::WireBytesStorage * wire_byte_storage,const wasm::FunctionBody & func_body,int func_index,Counters * counters,wasm::WasmFeatures * detected)8434 wasm::WasmCompilationResult ExecuteTurbofanWasmCompilation(
8435 wasm::CompilationEnv* env, const wasm::WireBytesStorage* wire_byte_storage,
8436 const wasm::FunctionBody& func_body, int func_index, Counters* counters,
8437 wasm::WasmFeatures* detected) {
8438 // Check that we do not accidentally compile a Wasm function to TurboFan if
8439 // --liftoff-only is set.
8440 DCHECK(!FLAG_liftoff_only);
8441
8442 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
8443 "wasm.CompileTopTier", "func_index", func_index, "body_size",
8444 func_body.end - func_body.start);
8445 Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME, kCompressGraphZone);
8446 MachineGraph* mcgraph = zone.New<MachineGraph>(
8447 zone.New<Graph>(&zone), zone.New<CommonOperatorBuilder>(&zone),
8448 zone.New<MachineOperatorBuilder>(
8449 &zone, MachineType::PointerRepresentation(),
8450 InstructionSelector::SupportedMachineOperatorFlags(),
8451 InstructionSelector::AlignmentRequirements()));
8452
8453 OptimizedCompilationInfo info(
8454 GetDebugName(&zone, env->module, wire_byte_storage, func_index), &zone,
8455 CodeKind::WASM_FUNCTION);
8456 if (env->runtime_exception_support) {
8457 info.set_wasm_runtime_exception_support();
8458 }
8459
8460 if (FLAG_experimental_wasm_gc) info.set_allocation_folding();
8461
8462 if (info.trace_turbo_json()) {
8463 TurboCfgFile tcf;
8464 tcf << AsC1VCompilation(&info);
8465 }
8466
8467 NodeOriginTable* node_origins =
8468 info.trace_turbo_json() ? zone.New<NodeOriginTable>(mcgraph->graph())
8469 : nullptr;
8470 SourcePositionTable* source_positions =
8471 mcgraph->zone()->New<SourcePositionTable>(mcgraph->graph());
8472
8473 std::vector<WasmLoopInfo> loop_infos;
8474
8475 wasm::WasmFeatures unused_detected_features;
8476 if (!detected) detected = &unused_detected_features;
8477 if (!BuildGraphForWasmFunction(env, func_body, func_index, detected, mcgraph,
8478 &loop_infos, node_origins, source_positions)) {
8479 return wasm::WasmCompilationResult{};
8480 }
8481
8482 if (node_origins) {
8483 node_origins->AddDecorator();
8484 }
8485
8486 // Run the compiler pipeline to generate machine code.
8487 auto call_descriptor = GetWasmCallDescriptor(&zone, func_body.sig);
8488 if (mcgraph->machine()->Is32()) {
8489 call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
8490 }
8491
8492 if (ContainsSimd(func_body.sig) && !CpuFeatures::SupportsWasmSimd128()) {
8493 // Fail compilation if hardware does not support SIMD.
8494 return wasm::WasmCompilationResult{};
8495 }
8496
8497 Pipeline::GenerateCodeForWasmFunction(
8498 &info, env, wire_byte_storage, mcgraph, call_descriptor, source_positions,
8499 node_origins, func_body, env->module, func_index, &loop_infos);
8500
8501 if (counters) {
8502 int zone_bytes =
8503 static_cast<int>(mcgraph->graph()->zone()->allocation_size());
8504 counters->wasm_compile_function_peak_memory_bytes()->AddSample(zone_bytes);
8505 if (func_body.end - func_body.start >= 100 * KB) {
8506 counters->wasm_compile_huge_function_peak_memory_bytes()->AddSample(
8507 zone_bytes);
8508 }
8509 }
8510 // If we tiered up only one function for debugging, dump statistics
8511 // immediately.
8512 if (V8_UNLIKELY(FLAG_turbo_stats_wasm && FLAG_wasm_tier_up_filter >= 0)) {
8513 wasm::GetWasmEngine()->DumpTurboStatistics();
8514 }
8515 auto result = info.ReleaseWasmCompilationResult();
8516 CHECK_NOT_NULL(result); // Compilation expected to succeed.
8517 DCHECK_EQ(wasm::ExecutionTier::kTurbofan, result->result_tier);
8518 return std::move(*result);
8519 }
8520
8521 namespace {
8522 // Helper for allocating either an GP or FP reg, or the next stack slot.
8523 class LinkageLocationAllocator {
8524 public:
8525 template <size_t kNumGpRegs, size_t kNumFpRegs>
LinkageLocationAllocator(const Register (& gp)[kNumGpRegs],const DoubleRegister (& fp)[kNumFpRegs],int slot_offset)8526 constexpr LinkageLocationAllocator(const Register (&gp)[kNumGpRegs],
8527 const DoubleRegister (&fp)[kNumFpRegs],
8528 int slot_offset)
8529 : allocator_(wasm::LinkageAllocator(gp, fp)), slot_offset_(slot_offset) {}
8530
Next(MachineRepresentation rep)8531 LinkageLocation Next(MachineRepresentation rep) {
8532 MachineType type = MachineType::TypeForRepresentation(rep);
8533 if (IsFloatingPoint(rep)) {
8534 if (allocator_.CanAllocateFP(rep)) {
8535 int reg_code = allocator_.NextFpReg(rep);
8536 return LinkageLocation::ForRegister(reg_code, type);
8537 }
8538 } else if (allocator_.CanAllocateGP()) {
8539 int reg_code = allocator_.NextGpReg();
8540 return LinkageLocation::ForRegister(reg_code, type);
8541 }
8542 // Cannot use register; use stack slot.
8543 int index = -1 - (slot_offset_ + allocator_.NextStackSlot(rep));
8544 return LinkageLocation::ForCallerFrameSlot(index, type);
8545 }
8546
NumStackSlots() const8547 int NumStackSlots() const { return allocator_.NumStackSlots(); }
EndSlotArea()8548 void EndSlotArea() { allocator_.EndSlotArea(); }
8549
8550 private:
8551 wasm::LinkageAllocator allocator_;
8552 // Since params and returns are in different stack frames, we must allocate
8553 // them separately. Parameter slots don't need an offset, but return slots
8554 // must be offset to just before the param slots, using this |slot_offset_|.
8555 int slot_offset_;
8556 };
8557
BuildLocations(Zone * zone,const wasm::FunctionSig * fsig,bool extra_callable_param,int * parameter_slots,int * return_slots)8558 LocationSignature* BuildLocations(Zone* zone, const wasm::FunctionSig* fsig,
8559 bool extra_callable_param,
8560 int* parameter_slots, int* return_slots) {
8561 int extra_params = extra_callable_param ? 2 : 1;
8562 LocationSignature::Builder locations(zone, fsig->return_count(),
8563 fsig->parameter_count() + extra_params);
8564
8565 // Add register and/or stack parameter(s).
8566 LinkageLocationAllocator params(
8567 wasm::kGpParamRegisters, wasm::kFpParamRegisters, 0 /* no slot offset */);
8568
8569 // The instance object.
8570 locations.AddParam(params.Next(MachineRepresentation::kTaggedPointer));
8571 const size_t param_offset = 1; // Actual params start here.
8572
8573 // Parameters are separated into two groups (first all untagged, then all
8574 // tagged parameters). This allows for easy iteration of tagged parameters
8575 // during frame iteration.
8576 const size_t parameter_count = fsig->parameter_count();
8577 for (size_t i = 0; i < parameter_count; i++) {
8578 MachineRepresentation param = fsig->GetParam(i).machine_representation();
8579 // Skip tagged parameters (e.g. any-ref).
8580 if (IsAnyTagged(param)) continue;
8581 auto l = params.Next(param);
8582 locations.AddParamAt(i + param_offset, l);
8583 }
8584
8585 // End the untagged area, so tagged slots come after.
8586 params.EndSlotArea();
8587
8588 for (size_t i = 0; i < parameter_count; i++) {
8589 MachineRepresentation param = fsig->GetParam(i).machine_representation();
8590 // Skip untagged parameters.
8591 if (!IsAnyTagged(param)) continue;
8592 auto l = params.Next(param);
8593 locations.AddParamAt(i + param_offset, l);
8594 }
8595
8596 // Import call wrappers have an additional (implicit) parameter, the callable.
8597 // For consistency with JS, we use the JSFunction register.
8598 if (extra_callable_param) {
8599 locations.AddParam(LinkageLocation::ForRegister(
8600 kJSFunctionRegister.code(), MachineType::TaggedPointer()));
8601 }
8602
8603 *parameter_slots = AddArgumentPaddingSlots(params.NumStackSlots());
8604
8605 // Add return location(s).
8606 LinkageLocationAllocator rets(wasm::kGpReturnRegisters,
8607 wasm::kFpReturnRegisters, *parameter_slots);
8608
8609 const size_t return_count = locations.return_count_;
8610 for (size_t i = 0; i < return_count; i++) {
8611 MachineRepresentation ret = fsig->GetReturn(i).machine_representation();
8612 locations.AddReturn(rets.Next(ret));
8613 }
8614
8615 *return_slots = rets.NumStackSlots();
8616
8617 return locations.Build();
8618 }
8619 } // namespace
8620
8621 // General code uses the above configuration data.
GetWasmCallDescriptor(Zone * zone,const wasm::FunctionSig * fsig,WasmCallKind call_kind,bool need_frame_state)8622 CallDescriptor* GetWasmCallDescriptor(Zone* zone, const wasm::FunctionSig* fsig,
8623 WasmCallKind call_kind,
8624 bool need_frame_state) {
8625 // The extra here is to accomodate the instance object as first parameter
8626 // and, when specified, the additional callable.
8627 bool extra_callable_param =
8628 call_kind == kWasmImportWrapper || call_kind == kWasmCapiFunction;
8629
8630 int parameter_slots;
8631 int return_slots;
8632 LocationSignature* location_sig = BuildLocations(
8633 zone, fsig, extra_callable_param, ¶meter_slots, &return_slots);
8634
8635 const RegList kCalleeSaveRegisters;
8636 const DoubleRegList kCalleeSaveFPRegisters;
8637
8638 // The target for wasm calls is always a code object.
8639 MachineType target_type = MachineType::Pointer();
8640 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
8641
8642 CallDescriptor::Kind descriptor_kind;
8643 if (call_kind == kWasmFunction) {
8644 descriptor_kind = CallDescriptor::kCallWasmFunction;
8645 } else if (call_kind == kWasmImportWrapper) {
8646 descriptor_kind = CallDescriptor::kCallWasmImportWrapper;
8647 } else {
8648 DCHECK_EQ(call_kind, kWasmCapiFunction);
8649 descriptor_kind = CallDescriptor::kCallWasmCapiFunction;
8650 }
8651
8652 CallDescriptor::Flags flags = need_frame_state
8653 ? CallDescriptor::kNeedsFrameState
8654 : CallDescriptor::kNoFlags;
8655 return zone->New<CallDescriptor>( // --
8656 descriptor_kind, // kind
8657 target_type, // target MachineType
8658 target_loc, // target location
8659 location_sig, // location_sig
8660 parameter_slots, // parameter slot count
8661 compiler::Operator::kNoProperties, // properties
8662 kCalleeSaveRegisters, // callee-saved registers
8663 kCalleeSaveFPRegisters, // callee-saved fp regs
8664 flags, // flags
8665 "wasm-call", // debug name
8666 StackArgumentOrder::kDefault, // order of the arguments in the stack
8667 fsig, // signature
8668 RegList{}, // allocatable registers
8669 return_slots); // return slot count
8670 }
8671
8672 namespace {
ReplaceTypeInSig(Zone * zone,const wasm::FunctionSig * sig,wasm::ValueType from,wasm::ValueType to,size_t num_replacements)8673 const wasm::FunctionSig* ReplaceTypeInSig(Zone* zone,
8674 const wasm::FunctionSig* sig,
8675 wasm::ValueType from,
8676 wasm::ValueType to,
8677 size_t num_replacements) {
8678 size_t param_occurences =
8679 std::count(sig->parameters().begin(), sig->parameters().end(), from);
8680 size_t return_occurences =
8681 std::count(sig->returns().begin(), sig->returns().end(), from);
8682 if (param_occurences == 0 && return_occurences == 0) return sig;
8683
8684 wasm::FunctionSig::Builder builder(
8685 zone, sig->return_count() + return_occurences * (num_replacements - 1),
8686 sig->parameter_count() + param_occurences * (num_replacements - 1));
8687
8688 for (wasm::ValueType ret : sig->returns()) {
8689 if (ret == from) {
8690 for (size_t i = 0; i < num_replacements; i++) builder.AddReturn(to);
8691 } else {
8692 builder.AddReturn(ret);
8693 }
8694 }
8695
8696 for (wasm::ValueType param : sig->parameters()) {
8697 if (param == from) {
8698 for (size_t i = 0; i < num_replacements; i++) builder.AddParam(to);
8699 } else {
8700 builder.AddParam(param);
8701 }
8702 }
8703
8704 return builder.Build();
8705 }
8706
ReplaceTypeInCallDescriptorWith(Zone * zone,const CallDescriptor * call_descriptor,size_t num_replacements,wasm::ValueType input_type,wasm::ValueType output_type)8707 CallDescriptor* ReplaceTypeInCallDescriptorWith(
8708 Zone* zone, const CallDescriptor* call_descriptor, size_t num_replacements,
8709 wasm::ValueType input_type, wasm::ValueType output_type) {
8710 if (call_descriptor->wasm_sig() == nullptr) {
8711 // This happens for builtins calls. They need no replacements anyway.
8712 #if DEBUG
8713 for (size_t i = 0; i < call_descriptor->ParameterCount(); i++) {
8714 DCHECK_NE(call_descriptor->GetParameterType(i),
8715 input_type.machine_type());
8716 }
8717 for (size_t i = 0; i < call_descriptor->ReturnCount(); i++) {
8718 DCHECK_NE(call_descriptor->GetReturnType(i), input_type.machine_type());
8719 }
8720 #endif
8721 return const_cast<CallDescriptor*>(call_descriptor);
8722 }
8723 const wasm::FunctionSig* sig =
8724 ReplaceTypeInSig(zone, call_descriptor->wasm_sig(), input_type,
8725 output_type, num_replacements);
8726 // If {ReplaceTypeInSig} took the early fast path, there's nothing to do.
8727 if (sig == call_descriptor->wasm_sig()) {
8728 return const_cast<CallDescriptor*>(call_descriptor);
8729 }
8730
8731 // The last parameter may be the special callable parameter. In that case we
8732 // have to preserve it as the last parameter, i.e. we allocate it in the new
8733 // location signature again in the same register.
8734 bool extra_callable_param =
8735 (call_descriptor->GetInputLocation(call_descriptor->InputCount() - 1) ==
8736 LinkageLocation::ForRegister(kJSFunctionRegister.code(),
8737 MachineType::TaggedPointer()));
8738
8739 int parameter_slots;
8740 int return_slots;
8741 LocationSignature* location_sig = BuildLocations(
8742 zone, sig, extra_callable_param, ¶meter_slots, &return_slots);
8743
8744 return zone->New<CallDescriptor>( // --
8745 call_descriptor->kind(), // kind
8746 call_descriptor->GetInputType(0), // target MachineType
8747 call_descriptor->GetInputLocation(0), // target location
8748 location_sig, // location_sig
8749 parameter_slots, // parameter slot count
8750 call_descriptor->properties(), // properties
8751 call_descriptor->CalleeSavedRegisters(), // callee-saved registers
8752 call_descriptor->CalleeSavedFPRegisters(), // callee-saved fp regs
8753 call_descriptor->flags(), // flags
8754 call_descriptor->debug_name(), // debug name
8755 call_descriptor->GetStackArgumentOrder(), // stack order
8756 sig, // signature
8757 call_descriptor->AllocatableRegisters(), // allocatable registers
8758 return_slots); // return slot count
8759 }
8760 } // namespace
8761
8762 // static
Int64LoweredSig(Zone * zone,const wasm::FunctionSig * sig)8763 const wasm::FunctionSig* WasmGraphBuilder::Int64LoweredSig(
8764 Zone* zone, const wasm::FunctionSig* sig) {
8765 return (kSystemPointerSize == 4)
8766 ? ReplaceTypeInSig(zone, sig, wasm::kWasmI64, wasm::kWasmI32, 2)
8767 : sig;
8768 }
8769
GetI32WasmCallDescriptor(Zone * zone,const CallDescriptor * call_descriptor)8770 CallDescriptor* GetI32WasmCallDescriptor(
8771 Zone* zone, const CallDescriptor* call_descriptor) {
8772 return ReplaceTypeInCallDescriptorWith(zone, call_descriptor, 2,
8773 wasm::kWasmI64, wasm::kWasmI32);
8774 }
8775
WasmAssemblerOptions()8776 AssemblerOptions WasmAssemblerOptions() {
8777 AssemblerOptions options;
8778 // Relocation info required to serialize {WasmCode} for proper functions.
8779 options.record_reloc_info_for_serialization = true;
8780 options.enable_root_relative_access = false;
8781 return options;
8782 }
8783
WasmStubAssemblerOptions()8784 AssemblerOptions WasmStubAssemblerOptions() {
8785 AssemblerOptions options;
8786 // Relocation info not necessary because stubs are not serialized.
8787 options.record_reloc_info_for_serialization = false;
8788 options.enable_root_relative_access = false;
8789 return options;
8790 }
8791
8792 #undef FATAL_UNSUPPORTED_OPCODE
8793 #undef WASM_INSTANCE_OBJECT_SIZE
8794 #undef LOAD_INSTANCE_FIELD
8795 #undef LOAD_MUTABLE_INSTANCE_FIELD
8796 #undef LOAD_ROOT
8797
8798 } // namespace compiler
8799 } // namespace internal
8800 } // namespace v8
8801