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