// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include 'src/builtins/builtins-wasm-gen.h' namespace runtime { extern runtime WasmMemoryGrow(Context, WasmInstanceObject, Smi): Smi; extern runtime WasmRefFunc(Context, WasmInstanceObject, Smi): JSAny; extern runtime WasmTableInit( Context, WasmInstanceObject, Object, Object, Smi, Smi, Smi): JSAny; extern runtime WasmTableCopy( Context, WasmInstanceObject, Object, Object, Smi, Smi, Smi): JSAny; extern runtime WasmFunctionTableGet( Context, WasmInstanceObject, Smi, Smi): JSAny; extern runtime WasmFunctionTableSet( Context, WasmInstanceObject, Smi, Smi, Object): JSAny; extern runtime ThrowWasmError(Context, Smi): JSAny; extern runtime Throw(Context, Object): JSAny; extern runtime ReThrow(Context, Object): JSAny; extern runtime WasmStackGuard(Context): JSAny; extern runtime ThrowWasmStackOverflow(Context): JSAny; extern runtime WasmTraceMemory(Context, Smi): JSAny; extern runtime WasmTraceEnter(Context): JSAny; extern runtime WasmAtomicNotify( Context, WasmInstanceObject, Number, Number): Smi; extern runtime WasmI32AtomicWait( Context, WasmInstanceObject, Number, Number, BigInt): Smi; extern runtime WasmI64AtomicWait( Context, WasmInstanceObject, Number, BigInt, BigInt): Smi; } namespace unsafe { extern macro TimesTaggedSize(intptr): intptr; extern macro Allocate(intptr): HeapObject; } namespace wasm { const kFuncTableType: constexpr int31 generates 'wasm::ValueType::Kind::kFuncRef'; extern macro WasmBuiltinsAssembler::LoadInstanceFromFrame(): WasmInstanceObject; // WasmInstanceObject has a field layout that Torque can't handle yet. // TODO(bbudge) Eliminate these functions when Torque is ready. extern macro WasmBuiltinsAssembler::LoadContextFromInstance(WasmInstanceObject): NativeContext; extern macro WasmBuiltinsAssembler::LoadTablesFromInstance(WasmInstanceObject): FixedArray; extern macro WasmBuiltinsAssembler::LoadExternalFunctionsFromInstance( WasmInstanceObject): FixedArray; extern macro WasmBuiltinsAssembler::LoadManagedObjectMapsFromInstance( WasmInstanceObject): FixedArray; macro LoadContextFromFrame(): NativeContext { return LoadContextFromInstance(LoadInstanceFromFrame()); } builtin WasmInt32ToHeapNumber(val: int32): HeapNumber { return AllocateHeapNumberWithValue(Convert<float64>(val)); } builtin WasmTaggedNonSmiToInt32(implicit context: Context)(val: JSAnyNotSmi): int32 { return ChangeTaggedNonSmiToInt32(val); } builtin WasmTaggedToFloat64(implicit context: Context)(val: JSAny): float64 { return ChangeTaggedToFloat64(val); } builtin WasmMemoryGrow(numPages: int32): int32 { if (!IsValidPositiveSmi(ChangeInt32ToIntPtr(numPages))) return Int32Constant(-1); const instance: WasmInstanceObject = LoadInstanceFromFrame(); const context: NativeContext = LoadContextFromInstance(instance); const result: Smi = runtime::WasmMemoryGrow(context, instance, SmiFromInt32(numPages)); return SmiToInt32(result); } builtin WasmTableInit( dstRaw: uint32, srcRaw: uint32, sizeRaw: uint32, tableIndex: Smi, segmentIndex: Smi): JSAny { try { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const dst: Smi = Convert<PositiveSmi>(dstRaw) otherwise TableOutOfBounds; const src: Smi = Convert<PositiveSmi>(srcRaw) otherwise TableOutOfBounds; const size: Smi = Convert<PositiveSmi>(sizeRaw) otherwise TableOutOfBounds; tail runtime::WasmTableInit( LoadContextFromInstance(instance), instance, tableIndex, segmentIndex, dst, src, size); } label TableOutOfBounds deferred { tail ThrowWasmTrapTableOutOfBounds(); } } builtin WasmTableCopy( dstRaw: uint32, srcRaw: uint32, sizeRaw: uint32, dstTable: Smi, srcTable: Smi): JSAny { try { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const dst: Smi = Convert<PositiveSmi>(dstRaw) otherwise TableOutOfBounds; const src: Smi = Convert<PositiveSmi>(srcRaw) otherwise TableOutOfBounds; const size: Smi = Convert<PositiveSmi>(sizeRaw) otherwise TableOutOfBounds; tail runtime::WasmTableCopy( LoadContextFromInstance(instance), instance, dstTable, srcTable, dst, src, size); } label TableOutOfBounds deferred { tail ThrowWasmTrapTableOutOfBounds(); } } builtin WasmTableGet(tableIndex: intptr, index: int32): Object { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const entryIndex: intptr = ChangeInt32ToIntPtr(index); try { assert(IsValidPositiveSmi(tableIndex)); if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange; const tables: FixedArray = LoadTablesFromInstance(instance); const table: WasmTableObject = %RawDownCast<WasmTableObject>( LoadFixedArrayElement(tables, tableIndex)); const entriesCount: intptr = Convert<intptr, Smi>(table.current_length); if (entryIndex >= entriesCount) goto IndexOutOfRange; const entries: FixedArray = table.entries; const entry: Object = LoadFixedArrayElement(entries, entryIndex); try { const entryObject: HeapObject = TaggedToHeapObject<HeapObject>(entry) otherwise ReturnEntry; if (IsTuple2Map(entryObject.map)) goto CallRuntime; goto ReturnEntry; } label ReturnEntry { return entry; } } label CallRuntime deferred { tail runtime::WasmFunctionTableGet( LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex), SmiFromIntPtr(entryIndex)); } label IndexOutOfRange deferred { tail ThrowWasmTrapTableOutOfBounds(); } } builtin WasmTableSet(tableIndex: intptr, index: int32, value: Object): Object { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const entryIndex: intptr = ChangeInt32ToIntPtr(index); try { assert(IsValidPositiveSmi(tableIndex)); if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange; const tables: FixedArray = LoadTablesFromInstance(instance); const table: WasmTableObject = %RawDownCast<WasmTableObject>( LoadFixedArrayElement(tables, tableIndex)); // Fall back to the runtime to set funcrefs, since we have to update // function dispatch tables. const tableType: Smi = table.raw_type; if (tableType == SmiConstant(kFuncTableType)) goto CallRuntime; const entriesCount: intptr = Convert<intptr, Smi>(table.current_length); if (entryIndex >= entriesCount) goto IndexOutOfRange; const entries: FixedArray = table.entries; StoreFixedArrayElement(entries, entryIndex, value); return Undefined; } label CallRuntime deferred { tail runtime::WasmFunctionTableSet( LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex), SmiFromIntPtr(entryIndex), value); } label IndexOutOfRange deferred { tail ThrowWasmTrapTableOutOfBounds(); } } builtin WasmRefFunc(index: uint32): Object { const instance: WasmInstanceObject = LoadInstanceFromFrame(); try { const table: FixedArray = LoadExternalFunctionsFromInstance(instance); if (table == Undefined) goto CallRuntime; const functionIndex: intptr = Signed(ChangeUint32ToWord(index)); const result: Object = LoadFixedArrayElement(table, functionIndex); if (result == Undefined) goto CallRuntime; return result; } label CallRuntime deferred { tail runtime::WasmRefFunc( LoadContextFromInstance(instance), instance, SmiFromUint32(index)); } } builtin WasmThrow(exception: Object): JSAny { tail runtime::Throw(LoadContextFromFrame(), exception); } builtin WasmRethrow(exception: Object): JSAny { if (exception == Null) tail ThrowWasmTrapRethrowNullRef(); tail runtime::ReThrow(LoadContextFromFrame(), exception); } builtin WasmStackGuard(): JSAny { tail runtime::WasmStackGuard(LoadContextFromFrame()); } builtin WasmStackOverflow(): JSAny { tail runtime::ThrowWasmStackOverflow(LoadContextFromFrame()); } builtin WasmTraceMemory(info: Smi): JSAny { tail runtime::WasmTraceMemory(LoadContextFromFrame(), info); } builtin WasmTraceEnter(): JSAny { tail runtime::WasmTraceEnter(LoadContextFromFrame()); } builtin WasmAllocateJSArray(implicit context: Context)(size: Smi): JSArray { const map: Map = GetFastPackedElementsJSArrayMap(); return AllocateJSArray(ElementsKind::PACKED_ELEMENTS, map, size, size); } builtin WasmAllocateStruct(implicit context: Context)(mapIndex: Smi): HeapObject { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const maps: FixedArray = LoadManagedObjectMapsFromInstance(instance); const map: Map = %RawDownCast<Map>(LoadFixedArrayElement(maps, mapIndex)); const instanceSize: intptr = unsafe::TimesTaggedSize(Convert<intptr>(map.instance_size_in_words)); const result: HeapObject = unsafe::Allocate(instanceSize); * UnsafeConstCast(& result.map) = map; return result; } builtin WasmInt32ToNumber(value: int32): Number { return ChangeInt32ToTagged(value); } builtin WasmUint32ToNumber(value: uint32): Number { return ChangeUint32ToTagged(value); } extern builtin I64ToBigInt(intptr): BigInt; builtin WasmAtomicNotify(address: uint32, count: uint32): uint32 { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const result: Smi = runtime::WasmAtomicNotify( LoadContextFromInstance(instance), instance, WasmUint32ToNumber(address), WasmUint32ToNumber(count)); return Unsigned(SmiToInt32(result)); } builtin WasmI32AtomicWait64( address: uint32, expectedValue: int32, timeout: intptr): uint32 { if constexpr (Is64()) { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const result: Smi = runtime::WasmI32AtomicWait( LoadContextFromInstance(instance), instance, WasmUint32ToNumber(address), WasmInt32ToNumber(expectedValue), I64ToBigInt(timeout)); return Unsigned(SmiToInt32(result)); } else { unreachable; } } builtin WasmI64AtomicWait64( address: uint32, expectedValue: intptr, timeout: intptr): uint32 { if constexpr (Is64()) { const instance: WasmInstanceObject = LoadInstanceFromFrame(); const result: Smi = runtime::WasmI64AtomicWait( LoadContextFromInstance(instance), instance, WasmUint32ToNumber(address), I64ToBigInt(expectedValue), I64ToBigInt(timeout)); return Unsigned(SmiToInt32(result)); } else { unreachable; } } extern macro TryHasOwnProperty(HeapObject, Map, InstanceType, Name): never labels Found, NotFound, Bailout; type OnNonExistent constexpr 'OnNonExistent'; const kReturnUndefined: constexpr OnNonExistent generates 'OnNonExistent::kReturnUndefined'; extern macro SmiConstant(constexpr OnNonExistent): Smi; extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)( JSAny, Name, JSAny, Smi): JSAny; transitioning builtin WasmGetOwnProperty(implicit context: Context)( object: Object, uniqueName: Name): JSAny { try { const heapObject: HeapObject = TaggedToHeapObject(object) otherwise NotFound; const receiver: JSReceiver = Cast<JSReceiver>(heapObject) otherwise NotFound; try { TryHasOwnProperty( receiver, receiver.map, receiver.instanceType, uniqueName) otherwise Found, NotFound, Bailout; } label Found { tail GetPropertyWithReceiver( receiver, uniqueName, receiver, SmiConstant(kReturnUndefined)); } } label NotFound deferred { return Undefined; } label Bailout deferred { unreachable; } } // Trap builtins. builtin WasmTrap(error: Smi): JSAny { tail runtime::ThrowWasmError(LoadContextFromFrame(), error); } builtin ThrowWasmTrapUnreachable(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnreachable)); } builtin ThrowWasmTrapMemOutOfBounds(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapMemOutOfBounds)); } builtin ThrowWasmTrapUnalignedAccess(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnalignedAccess)); } builtin ThrowWasmTrapDivByZero(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivByZero)); } builtin ThrowWasmTrapDivUnrepresentable(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivUnrepresentable)); } builtin ThrowWasmTrapRemByZero(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRemByZero)); } builtin ThrowWasmTrapFloatUnrepresentable(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFloatUnrepresentable)); } builtin ThrowWasmTrapFuncInvalid(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncInvalid)); } builtin ThrowWasmTrapFuncSigMismatch(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncSigMismatch)); } builtin ThrowWasmTrapDataSegmentDropped(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDataSegmentDropped)); } builtin ThrowWasmTrapElemSegmentDropped(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapElemSegmentDropped)); } builtin ThrowWasmTrapTableOutOfBounds(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapTableOutOfBounds)); } builtin ThrowWasmTrapBrOnExnNullRef(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapBrOnExnNullRef)); } builtin ThrowWasmTrapRethrowNullRef(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRethrowNullRef)); } builtin ThrowWasmTrapNullDereference(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapNullDereference)); } builtin ThrowWasmTrapIllegalCast(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapIllegalCast)); } builtin ThrowWasmTrapArrayOutOfBounds(): JSAny { tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapArrayOutOfBounds)); } }