1// Copyright 2020 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-wasm-gen.h'
6
7namespace runtime {
8extern runtime WasmMemoryGrow(Context, WasmInstanceObject, Smi): Smi;
9extern runtime WasmRefFunc(Context, WasmInstanceObject, Smi): JSAny;
10extern runtime WasmTableInit(
11    Context, WasmInstanceObject, Object, Object, Smi, Smi, Smi): JSAny;
12extern runtime WasmTableCopy(
13    Context, WasmInstanceObject, Object, Object, Smi, Smi, Smi): JSAny;
14extern runtime WasmFunctionTableGet(
15    Context, WasmInstanceObject, Smi, Smi): JSAny;
16extern runtime WasmFunctionTableSet(
17    Context, WasmInstanceObject, Smi, Smi, Object): JSAny;
18extern runtime ThrowWasmError(Context, Smi): JSAny;
19extern runtime Throw(Context, Object): JSAny;
20extern runtime ReThrow(Context, Object): JSAny;
21extern runtime WasmTriggerTierUp(Context, WasmInstanceObject): JSAny;
22extern runtime WasmStackGuard(Context): JSAny;
23extern runtime ThrowWasmStackOverflow(Context): JSAny;
24extern runtime WasmTraceMemory(Context, Smi): JSAny;
25extern runtime WasmTraceEnter(Context): JSAny;
26extern runtime WasmTraceExit(Context, Smi): JSAny;
27extern runtime WasmAtomicNotify(
28    Context, WasmInstanceObject, Number, Number): Smi;
29extern runtime WasmI32AtomicWait(
30    Context, WasmInstanceObject, Number, Number, BigInt): Smi;
31extern runtime WasmI64AtomicWait(
32    Context, WasmInstanceObject, Number, BigInt, BigInt): Smi;
33extern runtime WasmAllocateRtt(Context, Smi, Map): Map;
34}
35
36namespace unsafe {
37extern macro TimesTaggedSize(intptr): intptr;
38extern macro Allocate(intptr): HeapObject;
39}
40
41namespace wasm {
42const kFuncTableType: constexpr int31 generates 'wasm::HeapType::kFunc';
43
44extern macro WasmBuiltinsAssembler::LoadInstanceFromFrame(): WasmInstanceObject;
45
46// WasmInstanceObject has a field layout that Torque can't handle yet.
47// TODO(bbudge) Eliminate these functions when Torque is ready.
48extern macro WasmBuiltinsAssembler::LoadContextFromInstance(WasmInstanceObject):
49    NativeContext;
50extern macro WasmBuiltinsAssembler::LoadTablesFromInstance(WasmInstanceObject):
51    FixedArray;
52extern macro WasmBuiltinsAssembler::LoadExternalFunctionsFromInstance(
53    WasmInstanceObject): FixedArray;
54extern macro WasmBuiltinsAssembler::LoadManagedObjectMapsFromInstance(
55    WasmInstanceObject): FixedArray;
56
57macro LoadContextFromFrame(): NativeContext {
58  return LoadContextFromInstance(LoadInstanceFromFrame());
59}
60
61builtin WasmInt32ToHeapNumber(val: int32): HeapNumber {
62  return AllocateHeapNumberWithValue(Convert<float64>(val));
63}
64
65builtin WasmTaggedNonSmiToInt32(implicit context: Context)(val: JSAnyNotSmi):
66    int32 {
67  return ChangeTaggedNonSmiToInt32(val);
68}
69
70builtin WasmTaggedToFloat64(implicit context: Context)(val: JSAny): float64 {
71  return ChangeTaggedToFloat64(val);
72}
73
74builtin WasmMemoryGrow(numPages: int32): int32 {
75  if (!IsValidPositiveSmi(ChangeInt32ToIntPtr(numPages)))
76    return Int32Constant(-1);
77  const instance: WasmInstanceObject = LoadInstanceFromFrame();
78  const context: NativeContext = LoadContextFromInstance(instance);
79  const result: Smi =
80      runtime::WasmMemoryGrow(context, instance, SmiFromInt32(numPages));
81  return SmiToInt32(result);
82}
83
84builtin WasmTableInit(
85    dstRaw: uint32, srcRaw: uint32, sizeRaw: uint32, tableIndex: Smi,
86    segmentIndex: Smi): JSAny {
87  try {
88    const instance: WasmInstanceObject = LoadInstanceFromFrame();
89    const dst: Smi = Convert<PositiveSmi>(dstRaw) otherwise TableOutOfBounds;
90    const src: Smi = Convert<PositiveSmi>(srcRaw) otherwise TableOutOfBounds;
91    const size: Smi = Convert<PositiveSmi>(sizeRaw) otherwise TableOutOfBounds;
92    tail runtime::WasmTableInit(
93        LoadContextFromInstance(instance), instance, tableIndex, segmentIndex,
94        dst, src, size);
95  } label TableOutOfBounds deferred {
96    tail ThrowWasmTrapTableOutOfBounds();
97  }
98}
99
100builtin WasmTableCopy(
101    dstRaw: uint32, srcRaw: uint32, sizeRaw: uint32, dstTable: Smi,
102    srcTable: Smi): JSAny {
103  try {
104    const instance: WasmInstanceObject = LoadInstanceFromFrame();
105    const dst: Smi = Convert<PositiveSmi>(dstRaw) otherwise TableOutOfBounds;
106    const src: Smi = Convert<PositiveSmi>(srcRaw) otherwise TableOutOfBounds;
107    const size: Smi = Convert<PositiveSmi>(sizeRaw) otherwise TableOutOfBounds;
108    tail runtime::WasmTableCopy(
109        LoadContextFromInstance(instance), instance, dstTable, srcTable, dst,
110        src, size);
111  } label TableOutOfBounds deferred {
112    tail ThrowWasmTrapTableOutOfBounds();
113  }
114}
115
116builtin WasmTableGet(tableIndex: intptr, index: int32): Object {
117  const instance: WasmInstanceObject = LoadInstanceFromFrame();
118  const entryIndex: intptr = ChangeInt32ToIntPtr(index);
119  try {
120    assert(IsValidPositiveSmi(tableIndex));
121    if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange;
122
123    const tables: FixedArray = LoadTablesFromInstance(instance);
124    const table: WasmTableObject = %RawDownCast<WasmTableObject>(
125        LoadFixedArrayElement(tables, tableIndex));
126    const entriesCount: intptr = Convert<intptr, Smi>(table.current_length);
127    if (entryIndex >= entriesCount) goto IndexOutOfRange;
128
129    const entries: FixedArray = table.entries;
130    const entry: Object = LoadFixedArrayElement(entries, entryIndex);
131
132    try {
133      const entryObject: HeapObject =
134          TaggedToHeapObject<HeapObject>(entry) otherwise ReturnEntry;
135      if (IsTuple2Map(entryObject.map)) goto CallRuntime;
136      goto ReturnEntry;
137    } label ReturnEntry {
138      return entry;
139    }
140  } label CallRuntime deferred {
141    tail runtime::WasmFunctionTableGet(
142        LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex),
143        SmiFromIntPtr(entryIndex));
144  } label IndexOutOfRange deferred {
145    tail ThrowWasmTrapTableOutOfBounds();
146  }
147}
148
149builtin WasmTableSet(tableIndex: intptr, index: int32, value: Object): Object {
150  const instance: WasmInstanceObject = LoadInstanceFromFrame();
151  const entryIndex: intptr = ChangeInt32ToIntPtr(index);
152  try {
153    assert(IsValidPositiveSmi(tableIndex));
154    if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange;
155
156    const tables: FixedArray = LoadTablesFromInstance(instance);
157    const table: WasmTableObject = %RawDownCast<WasmTableObject>(
158        LoadFixedArrayElement(tables, tableIndex));
159
160    // Fall back to the runtime to set funcrefs, since we have to update
161    // function dispatch tables.
162    const tableType: Smi = table.raw_type;
163    if (tableType == SmiConstant(kFuncTableType)) goto CallRuntime;
164
165    const entriesCount: intptr = Convert<intptr, Smi>(table.current_length);
166    if (entryIndex >= entriesCount) goto IndexOutOfRange;
167
168    const entries: FixedArray = table.entries;
169    StoreFixedArrayElement(entries, entryIndex, value);
170    return Undefined;
171  } label CallRuntime deferred {
172    tail runtime::WasmFunctionTableSet(
173        LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex),
174        SmiFromIntPtr(entryIndex), value);
175  } label IndexOutOfRange deferred {
176    tail ThrowWasmTrapTableOutOfBounds();
177  }
178}
179
180builtin WasmRefFunc(index: uint32): Object {
181  const instance: WasmInstanceObject = LoadInstanceFromFrame();
182  try {
183    const table: FixedArray = LoadExternalFunctionsFromInstance(instance);
184    if (table == Undefined) goto CallRuntime;
185    const functionIndex: intptr = Signed(ChangeUint32ToWord(index));
186    const result: Object = LoadFixedArrayElement(table, functionIndex);
187    if (result == Undefined) goto CallRuntime;
188    return result;
189  } label CallRuntime deferred {
190    tail runtime::WasmRefFunc(
191        LoadContextFromInstance(instance), instance, SmiFromUint32(index));
192  }
193}
194
195builtin WasmThrow(exception: Object): JSAny {
196  tail runtime::Throw(LoadContextFromFrame(), exception);
197}
198
199builtin WasmRethrow(exception: Object): JSAny {
200  if (exception == Null) tail ThrowWasmTrapRethrowNull();
201  tail runtime::ReThrow(LoadContextFromFrame(), exception);
202}
203
204builtin WasmTriggerTierUp(): JSAny {
205  const instance: WasmInstanceObject = LoadInstanceFromFrame();
206  tail runtime::WasmTriggerTierUp(LoadContextFromFrame(), instance);
207}
208
209builtin WasmStackGuard(): JSAny {
210  tail runtime::WasmStackGuard(LoadContextFromFrame());
211}
212
213builtin WasmStackOverflow(): JSAny {
214  tail runtime::ThrowWasmStackOverflow(LoadContextFromFrame());
215}
216
217builtin WasmTraceMemory(info: Smi): JSAny {
218  tail runtime::WasmTraceMemory(LoadContextFromFrame(), info);
219}
220
221builtin WasmTraceEnter(): JSAny {
222  tail runtime::WasmTraceEnter(LoadContextFromFrame());
223}
224
225builtin WasmTraceExit(info: Smi): JSAny {
226  tail runtime::WasmTraceExit(LoadContextFromFrame(), info);
227}
228
229builtin WasmAllocateJSArray(implicit context: Context)(size: Smi): JSArray {
230  const map: Map = GetFastPackedElementsJSArrayMap();
231  return AllocateJSArray(ElementsKind::PACKED_ELEMENTS, map, size, size);
232}
233
234builtin WasmAllocateRtt(implicit context: Context)(
235    typeIndex: Smi, parent: Map): Map {
236  tail runtime::WasmAllocateRtt(context, typeIndex, parent);
237}
238
239builtin WasmAllocateStructWithRtt(implicit context: Context)(rtt: Map):
240    HeapObject {
241  const instanceSize: intptr =
242      unsafe::TimesTaggedSize(Convert<intptr>(rtt.instance_size_in_words));
243  const result: HeapObject = unsafe::Allocate(instanceSize);
244  *UnsafeConstCast(&result.map) = rtt;
245  return result;
246}
247
248builtin WasmIsRttSubtype(implicit context: Context)(sub: Map, super: Map): Smi {
249  let map = sub;
250  while (true) {
251    if (map == super) return SmiConstant(1);  // "true"
252    // This code relies on the fact that we use a non-WasmObject map as the
253    // end of the chain, e.g. for "rtt any", which then doesn't have a
254    // WasmTypeInfo.
255    // TODO(7748): Use a more explicit sentinel mechanism?
256    const maybeTypeInfo = map.constructor_or_back_pointer_or_native_context;
257    if (!Is<WasmTypeInfo>(maybeTypeInfo)) return SmiConstant(0);  // "false"
258    const typeInfo = %RawDownCast<WasmTypeInfo>(maybeTypeInfo);
259    map = typeInfo.parent;
260  }
261  unreachable;
262}
263
264// Redeclaration with different typing (value is an Object, not JSAny).
265extern transitioning runtime
266CreateDataProperty(implicit context: Context)(JSReceiver, JSAny, Object);
267
268transitioning builtin WasmAllocateObjectWrapper(implicit context: Context)(
269    obj: Object): JSObject {
270  // Note: {obj} can be null, or i31ref. The code below is agnostic to that.
271  const wrapper = NewJSObject();
272  const symbol = WasmWrappedObjectSymbolConstant();
273  CreateDataProperty(wrapper, symbol, obj);
274  return wrapper;
275}
276
277builtin WasmInt32ToNumber(value: int32): Number {
278  return ChangeInt32ToTagged(value);
279}
280
281builtin WasmUint32ToNumber(value: uint32): Number {
282  return ChangeUint32ToTagged(value);
283}
284
285builtin UintPtr53ToNumber(value: uintptr): Number {
286  if (value <= kSmiMaxValue) return Convert<Smi>(Convert<intptr>(value));
287  const valueFloat = ChangeUintPtrToFloat64(value);
288  // Values need to be within [0..2^53], such that they can be represented as
289  // float64.
290  assert(ChangeFloat64ToUintPtr(valueFloat) == value);
291  return AllocateHeapNumberWithValue(valueFloat);
292}
293
294extern builtin I64ToBigInt(intptr): BigInt;
295
296builtin WasmAtomicNotify(offset: uintptr, count: uint32): uint32 {
297  const instance: WasmInstanceObject = LoadInstanceFromFrame();
298  const result: Smi = runtime::WasmAtomicNotify(
299      LoadContextFromInstance(instance), instance, UintPtr53ToNumber(offset),
300      WasmUint32ToNumber(count));
301  return Unsigned(SmiToInt32(result));
302}
303
304builtin WasmI32AtomicWait64(
305    offset: uintptr, expectedValue: int32, timeout: intptr): uint32 {
306  if constexpr (Is64()) {
307    const instance: WasmInstanceObject = LoadInstanceFromFrame();
308    const result: Smi = runtime::WasmI32AtomicWait(
309        LoadContextFromInstance(instance), instance, UintPtr53ToNumber(offset),
310        WasmInt32ToNumber(expectedValue), I64ToBigInt(timeout));
311    return Unsigned(SmiToInt32(result));
312  } else {
313    unreachable;
314  }
315}
316
317builtin WasmI64AtomicWait64(
318    offset: uintptr, expectedValue: intptr, timeout: intptr): uint32 {
319  if constexpr (Is64()) {
320    const instance: WasmInstanceObject = LoadInstanceFromFrame();
321    const result: Smi = runtime::WasmI64AtomicWait(
322        LoadContextFromInstance(instance), instance, UintPtr53ToNumber(offset),
323        I64ToBigInt(expectedValue), I64ToBigInt(timeout));
324    return Unsigned(SmiToInt32(result));
325  } else {
326    unreachable;
327  }
328}
329
330extern macro TryHasOwnProperty(HeapObject, Map, InstanceType, Name): never
331    labels Found, NotFound, Bailout;
332type OnNonExistent constexpr 'OnNonExistent';
333const kReturnUndefined: constexpr OnNonExistent
334    generates 'OnNonExistent::kReturnUndefined';
335extern macro SmiConstant(constexpr OnNonExistent): Smi;
336extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)(
337    JSAny, Name, JSAny, Smi): JSAny;
338
339transitioning builtin WasmGetOwnProperty(implicit context: Context)(
340    object: Object, uniqueName: Name): JSAny {
341  try {
342    const heapObject: HeapObject =
343        TaggedToHeapObject(object) otherwise NotFound;
344    const receiver: JSReceiver =
345        Cast<JSReceiver>(heapObject) otherwise NotFound;
346    try {
347      TryHasOwnProperty(
348          receiver, receiver.map, receiver.instanceType, uniqueName)
349          otherwise Found, NotFound, Bailout;
350    } label Found {
351      tail GetPropertyWithReceiver(
352          receiver, uniqueName, receiver, SmiConstant(kReturnUndefined));
353    }
354  } label NotFound deferred {
355    return Undefined;
356  } label Bailout deferred {
357    unreachable;
358  }
359}
360
361// Trap builtins.
362
363builtin WasmTrap(error: Smi): JSAny {
364  tail runtime::ThrowWasmError(LoadContextFromFrame(), error);
365}
366
367builtin ThrowWasmTrapUnreachable(): JSAny {
368  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnreachable));
369}
370
371builtin ThrowWasmTrapMemOutOfBounds(): JSAny {
372  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapMemOutOfBounds));
373}
374
375builtin ThrowWasmTrapUnalignedAccess(): JSAny {
376  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnalignedAccess));
377}
378
379builtin ThrowWasmTrapDivByZero(): JSAny {
380  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivByZero));
381}
382
383builtin ThrowWasmTrapDivUnrepresentable(): JSAny {
384  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivUnrepresentable));
385}
386
387builtin ThrowWasmTrapRemByZero(): JSAny {
388  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRemByZero));
389}
390
391builtin ThrowWasmTrapFloatUnrepresentable(): JSAny {
392  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFloatUnrepresentable));
393}
394
395builtin ThrowWasmTrapFuncSigMismatch(): JSAny {
396  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncSigMismatch));
397}
398
399builtin ThrowWasmTrapDataSegmentDropped(): JSAny {
400  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDataSegmentDropped));
401}
402
403builtin ThrowWasmTrapElemSegmentDropped(): JSAny {
404  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapElemSegmentDropped));
405}
406
407builtin ThrowWasmTrapTableOutOfBounds(): JSAny {
408  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapTableOutOfBounds));
409}
410
411builtin ThrowWasmTrapBrOnExnNull(): JSAny {
412  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapBrOnExnNull));
413}
414
415builtin ThrowWasmTrapRethrowNull(): JSAny {
416  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRethrowNull));
417}
418
419builtin ThrowWasmTrapNullDereference(): JSAny {
420  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapNullDereference));
421}
422
423builtin ThrowWasmTrapIllegalCast(): JSAny {
424  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapIllegalCast));
425}
426
427builtin ThrowWasmTrapArrayOutOfBounds(): JSAny {
428  tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapArrayOutOfBounds));
429}
430}
431