1 // Copyright 2017 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/builtins/builtins-utils-gen.h"
6 #include "src/codegen/code-stub-assembler.h"
7 #include "src/codegen/interface-descriptors.h"
8 #include "src/objects/objects-inl.h"
9 #include "src/wasm/wasm-objects.h"
10 #include "src/wasm/wasm-opcodes.h"
11
12 namespace v8 {
13 namespace internal {
14
15 class WasmBuiltinsAssembler : public CodeStubAssembler {
16 public:
WasmBuiltinsAssembler(compiler::CodeAssemblerState * state)17 explicit WasmBuiltinsAssembler(compiler::CodeAssemblerState* state)
18 : CodeStubAssembler(state) {}
19
20 protected:
LoadInstanceFromFrame()21 TNode<WasmInstanceObject> LoadInstanceFromFrame() {
22 return CAST(
23 LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset));
24 }
25
LoadContextFromInstance(TNode<WasmInstanceObject> instance)26 TNode<Context> LoadContextFromInstance(TNode<WasmInstanceObject> instance) {
27 return CAST(Load(MachineType::AnyTagged(), instance,
28 IntPtrConstant(WasmInstanceObject::kNativeContextOffset -
29 kHeapObjectTag)));
30 }
31
SmiFromUint32WithSaturation(TNode<Uint32T> value,uint32_t max)32 TNode<Smi> SmiFromUint32WithSaturation(TNode<Uint32T> value, uint32_t max) {
33 DCHECK_LE(max, static_cast<uint32_t>(Smi::kMaxValue));
34 TNode<Uint32T> capped_value = SelectConstant(
35 Uint32LessThan(value, Uint32Constant(max)), value, Uint32Constant(max));
36 return SmiFromUint32(capped_value);
37 }
38 };
39
TF_BUILTIN(WasmStackGuard,WasmBuiltinsAssembler)40 TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) {
41 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
42 TNode<Context> context = LoadContextFromInstance(instance);
43 TailCallRuntime(Runtime::kWasmStackGuard, context);
44 }
45
TF_BUILTIN(WasmStackOverflow,WasmBuiltinsAssembler)46 TF_BUILTIN(WasmStackOverflow, WasmBuiltinsAssembler) {
47 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
48 TNode<Context> context = LoadContextFromInstance(instance);
49 TailCallRuntime(Runtime::kThrowWasmStackOverflow, context);
50 }
51
TF_BUILTIN(WasmThrow,WasmBuiltinsAssembler)52 TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) {
53 TNode<Object> exception = CAST(Parameter(Descriptor::kException));
54 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
55 TNode<Context> context = LoadContextFromInstance(instance);
56 TailCallRuntime(Runtime::kThrow, context, exception);
57 }
58
TF_BUILTIN(WasmRethrow,WasmBuiltinsAssembler)59 TF_BUILTIN(WasmRethrow, WasmBuiltinsAssembler) {
60 TNode<Object> exception = CAST(Parameter(Descriptor::kException));
61 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
62 TNode<Context> context = LoadContextFromInstance(instance);
63
64 Label nullref(this, Label::kDeferred);
65 GotoIf(TaggedEqual(NullConstant(), exception), &nullref);
66
67 TailCallRuntime(Runtime::kReThrow, context, exception);
68
69 BIND(&nullref);
70 MessageTemplate message_id = MessageTemplate::kWasmTrapRethrowNullRef;
71 TailCallRuntime(Runtime::kThrowWasmError, context,
72 SmiConstant(static_cast<int>(message_id)));
73 }
74
TF_BUILTIN(WasmTraceMemory,WasmBuiltinsAssembler)75 TF_BUILTIN(WasmTraceMemory, WasmBuiltinsAssembler) {
76 TNode<Smi> info = CAST(Parameter(Descriptor::kMemoryTracingInfo));
77 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
78 TNode<Context> context = LoadContextFromInstance(instance);
79 TailCallRuntime(Runtime::kWasmTraceMemory, context, info);
80 }
81
TF_BUILTIN(WasmAtomicNotify,WasmBuiltinsAssembler)82 TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) {
83 TNode<Uint32T> address =
84 UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
85 TNode<Uint32T> count = UncheckedCast<Uint32T>(Parameter(Descriptor::kCount));
86
87 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
88 TNode<Number> address_number = ChangeUint32ToTagged(address);
89 TNode<Number> count_number = ChangeUint32ToTagged(count);
90 TNode<Context> context = LoadContextFromInstance(instance);
91
92 TNode<Smi> result_smi =
93 CAST(CallRuntime(Runtime::kWasmAtomicNotify, context, instance,
94 address_number, count_number));
95 Return(Unsigned(SmiToInt32(result_smi)));
96 }
97
TF_BUILTIN(WasmI32AtomicWait32,WasmBuiltinsAssembler)98 TF_BUILTIN(WasmI32AtomicWait32, WasmBuiltinsAssembler) {
99 if (!Is32()) {
100 Unreachable();
101 return;
102 }
103
104 TNode<Uint32T> address =
105 UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
106 TNode<Number> address_number = ChangeUint32ToTagged(address);
107
108 TNode<Int32T> expected_value =
109 UncheckedCast<Int32T>(Parameter(Descriptor::kExpectedValue));
110 TNode<Number> expected_value_number = ChangeInt32ToTagged(expected_value);
111
112 TNode<IntPtrT> timeout_low =
113 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeoutLow));
114 TNode<IntPtrT> timeout_high =
115 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeoutHigh));
116 TNode<BigInt> timeout = BigIntFromInt32Pair(timeout_low, timeout_high);
117
118 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
119 TNode<Context> context = LoadContextFromInstance(instance);
120
121 TNode<Smi> result_smi =
122 CAST(CallRuntime(Runtime::kWasmI32AtomicWait, context, instance,
123 address_number, expected_value_number, timeout));
124 Return(Unsigned(SmiToInt32(result_smi)));
125 }
126
TF_BUILTIN(WasmI32AtomicWait64,WasmBuiltinsAssembler)127 TF_BUILTIN(WasmI32AtomicWait64, WasmBuiltinsAssembler) {
128 if (!Is64()) {
129 Unreachable();
130 return;
131 }
132
133 TNode<Uint32T> address =
134 UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
135 TNode<Number> address_number = ChangeUint32ToTagged(address);
136
137 TNode<Int32T> expected_value =
138 UncheckedCast<Int32T>(Parameter(Descriptor::kExpectedValue));
139 TNode<Number> expected_value_number = ChangeInt32ToTagged(expected_value);
140
141 TNode<IntPtrT> timeout_raw =
142 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeout));
143 TNode<BigInt> timeout = BigIntFromInt64(timeout_raw);
144
145 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
146 TNode<Context> context = LoadContextFromInstance(instance);
147
148 TNode<Smi> result_smi =
149 CAST(CallRuntime(Runtime::kWasmI32AtomicWait, context, instance,
150 address_number, expected_value_number, timeout));
151 Return(Unsigned(SmiToInt32(result_smi)));
152 }
153
TF_BUILTIN(WasmI64AtomicWait32,WasmBuiltinsAssembler)154 TF_BUILTIN(WasmI64AtomicWait32, WasmBuiltinsAssembler) {
155 if (!Is32()) {
156 Unreachable();
157 return;
158 }
159
160 TNode<Uint32T> address =
161 UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
162 TNode<Number> address_number = ChangeUint32ToTagged(address);
163
164 TNode<IntPtrT> expected_value_low =
165 UncheckedCast<IntPtrT>(Parameter(Descriptor::kExpectedValueLow));
166 TNode<IntPtrT> expected_value_high =
167 UncheckedCast<IntPtrT>(Parameter(Descriptor::kExpectedValueHigh));
168 TNode<BigInt> expected_value =
169 BigIntFromInt32Pair(expected_value_low, expected_value_high);
170
171 TNode<IntPtrT> timeout_low =
172 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeoutLow));
173 TNode<IntPtrT> timeout_high =
174 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeoutHigh));
175 TNode<BigInt> timeout = BigIntFromInt32Pair(timeout_low, timeout_high);
176
177 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
178 TNode<Context> context = LoadContextFromInstance(instance);
179
180 TNode<Smi> result_smi =
181 CAST(CallRuntime(Runtime::kWasmI64AtomicWait, context, instance,
182 address_number, expected_value, timeout));
183 Return(Unsigned(SmiToInt32(result_smi)));
184 }
185
TF_BUILTIN(WasmI64AtomicWait64,WasmBuiltinsAssembler)186 TF_BUILTIN(WasmI64AtomicWait64, WasmBuiltinsAssembler) {
187 if (!Is64()) {
188 Unreachable();
189 return;
190 }
191
192 TNode<Uint32T> address =
193 UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
194 TNode<Number> address_number = ChangeUint32ToTagged(address);
195
196 TNode<IntPtrT> expected_value_raw =
197 UncheckedCast<IntPtrT>(Parameter(Descriptor::kExpectedValue));
198 TNode<BigInt> expected_value = BigIntFromInt64(expected_value_raw);
199
200 TNode<IntPtrT> timeout_raw =
201 UncheckedCast<IntPtrT>(Parameter(Descriptor::kTimeout));
202 TNode<BigInt> timeout = BigIntFromInt64(timeout_raw);
203
204 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
205 TNode<Context> context = LoadContextFromInstance(instance);
206
207 TNode<Smi> result_smi =
208 CAST(CallRuntime(Runtime::kWasmI64AtomicWait, context, instance,
209 address_number, expected_value, timeout));
210 Return(Unsigned(SmiToInt32(result_smi)));
211 }
212
TF_BUILTIN(WasmMemoryGrow,WasmBuiltinsAssembler)213 TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) {
214 TNode<Int32T> num_pages =
215 UncheckedCast<Int32T>(Parameter(Descriptor::kNumPages));
216 Label num_pages_out_of_range(this, Label::kDeferred);
217
218 TNode<BoolT> num_pages_fits_in_smi =
219 IsValidPositiveSmi(ChangeInt32ToIntPtr(num_pages));
220 GotoIfNot(num_pages_fits_in_smi, &num_pages_out_of_range);
221
222 TNode<Smi> num_pages_smi = SmiFromInt32(num_pages);
223 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
224 TNode<Context> context = LoadContextFromInstance(instance);
225 TNode<Smi> ret_smi = CAST(
226 CallRuntime(Runtime::kWasmMemoryGrow, context, instance, num_pages_smi));
227 Return(SmiToInt32(ret_smi));
228
229 BIND(&num_pages_out_of_range);
230 Return(Int32Constant(-1));
231 }
232
TF_BUILTIN(WasmTableInit,WasmBuiltinsAssembler)233 TF_BUILTIN(WasmTableInit, WasmBuiltinsAssembler) {
234 TNode<Uint32T> dst_raw =
235 UncheckedCast<Uint32T>(Parameter(Descriptor::kDestination));
236 // We cap {dst}, {src}, and {size} by {wasm::kV8MaxWasmTableSize + 1} to make
237 // sure that the values fit into a Smi.
238 STATIC_ASSERT(static_cast<size_t>(Smi::kMaxValue) >=
239 wasm::kV8MaxWasmTableSize + 1);
240 constexpr uint32_t kCap =
241 static_cast<uint32_t>(wasm::kV8MaxWasmTableSize + 1);
242 TNode<Smi> dst = SmiFromUint32WithSaturation(dst_raw, kCap);
243 TNode<Uint32T> src_raw =
244 UncheckedCast<Uint32T>(Parameter(Descriptor::kSource));
245 TNode<Smi> src = SmiFromUint32WithSaturation(src_raw, kCap);
246 TNode<Uint32T> size_raw =
247 UncheckedCast<Uint32T>(Parameter(Descriptor::kSize));
248 TNode<Smi> size = SmiFromUint32WithSaturation(size_raw, kCap);
249 TNode<Smi> table_index =
250 UncheckedCast<Smi>(Parameter(Descriptor::kTableIndex));
251 TNode<Smi> segment_index =
252 UncheckedCast<Smi>(Parameter(Descriptor::kSegmentIndex));
253 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
254 TNode<Context> context = LoadContextFromInstance(instance);
255
256 TailCallRuntime(Runtime::kWasmTableInit, context, instance, table_index,
257 segment_index, dst, src, size);
258 }
259
TF_BUILTIN(WasmTableCopy,WasmBuiltinsAssembler)260 TF_BUILTIN(WasmTableCopy, WasmBuiltinsAssembler) {
261 // We cap {dst}, {src}, and {size} by {wasm::kV8MaxWasmTableSize + 1} to make
262 // sure that the values fit into a Smi.
263 STATIC_ASSERT(static_cast<size_t>(Smi::kMaxValue) >=
264 wasm::kV8MaxWasmTableSize + 1);
265 constexpr uint32_t kCap =
266 static_cast<uint32_t>(wasm::kV8MaxWasmTableSize + 1);
267
268 TNode<Uint32T> dst_raw =
269 UncheckedCast<Uint32T>(Parameter(Descriptor::kDestination));
270 TNode<Smi> dst = SmiFromUint32WithSaturation(dst_raw, kCap);
271
272 TNode<Uint32T> src_raw =
273 UncheckedCast<Uint32T>(Parameter(Descriptor::kSource));
274 TNode<Smi> src = SmiFromUint32WithSaturation(src_raw, kCap);
275
276 TNode<Uint32T> size_raw =
277 UncheckedCast<Uint32T>(Parameter(Descriptor::kSize));
278 TNode<Smi> size = SmiFromUint32WithSaturation(size_raw, kCap);
279
280 TNode<Smi> dst_table =
281 UncheckedCast<Smi>(Parameter(Descriptor::kDestinationTable));
282
283 TNode<Smi> src_table =
284 UncheckedCast<Smi>(Parameter(Descriptor::kSourceTable));
285
286 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
287 TNode<Context> context = LoadContextFromInstance(instance);
288
289 TailCallRuntime(Runtime::kWasmTableCopy, context, instance, dst_table,
290 src_table, dst, src, size);
291 }
292
TF_BUILTIN(WasmTableGet,WasmBuiltinsAssembler)293 TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) {
294 TNode<Int32T> entry_index =
295 UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex));
296 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
297 TNode<Context> context = LoadContextFromInstance(instance);
298 Label entry_index_out_of_range(this, Label::kDeferred);
299
300 TNode<BoolT> entry_index_fits_in_smi =
301 IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index));
302 GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range);
303
304 TNode<Smi> entry_index_smi = SmiFromInt32(entry_index);
305 TNode<Smi> table_index_smi = CAST(Parameter(Descriptor::kTableIndex));
306
307 TailCallRuntime(Runtime::kWasmFunctionTableGet, context, instance,
308 table_index_smi, entry_index_smi);
309
310 BIND(&entry_index_out_of_range);
311 MessageTemplate message_id =
312 wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds);
313 TailCallRuntime(Runtime::kThrowWasmError, context,
314 SmiConstant(static_cast<int>(message_id)));
315 }
316
TF_BUILTIN(WasmTableSet,WasmBuiltinsAssembler)317 TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) {
318 TNode<Int32T> entry_index =
319 UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex));
320 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
321 TNode<Context> context = LoadContextFromInstance(instance);
322 Label entry_index_out_of_range(this, Label::kDeferred);
323
324 TNode<BoolT> entry_index_fits_in_smi =
325 IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index));
326 GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range);
327
328 TNode<Smi> entry_index_smi = SmiFromInt32(entry_index);
329 TNode<Smi> table_index_smi = CAST(Parameter(Descriptor::kTableIndex));
330 TNode<Object> value = CAST(Parameter(Descriptor::kValue));
331 TailCallRuntime(Runtime::kWasmFunctionTableSet, context, instance,
332 table_index_smi, entry_index_smi, value);
333
334 BIND(&entry_index_out_of_range);
335 MessageTemplate message_id =
336 wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds);
337 TailCallRuntime(Runtime::kThrowWasmError, context,
338 SmiConstant(static_cast<int>(message_id)));
339 }
340
341 #define DECLARE_THROW_RUNTIME_FN(name) \
342 TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \
343 TNode<WasmInstanceObject> instance = LoadInstanceFromFrame(); \
344 TNode<Context> context = LoadContextFromInstance(instance); \
345 MessageTemplate message_id = \
346 wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \
347 TailCallRuntime(Runtime::kThrowWasmError, context, \
348 SmiConstant(static_cast<int>(message_id))); \
349 }
350 FOREACH_WASM_TRAPREASON(DECLARE_THROW_RUNTIME_FN)
351 #undef DECLARE_THROW_RUNTIME_FN
352
353 } // namespace internal
354 } // namespace v8
355