diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 8a7bd2ea6b6f2..9f698c0b33f8d 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -47,6 +47,14 @@ BUILTIN_CAST_OR_BITCAST_OPERATION(TruncOrBitCast, "truncOrBitCast", "n") BUILTIN_CAST_OR_BITCAST_OPERATION(ZExtOrBitCast, "zextOrBitCast", "n") BUILTIN_CAST_OR_BITCAST_OPERATION(SExtOrBitCast, "sextOrBitCast", "n") +BUILTIN(WasmRefNullExtern, "wasmRefNullExtern", "n") +BUILTIN(WasmTableSizeExternRef, "wasmTableSizeExternRef", "") +BUILTIN(WasmTableGetExternRef, "wasmTableGetExternRef", "") +BUILTIN(WasmTableSetExternRef, "wasmTableSetExternRef", "") +BUILTIN(WasmTableGrowExternRef, "wasmTableGrowExternRef", "") +BUILTIN(WasmTableFillExternRef, "wasmTableFillExternRef", "") +BUILTIN(WasmTableCopyExternRef, "wasmTableCopyExternRef", "") + /// Binary operations have type (T,T) -> T. /// /// We define two different sorts of operations varying when T is static, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1d22df7383a27..2aaa30f4a557d 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -660,6 +660,12 @@ ERROR(expr_keypath_noncopyable_type,none, "key path cannot refer to noncopyable type %0", (Type)) ERROR(expr_keypath_nonescapable_type,none, "key path cannot refer to nonescapable type %0", (Type)) +ERROR(expr_inout_wasm_externref,none, + "cannot pass value of type %0 as 'inout'; WebAssembly references are not addressable", + (Type)) +ERROR(expr_inout_wasm_table,none, + "cannot pass value of type %0 as 'inout'; WebAssembly tables are not addressable", + (Type)) ERROR(expr_keypath_not_property,none, "%select{key path|dynamic key path member lookup}1 cannot refer to %kind0", (const ValueDecl *, bool)) @@ -5205,6 +5211,12 @@ ERROR(capture_across_type_decl,none, "%kindonly0 declaration cannot close over value %base1 defined in outer " "scope", (const NominalTypeDecl *, const ValueDecl *)) +ERROR(capture_wasm_externref,none, + "cannot capture value of type %0; WebAssembly references cannot be stored in closure contexts", + (Type)) +ERROR(capture_wasm_table,none, + "cannot capture value of type %0; WebAssembly tables cannot be stored in closure contexts", + (Type)) ERROR(isolated_any_conversion_to_synchronous_func,none, "converting @isolated(any) function of type %0 to synchronous function type %1 is not allowed", @@ -6385,6 +6397,30 @@ ERROR(recursive_enum_not_indirect,none, "recursive enum %0 is not marked 'indirect'", (Type)) ERROR(unsupported_infinitely_sized_type,none, "value type %0 has infinite size", (Type)) +ERROR(wasm_externref_pointer_type,none, + "type %0 cannot contain a pointer to a WebAssembly reference", + (Type)) +ERROR(wasm_table_pointer_type,none, + "type %0 cannot contain a pointer to a WebAssembly table", + (Type)) +ERROR(wasm_externref_storage,none, + "%select{stored property|static property|global variable}0 cannot have type %1; WebAssembly references cannot be stored in Swift memory", + (unsigned, Type)) +ERROR(wasm_table_storage,none, + "%select{local variable|stored property|static property|global variable}0 cannot have type %1; WebAssembly tables are not Swift values", + (unsigned, Type)) +ERROR(wasm_externref_variadic,none, + "parameter %0 cannot be variadic because type %1 contains a WebAssembly reference", + (Identifier, Type)) +ERROR(wasm_externref_inout_parameter,none, + "'inout' parameter %0 cannot have type %1; WebAssembly references are not addressable", + (Identifier, Type)) +ERROR(wasm_table_parameter,none, + "parameter %0 cannot have type %1; WebAssembly tables cannot be passed as Swift values", + (Identifier, Type)) +ERROR(wasm_table_result_type,none, + "result type %0 cannot contain a WebAssembly table", + (Type)) NOTE(note_type_cycle_starts_here,none, "cycle beginning here: %0", (StringRef)) NOTE(note_recursive_enum_case_here,none, diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index 116037dc168e2..7c80ef0866933 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -79,6 +79,8 @@ KNOWN_STDLIB_TYPE_DECL(UnsafeRawPointer, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(UnsafeMutablePointer, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(UnsafePointer, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(OpaquePointer, NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL(WasmExternref, NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL(WasmExternrefTable, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(CVaListPointer, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(AutoreleasingUnsafeMutablePointer, NominalTypeDecl, 1) diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index c6c04366b2942..c42538d265ee7 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -134,6 +134,7 @@ ABSTRACT_TYPE(Builtin, Type) BUILTIN_CONCRETE_TYPE(BuiltinJob, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinPackIndex, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinRawPointer, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinWasmExternRef, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinRawUnsafeContinuation, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinNativeObject, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinBridgeObject, BuiltinType) @@ -147,7 +148,8 @@ ABSTRACT_TYPE(Builtin, Type) TYPE_RANGE(BuiltinGeneric, BuiltinFixedArray, BuiltinBorrow) BUILTIN_CONCRETE_TYPE(BuiltinUnboundGeneric, BuiltinType) BUILTIN_CONCRETE_TYPE(BuiltinImplicitActor, BuiltinType) - TYPE_RANGE(Builtin, BuiltinInteger, BuiltinImplicitActor) + BUILTIN_CONCRETE_TYPE(BuiltinWasmExternRefTable, BuiltinType) + TYPE_RANGE(Builtin, BuiltinInteger, BuiltinWasmExternRefTable) TYPE(Tuple, Type) ABSTRACT_TYPE(ReferenceStorage, Type) #define REF_STORAGE(Name, ...) \ @@ -236,6 +238,8 @@ SINGLETON_TYPE(Job, BuiltinJob) SINGLETON_TYPE(Executor, BuiltinExecutor) SINGLETON_TYPE(PackIndex, BuiltinPackIndex) SINGLETON_TYPE(RawPointer, BuiltinRawPointer) +SINGLETON_TYPE(WasmExternRef, BuiltinWasmExternRef) +SINGLETON_TYPE(WasmExternRefTable, BuiltinWasmExternRefTable) SINGLETON_TYPE(RawUnsafeContinuation, BuiltinRawUnsafeContinuation) SINGLETON_TYPE(NativeObject, BuiltinNativeObject) SINGLETON_TYPE(BridgeObject, BuiltinBridgeObject) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 98cb84644f248..e2a08e30a6ea3 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -941,6 +941,22 @@ class alignas(1 << TypeAlignInBits) TypeBase return getRecursiveProperties().hasUnboundGeneric(); } + /// Determine whether this type contains a WebAssembly externref, either + /// directly as a builtin or via the standard library wrapper. + bool containsWasmExternref() const; + + /// Determine whether this type contains an unsafe pointer whose pointee + /// contains a WebAssembly externref. + bool containsWasmExternrefPointer() const; + + /// Determine whether this type contains a WebAssembly externref table, + /// either directly as a builtin or via the standard library wrapper. + bool containsWasmExternrefTable() const; + + /// Determine whether this type contains an unsafe pointer whose pointee + /// contains a WebAssembly externref table. + bool containsWasmExternrefTablePointer() const; + /// Determine whether this type contains an error type. bool hasError() const { return getRecursiveProperties().hasError(); @@ -1944,6 +1960,34 @@ class BuiltinRawPointerType : public BuiltinType { }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinRawPointerType, BuiltinType) +/// BuiltinWasmExternRefType - The builtin WebAssembly externref type. +/// In LLVM IR, this is represented as a pointer in the reserved externref +/// address space. +class BuiltinWasmExternRefType : public BuiltinType { + friend class ASTContext; + BuiltinWasmExternRefType(const ASTContext &C) + : BuiltinType(TypeKind::BuiltinWasmExternRef, C) {} +public: + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::BuiltinWasmExternRef; + } +}; +DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinWasmExternRefType, BuiltinType) + +/// BuiltinWasmExternRefTableType - The builtin WebAssembly externref table +/// type. In LLVM IR, this is represented as a pointer in the reserved table +/// address space. +class BuiltinWasmExternRefTableType : public BuiltinType { + friend class ASTContext; + BuiltinWasmExternRefTableType(const ASTContext &C) + : BuiltinType(TypeKind::BuiltinWasmExternRefTable, C) {} +public: + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::BuiltinWasmExternRefTable; + } +}; +DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinWasmExternRefTableType, BuiltinType) + /// BuiltinRawContinuationType - The builtin raw unsafe continuation type. /// In C, this is a non-null AsyncTask*. This pointer is completely /// unmanaged (the unresumed task is self-owning), but has more spare bits diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index c648abac98ece..130db55c2a50b 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -461,6 +461,9 @@ EXPERIMENTAL_FEATURE(Embedded, true) /// Enables importing the Volatile module EXPERIMENTAL_FEATURE(Volatile, true) +/// Enables WebAssembly externref interop support. +EXPERIMENTAL_FEATURE(WasmExternref, false) + // LEGACY: Enables ~Copyable and ~Escapable annotations on associatedtype declarations. EXPERIMENTAL_FEATURE(SuppressedAssociatedTypes, true) diff --git a/include/swift/ClangImporter/BuiltinMappedTypes.def b/include/swift/ClangImporter/BuiltinMappedTypes.def index 4f9fd725263a1..a5bc1e75bb83a 100644 --- a/include/swift/ClangImporter/BuiltinMappedTypes.def +++ b/include/swift/ClangImporter/BuiltinMappedTypes.def @@ -61,6 +61,7 @@ MAP_BUILTIN_TYPE(Half, CFloat16) MAP_BUILTIN_TYPE(Float, CFloat) MAP_BUILTIN_TYPE(Double, CDouble) MAP_BUILTIN_TYPE(LongDouble, CLongDouble) +MAP_BUILTIN_TYPE(WasmExternRef, WasmExternref) #undef MAP_BUILTIN_TYPE #undef MAP_BUILTIN_INTEGER_TYPE diff --git a/include/swift/Strings.h b/include/swift/Strings.h index db239e2f8e164..e10d777b31de6 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -151,6 +151,12 @@ constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_IMPLICITACTOR = { /// The name of the Builtin type for RawPointer constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_RAWPOINTER = { "Builtin.RawPointer"}; +/// The name of the Builtin type for WasmExternRef +constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_WASMEXTERNREF = { + "Builtin.WasmExternRef"}; +/// The name of the Builtin type for WasmExternRefTable +constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_WASMEXTERNREFTABLE = { + "Builtin.WasmExternRefTable"}; /// The name of the Builtin type for RawUnsafeContinuation constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_RAWUNSAFECONTINUATION = { "Builtin.RawUnsafeContinuation"}; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 6f986dcfd90b2..08994e6c660be 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -6260,6 +6260,8 @@ namespace { TRIVIAL_TYPE_PRINTER(BuiltinNonDefaultDistributedActorStorage, builtin_non_default_distributed_actor_storage) TRIVIAL_TYPE_PRINTER(BuiltinPackIndex, builtin_pack_index) TRIVIAL_TYPE_PRINTER(BuiltinRawPointer, builtin_raw_pointer) + TRIVIAL_TYPE_PRINTER(BuiltinWasmExternRef, builtin_wasm_extern_ref) + TRIVIAL_TYPE_PRINTER(BuiltinWasmExternRefTable, builtin_wasm_extern_ref_table) TRIVIAL_TYPE_PRINTER(BuiltinRawUnsafeContinuation, builtin_raw_unsafe_continuation) TRIVIAL_TYPE_PRINTER(BuiltinNativeObject, builtin_native_object) TRIVIAL_TYPE_PRINTER(BuiltinBridgeObject, builtin_bridge_object) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index f3deef9f9f77b..6e44ac6608d93 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1452,6 +1452,10 @@ void ASTMangler::appendType(Type type, GenericSignature sig, return appendOperator("BP"); case TypeKind::BuiltinRawPointer: return appendOperator("Bp"); + case TypeKind::BuiltinWasmExternRef: + return appendOperator("Br"); + case TypeKind::BuiltinWasmExternRefTable: + return appendOperator("BQ"); case TypeKind::BuiltinRawUnsafeContinuation: return appendOperator("Bc"); case TypeKind::BuiltinNativeObject: diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index b2e6a76054226..51ef0da75e04a 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -6718,6 +6718,8 @@ class TypePrinter : public TypeVisitor &result, case BuiltinTypeKind::BuiltinRawPointer: printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_RAWPOINTER); break; + case BuiltinTypeKind::BuiltinWasmExternRef: + printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_WASMEXTERNREF); + break; + case BuiltinTypeKind::BuiltinWasmExternRefTable: + printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_WASMEXTERNREFTABLE); + break; case BuiltinTypeKind::BuiltinRawUnsafeContinuation: printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_RAWUNSAFECONTINUATION); break; diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 464a595942de1..34638f14173bb 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -789,6 +789,12 @@ ClangTypeConverter::visitBuiltinRawPointerType(BuiltinRawPointerType *type) { return ClangASTContext.VoidPtrTy; } +clang::QualType ClangTypeConverter::visitBuiltinWasmExternRefType( + BuiltinWasmExternRefType *) { + return getClangBuiltinTypeFromKind(ClangASTContext, + clang::BuiltinType::WasmExternRef); +} + clang::QualType ClangTypeConverter::visitBuiltinIntegerType(BuiltinIntegerType *type) { auto &clangCtx = ClangASTContext; @@ -963,6 +969,10 @@ clang::QualType ClangTypeConverter::convertTemplateArgument(Type type) { if (auto pointerType = type->getAs()) return withCache([&]() { return visitBuiltinRawPointerType(pointerType); }); + if (auto externRefType = type->getAs()) + return withCache( + [&]() { return visitBuiltinWasmExternRefType(externRefType); }); + if (auto integerType = type->getAs()) return withCache([&]() { return visitBuiltinIntegerType(integerType); }); diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index 1cea5071cbd6f..6844f42f7d934 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -155,6 +155,7 @@ class ClangTypeConverter : clang::QualType visitProtocolCompositionType(ProtocolCompositionType *type); clang::QualType visitExistentialType(ExistentialType *type); clang::QualType visitBuiltinRawPointerType(BuiltinRawPointerType *type); + clang::QualType visitBuiltinWasmExternRefType(BuiltinWasmExternRefType *type); clang::QualType visitBuiltinIntegerType(BuiltinIntegerType *type); clang::QualType visitBuiltinFloatType(BuiltinFloatType *type); clang::QualType visitBuiltinVectorType(BuiltinVectorType *type); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 3579cd54454df..773a6a7d5af65 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -242,6 +242,8 @@ bool CanType::isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig, case TypeKind::BuiltinIntegerLiteral: case TypeKind::BuiltinFloat: case TypeKind::BuiltinRawPointer: + case TypeKind::BuiltinWasmExternRef: + case TypeKind::BuiltinWasmExternRefTable: case TypeKind::BuiltinRawUnsafeContinuation: case TypeKind::BuiltinJob: case TypeKind::BuiltinExecutor: @@ -768,6 +770,36 @@ bool TypeBase::isLegalFormalType() { return ::isLegalFormalType(getCanonicalType()); } +bool TypeBase::containsWasmExternref() const { + return Type(const_cast(this)).findIf([](Type subTy) -> bool { + return subTy->is() || subTy->isWasmExternref(); + }); +} + +bool TypeBase::containsWasmExternrefPointer() const { + return Type(const_cast(this)).findIf([](Type subTy) -> bool { + PointerTypeKind pointerKind; + auto pointeeTy = subTy->getAnyPointerElementType(pointerKind); + return pointeeTy && pointeeTy->containsWasmExternref(); + }); +} + +bool TypeBase::containsWasmExternrefTable() const { + return Type(const_cast(this)).findIf([](Type subTy) -> bool { + return subTy->is() || + subTy->getAnyNominal() == + subTy->getASTContext().getWasmExternrefTableDecl(); + }); +} + +bool TypeBase::containsWasmExternrefTablePointer() const { + return Type(const_cast(this)).findIf([](Type subTy) -> bool { + PointerTypeKind pointerKind; + auto pointeeTy = subTy->getAnyPointerElementType(pointerKind); + return pointeeTy && pointeeTy->containsWasmExternrefTable(); + }); +} + bool TypeBase::hasTypeRepr() const { // A type has a source-printable representation if none of its sub-pieces do // /not/ have a source-printable representation. @@ -4717,6 +4749,8 @@ ReferenceCounting TypeBase::getReferenceCounting() { case TypeKind::BuiltinIntegerLiteral: case TypeKind::BuiltinFloat: case TypeKind::BuiltinRawPointer: + case TypeKind::BuiltinWasmExternRef: + case TypeKind::BuiltinWasmExternRefTable: case TypeKind::BuiltinRawUnsafeContinuation: case TypeKind::BuiltinJob: case TypeKind::BuiltinExecutor: diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index d96f897884fef..de066be469b81 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -441,10 +441,8 @@ OmissionTypeName importer::getClangTypeNameForOmission(clang::ASTContext &ctx, #include "clang/Basic/RISCVVTypes.def" return OmissionTypeName(); - // WASM builtin types that don't have Swift equivalents. -#define WASM_TYPE(Name, Id, ...) case clang::BuiltinType::Id: -#include "clang/Basic/WebAssemblyReferenceTypes.def" - return OmissionTypeName(); + case clang::BuiltinType::WasmExternRef: + return OmissionTypeName("WasmExternref"); // AMDGPU builtins that don't have Swift equivalents. #define AMDGPU_TYPE(Name, Id, ...) case clang::BuiltinType::Id: diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index dcf7990418f09..bfc0144d85e38 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -365,10 +365,6 @@ namespace { #include "clang/Basic/RISCVVTypes.def" return Type(); -#define WASM_TYPE(Name, Id, Size) case clang::BuiltinType::Id: -#include "clang/Basic/WebAssemblyReferenceTypes.def" - return Type(); - // AMDGPU builtin types that don't have Swift equivalents. #define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) \ case clang::BuiltinType::Id: @@ -664,10 +660,6 @@ namespace { } ImportResult VisitConstantArrayType(const clang::ConstantArrayType *type) { - // FIXME: Map to a real fixed-size Swift array type when we have those. - // Importing as a tuple at least fills the right amount of space, and - // we can cheese static-offset "indexing" using .$n operations. - Type elementType = Impl.importTypeIgnoreIUO( type->getElementType(), ImportTypeKind::Value, addImportDiagnostic, AllowNSUIntegerAsInt, Bridgeability::None, ImportTypeAttrs()); @@ -676,6 +668,15 @@ namespace { auto size = type->getSize().getZExtValue(); + if (size == 0 && elementType->isWasmExternref()) { + if (auto tableDecl = Impl.SwiftContext.getWasmExternrefTableDecl()) + return tableDecl->getDeclaredInterfaceType(); + } + + // FIXME: Map to a real fixed-size Swift array type when we have those. + // Importing as a tuple at least fills the right amount of space, and + // we can cheese static-offset "indexing" using .$n operations. + // An array of size N is imported as an N-element tuple which // takes very long to compile. We chose 4096 as the upper limit because // we don't want to break arrays of size PATH_MAX. diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 7262d0d3dc8eb..690a6e28c9ac7 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1543,6 +1543,14 @@ NodePointer Demangler::demangleBuiltinType() { Ty = createNode(Node::Kind::BuiltinTypeName, BUILTIN_TYPE_NAME_RAWPOINTER); break; + case 'r': + Ty = createNode(Node::Kind::BuiltinTypeName, + BUILTIN_TYPE_NAME_WASMEXTERNREF); + break; + case 'Q': + Ty = createNode(Node::Kind::BuiltinTypeName, + BUILTIN_TYPE_NAME_WASMEXTERNREFTABLE); + break; case 'j': Ty = createNode(Node::Kind::BuiltinTypeName, BUILTIN_TYPE_NAME_JOB); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 2927a8181d498..a82e2136a1d50 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -930,6 +930,10 @@ ManglingError Remangler::mangleBuiltinTypeName(Node *node, unsigned depth) { Buffer << 'o'; } else if (text == BUILTIN_TYPE_NAME_RAWPOINTER) { Buffer << 'p'; + } else if (text == BUILTIN_TYPE_NAME_WASMEXTERNREF) { + Buffer << 'r'; + } else if (text == BUILTIN_TYPE_NAME_WASMEXTERNREFTABLE) { + Buffer << 'Q'; } else if (text == BUILTIN_TYPE_NAME_RAWUNSAFECONTINUATION) { Buffer << 'c'; } else if (text == BUILTIN_TYPE_NAME_JOB) { diff --git a/lib/IRGen/GenBuiltin.cpp b/lib/IRGen/GenBuiltin.cpp index d87666e19feed..a672f84ffb9f8 100644 --- a/lib/IRGen/GenBuiltin.cpp +++ b/lib/IRGen/GenBuiltin.cpp @@ -211,6 +211,72 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin, return; } + case BuiltinValueKind::WasmRefNullExtern: { + auto *call = IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_ref_null_extern, {}); + out.add(call); + return; + } + + case BuiltinValueKind::WasmTableSizeExternRef: { + auto table = args.claimNext(); + auto *call = IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_size, {table}); + out.add(call); + return; + } + + case BuiltinValueKind::WasmTableGetExternRef: { + auto table = args.claimNext(); + auto index = args.claimNext(); + auto *call = IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_get_externref, {table, index}); + out.add(call); + return; + } + + case BuiltinValueKind::WasmTableSetExternRef: { + auto table = args.claimNext(); + auto index = args.claimNext(); + auto value = args.claimNext(); + IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_set_externref, {table, index, value}); + return; + } + + case BuiltinValueKind::WasmTableGrowExternRef: { + auto table = args.claimNext(); + auto value = args.claimNext(); + auto delta = args.claimNext(); + auto *call = IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_grow_externref, {table, value, delta}); + out.add(call); + return; + } + + case BuiltinValueKind::WasmTableFillExternRef: { + auto table = args.claimNext(); + auto index = args.claimNext(); + auto value = args.claimNext(); + auto count = args.claimNext(); + IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_fill_externref, + {table, index, value, count}); + return; + } + + case BuiltinValueKind::WasmTableCopyExternRef: { + auto destinationTable = args.claimNext(); + auto sourceTable = args.claimNext(); + auto sourceIndex = args.claimNext(); + auto destinationIndex = args.claimNext(); + auto count = args.claimNext(); + IGF.Builder.CreateIntrinsicCall( + llvm::Intrinsic::wasm_table_copy, + {destinationTable, sourceTable, sourceIndex, destinationIndex, count}); + return; + } + // These builtins don't care about their argument: case BuiltinValueKind::Sizeof: { (void)args.claimAll(); diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 803810375326f..87ab81515bc55 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -61,6 +61,9 @@ using namespace swift; using namespace irgen; +static constexpr unsigned WasmTableAddressSpace = 1; +static constexpr unsigned WasmExternRefAddressSpace = 10; + Alignment IRGenModule::getCappedAlignment(Alignment align) { return std::min(align, Alignment(MaximumAlignment)); } @@ -1798,6 +1801,34 @@ const LoadableTypeInfo &TypeConverter::getRawPointerTypeInfo() { return *RawPointerTI; } +const LoadableTypeInfo &IRGenModule::getWasmExternRefTypeInfo() { + return Types.getWasmExternRefTypeInfo(); +} + +const LoadableTypeInfo &TypeConverter::getWasmExternRefTypeInfo() { + if (WasmExternRefTI) return *WasmExternRefTI; + WasmExternRefTI = new RawPointerTypeInfo( + IGM.getOpaquePointerType(WasmExternRefAddressSpace), + IGM.getPointerSize(), IGM.getPointerAlignment()); + WasmExternRefTI->NextConverted = FirstType; + FirstType = WasmExternRefTI; + return *WasmExternRefTI; +} + +const LoadableTypeInfo &IRGenModule::getWasmExternRefTableTypeInfo() { + return Types.getWasmExternRefTableTypeInfo(); +} + +const LoadableTypeInfo &TypeConverter::getWasmExternRefTableTypeInfo() { + if (WasmExternRefTableTI) return *WasmExternRefTableTI; + WasmExternRefTableTI = new RawPointerTypeInfo( + IGM.getOpaquePointerType(WasmTableAddressSpace), + IGM.getPointerSize(), IGM.getPointerAlignment()); + WasmExternRefTableTI->NextConverted = FirstType; + FirstType = WasmExternRefTableTI; + return *WasmExternRefTableTI; +} + const LoadableTypeInfo &IRGenModule::getRawUnsafeContinuationTypeInfo() { return Types.getRawUnsafeContinuationTypeInfo(); } @@ -2183,6 +2214,12 @@ convertPrimitiveBuiltin(IRGenModule &IGM, CanType canTy) { case TypeKind::BuiltinRawPointer: return RetTy{ IGM.Int8PtrTy, IGM.getPointerSize(), IGM.getPointerAlignment() }; + case TypeKind::BuiltinWasmExternRefTable: + return RetTy{ IGM.getOpaquePointerType(WasmTableAddressSpace), + IGM.getPointerSize(), IGM.getPointerAlignment() }; + case TypeKind::BuiltinWasmExternRef: + return RetTy{ IGM.getOpaquePointerType(WasmExternRefAddressSpace), + IGM.getPointerSize(), IGM.getPointerAlignment() }; case TypeKind::BuiltinFloat: switch (cast(ty)->getFPKind()) { case BuiltinFloatType::IEEE16: @@ -2283,6 +2320,10 @@ const TypeInfo *TypeConverter::convertType(CanType ty) { getFixedBufferAlignment(IGM)); case TypeKind::BuiltinRawPointer: return &getRawPointerTypeInfo(); + case TypeKind::BuiltinWasmExternRefTable: + return &getWasmExternRefTableTypeInfo(); + case TypeKind::BuiltinWasmExternRef: + return &getWasmExternRefTypeInfo(); case TypeKind::BuiltinRawUnsafeContinuation: return &getRawUnsafeContinuationTypeInfo(); case TypeKind::BuiltinJob: diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index fafe9125b4cca..a403b5a8f1ce3 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -108,6 +108,8 @@ class TypeConverter { const LoadableTypeInfo *UnknownObjectTI = nullptr; const LoadableTypeInfo *BridgeObjectTI = nullptr; const LoadableTypeInfo *RawPointerTI = nullptr; + const LoadableTypeInfo *WasmExternRefTI = nullptr; + const LoadableTypeInfo *WasmExternRefTableTI = nullptr; const LoadableTypeInfo *RawUnsafeContinuationTI = nullptr; const LoadableTypeInfo *JobTI = nullptr; const LoadableTypeInfo *ExecutorTI = nullptr; @@ -205,6 +207,8 @@ class TypeConverter { const LoadableTypeInfo &getBridgeObjectTypeInfo(); const LoadableTypeInfo &getImplicitActorTypeInfo(); const LoadableTypeInfo &getRawPointerTypeInfo(); + const LoadableTypeInfo &getWasmExternRefTypeInfo(); + const LoadableTypeInfo &getWasmExternRefTableTypeInfo(); const LoadableTypeInfo &getRawUnsafeContinuationTypeInfo(); const LoadableTypeInfo &getJobTypeInfo(); const LoadableTypeInfo &getExecutorTypeInfo(); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 4904e2ba826b9..b031b8dee28fb 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -2018,6 +2018,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { case TypeKind::BuiltinNativeObject: case TypeKind::BuiltinBridgeObject: case TypeKind::BuiltinRawPointer: + case TypeKind::BuiltinWasmExternRef: + case TypeKind::BuiltinWasmExternRefTable: case TypeKind::BuiltinRawUnsafeContinuation: case TypeKind::BuiltinJob: { unsigned PtrSize = diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index a0babcea87201..5d1f8d551293b 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1146,6 +1146,8 @@ class IRGenModule { const LoadableTypeInfo &getUnknownObjectTypeInfo(); const LoadableTypeInfo &getBridgeObjectTypeInfo(); const LoadableTypeInfo &getRawPointerTypeInfo(); + const LoadableTypeInfo &getWasmExternRefTypeInfo(); + const LoadableTypeInfo &getWasmExternRefTableTypeInfo(); const LoadableTypeInfo &getRawUnsafeContinuationTypeInfo(); llvm::Type *getStorageTypeForUnlowered(Type T); llvm::Type *getStorageTypeForLowered(CanType T); diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 8c97fccd42d1d..c8197d1e2aa25 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1919,6 +1919,18 @@ namespace { return emitDirectMetadataRef(type); } + MetadataResponse + visitBuiltinWasmExternRefType(CanBuiltinWasmExternRefType type, + DynamicMetadataRequest request) { + return emitDirectMetadataRef(type); + } + + MetadataResponse + visitBuiltinWasmExternRefTableType(CanBuiltinWasmExternRefTableType type, + DynamicMetadataRequest request) { + return emitDirectMetadataRef(type); + } + MetadataResponse visitBuiltinRawUnsafeContinuationType(CanBuiltinRawUnsafeContinuationType type, DynamicMetadataRequest request) { diff --git a/lib/IRGen/TypeVisitor.h b/lib/IRGen/TypeVisitor.h index e6ccb703ba107..ec2c66ad06eb2 100644 --- a/lib/IRGen/TypeVisitor.h +++ b/lib/IRGen/TypeVisitor.h @@ -36,6 +36,8 @@ class ReferenceTypeVisitor : public CanTypeVisitor { TYPE(BuiltinFloat) TYPE(BuiltinInteger) TYPE(BuiltinRawPointer) + TYPE(BuiltinWasmExternRef) + TYPE(BuiltinWasmExternRefTable) TYPE(BuiltinVector) TYPE(LValue) TYPE(Metatype) diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 28aaf43a6b574..13fe10868689d 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -269,7 +269,9 @@ bool SILType::isPointerSizeAndAligned(SILModule &M, ResilienceExpansion expansion) const { auto &C = getASTContext(); if (isHeapObjectReferenceType() - || getASTType()->isEqual(C.TheRawPointerType)) { + || getASTType()->isEqual(C.TheRawPointerType) + || getASTType()->isEqual(C.TheWasmExternRefType) + || getASTType()->isEqual(C.TheWasmExternRefTableType)) { return true; } if (auto intTy = dyn_cast(getASTType())) diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 3c6c1fa61bb9e..3d4d61862a373 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -348,6 +348,8 @@ namespace { IMPL(BuiltinInteger, Trivial) IMPL(BuiltinIntegerLiteral, Trivial) IMPL(BuiltinFloat, Trivial) + IMPL(BuiltinWasmExternRef, Trivial) + IMPL(BuiltinWasmExternRefTable, Trivial) IMPL(BuiltinRawUnsafeContinuation, Trivial) IMPL(BuiltinJob, Trivial) IMPL(BuiltinExecutor, Trivial) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index f9a6ddf924df0..c1d9193471d8b 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -338,6 +338,23 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, checkBorrowExpr(borrowExpr); } + if (auto *inOutExpr = dyn_cast(E)) { + auto inOutType = inOutExpr->getSubExpr()->getType(); + if (inOutType && inOutType->containsWasmExternref()) { + Ctx.Diags.diagnose(inOutExpr->getLoc(), + diag::expr_inout_wasm_externref, + inOutType->getInOutObjectType() + ? inOutType->getInOutObjectType() + : inOutType); + } + if (inOutType && inOutType->containsWasmExternrefTable()) { + Ctx.Diags.diagnose(inOutExpr->getLoc(), diag::expr_inout_wasm_table, + inOutType->getInOutObjectType() + ? inOutType->getInOutObjectType() + : inOutType); + } + } + return Action::Continue(E); } diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 95450c8caccc3..3d96007b94db3 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -238,6 +238,20 @@ class FindCapturedVars : public ASTWalker { } } + if (VD->getInterfaceType() && + VD->getInterfaceType()->containsWasmExternref()) { + Context.Diags.diagnose(capture.getLoc(), diag::capture_wasm_externref, + VD->getInterfaceType()); + return; + } + + if (VD->getInterfaceType() && + VD->getInterfaceType()->containsWasmExternrefTable()) { + Context.Diags.diagnose(capture.getLoc(), diag::capture_wasm_table, + VD->getInterfaceType()); + return; + } + // Check to see if we already have an entry for this decl. unsigned &entryNumber = captureEntryNumber[VD]; if (entryNumber == 0) { diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 9b079d9a0465e..9abd03c7936d4 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -492,6 +492,118 @@ static void checkInheritanceClause( } } +static bool diagnoseWasmExternrefPointerType(ASTContext &ctx, SourceLoc loc, + Type type) { + if (!type || !type->containsWasmExternrefPointer()) + return false; + + ctx.Diags.diagnose(loc, diag::wasm_externref_pointer_type, type); + return true; +} + +static bool diagnoseWasmExternrefTablePointerType(ASTContext &ctx, SourceLoc loc, + Type type) { + if (!type || !type->containsWasmExternrefTablePointer()) + return false; + + ctx.Diags.diagnose(loc, diag::wasm_table_pointer_type, type); + return true; +} + +static bool shouldDiagnoseWasmExternrefStorage(VarDecl *var) { + if (!var->hasStorage() || !var->getInterfaceType() || + !var->getInterfaceType()->containsWasmExternref()) { + return false; + } + + auto *dc = var->getDeclContext(); + if (dc->isTypeContext()) { + if (var->isStatic()) + return true; + + auto *nominal = dc->getSelfNominalTypeDecl(); + return !(nominal && nominal->getName().is("WasmExternref") && + nominal->getModuleContext()->isStdlibModule()); + } + + return var->isGlobalStorage(); +} + +static bool diagnoseWasmExternrefStorage(VarDecl *var) { + if (!shouldDiagnoseWasmExternrefStorage(var)) + return false; + + unsigned kind = 2; + if (var->getDeclContext()->isTypeContext()) + kind = var->isStatic() ? 1 : 0; + + var->diagnose(diag::wasm_externref_storage, kind, var->getInterfaceType()); + return true; +} + +static bool shouldDiagnoseWasmExternrefTableStorage(VarDecl *var) { + if (!var->hasStorage() || !var->getInterfaceType() || + !var->getInterfaceType()->containsWasmExternrefTable()) { + return false; + } + + if (var->hasClangNode()) + return false; + + auto *dc = var->getDeclContext(); + if (dc->isTypeContext()) { + if (var->isStatic()) + return true; + + auto *nominal = dc->getSelfNominalTypeDecl(); + return !(nominal && nominal->getName().is("WasmExternrefTable") && + nominal->getModuleContext()->isStdlibModule()); + } + + if (dc->isLocalContext()) + return true; + + return var->isGlobalStorage(); +} + +static bool diagnoseWasmExternrefTableStorage(VarDecl *var) { + if (!shouldDiagnoseWasmExternrefTableStorage(var)) + return false; + + unsigned kind = 3; + if (var->getDeclContext()->isLocalContext()) { + kind = 0; + } else if (var->getDeclContext()->isTypeContext()) { + kind = var->isStatic() ? 2 : 1; + } + + var->diagnose(diag::wasm_table_storage, kind, var->getInterfaceType()); + return true; +} + +static bool diagnoseWasmExternrefResultType(ValueDecl *decl, Type type, + SourceLoc loc) { + if (!type) + return false; + + return diagnoseWasmExternrefPointerType(decl->getASTContext(), loc, type); +} + +static bool diagnoseWasmExternrefTableResultType(ValueDecl *decl, Type type, + SourceLoc loc) { + if (!type || decl->getModuleContext()->isStdlibModule()) + return false; + + if (diagnoseWasmExternrefTablePointerType(decl->getASTContext(), loc, type)) + return true; + + if (!type->containsWasmExternrefTable()) + return false; + + decl->getASTContext().Diags.diagnose(loc, diag::wasm_table_result_type, type); + return true; +} + static void installCodingKeysIfNecessary(NominalTypeDecl *NTD) { auto req = ResolveImplicitMemberRequest{NTD, ImplicitMemberAction::ResolveCodingKeys}; @@ -2446,6 +2558,20 @@ class DeclChecker : public DeclVisitor { this->visitBoundVariable(var); }); + if (diagnoseWasmExternrefPointerType(Ctx, VD->getLoc(), + VD->getInterfaceType())) + VD->setInvalid(); + + if (diagnoseWasmExternrefStorage(VD)) + VD->setInvalid(); + + if (diagnoseWasmExternrefTablePointerType(Ctx, VD->getLoc(), + VD->getInterfaceType())) + VD->setInvalid(); + + if (diagnoseWasmExternrefTableStorage(VD)) + VD->setInvalid(); + // Reject cases where this is a variable that has storage but it isn't // allowed. if (VD->hasStorage()) { @@ -2871,6 +2997,14 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkParameterList(SD->getIndices(), SD); + if (diagnoseWasmExternrefResultType(SD, SD->getElementInterfaceType(), + SD->getLoc())) + SD->setInvalid(); + + if (diagnoseWasmExternrefTableResultType(SD, SD->getElementInterfaceType(), + SD->getLoc())) + SD->setInvalid(); + checkDefaultArguments(SD->getIndices()); checkVariadicParameters(SD->getIndices(), SD); @@ -3562,6 +3696,14 @@ class DeclChecker : public DeclVisitor { } TypeChecker::checkParameterList(FD->getParameters(), FD); + + if (diagnoseWasmExternrefResultType(FD, FD->getResultInterfaceType(), + FD->getLoc())) + FD->setInvalid(); + + if (diagnoseWasmExternrefTableResultType(FD, FD->getResultInterfaceType(), + FD->getLoc())) + FD->setInvalid(); } checkAccessControl(FD); @@ -4240,6 +4382,36 @@ void TypeChecker::checkParameterList(ParameterList *params, } } + auto interfaceType = param->getInterfaceType(); + if (diagnoseWasmExternrefPointerType(owner->getASTContext(), param->getLoc(), + interfaceType)) + param->setInvalid(); + + if (diagnoseWasmExternrefTablePointerType(owner->getASTContext(), + param->getLoc(), interfaceType)) + param->setInvalid(); + + if (interfaceType && interfaceType->containsWasmExternref()) { + if (param->isVariadic()) { + param->diagnose(diag::wasm_externref_variadic, param->getName(), + interfaceType); + param->setInvalid(); + } + + if (param->isInOut()) { + param->diagnose(diag::wasm_externref_inout_parameter, param->getName(), + interfaceType); + param->setInvalid(); + } + } + + if (interfaceType && interfaceType->containsWasmExternrefTable() && + !param->getModuleContext()->isStdlibModule()) { + param->diagnose(diag::wasm_table_parameter, param->getName(), + interfaceType); + param->setInvalid(); + } + // @_staticExclusiveOnly types cannot be passed as 'inout', only as either // a borrow or as consuming. if (auto SD = param->getInterfaceType()->getStructOrBoundGenericStruct()) { diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index 26dba90abebf6..e8a8434dd6fdf 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -169,6 +169,124 @@ public struct OpaquePointer { @available(*, unavailable) extension OpaquePointer: Sendable {} +#if hasFeature(WasmExternref) +/// A wrapper around a WebAssembly `externref` value. +/// +/// `externref` values are used to represent host references in WebAssembly. +@frozen +@unsafe +public struct WasmExternref { + @usableFromInline + internal var _rawValue: Builtin.WasmExternRef + + @usableFromInline @_transparent + internal init(_ v: Builtin.WasmExternRef) { + unsafe self._rawValue = v + } +} + +@available(*, unavailable) +extension WasmExternref: Sendable {} + +extension WasmExternref { + /// A null WebAssembly `externref` value. + @_transparent + public static var null: WasmExternref { + WasmExternref(Builtin.wasmRefNullExtern()) + } +} + +/// A wrapper around an index into a WebAssembly `externref` table. +@frozen +public struct WasmExternrefIndex: Sendable, Hashable { + public var rawValue: UInt32 + + @_transparent + public init(rawValue: UInt32) { + self.rawValue = rawValue + } +} + +/// A wrapper around a WebAssembly `externref` table handle. +@frozen +@unsafe +public struct WasmExternrefTable { + @usableFromInline + internal var _rawValue: Builtin.WasmExternRefTable + + @usableFromInline @_transparent + internal init(_ v: Builtin.WasmExternRefTable) { + unsafe self._rawValue = v + } +} + +@available(*, unavailable) +extension WasmExternrefTable: Sendable {} + +extension WasmExternrefTable { + /// Returns the current number of entries in the table. + @_transparent + public func size() -> WasmExternrefIndex { + WasmExternrefIndex( + rawValue: UInt32(Builtin.wasmTableSizeExternRef(_rawValue))) + } + + /// Reads an `externref` from the table. + @_transparent + public func get(_ index: WasmExternrefIndex) -> WasmExternref { + WasmExternref( + Builtin.wasmTableGetExternRef(_rawValue, index.rawValue._value)) + } + + /// Stores an `externref` into the table. + @_transparent + public func set(_ value: WasmExternref, at index: WasmExternrefIndex) { + Builtin.wasmTableSetExternRef( + _rawValue, index.rawValue._value, value._rawValue) + } + + /// Grows the table by `delta` entries and returns the previous size on + /// success, or `nil` if the grow operation fails. + @_transparent + public func grow( + by delta: UInt32, + with value: WasmExternref = .null + ) -> WasmExternrefIndex? { + let previousSize = Int32( + Builtin.wasmTableGrowExternRef(_rawValue, value._rawValue, delta._value)) + if previousSize < 0 { + return nil + } + return WasmExternrefIndex(rawValue: UInt32(bitPattern: previousSize)) + } + + /// Fills a range of table entries with the same value. + @_transparent + public func fill( + startingAt index: WasmExternrefIndex, + with value: WasmExternref, + count: UInt32 + ) { + Builtin.wasmTableFillExternRef( + _rawValue, index.rawValue._value, value._rawValue, count._value) + } + + /// Copies entries from a source table into this table. + @_transparent + public func copy( + from source: WasmExternrefTable, + sourceIndex: WasmExternrefIndex, + to destinationIndex: WasmExternrefIndex, + count: UInt32 + ) { + Builtin.wasmTableCopyExternRef( + _rawValue, source._rawValue, + sourceIndex.rawValue._value, destinationIndex.rawValue._value, + count._value) + } +} +#endif + extension OpaquePointer { /// Creates a new `OpaquePointer` from the given address, specified as a bit /// pattern. diff --git a/stdlib/public/core/MemoryLayout.swift b/stdlib/public/core/MemoryLayout.swift index d09f0065b30f5..56986508f478a 100644 --- a/stdlib/public/core/MemoryLayout.swift +++ b/stdlib/public/core/MemoryLayout.swift @@ -167,6 +167,68 @@ extension MemoryLayout where T: ~Copyable & ~Escapable { } } +#if hasFeature(WasmExternref) +extension MemoryLayout where T == WasmExternref { + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static var size: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static var stride: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static var alignment: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static func size(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static func stride(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static func alignment(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly references do not have a Swift memory layout") + public static func offset(of key: PartialKeyPath) -> Int? { nil } +} + +extension MemoryLayout where T == WasmExternrefTable { + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static var size: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static var stride: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static var alignment: Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static func size(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static func stride(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static func alignment(ofValue value: borrowing T) -> Int { 0 } + + @available(*, unavailable, + message: "WebAssembly tables do not have a Swift memory layout") + public static func offset(of key: PartialKeyPath) -> Int? { nil } +} +#endif + extension MemoryLayout { /// Returns the offset of an inline stored property within a type's in-memory /// representation. diff --git a/test/ClangImporter/wasm-externref.swift b/test/ClangImporter/wasm-externref.swift new file mode 100644 index 0000000000000..d2eeb4ed29da6 --- /dev/null +++ b/test/ClangImporter/wasm-externref.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %swift-frontend -target wasm32-unknown-wasip1 -parse-stdlib -module-name Swift -typecheck -enable-experimental-feature WasmExternref -I %t %t/test.swift + +// REQUIRES: CODEGENERATOR=WebAssembly +// REQUIRES: swift_feature_WasmExternref + +//--- wasm_externref.h +__externref_t c_roundtrip_externref(__externref_t); +extern __externref_t objects[0]; + +//--- module.modulemap +module wasm_externref { + header "wasm_externref.h" + export * +} + +//--- test.swift +import wasm_externref + +/// ===== Minimal stdlib definitions ===== +typealias Void = () +enum Optional {} + +public struct WasmExternref { + internal var _rawValue: Builtin.WasmExternRef + + internal init(_ value: Builtin.WasmExternRef) { + _rawValue = value + } +} + +public struct WasmExternrefTable { + internal var _rawValue: Builtin.WasmExternRefTable + + internal init(_ value: Builtin.WasmExternRefTable) { + _rawValue = value + } +} +/// ====================================== + +func test(_ value: WasmExternref) { + let _: (WasmExternref) -> WasmExternref = c_roundtrip_externref + let result: WasmExternref = c_roundtrip_externref(value) + let _: Builtin.WasmExternRef = result._rawValue + _ = Builtin.wasmTableSizeExternRef(objects._rawValue) + takesAddress(&objects) // expected-error {{cannot pass value of type 'WasmExternrefTable' as 'inout'; WebAssembly tables are not addressable}} +} + +func takesAddress(_ value: inout T) {} diff --git a/test/IRGen/wasm-externref-table.swift b/test/IRGen/wasm-externref-table.swift new file mode 100644 index 0000000000000..f37c4259d12c9 --- /dev/null +++ b/test/IRGen/wasm-externref-table.swift @@ -0,0 +1,36 @@ +// RUN: %swift-frontend -target wasm32-unknown-wasip1 -parse-stdlib -module-name Swift -emit-ir -enable-experimental-feature Extern -enable-experimental-feature WasmExternref %s | %FileCheck %s + +// REQUIRES: CODEGENERATOR=WebAssembly +// REQUIRES: swift_feature_Extern +// REQUIRES: swift_feature_WasmExternref + +typealias Void = () +enum Optional {} + +@_extern(c, "use_table") +public func use_table( + _ table: Builtin.WasmExternRefTable, + _ source: Builtin.WasmExternRefTable, + _ value: Builtin.WasmExternRef, + _ sourceIndex: Builtin.Int32, + _ destinationIndex: Builtin.Int32, + _ count: Builtin.Int32 +) -> Builtin.WasmExternRef { + _ = Builtin.wasmRefNullExtern() + _ = Builtin.wasmTableSizeExternRef(table) + Builtin.wasmTableSetExternRef(table, destinationIndex, value) + Builtin.wasmTableFillExternRef(table, destinationIndex, value, count) + _ = Builtin.wasmTableGrowExternRef(table, value, count) + Builtin.wasmTableCopyExternRef( + table, source, sourceIndex, destinationIndex, count) + return Builtin.wasmTableGetExternRef(table, sourceIndex) +} + +// CHECK: define {{.*}}ptr addrspace(10) @use_table(ptr addrspace(1) %0, ptr addrspace(1) %1, ptr addrspace(10) %2, i32 %3, i32 %4, i32 %5) +// CHECK: call ptr addrspace(10) @llvm.wasm.ref.null.extern() +// CHECK: call i32 @llvm.wasm.table.size(ptr addrspace(1) %0) +// CHECK: call void @llvm.wasm.table.set.externref(ptr addrspace(1) %0, i32 %4, ptr addrspace(10) %2) +// CHECK: call void @llvm.wasm.table.fill.externref(ptr addrspace(1) %0, i32 %4, ptr addrspace(10) %2, i32 %5) +// CHECK: call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) %0, ptr addrspace(10) %2, i32 %5) +// CHECK: call void @llvm.wasm.table.copy(ptr addrspace(1) %0, ptr addrspace(1) %1, i32 %3, i32 %4, i32 %5) +// CHECK: call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) %0, i32 %3) diff --git a/test/IRGen/wasm-externref.swift b/test/IRGen/wasm-externref.swift new file mode 100644 index 0000000000000..0888b05717624 --- /dev/null +++ b/test/IRGen/wasm-externref.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %swift-frontend -target wasm32-unknown-wasip1 -parse-stdlib -module-name Swift -emit-ir -enable-experimental-feature Extern -enable-experimental-feature WasmExternref -I %t %t/test.swift | %FileCheck %s + +// REQUIRES: CODEGENERATOR=WebAssembly +// REQUIRES: swift_feature_Extern +// REQUIRES: swift_feature_WasmExternref + +//--- wasm_externref.h +__externref_t c_roundtrip_externref(__externref_t); + +//--- module.modulemap +module wasm_externref { + header "wasm_externref.h" + export * +} + +//--- test.swift +import wasm_externref + +/// ===== Minimal stdlib definitions ===== +typealias Void = () +enum Optional {} + +@frozen +public struct WasmExternref { + public var _rawValue: Builtin.WasmExternRef + + public init(_ value: Builtin.WasmExternRef) { + _rawValue = value + } +} +/// ====================================== + +@_extern(c, "swift_roundtrip_externref") +public func swift_roundtrip_externref(_ value: WasmExternref) -> WasmExternref { + value +} + +public func test(_ value: WasmExternref) -> WasmExternref { + let imported = c_roundtrip_externref(value) + return swift_roundtrip_externref(imported) +} + +// CHECK: declare ptr addrspace(10) @c_roundtrip_externref(ptr addrspace(10)) +// CHECK: define {{.*}}ptr addrspace(10) @swift_roundtrip_externref(ptr addrspace(10) +// CHECK: call ptr addrspace(10) @c_roundtrip_externref(ptr addrspace(10) +// CHECK: call ptr addrspace(10) @swift_roundtrip_externref(ptr addrspace(10) diff --git a/test/Sema/wasm-externref.swift b/test/Sema/wasm-externref.swift new file mode 100644 index 0000000000000..75737afde418d --- /dev/null +++ b/test/Sema/wasm-externref.swift @@ -0,0 +1,74 @@ +// RUN: %target-swift-frontend -target wasm32-unknown-wasip1 -typecheck -enable-experimental-feature WasmExternref %s + +// REQUIRES: CODEGENERATOR=WebAssembly +// REQUIRES: swift_feature_WasmExternref + +#if hasFeature(WasmExternref) +struct StoredProperty { + var value: WasmExternref // expected-error {{stored property cannot have type 'WasmExternref'; WebAssembly references cannot be stored in Swift memory}} +} + +enum GlobalStorage { + static var value: WasmExternref = fatalError() // expected-error {{static property cannot have type 'WasmExternref'; WebAssembly references cannot be stored in Swift memory}} +} + +var globalValue: WasmExternref = fatalError() // expected-error {{global variable cannot have type 'WasmExternref'; WebAssembly references cannot be stored in Swift memory}} + +struct TableStoredProperty { + var table: WasmExternrefTable // expected-error {{stored property cannot have type 'WasmExternrefTable'; WebAssembly tables are not Swift values}} +} + +enum TableGlobalStorage { + static var table: WasmExternrefTable = fatalError() // expected-error {{static property cannot have type 'WasmExternrefTable'; WebAssembly tables are not Swift values}} +} + +var globalTable: WasmExternrefTable = fatalError() // expected-error {{global variable cannot have type 'WasmExternrefTable'; WebAssembly tables are not Swift values}} + +func takesInOut(_ value: inout WasmExternref) {} // expected-error {{'inout' parameter 'value' cannot have type 'WasmExternref'; WebAssembly references are not addressable}} + +func takesVariadic(_ values: WasmExternref...) {} // expected-error {{parameter 'values' cannot be variadic because type 'WasmExternref' contains a WebAssembly reference}} + +func takesPointer(_ value: UnsafePointer) {} // expected-error {{type 'UnsafePointer' cannot contain a pointer to a WebAssembly reference}} + +func takesTable(_ table: WasmExternrefTable) {} // expected-error {{parameter 'table' cannot have type 'WasmExternrefTable'; WebAssembly tables cannot be passed as Swift values}} + +func takesTablePointer(_ table: UnsafePointer) {} // expected-error {{type 'UnsafePointer' cannot contain a pointer to a WebAssembly table}} + +func returnsPointer() -> UnsafeMutablePointer { // expected-error {{type 'UnsafeMutablePointer' cannot contain a pointer to a WebAssembly reference}} + fatalError() +} + +func returnsTable() -> WasmExternrefTable { // expected-error {{result type 'WasmExternrefTable' cannot contain a WebAssembly table}} + fatalError() +} + +func rejectInOutExpr(_ value: WasmExternref) { + var local = value + takesAddress(&local) // expected-error {{cannot pass value of type 'WasmExternref' as 'inout'; WebAssembly references are not addressable}} +} + +func takesAddress(_ value: inout T) {} + +func rejectCapture(_ value: WasmExternref) -> () -> WasmExternref { + { value } // expected-error {{cannot capture value of type 'WasmExternref'; WebAssembly references cannot be stored in closure contexts}} +} + +func localRoundTrip(_ value: WasmExternref) -> WasmExternref { + let local = value + return local +} + +let _: WasmExternref = .null +let _: UInt32 = WasmExternrefIndex(rawValue: 0).rawValue + +func rejectTableStorage() { + let _: WasmExternrefTable = fatalError() // expected-error {{local variable cannot have type 'WasmExternrefTable'; WebAssembly tables are not Swift values}} +} + +let _: Int = MemoryLayout.size // expected-error {{'size' is unavailable: WebAssembly references do not have a Swift memory layout}} +let _: Int = MemoryLayout.size // expected-error {{'size' is unavailable: WebAssembly tables do not have a Swift memory layout}} + +func rejectLayout(_ value: WasmExternref) { + _ = MemoryLayout.size(ofValue: value) // expected-error {{'size(ofValue:)' is unavailable: WebAssembly references do not have a Swift memory layout}} +} +#endif