| File: | out/../deps/v8/src/compiler/wasm-compiler.cc |
| Warning: | line 1879, column 3 Undefined or garbage value returned to caller |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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), \V8_Fatal("Unsupported opcode 0x%x:%s", (opcode), wasm::WasmOpcodes ::OpcodeName(opcode)) | |||
| 75 | wasm::WasmOpcodes::OpcodeName(opcode))V8_Fatal("Unsupported opcode 0x%x:%s", (opcode), wasm::WasmOpcodes ::OpcodeName(opcode)); | |||
| 76 | ||||
| 77 | MachineType assert_size(int expected_size, MachineType type) { | |||
| 78 | DCHECK_EQ(expected_size, ElementSizeInBytes(type.representation()))((void) 0); | |||
| 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)gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(name), type ), GetInstance(), wasm::ObjectAccess::ToTagged(WasmInstanceObject ::knameOffset)) \ | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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)DEF_TRAP_CASE(TrapUnreachable) DEF_TRAP_CASE(TrapMemOutOfBounds ) DEF_TRAP_CASE(TrapUnalignedAccess) DEF_TRAP_CASE(TrapDivByZero ) DEF_TRAP_CASE(TrapDivUnrepresentable) DEF_TRAP_CASE(TrapRemByZero ) DEF_TRAP_CASE(TrapFloatUnrepresentable) DEF_TRAP_CASE(TrapFuncSigMismatch ) DEF_TRAP_CASE(TrapDataSegmentOutOfBounds) DEF_TRAP_CASE(TrapElemSegmentDropped ) DEF_TRAP_CASE(TrapTableOutOfBounds) DEF_TRAP_CASE(TrapRethrowNull ) DEF_TRAP_CASE(TrapNullDereference) DEF_TRAP_CASE(TrapIllegalCast ) DEF_TRAP_CASE(TrapArrayOutOfBounds) DEF_TRAP_CASE(TrapArrayTooLarge ) DEF_CASE(WasmCompileLazy) DEF_CASE(WasmTriggerTierUp) DEF_CASE (WasmDebugBreak) DEF_CASE(WasmInt32ToHeapNumber) DEF_CASE(WasmTaggedNonSmiToInt32 ) DEF_CASE(WasmFloat32ToNumber) DEF_CASE(WasmFloat64ToNumber) DEF_CASE(WasmTaggedToFloat64) DEF_CASE(WasmAllocateJSArray) DEF_CASE (WasmAtomicNotify) DEF_CASE(WasmI32AtomicWait32) DEF_CASE(WasmI32AtomicWait64 ) DEF_CASE(WasmI64AtomicWait32) DEF_CASE(WasmI64AtomicWait64) DEF_CASE(WasmGetOwnProperty) DEF_CASE(WasmRefFunc) DEF_CASE( WasmMemoryGrow) DEF_CASE(WasmTableInit) DEF_CASE(WasmTableCopy ) DEF_CASE(WasmTableFill) DEF_CASE(WasmTableGrow) DEF_CASE(WasmTableGet ) DEF_CASE(WasmTableSet) DEF_CASE(WasmStackGuard) DEF_CASE(WasmStackOverflow ) DEF_CASE(WasmAllocateFixedArray) DEF_CASE(WasmThrow) DEF_CASE (WasmRethrow) DEF_CASE(WasmRethrowExplicitContext) DEF_CASE(WasmTraceEnter ) DEF_CASE(WasmTraceExit) DEF_CASE(WasmTraceMemory) DEF_CASE( BigIntToI32Pair) DEF_CASE(BigIntToI64) DEF_CASE(CallRefIC) DEF_CASE (DoubleToI) DEF_CASE(I32PairToBigInt) DEF_CASE(I64ToBigInt) DEF_CASE (RecordWriteEmitRememberedSetSaveFP) DEF_CASE(RecordWriteOmitRememberedSetSaveFP ) DEF_CASE(RecordWriteEmitRememberedSetIgnoreFP) DEF_CASE(RecordWriteOmitRememberedSetIgnoreFP ) DEF_CASE(ToNumber) DEF_CASE(WasmAllocateArray_Uninitialized ) DEF_CASE(WasmAllocateArray_InitNull) DEF_CASE(WasmAllocateArray_InitZero ) DEF_CASE(WasmArrayCopy) DEF_CASE(WasmArrayCopyWithChecks) DEF_CASE (WasmArrayInitFromData) DEF_CASE(WasmAllocateStructWithRtt) DEF_CASE (WasmSubtypeCheck) DEF_CASE(WasmOnStackReplace) DEF_CASE(WasmSuspend ) | |||
| 133 | #undef DEF_CASE | |||
| 134 | #undef DEF_TRAP_CASE | |||
| 135 | default: | |||
| 136 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 137 | } | |||
| 138 | } | |||
| 139 | ||||
| 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 | ||||
| 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 | ||||
| 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: | |||
| 177 | WasmGraphAssembler(MachineGraph* mcgraph, Zone* zone) | |||
| 178 | : GraphAssembler(mcgraph, zone), simplified_(zone) {} | |||
| 179 | ||||
| 180 | template <typename... 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> | |||
| 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 | ||||
| 203 | void MergeControlToEnd(Node* node) { | |||
| 204 | NodeProperties::MergeControlToEnd(graph(), mcgraph()->common(), node); | |||
| 205 | } | |||
| 206 | ||||
| 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 | ||||
| 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(). | |||
| 225 | Node* Branch(Node* cond, Node** true_node, Node** false_node, | |||
| 226 | BranchHint hint) { | |||
| 227 | DCHECK_NOT_NULL(cond)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 264 | Node* LoadFromObject(MachineType type, Node* base, int offset) { | |||
| 265 | return LoadFromObject(type, base, IntPtrConstant(offset)); | |||
| 266 | } | |||
| 267 | ||||
| 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 | ||||
| 274 | Node* LoadImmutableFromObject(MachineType type, Node* base, int offset) { | |||
| 275 | return LoadImmutableFromObject(type, base, IntPtrConstant(offset)); | |||
| 276 | } | |||
| 277 | ||||
| 278 | Node* LoadImmutable(LoadRepresentation rep, Node* base, Node* offset) { | |||
| 279 | return AddNode(graph()->NewNode(mcgraph()->machine()->LoadImmutable(rep), | |||
| 280 | base, offset)); | |||
| 281 | } | |||
| 282 | ||||
| 283 | Node* LoadImmutable(LoadRepresentation rep, Node* base, int offset) { | |||
| 284 | return LoadImmutable(rep, base, IntPtrConstant(offset)); | |||
| 285 | } | |||
| 286 | ||||
| 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 | ||||
| 293 | Node* StoreToObject(ObjectAccess access, Node* base, int offset, | |||
| 294 | Node* value) { | |||
| 295 | return StoreToObject(access, base, IntPtrConstant(offset), value); | |||
| 296 | } | |||
| 297 | ||||
| 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 | ||||
| 305 | Node* InitializeImmutableInObject(ObjectAccess access, Node* base, int offset, | |||
| 306 | Node* value) { | |||
| 307 | return InitializeImmutableInObject(access, base, IntPtrConstant(offset), | |||
| 308 | value); | |||
| 309 | } | |||
| 310 | ||||
| 311 | Node* IsI31(Node* object) { | |||
| 312 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 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. | |||
| 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 | ||||
| 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 | ||||
| 342 | Node* LoadInstanceType(Node* map) { | |||
| 343 | return LoadImmutableFromObject( | |||
| 344 | MachineType::Uint16(), map, | |||
| 345 | wasm::ObjectAccess::ToTagged(Map::kInstanceTypeOffset)); | |||
| 346 | } | |||
| 347 | Node* LoadWasmTypeInfo(Node* map) { | |||
| 348 | int offset = Map::kConstructorOrBackPointerOrNativeContextOffset; | |||
| 349 | return LoadImmutableFromObject(MachineType::TaggedPointer(), map, | |||
| 350 | wasm::ObjectAccess::ToTagged(offset)); | |||
| 351 | } | |||
| 352 | ||||
| 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 | ||||
| 361 | Node* LoadFixedArrayLengthAsSmi(Node* fixed_array) { | |||
| 362 | return LoadImmutableFromObject( | |||
| 363 | MachineType::TaggedSigned(), fixed_array, | |||
| 364 | wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset)); | |||
| 365 | } | |||
| 366 | ||||
| 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 | ||||
| 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 | ||||
| 384 | Node* LoadFixedArrayElement(Node* array, int index, MachineType type) { | |||
| 385 | return LoadFromObject( | |||
| 386 | type, array, | |||
| 387 | wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(index)); | |||
| 388 | } | |||
| 389 | ||||
| 390 | Node* LoadFixedArrayElementSmi(Node* array, int index) { | |||
| 391 | return LoadFixedArrayElement(array, index, MachineType::TaggedSigned()); | |||
| 392 | } | |||
| 393 | ||||
| 394 | Node* LoadFixedArrayElementPtr(Node* array, int index) { | |||
| 395 | return LoadFixedArrayElement(array, index, MachineType::TaggedPointer()); | |||
| 396 | } | |||
| 397 | ||||
| 398 | Node* LoadFixedArrayElementAny(Node* array, int index) { | |||
| 399 | return LoadFixedArrayElement(array, index, MachineType::AnyTagged()); | |||
| 400 | } | |||
| 401 | ||||
| 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 | ||||
| 409 | Node* StoreFixedArrayElementSmi(Node* array, int index, Node* value) { | |||
| 410 | return StoreFixedArrayElement( | |||
| 411 | array, index, value, | |||
| 412 | ObjectAccess(MachineType::TaggedSigned(), kNoWriteBarrier)); | |||
| 413 | } | |||
| 414 | ||||
| 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 | ||||
| 423 | Node* LoadSharedFunctionInfo(Node* js_function) { | |||
| 424 | return LoadFromObject( | |||
| 425 | MachineType::TaggedPointer(), js_function, | |||
| 426 | wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction()); | |||
| 427 | } | |||
| 428 | Node* LoadContextFromJSFunction(Node* js_function) { | |||
| 429 | return LoadFromObject( | |||
| 430 | MachineType::TaggedPointer(), js_function, | |||
| 431 | wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()); | |||
| 432 | } | |||
| 433 | ||||
| 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 | ||||
| 441 | Node* LoadExportedFunctionIndexAsSmi(Node* exported_function_data) { | |||
| 442 | return LoadImmutableFromObject( | |||
| 443 | MachineType::TaggedSigned(), exported_function_data, | |||
| 444 | wasm::ObjectAccess::ToTagged( | |||
| 445 | WasmExportedFunctionData::kFunctionIndexOffset)); | |||
| 446 | } | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 488 | Node* LoadWasmArrayLength(Node* array) { | |||
| 489 | return LoadImmutableFromObject( | |||
| 490 | MachineType::Uint32(), array, | |||
| 491 | wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset)); | |||
| 492 | } | |||
| 493 | ||||
| 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 | ||||
| 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 | ||||
| 513 | SimplifiedOperatorBuilder* simplified() { return &simplified_; } | |||
| 514 | ||||
| 515 | private: | |||
| 516 | SimplifiedOperatorBuilder simplified_; | |||
| 517 | }; | |||
| 518 | ||||
| 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)((void) 0); | |||
| 534 | DCHECK_IMPLIES(env && env->bounds_checks == wasm::kTrapHandler,((void) 0) | |||
| 535 | trap_handler::IsTrapHandlerEnabled())((void) 0); | |||
| 536 | DCHECK_NOT_NULL(mcgraph_)((void) 0); | |||
| 537 | } | |||
| 538 | ||||
| 539 | // Destructor define here where the definition of {WasmGraphAssembler} is | |||
| 540 | // available. | |||
| 541 | WasmGraphBuilder::~WasmGraphBuilder() = default; | |||
| 542 | ||||
| 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 | ||||
| 571 | Node* WasmGraphBuilder::Param(int index, const char* debug_name) { | |||
| 572 | DCHECK_NOT_NULL(graph()->start())((void) 0); | |||
| 573 | // Turbofan allows negative parameter indices. | |||
| 574 | static constexpr int kMinParameterIndex = -1; | |||
| 575 | DCHECK_GE(index, kMinParameterIndex)((void) 0); | |||
| 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 | ||||
| 584 | Node* WasmGraphBuilder::Loop(Node* entry) { | |||
| 585 | return graph()->NewNode(mcgraph()->common()->Loop(1), entry); | |||
| 586 | } | |||
| 587 | ||||
| 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 | ||||
| 594 | Node* WasmGraphBuilder::LoopExit(Node* loop_node) { | |||
| 595 | DCHECK(loop_node->opcode() == IrOpcode::kLoop)((void) 0); | |||
| 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 | ||||
| 604 | Node* WasmGraphBuilder::LoopExitValue(Node* value, | |||
| 605 | MachineRepresentation representation) { | |||
| 606 | DCHECK(control()->opcode() == IrOpcode::kLoopExit)((void) 0); | |||
| 607 | return graph()->NewNode(mcgraph()->common()->LoopExitValue(representation), | |||
| 608 | value, control()); | |||
| 609 | } | |||
| 610 | ||||
| 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 | ||||
| 617 | bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) { | |||
| 618 | return phi && IrOpcode::IsPhiOpcode(phi->opcode()) && | |||
| 619 | NodeProperties::GetControlInput(phi) == merge; | |||
| 620 | } | |||
| 621 | ||||
| 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 | ||||
| 635 | void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) { | |||
| 636 | DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()))((void) 0); | |||
| 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 | ||||
| 643 | void WasmGraphBuilder::AppendToPhi(Node* phi, Node* from) { | |||
| 644 | DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()))((void) 0); | |||
| 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> | |||
| 652 | Node* WasmGraphBuilder::Merge(Node* fst, Nodes*... args) { | |||
| 653 | return graph()->NewNode(this->mcgraph()->common()->Merge(1 + sizeof...(args)), | |||
| 654 | fst, args...); | |||
| 655 | } | |||
| 656 | ||||
| 657 | Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) { | |||
| 658 | return graph()->NewNode(mcgraph()->common()->Merge(count), count, controls); | |||
| 659 | } | |||
| 660 | ||||
| 661 | Node* WasmGraphBuilder::Phi(wasm::ValueType type, unsigned count, | |||
| 662 | Node** vals_and_control) { | |||
| 663 | DCHECK(IrOpcode::IsMergeOpcode(vals_and_control[count]->opcode()))((void) 0); | |||
| 664 | DCHECK_EQ(vals_and_control[count]->op()->ControlInputCount(), count)((void) 0); | |||
| 665 | return graph()->NewNode( | |||
| 666 | mcgraph()->common()->Phi(type.machine_representation(), count), count + 1, | |||
| 667 | vals_and_control); | |||
| 668 | } | |||
| 669 | ||||
| 670 | Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects_and_control) { | |||
| 671 | DCHECK(IrOpcode::IsMergeOpcode(effects_and_control[count]->opcode()))((void) 0); | |||
| 672 | return graph()->NewNode(mcgraph()->common()->EffectPhi(count), count + 1, | |||
| 673 | effects_and_control); | |||
| 674 | } | |||
| 675 | ||||
| 676 | Node* WasmGraphBuilder::RefNull() { return LOAD_ROOT(NullValue, null_value); } | |||
| 677 | ||||
| 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 | ||||
| 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 | ||||
| 692 | Node* WasmGraphBuilder::NoContextConstant() { | |||
| 693 | return mcgraph()->IntPtrConstant(0); | |||
| 694 | } | |||
| 695 | ||||
| 696 | Node* WasmGraphBuilder::GetInstance() { return instance_node_.get(); } | |||
| 697 | ||||
| 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 | ||||
| 716 | Node* WasmGraphBuilder::Int32Constant(int32_t value) { | |||
| 717 | return mcgraph()->Int32Constant(value); | |||
| 718 | } | |||
| 719 | ||||
| 720 | Node* WasmGraphBuilder::Int64Constant(int64_t value) { | |||
| 721 | return mcgraph()->Int64Constant(value); | |||
| 722 | } | |||
| 723 | ||||
| 724 | Node* WasmGraphBuilder::UndefinedValue() { | |||
| 725 | return LOAD_ROOT(UndefinedValue, undefined_value); | |||
| 726 | } | |||
| 727 | ||||
| 728 | void WasmGraphBuilder::StackCheck( | |||
| 729 | WasmInstanceCacheNodes* shared_memory_instance_cache, | |||
| 730 | wasm::WasmCodePosition position) { | |||
| 731 | DCHECK_NOT_NULL(env_)((void) 0); // 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) !=static_assert((properties & Operator::kEliminatable) != Operator ::kEliminatable, "(properties & Operator::kEliminatable) != Operator::kEliminatable" ) | |||
| 762 | Operator::kEliminatable)static_assert((properties & Operator::kEliminatable) != Operator ::kEliminatable, "(properties & Operator::kEliminatable) != 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)((void) 0); | |||
| 779 | DCHECK_EQ(call->op()->ControlOutputCount(), 0)((void) 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())gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(MemorySize ), MachineType::UintPtr()), GetInstance(), wasm::ObjectAccess ::ToTagged(WasmInstanceObject::kMemorySizeOffset)); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 1413 | Node* WasmGraphBuilder::Float32Constant(float value) { | |||
| 1414 | return mcgraph()->Float32Constant(value); | |||
| 1415 | } | |||
| 1416 | ||||
| 1417 | Node* WasmGraphBuilder::Float64Constant(double value) { | |||
| 1418 | return mcgraph()->Float64Constant(value); | |||
| 1419 | } | |||
| 1420 | ||||
| 1421 | Node* WasmGraphBuilder::Simd128Constant(const uint8_t value[16]) { | |||
| 1422 | has_simd_ = true; | |||
| 1423 | return graph()->NewNode(mcgraph()->machine()->S128Const(value)); | |||
| 1424 | } | |||
| 1425 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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)TRAPREASON_TO_TRAPID(TrapUnreachable) TRAPREASON_TO_TRAPID(TrapMemOutOfBounds ) TRAPREASON_TO_TRAPID(TrapUnalignedAccess) TRAPREASON_TO_TRAPID (TrapDivByZero) TRAPREASON_TO_TRAPID(TrapDivUnrepresentable) TRAPREASON_TO_TRAPID (TrapRemByZero) TRAPREASON_TO_TRAPID(TrapFloatUnrepresentable ) TRAPREASON_TO_TRAPID(TrapFuncSigMismatch) TRAPREASON_TO_TRAPID (TrapDataSegmentOutOfBounds) TRAPREASON_TO_TRAPID(TrapElemSegmentDropped ) TRAPREASON_TO_TRAPID(TrapTableOutOfBounds) TRAPREASON_TO_TRAPID (TrapRethrowNull) TRAPREASON_TO_TRAPID(TrapNullDereference) TRAPREASON_TO_TRAPID (TrapIllegalCast) TRAPREASON_TO_TRAPID(TrapArrayOutOfBounds) TRAPREASON_TO_TRAPID (TrapArrayTooLarge) | |||
| 1489 | #undef TRAPREASON_TO_TRAPID | |||
| 1490 | default: | |||
| 1491 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 1492 | } | |||
| 1493 | } | |||
| 1494 | ||||
| 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 | ||||
| 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}. | |||
| 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. | |||
| 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}. | |||
| 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. | |||
| 1540 | void WasmGraphBuilder::ZeroCheck64(wasm::TrapReason reason, Node* node, | |||
| 1541 | wasm::WasmCodePosition position) { | |||
| 1542 | TrapIfEq64(reason, node, 0, position); | |||
| 1543 | } | |||
| 1544 | ||||
| 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)((void) 0); // value_range + 2 | |||
| 1549 | DCHECK_LE(count, wasm::kV8MaxWasmFunctionBrTableSize + 1)((void) 0); // plus IfDefault | |||
| 1550 | return graph()->NewNode(mcgraph()->common()->Switch(count), key, control()); | |||
| 1551 | } | |||
| 1552 | ||||
| 1553 | Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) { | |||
| 1554 | DCHECK_EQ(IrOpcode::kSwitch, sw->opcode())((void) 0); | |||
| 1555 | return graph()->NewNode(mcgraph()->common()->IfValue(value), sw); | |||
| 1556 | } | |||
| 1557 | ||||
| 1558 | Node* WasmGraphBuilder::IfDefault(Node* sw) { | |||
| 1559 | DCHECK_EQ(IrOpcode::kSwitch, sw->opcode())((void) 0); | |||
| 1560 | return graph()->NewNode(mcgraph()->common()->IfDefault(), sw); | |||
| 1561 | } | |||
| 1562 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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[[clang::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[[clang::fallthrough]]; | |||
| 1655 | case wasm::kI32: | |||
| 1656 | result = Int32Constant(0); | |||
| 1657 | break; | |||
| 1658 | case wasm::kS128: | |||
| 1659 | DCHECK(ReverseBytesSupported(m, valueSizeInBytes))((void) 0); | |||
| 1660 | break; | |||
| 1661 | default: | |||
| 1662 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 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)((void) 0); | |||
| 1709 | DCHECK_EQ(0, (shiftCount + 8) % 16)((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 1745 | } | |||
| 1746 | } | |||
| 1747 | ||||
| 1748 | return result; | |||
| 1749 | } | |||
| 1750 | ||||
| 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[[clang::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[[clang::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))((void) 0); | |||
| 1782 | break; | |||
| 1783 | default: | |||
| 1784 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 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)((void) 0); | |||
| 1817 | DCHECK_EQ(0, (shiftCount + 8) % 16)((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 1853 | } | |||
| 1854 | } | |||
| 1855 | ||||
| 1856 | // We need to sign or zero extend the value | |||
| 1857 | if (memtype.IsSigned()) { | |||
| 1858 | DCHECK(!isFloat)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 1941 | } | |||
| 1942 | } | |||
| 1943 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 1966 | } | |||
| 1967 | } | |||
| 1968 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2003 | } | |||
| 2004 | } | |||
| 2005 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2022 | } | |||
| 2023 | } | |||
| 2024 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2047 | } | |||
| 2048 | } | |||
| 2049 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2062 | } | |||
| 2063 | } | |||
| 2064 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2077 | } | |||
| 2078 | } | |||
| 2079 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2092 | } | |||
| 2093 | } | |||
| 2094 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2103 | } | |||
| 2104 | } | |||
| 2105 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2114 | } | |||
| 2115 | } | |||
| 2116 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2125 | } | |||
| 2126 | } | |||
| 2127 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 2201 | Node* WasmGraphBuilder::BuildI32AsmjsSConvertF32(Node* input) { | |||
| 2202 | // asm.js must use the wacky JS semantics. | |||
| 2203 | return gasm_->TruncateFloat64ToWord32(gasm_->ChangeFloat32ToFloat64(input)); | |||
| 2204 | } | |||
| 2205 | ||||
| 2206 | Node* WasmGraphBuilder::BuildI32AsmjsSConvertF64(Node* input) { | |||
| 2207 | // asm.js must use the wacky JS semantics. | |||
| 2208 | return gasm_->TruncateFloat64ToWord32(input); | |||
| 2209 | } | |||
| 2210 | ||||
| 2211 | Node* WasmGraphBuilder::BuildI32AsmjsUConvertF32(Node* input) { | |||
| 2212 | // asm.js must use the wacky JS semantics. | |||
| 2213 | return gasm_->TruncateFloat64ToWord32(gasm_->ChangeFloat32ToFloat64(input)); | |||
| 2214 | } | |||
| 2215 | ||||
| 2216 | Node* WasmGraphBuilder::BuildI32AsmjsUConvertF64(Node* input) { | |||
| 2217 | // asm.js must use the wacky JS semantics. | |||
| 2218 | return gasm_->TruncateFloat64ToWord32(input); | |||
| 2219 | } | |||
| 2220 | ||||
| 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 | ||||
| 2233 | Node* WasmGraphBuilder::BuildI32Ctz(Node* input) { | |||
| 2234 | return BuildBitCountingCall(input, ExternalReference::wasm_word32_ctz(), | |||
| 2235 | MachineRepresentation::kWord32); | |||
| 2236 | } | |||
| 2237 | ||||
| 2238 | Node* WasmGraphBuilder::BuildI64Ctz(Node* input) { | |||
| 2239 | return Unop(wasm::kExprI64UConvertI32, | |||
| 2240 | BuildBitCountingCall(input, ExternalReference::wasm_word64_ctz(), | |||
| 2241 | MachineRepresentation::kWord64)); | |||
| 2242 | } | |||
| 2243 | ||||
| 2244 | Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) { | |||
| 2245 | return BuildBitCountingCall(input, ExternalReference::wasm_word32_popcnt(), | |||
| 2246 | MachineRepresentation::kWord32); | |||
| 2247 | } | |||
| 2248 | ||||
| 2249 | Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) { | |||
| 2250 | return Unop( | |||
| 2251 | wasm::kExprI64UConvertI32, | |||
| 2252 | BuildBitCountingCall(input, ExternalReference::wasm_word64_popcnt(), | |||
| 2253 | MachineRepresentation::kWord64)); | |||
| 2254 | } | |||
| 2255 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | } | |||
| 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 | } | |||
| 2366 | Node* WasmGraphBuilder::BuildF64SConvertI64(Node* input) { | |||
| 2367 | return BuildIntToFloatConversionInstruction( | |||
| 2368 | input, ExternalReference::wasm_int64_to_float64(), | |||
| 2369 | MachineRepresentation::kWord64, MachineType::Float64()); | |||
| 2370 | } | |||
| 2371 | Node* WasmGraphBuilder::BuildF64UConvertI64(Node* input) { | |||
| 2372 | return BuildIntToFloatConversionInstruction( | |||
| 2373 | input, ExternalReference::wasm_uint64_to_float64(), | |||
| 2374 | MachineRepresentation::kWord64, MachineType::Float64()); | |||
| 2375 | } | |||
| 2376 | ||||
| 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 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 2414 | } | |||
| 2415 | } | |||
| 2416 | ||||
| 2417 | } // namespace | |||
| 2418 | ||||
| 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 | ||||
| 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 | ||||
| 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[[clang::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[[clang::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()V8_Fatal("unreachable code"); | |||
| 2544 | } | |||
| 2545 | } | |||
| 2546 | DCHECK_EQ(encoded_size, index)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 2602 | Node* WasmGraphBuilder::ExceptionTagEqual(Node* caught_tag, | |||
| 2603 | Node* expected_tag) { | |||
| 2604 | return gasm_->WordEqual(caught_tag, expected_tag); | |||
| 2605 | } | |||
| 2606 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 2675 | } | |||
| 2676 | values[i] = value; | |||
| 2677 | } | |||
| 2678 | DCHECK_EQ(index, WasmExceptionPackage::GetEncodedSize(tag))((void) 0); | |||
| 2679 | return values_array; | |||
| 2680 | } | |||
| 2681 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | } | |||
| 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 | ||||
| 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 | ||||
| 2972 | Node* WasmGraphBuilder::IsNull(Node* object) { | |||
| 2973 | return gasm_->TaggedEqual(object, RefNull()); | |||
| 2974 | } | |||
| 2975 | ||||
| 2976 | template <typename... Args> | |||
| 2977 | Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node* function, | |||
| 2978 | Args... args) { | |||
| 2979 | DCHECK_LE(sig->return_count(), 1)((void) 0); | |||
| 2980 | DCHECK_EQ(sizeof...(args), sig->parameter_count())((void) 0); | |||
| 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)(sizeof(ArraySizeHelper(call_args))), call_args); | |||
| 2987 | } | |||
| 2988 | ||||
| 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())((void) 0); | |||
| 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)((void) 0); | |||
| 3021 | if (position > 0) SetSourcePosition(call, position); | |||
| 3022 | ||||
| 3023 | return call; | |||
| 3024 | } | |||
| 3025 | ||||
| 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)((void) 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())((void) 0); | |||
| 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 | ||||
| 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)((void) 0); | |||
| 3068 | gasm_->MergeControlToEnd(call); | |||
| 3069 | ||||
| 3070 | return call; | |||
| 3071 | } | |||
| 3072 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 3112 | return BuildWasmReturnCall(sig, args, position, ref_node); | |||
| 3113 | } | |||
| 3114 | } | |||
| 3115 | ||||
| 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])((void) 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 | ||||
| 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 | ||||
| 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 | ||||
| 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])((void) 0); | |||
| 3199 | DCHECK_NOT_NULL(env_)((void) 0); | |||
| 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 | ||||
| 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)static_assert(kExternalPointerIndexShift > kSystemPointerSizeLog2 , "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 | ||||
| 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. | |||
| 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_BOOLfalse) { | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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])((void) 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 3434 | Node* WasmGraphBuilder::Invert(Node* node) { | |||
| 3435 | return Unop(wasm::kExprI32Eqz, node); | |||
| 3436 | } | |||
| 3437 | ||||
| 3438 | Node* WasmGraphBuilder::BuildTruncateIntPtrToInt32(Node* value) { | |||
| 3439 | return mcgraph()->machine()->Is64() ? gasm_->TruncateInt64ToInt32(value) | |||
| 3440 | : value; | |||
| 3441 | } | |||
| 3442 | ||||
| 3443 | Node* WasmGraphBuilder::BuildChangeInt32ToIntPtr(Node* value) { | |||
| 3444 | return mcgraph()->machine()->Is64() ? gasm_->ChangeInt32ToInt64(value) | |||
| 3445 | : value; | |||
| 3446 | } | |||
| 3447 | ||||
| 3448 | Node* WasmGraphBuilder::BuildChangeIntPtrToInt64(Node* value) { | |||
| 3449 | return mcgraph()->machine()->Is32() ? gasm_->ChangeInt32ToInt64(value) | |||
| 3450 | : value; | |||
| 3451 | } | |||
| 3452 | ||||
| 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 | ||||
| 3464 | Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() { | |||
| 3465 | return gasm_->IntPtrConstant(kSmiShiftSize + kSmiTagSize); | |||
| 3466 | } | |||
| 3467 | ||||
| 3468 | Node* WasmGraphBuilder::BuildSmiShiftBitsConstant32() { | |||
| 3469 | return Int32Constant(kSmiShiftSize + kSmiTagSize); | |||
| 3470 | } | |||
| 3471 | ||||
| 3472 | Node* WasmGraphBuilder::BuildChangeInt32ToSmi(Node* value) { | |||
| 3473 | // With pointer compression, only the lower 32 bits are used. | |||
| 3474 | return COMPRESS_POINTERS_BOOLfalse | |||
| 3475 | ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32()) | |||
| 3476 | : gasm_->WordShl(BuildChangeInt32ToIntPtr(value), | |||
| 3477 | BuildSmiShiftBitsConstant()); | |||
| 3478 | } | |||
| 3479 | ||||
| 3480 | Node* WasmGraphBuilder::BuildChangeUint31ToSmi(Node* value) { | |||
| 3481 | return COMPRESS_POINTERS_BOOLfalse | |||
| 3482 | ? gasm_->Word32Shl(value, BuildSmiShiftBitsConstant32()) | |||
| 3483 | : gasm_->WordShl(BuildChangeUint32ToUintPtr(value), | |||
| 3484 | BuildSmiShiftBitsConstant()); | |||
| 3485 | } | |||
| 3486 | ||||
| 3487 | Node* WasmGraphBuilder::BuildChangeSmiToInt32(Node* value) { | |||
| 3488 | return COMPRESS_POINTERS_BOOLfalse | |||
| 3489 | ? gasm_->Word32Sar(value, BuildSmiShiftBitsConstant32()) | |||
| 3490 | : BuildTruncateIntPtrToInt32( | |||
| 3491 | gasm_->WordSar(value, BuildSmiShiftBitsConstant())); | |||
| 3492 | } | |||
| 3493 | ||||
| 3494 | Node* WasmGraphBuilder::BuildChangeSmiToIntPtr(Node* value) { | |||
| 3495 | return COMPRESS_POINTERS_BOOLfalse | |||
| 3496 | ? BuildChangeInt32ToIntPtr( | |||
| 3497 | gasm_->Word32Sar(value, BuildSmiShiftBitsConstant32())) | |||
| 3498 | : gasm_->WordSar(value, BuildSmiShiftBitsConstant()); | |||
| 3499 | } | |||
| 3500 | ||||
| 3501 | Node* WasmGraphBuilder::BuildConvertUint32ToSmiWithSaturation(Node* value, | |||
| 3502 | uint32_t maxval) { | |||
| 3503 | DCHECK(Smi::IsValid(maxval))((void) 0); | |||
| 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 | ||||
| 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(gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(MemoryStart ), MachineType::SandboxedPointer()), GetInstance(), wasm::ObjectAccess ::ToTagged(WasmInstanceObject::kMemoryStartOffset)) | |||
| 3523 | MemoryStart, MachineType::SandboxedPointer())gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(MemoryStart ), MachineType::SandboxedPointer()), GetInstance(), wasm::ObjectAccess ::ToTagged(WasmInstanceObject::kMemoryStartOffset)); | |||
| 3524 | #else | |||
| 3525 | instance_cache->mem_start = | |||
| 3526 | LOAD_INSTANCE_FIELD_NO_ELIMINATION(MemoryStart, MachineType::UintPtr())gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(MemoryStart ), MachineType::UintPtr()), GetInstance(), wasm::ObjectAccess ::ToTagged(WasmInstanceObject::kMemoryStartOffset)); | |||
| 3527 | #endif | |||
| 3528 | ||||
| 3529 | // Load the memory size. | |||
| 3530 | instance_cache->mem_size = | |||
| 3531 | LOAD_INSTANCE_FIELD_NO_ELIMINATION(MemorySize, MachineType::UintPtr())gasm_->Load( assert_size(WASM_INSTANCE_OBJECT_SIZE(MemorySize ), MachineType::UintPtr()), GetInstance(), wasm::ObjectAccess ::ToTagged(WasmInstanceObject::kMemorySizeOffset)); | |||
| 3532 | } | |||
| 3533 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 3609 | Node* WasmGraphBuilder::effect() { return gasm_->effect(); } | |||
| 3610 | ||||
| 3611 | Node* WasmGraphBuilder::control() { return gasm_->control(); } | |||
| 3612 | ||||
| 3613 | Node* WasmGraphBuilder::SetEffect(Node* node) { | |||
| 3614 | SetEffectControl(node, control()); | |||
| 3615 | return node; | |||
| 3616 | } | |||
| 3617 | ||||
| 3618 | Node* WasmGraphBuilder::SetControl(Node* node) { | |||
| 3619 | SetEffectControl(effect(), node); | |||
| 3620 | return node; | |||
| 3621 | } | |||
| 3622 | ||||
| 3623 | void WasmGraphBuilder::SetEffectControl(Node* effect, Node* control) { | |||
| 3624 | gasm_->InitializeEffectControl(effect, control); | |||
| 3625 | } | |||
| 3626 | ||||
| 3627 | Node* WasmGraphBuilder::MemBuffer(uintptr_t offset) { | |||
| 3628 | DCHECK_NOT_NULL(instance_cache_)((void) 0); | |||
| 3629 | Node* mem_start = instance_cache_->mem_start; | |||
| 3630 | DCHECK_NOT_NULL(mem_start)((void) 0); | |||
| 3631 | if (offset == 0) return mem_start; | |||
| 3632 | return gasm_->IntAdd(mem_start, gasm_->UintPtrConstant(offset)); | |||
| 3633 | } | |||
| 3634 | ||||
| 3635 | Node* WasmGraphBuilder::CurrentMemoryPages() { | |||
| 3636 | // CurrentMemoryPages can not be called from asm.js. | |||
| 3637 | DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin)((void) 0); | |||
| 3638 | DCHECK_NOT_NULL(instance_cache_)((void) 0); | |||
| 3639 | Node* mem_size = instance_cache_->mem_size; | |||
| 3640 | DCHECK_NOT_NULL(mem_size)((void) 0); | |||
| 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. | |||
| 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)((void) 0); | |||
| 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)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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> | |||
| 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)((void) 0); | |||
| 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)((void) 0); | |||
| 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 | ||||
| 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_BOOLfalse && 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 | ||||
| 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_BOOLfalse && 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 | ||||
| 3918 | void WasmGraphBuilder::TraceFunctionEntry(wasm::WasmCodePosition position) { | |||
| 3919 | Node* call = BuildCallToRuntime(Runtime::kWasmTraceEnter, nullptr, 0); | |||
| 3920 | SetSourcePosition(call, position); | |||
| 3921 | } | |||
| 3922 | ||||
| 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 | ||||
| 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)__builtin_offsetof(wasm::MemoryTracingInfo, offset), | |||
| 3956 | MachineType::PointerRepresentation(), effective_offset); | |||
| 3957 | store(offsetof(wasm::MemoryTracingInfo, is_store)__builtin_offsetof(wasm::MemoryTracingInfo, is_store), | |||
| 3958 | MachineRepresentation::kWord8, Int32Constant(is_store ? 1 : 0)); | |||
| 3959 | store(offsetof(wasm::MemoryTracingInfo, mem_rep)__builtin_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)(sizeof(ArraySizeHelper(args)))); | |||
| 3965 | SetSourcePosition(call, position); | |||
| 3966 | } | |||
| 3967 | ||||
| 3968 | namespace { | |||
| 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()V8_Fatal("unreachable code"); | |||
| 4010 | } | |||
| 4011 | ||||
| 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 ||((void) 0) | |||
| 4020 | mcgraph->machine()->UnalignedLoadSupported(memrep))((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 4225 | Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) { | |||
| 4226 | DCHECK_NOT_NULL(instance_cache_)((void) 0); | |||
| 4227 | Node* mem_start = instance_cache_->mem_start; | |||
| 4228 | Node* mem_size = instance_cache_->mem_size; | |||
| 4229 | DCHECK_NOT_NULL(mem_start)((void) 0); | |||
| 4230 | DCHECK_NOT_NULL(mem_size)((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 4265 | } | |||
| 4266 | ||||
| 4267 | return bounds_check.Phi(type.representation(), load, oob_value); | |||
| 4268 | } | |||
| 4269 | ||||
| 4270 | Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index, | |||
| 4271 | Node* val) { | |||
| 4272 | DCHECK_NOT_NULL(instance_cache_)((void) 0); | |||
| 4273 | Node* mem_start = instance_cache_->mem_start; | |||
| 4274 | Node* mem_size = instance_cache_->mem_size; | |||
| 4275 | DCHECK_NOT_NULL(mem_start)((void) 0); | |||
| 4276 | DCHECK_NOT_NULL(mem_size)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 4344 | void WasmGraphBuilder::PrintDebugName(Node* node) { | |||
| 4345 | PrintF("#%d:%s", node->id(), node->op()->mnemonic()); | |||
| 4346 | } | |||
| 4347 | ||||
| 4348 | Graph* WasmGraphBuilder::graph() { return mcgraph()->graph(); } | |||
| 4349 | ||||
| 4350 | Zone* WasmGraphBuilder::graph_zone() { return graph()->zone(); } | |||
| 4351 | ||||
| 4352 | namespace { | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 4425 | void WasmGraphBuilder::LowerInt64(CallOrigin origin) { | |||
| 4426 | LowerInt64(CreateMachineSignature(mcgraph()->zone(), sig_, origin)); | |||
| 4427 | } | |||
| 4428 | ||||
| 4429 | void WasmGraphBuilder::SetSourcePosition(Node* node, | |||
| 4430 | wasm::WasmCodePosition position) { | |||
| 4431 | DCHECK_NE(position, wasm::kNoCodePosition)((void) 0); | |||
| 4432 | if (source_position_table_) { | |||
| 4433 | source_position_table_->SetSourcePosition(node, SourcePosition(position)); | |||
| 4434 | } | |||
| 4435 | } | |||
| 4436 | ||||
| 4437 | Node* WasmGraphBuilder::S128Zero() { | |||
| 4438 | has_simd_ = true; | |||
| 4439 | return graph()->NewNode(mcgraph()->machine()->S128Zero()); | |||
| 4440 | } | |||
| 4441 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 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 | ||||
| 5408 | void WasmGraphBuilder::AtomicFence() { | |||
| 5409 | SetEffect(graph()->NewNode(mcgraph()->machine()->MemBarrier(), effect(), | |||
| 5410 | control())); | |||
| 5411 | } | |||
| 5412 | ||||
| 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)((void) 0); | |||
| 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 | ||||
| 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)((void) 0); | |||
| 5443 | ||||
| 5444 | Node* seg_size_array = | |||
| 5445 | LOAD_INSTANCE_FIELD(DataSegmentSizes, MachineType::Pointer()); | |||
| 5446 | STATIC_ASSERT(wasm::kV8MaxWasmDataSegments <= kMaxUInt32 >> 2)static_assert(wasm::kV8MaxWasmDataSegments <= kMaxUInt32 >> 2, "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 | ||||
| 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)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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())((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 5996 | Node* WasmGraphBuilder::RefIsI31(Node* object) { return gasm_->IsI31(object); } | |||
| 5997 | ||||
| 5998 | Node* WasmGraphBuilder::RefAsI31(Node* object, | |||
| 5999 | wasm::WasmCodePosition position) { | |||
| 6000 | TrapIfFalse(wasm::kTrapIllegalCast, gasm_->IsI31(object), position); | |||
| 6001 | return object; | |||
| 6002 | } | |||
| 6003 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 6047 | void WasmGraphBuilder::BoundsCheckArray(Node* array, Node* index, | |||
| 6048 | wasm::WasmCodePosition position) { | |||
| 6049 | if (V8_UNLIKELY(FLAG_experimental_wasm_skip_bounds_checks)(__builtin_expect(!!(FLAG_experimental_wasm_skip_bounds_checks ), 0))) return; | |||
| 6050 | Node* length = gasm_->LoadWasmArrayLength(array); | |||
| 6051 | TrapIfFalse(wasm::kTrapArrayOutOfBounds, gasm_->Uint32LessThan(index, length), | |||
| 6052 | position); | |||
| 6053 | } | |||
| 6054 | ||||
| 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)(__builtin_expect(!!(FLAG_experimental_wasm_skip_bounds_checks ), 0))) 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 | ||||
| 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 | ||||
| 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 | ||||
| 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. | |||
| 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 | ||||
| 6142 | Node* WasmGraphBuilder::I31New(Node* input) { | |||
| 6143 | if (SmiValuesAre31Bits()) { | |||
| 6144 | return gasm_->Word32Shl(input, BuildSmiShiftBitsConstant32()); | |||
| 6145 | } | |||
| 6146 | DCHECK(SmiValuesAre32Bits())((void) 0); | |||
| 6147 | input = BuildChangeInt32ToIntPtr(input); | |||
| 6148 | return gasm_->WordShl(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)); | |||
| 6149 | } | |||
| 6150 | ||||
| 6151 | Node* WasmGraphBuilder::I31GetS(Node* input) { | |||
| 6152 | if (SmiValuesAre31Bits()) { | |||
| 6153 | input = BuildTruncateIntPtrToInt32(input); | |||
| 6154 | return gasm_->Word32SarShiftOutZeros(input, BuildSmiShiftBitsConstant32()); | |||
| 6155 | } | |||
| 6156 | DCHECK(SmiValuesAre32Bits())((void) 0); | |||
| 6157 | return BuildTruncateIntPtrToInt32( | |||
| 6158 | gasm_->WordSar(input, gasm_->IntPtrConstant(kI31To32BitSmiShift))); | |||
| 6159 | } | |||
| 6160 | ||||
| 6161 | Node* WasmGraphBuilder::I31GetU(Node* input) { | |||
| 6162 | if (SmiValuesAre31Bits()) { | |||
| 6163 | input = BuildTruncateIntPtrToInt32(input); | |||
| 6164 | return gasm_->Word32Shr(input, BuildSmiShiftBitsConstant32()); | |||
| 6165 | } | |||
| 6166 | DCHECK(SmiValuesAre32Bits())((void) 0); | |||
| 6167 | return BuildTruncateIntPtrToInt32( | |||
| 6168 | gasm_->WordShr(input, gasm_->IntPtrConstant(kI31To32BitSmiShift))); | |||
| 6169 | } | |||
| 6170 | ||||
| 6171 | class WasmDecorator final : public GraphDecorator { | |||
| 6172 | public: | |||
| 6173 | explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder) | |||
| 6174 | : origins_(origins), decoder_(decoder) {} | |||
| 6175 | ||||
| 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 | ||||
| 6187 | void WasmGraphBuilder::AddBytecodePositionDecorator( | |||
| 6188 | NodeOriginTable* node_origins, wasm::Decoder* decoder) { | |||
| 6189 | DCHECK_NULL(decorator_)((void) 0); | |||
| 6190 | decorator_ = graph()->zone()->New<WasmDecorator>(node_origins, decoder); | |||
| 6191 | graph()->AddDecorator(decorator_); | |||
| 6192 | } | |||
| 6193 | ||||
| 6194 | void WasmGraphBuilder::RemoveBytecodePositionDecorator() { | |||
| 6195 | DCHECK_NOT_NULL(decorator_)((void) 0); | |||
| 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: | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 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. | |||
| 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}. | |||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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)static_assert(wasm::ValueType::kLastUsedBit + 1 <= kSmiValueSize , "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 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 6660 | } | |||
| 6661 | } | |||
| 6662 | ||||
| 6663 | Node* SmiToFloat32(Node* input) { | |||
| 6664 | return gasm_->RoundInt32ToFloat32(BuildChangeSmiToInt32(input)); | |||
| 6665 | } | |||
| 6666 | ||||
| 6667 | Node* SmiToFloat64(Node* input) { | |||
| 6668 | return gasm_->ChangeInt32ToFloat64(BuildChangeSmiToInt32(input)); | |||
| 6669 | } | |||
| 6670 | ||||
| 6671 | Node* HeapNumberToFloat64(Node* input) { | |||
| 6672 | return gasm_->LoadFromObject( | |||
| 6673 | MachineType::Float64(), input, | |||
| 6674 | wasm::ObjectAccess::ToTagged(HeapNumber::kValueOffset)); | |||
| 6675 | } | |||
| 6676 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 6713 | } | |||
| 6714 | } | |||
| 6715 | ||||
| 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 | ||||
| 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: | |||
| 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 | ||||
| 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 | ||||
| 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. | |||
| 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 <=static_assert(wasm::kV8MaxWasmFunctionReturns <= JSArray:: kInitialMaxFastElementArray, "wasm::kV8MaxWasmFunctionReturns <= JSArray::kInitialMaxFastElementArray" ) | |||
| 6799 | JSArray::kInitialMaxFastElementArray)static_assert(wasm::kV8MaxWasmFunctionReturns <= JSArray:: kInitialMaxFastElementArray, "wasm::kV8MaxWasmFunctionReturns <= JSArray::kInitialMaxFastElementArray" ); | |||
| 6800 | return gasm_->CallBuiltin(Builtin::kWasmAllocateJSArray, | |||
| 6801 | Operator::kEliminatable, array_length, context); | |||
| 6802 | } | |||
| 6803 | ||||
| 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 | ||||
| 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 | ||||
| 6887 | Node* IsSmi(Node* input) { | |||
| 6888 | return gasm_->Word32Equal( | |||
| 6889 | gasm_->Word32And(BuildTruncateIntPtrToInt32(input), | |||
| 6890 | Int32Constant(kSmiTagMask)), | |||
| 6891 | Int32Constant(kSmiTag)); | |||
| 6892 | } | |||
| 6893 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 6928 | } | |||
| 6929 | } | |||
| 6930 | ||||
| 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 | ||||
| 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 | ||||
| 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. | |||
| 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())((void) 0); | |||
| 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())((void) 0); | |||
| 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())((void) 0); | |||
| 7221 | call = gasm_->Call(call_descriptor, pos, args.begin()); | |||
| 7222 | // TODO(12191): Handle suspending wrapper. | |||
| 7223 | break; | |||
| 7224 | } | |||
| 7225 | default: | |||
| 7226 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 7227 | } | |||
| 7228 | DCHECK_NOT_NULL(call)((void) 0); | |||
| 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 | ||||
| 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)((void) 0); | |||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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())((void) 0); | |||
| 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_)((void) 0); | |||
| 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 | ||||
| 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 | ||||
| 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__func__, 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 | ||||
| 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 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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)((void) 0); \ | |||
| 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 | ||||
| 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()V8_Fatal("unreachable code"); | |||
| 7976 | } | |||
| 7977 | #undef CASE | |||
| 7978 | } | |||
| 7979 | ||||
| 7980 | wasm::WasmCompilationResult CompileWasmMathIntrinsic( | |||
| 7981 | WasmImportCallKind kind, const wasm::FunctionSig* sig) { | |||
| 7982 | DCHECK_EQ(1, sig->return_count())((void) 0); | |||
| 7983 | ||||
| 7984 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),static v8::base::AtomicWord trace_event_unique_atomic7985 = 0 ; const uint8_t* trace_event_unique_category_group_enabled7985 ; trace_event_unique_category_group_enabled7985 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic7985 ))); if (!trace_event_unique_category_group_enabled7985) { trace_event_unique_category_group_enabled7985 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic7985 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled7985 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer7985 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled7985 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled7985, "wasm.CompileWasmMathIntrinsic" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer7985 .Initialize(trace_event_unique_category_group_enabled7985 , "wasm.CompileWasmMathIntrinsic", h); } | |||
| 7985 | "wasm.CompileWasmMathIntrinsic")static v8::base::AtomicWord trace_event_unique_atomic7985 = 0 ; const uint8_t* trace_event_unique_category_group_enabled7985 ; trace_event_unique_category_group_enabled7985 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic7985 ))); if (!trace_event_unique_category_group_enabled7985) { trace_event_unique_category_group_enabled7985 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic7985 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled7985 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer7985 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled7985 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled7985, "wasm.CompileWasmMathIntrinsic" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer7985 .Initialize(trace_event_unique_category_group_enabled7985 , "wasm.CompileWasmMathIntrinsic", h); }; | |||
| 7986 | ||||
| 7987 | Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME__func__, 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()V8_Fatal("unreachable code"); | |||
| 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 | ||||
| 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)((void) 0); | |||
| 8048 | DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind)((void) 0); | |||
| 8049 | DCHECK_NE(WasmImportCallKind::kWasmToJSFastApi, kind)((void) 0); | |||
| 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"),static v8::base::AtomicWord trace_event_unique_atomic8059 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8059 ; trace_event_unique_category_group_enabled8059 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8059 ))); if (!trace_event_unique_category_group_enabled8059) { trace_event_unique_category_group_enabled8059 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8059 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8059 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8059 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8059 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8059, "wasm.CompileWasmImportCallWrapper" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8059 .Initialize(trace_event_unique_category_group_enabled8059 , "wasm.CompileWasmImportCallWrapper", h); } | |||
| 8059 | "wasm.CompileWasmImportCallWrapper")static v8::base::AtomicWord trace_event_unique_atomic8059 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8059 ; trace_event_unique_category_group_enabled8059 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8059 ))); if (!trace_event_unique_category_group_enabled8059) { trace_event_unique_category_group_enabled8059 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8059 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8059 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8059 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8059 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8059, "wasm.CompileWasmImportCallWrapper" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8059 .Initialize(trace_event_unique_category_group_enabled8059 , "wasm.CompileWasmImportCallWrapper", h); }; | |||
| 8060 | base::TimeTicks start_time; | |||
| 8061 | if (V8_UNLIKELY(FLAG_trace_wasm_compilation_times)(__builtin_expect(!!(FLAG_trace_wasm_compilation_times), 0))) { | |||
| 8062 | start_time = base::TimeTicks::Now(); | |||
| 8063 | } | |||
| 8064 | ||||
| 8065 | //---------------------------------------------------------------------------- | |||
| 8066 | // Create the Graph | |||
| 8067 | //---------------------------------------------------------------------------- | |||
| 8068 | Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME__func__, 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)(__builtin_expect(!!(FLAG_trace_wasm_compilation_times), 0))) { | |||
| 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 | ||||
| 8115 | wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::NativeModule* native_module, | |||
| 8116 | const wasm::FunctionSig* sig) { | |||
| 8117 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),static v8::base::AtomicWord trace_event_unique_atomic8118 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8118 ; trace_event_unique_category_group_enabled8118 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8118 ))); if (!trace_event_unique_category_group_enabled8118) { trace_event_unique_category_group_enabled8118 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8118 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8118 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8118 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8118 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8118, "wasm.CompileWasmCapiFunction" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8118 .Initialize(trace_event_unique_category_group_enabled8118 , "wasm.CompileWasmCapiFunction", h); } | |||
| 8118 | "wasm.CompileWasmCapiFunction")static v8::base::AtomicWord trace_event_unique_atomic8118 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8118 ; trace_event_unique_category_group_enabled8118 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8118 ))); if (!trace_event_unique_category_group_enabled8118) { trace_event_unique_category_group_enabled8118 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8118 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8118 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8118 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8118 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8118, "wasm.CompileWasmCapiFunction" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8118 .Initialize(trace_event_unique_category_group_enabled8118 , "wasm.CompileWasmCapiFunction", h); }; | |||
| 8119 | ||||
| 8120 | Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME__func__, 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 | ||||
| 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"),static v8::base::AtomicWord trace_event_unique_atomic8167 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8167 ; trace_event_unique_category_group_enabled8167 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8167 ))); if (!trace_event_unique_category_group_enabled8167) { trace_event_unique_category_group_enabled8167 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8167 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8167 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8167 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8167 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8167, "wasm.CompileWasmJSFastCallWrapper" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8167 .Initialize(trace_event_unique_category_group_enabled8167 , "wasm.CompileWasmJSFastCallWrapper", h); } | |||
| 8167 | "wasm.CompileWasmJSFastCallWrapper")static v8::base::AtomicWord trace_event_unique_atomic8167 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8167 ; trace_event_unique_category_group_enabled8167 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8167 ))); if (!trace_event_unique_category_group_enabled8167) { trace_event_unique_category_group_enabled8167 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8167 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8167 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8167 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8167 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8167, "wasm.CompileWasmJSFastCallWrapper" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0))); trace_event_unique_tracer8167 .Initialize(trace_event_unique_category_group_enabled8167 , "wasm.CompileWasmJSFastCallWrapper", h); }; | |||
| 8168 | ||||
| 8169 | Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME__func__, 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 | ||||
| 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__func__, 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 | ||||
| 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__func__, 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 | ||||
| 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__func__, 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),do { bool _cmp = ::v8::base::CmpNEImpl< typename ::v8::base ::pass_value_or_ref<decltype(job->ExecuteJob(isolate-> counters()->runtime_call_stats(), nullptr))>::type, typename ::v8::base::pass_value_or_ref<decltype(CompilationJob::FAILED )>::type>((job->ExecuteJob(isolate->counters()-> runtime_call_stats(), nullptr)), (CompilationJob::FAILED)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "job->ExecuteJob(isolate->counters()->runtime_call_stats(), nullptr)" " " "!=" " " "CompilationJob::FAILED"); } } while (false); } while (false) | |||
| 8364 | CompilationJob::FAILED)do { bool _cmp = ::v8::base::CmpNEImpl< typename ::v8::base ::pass_value_or_ref<decltype(job->ExecuteJob(isolate-> counters()->runtime_call_stats(), nullptr))>::type, typename ::v8::base::pass_value_or_ref<decltype(CompilationJob::FAILED )>::type>((job->ExecuteJob(isolate->counters()-> runtime_call_stats(), nullptr)), (CompilationJob::FAILED)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "job->ExecuteJob(isolate->counters()->runtime_call_stats(), nullptr)" " " "!=" " " "CompilationJob::FAILED"); } } while (false); } while (false); | |||
| 8365 | CHECK_NE(job->FinalizeJob(isolate), CompilationJob::FAILED)do { bool _cmp = ::v8::base::CmpNEImpl< typename ::v8::base ::pass_value_or_ref<decltype(job->FinalizeJob(isolate)) >::type, typename ::v8::base::pass_value_or_ref<decltype (CompilationJob::FAILED)>::type>((job->FinalizeJob(isolate )), (CompilationJob::FAILED)); do { if ((__builtin_expect(!!( !(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "job->FinalizeJob(isolate)" " " "!=" " " "CompilationJob::FAILED"); } } while (false); } while (false); | |||
| 8366 | ||||
| 8367 | return ToCodeT(job->compilation_info()->code(), isolate); | |||
| 8368 | } | |||
| 8369 | ||||
| 8370 | namespace { | |||
| 8371 | ||||
| 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 | ||||
| 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())((void) 0); | |||
| 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 | ||||
| 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)((void) 0); | |||
| 8441 | ||||
| 8442 | TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),static v8::base::AtomicWord trace_event_unique_atomic8444 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8444 ; trace_event_unique_category_group_enabled8444 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8444 ))); if (!trace_event_unique_category_group_enabled8444) { trace_event_unique_category_group_enabled8444 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8444 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8444 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8444 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8444 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0)), "func_index", func_index, "body_size", func_body .end - func_body.start); trace_event_unique_tracer8444 .Initialize (trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , h); } | |||
| 8443 | "wasm.CompileTopTier", "func_index", func_index, "body_size",static v8::base::AtomicWord trace_event_unique_atomic8444 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8444 ; trace_event_unique_category_group_enabled8444 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8444 ))); if (!trace_event_unique_category_group_enabled8444) { trace_event_unique_category_group_enabled8444 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8444 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8444 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8444 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8444 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0)), "func_index", func_index, "body_size", func_body .end - func_body.start); trace_event_unique_tracer8444 .Initialize (trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , h); } | |||
| 8444 | func_body.end - func_body.start)static v8::base::AtomicWord trace_event_unique_atomic8444 = 0 ; const uint8_t* trace_event_unique_category_group_enabled8444 ; trace_event_unique_category_group_enabled8444 = reinterpret_cast <const uint8_t*>(v8::base::Relaxed_Load(&(trace_event_unique_atomic8444 ))); if (!trace_event_unique_category_group_enabled8444) { trace_event_unique_category_group_enabled8444 = v8::internal::tracing::TraceEventHelper::GetTracingController () ->GetCategoryGroupEnabled("disabled-by-default-" "v8.wasm.detailed" ); v8::base::Relaxed_Store(&(trace_event_unique_atomic8444 ), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled8444 ))); };; v8::internal::tracing::ScopedTracer trace_event_unique_tracer8444 ; if (v8::base::Relaxed_Load(reinterpret_cast<const v8::base ::Atomic8*>( trace_event_unique_category_group_enabled8444 )) & (kEnabledForRecording_CategoryGroupEnabledFlags | kEnabledForEventCallback_CategoryGroupEnabledFlags )) { uint64_t h = v8::internal::tracing::AddTraceEvent( ('X') , trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , v8::internal::tracing::kGlobalScope, v8::internal::tracing:: kNoId, v8::internal::tracing::kNoId, (static_cast<unsigned int>(0)), "func_index", func_index, "body_size", func_body .end - func_body.start); trace_event_unique_tracer8444 .Initialize (trace_event_unique_category_group_enabled8444, "wasm.CompileTopTier" , h); }; | |||
| 8445 | Zone zone(wasm::GetWasmEngine()->allocator(), ZONE_NAME__func__, 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)(__builtin_expect(!!(FLAG_turbo_stats_wasm && FLAG_wasm_tier_up_filter >= 0), 0))) { | |||
| 8513 | wasm::GetWasmEngine()->DumpTurboStatistics(); | |||
| 8514 | } | |||
| 8515 | auto result = info.ReleaseWasmCompilationResult(); | |||
| 8516 | CHECK_NOT_NULL(result)do { if ((__builtin_expect(!!(!((result) != nullptr)), 0))) { V8_Fatal("Check failed: %s.", "(result) != nullptr"); } } while (false); // Compilation expected to succeed. | |||
| 8517 | DCHECK_EQ(wasm::ExecutionTier::kTurbofan, result->result_tier)((void) 0); | |||
| 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> | |||
| 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 | ||||
| 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 | ||||
| 8547 | int NumStackSlots() const { return allocator_.NumStackSlots(); } | |||
| 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 | ||||
| 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. | |||
| 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)((void) 0); | |||
| 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 { | |||
| 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 | ||||
| 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),((void) 0) | |||
| 8715 | input_type.machine_type())((void) 0); | |||
| 8716 | } | |||
| 8717 | for (size_t i = 0; i < call_descriptor->ReturnCount(); i++) { | |||
| 8718 | DCHECK_NE(call_descriptor->GetReturnType(i), input_type.machine_type())((void) 0); | |||
| 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 | |||
| 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 | ||||
| 8770 | CallDescriptor* GetI32WasmCallDescriptor( | |||
| 8771 | Zone* zone, const CallDescriptor* call_descriptor) { | |||
| 8772 | return ReplaceTypeInCallDescriptorWith(zone, call_descriptor, 2, | |||
| 8773 | wasm::kWasmI64, wasm::kWasmI32); | |||
| 8774 | } | |||
| 8775 | ||||
| 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 | ||||
| 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 |