From 11e5ace808b5d9a8835fa5bb9408c270621e8423 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 12 Nov 2025 22:18:17 -0800 Subject: [PATCH 1/6] [Clang mangling] Don't mangle when Clang says not to We have been mangling extern "C" symbols when building with C++ interoperability, leading to incorrectly-mangled names such as _Z6memset that should have been unmangled "memset". Fix this so we get consistent mangling between C and C++ interoperability modes. Fixes rdar://164495210. (cherry picked from commit c02811d47b6877581388aa3c13fbada1db6aa5df) --- lib/ClangImporter/ClangImporter.cpp | 4 +++- test/Inputs/clang-importer-sdk/usr/include/string.h | 10 ++++++++++ test/Interop/Cxx/extern-c/decls.swift | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/Interop/Cxx/extern-c/decls.swift diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index a3919ae483e1f..f1cb91242b10c 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -4766,8 +4766,10 @@ void ClangImporter::Implementation::getMangledName( auto ctorGlobalDecl = clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete); mangler->mangleCXXName(ctorGlobalDecl, os); - } else { + } else if (mangler->shouldMangleDeclName(clangDecl)) { mangler->mangleName(clangDecl, os); + } else { + os << clangDecl->getName(); } } diff --git a/test/Inputs/clang-importer-sdk/usr/include/string.h b/test/Inputs/clang-importer-sdk/usr/include/string.h index 4c6a4ab2eb2b4..4d73f9e4e7304 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/string.h +++ b/test/Inputs/clang-importer-sdk/usr/include/string.h @@ -7,6 +7,12 @@ #include +#ifdef __cplusplus +extern "C" { + +typedef __SIZE_TYPE__ size_t; +#endif + void* memcpy(void* s1, const void* s2, size_t n); void* memmove(void* s1, const void* s2, size_t n); char* strcpy (char* s1, const char* s2); @@ -30,4 +36,8 @@ void* memset(void* s, int c, size_t n); char* strerror(int errnum); size_t strlen(const char* s); +#ifdef __cplusplus +} +#endif + #endif // SDK_STRING_H diff --git a/test/Interop/Cxx/extern-c/decls.swift b/test/Interop/Cxx/extern-c/decls.swift new file mode 100644 index 0000000000000..75c24c9845ad0 --- /dev/null +++ b/test/Interop/Cxx/extern-c/decls.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-emit-silgen(mock-sdk: %clang-importer-sdk) %s -I %S/Inputs -import-bridging-header %S/../../../Inputs/clang-importer-sdk/usr/include/string.h -enable-experimental-cxx-interop | %FileCheck %s + + +func zerome(ptr: UnsafeMutablePointer) { + memset(ptr, 0, MemoryLayout.size) +} + +// Verify that the asmname is "memset", not a C++-mangled version +// CHECK: sil [serialized] [asmname "memset"] [clang memset] @$sSo6memsetySvSgAB_s5Int32VSitFTo : $@convention(c) (Optional, Int32, Int) -> Optional From 27ed57ccfca8ef65060eba165902ea8af321db25 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 13 Nov 2025 06:37:11 -0800 Subject: [PATCH 2/6] Generalize test to also work on Windows (cherry picked from commit e423d77c79075071abf9e490b41cdf78051dfdb7) --- test/Interop/Cxx/extern-c/Inputs/my-memory.h | 11 +++++++++++ test/Interop/Cxx/extern-c/decls.swift | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/Interop/Cxx/extern-c/Inputs/my-memory.h diff --git a/test/Interop/Cxx/extern-c/Inputs/my-memory.h b/test/Interop/Cxx/extern-c/Inputs/my-memory.h new file mode 100644 index 0000000000000..e5fb73e128258 --- /dev/null +++ b/test/Interop/Cxx/extern-c/Inputs/my-memory.h @@ -0,0 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + +typedef __SIZE_TYPE__ size_t; + +void* memset(void* s, int c, size_t n); + +#ifdef __cplusplus +} +#endif diff --git a/test/Interop/Cxx/extern-c/decls.swift b/test/Interop/Cxx/extern-c/decls.swift index 75c24c9845ad0..efa20d351c874 100644 --- a/test/Interop/Cxx/extern-c/decls.swift +++ b/test/Interop/Cxx/extern-c/decls.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-emit-silgen(mock-sdk: %clang-importer-sdk) %s -I %S/Inputs -import-bridging-header %S/../../../Inputs/clang-importer-sdk/usr/include/string.h -enable-experimental-cxx-interop | %FileCheck %s +// RUN: %target-swift-emit-silgen(mock-sdk: %clang-importer-sdk) %s -I %S/Inputs -import-bridging-header %S/Inputs/my-memory.h -enable-experimental-cxx-interop | %FileCheck %s func zerome(ptr: UnsafeMutablePointer) { From 45194805f8d964b5370da4c94110223717fb798a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 13 Nov 2025 15:53:26 -0800 Subject: [PATCH 3/6] Only foreign thunks need to be serialized, not foreign definitions This eliminates a SIL verification error with `@c` functions, which provide definitions for foreign entrypoints. We were serializing @c definitions when we shouldn't be, which would cause problems down the line if those @c definitions referenced something internal that they shouldn't. (cherry picked from commit b21485dd1f802681ce09c4111580b8d5cf796a96) --- lib/SIL/IR/SILDeclRef.cpp | 3 +- test/Interop/Cxx/extern-c/decls.swift | 2 +- .../foreign-reference/singleton-silgen.swift | 2 +- test/SILGen/cdecl-official.swift | 2 +- test/SILGen/extern_c.swift | 2 +- test/embedded/linkage/leaf_application.swift | 3 -- test/embedded/serialization.swift | 32 +++++++++++++++++++ 7 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 test/embedded/serialization.swift diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 9e8a1a69e18ce..3dd5f5ee18af8 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1025,7 +1025,8 @@ SerializedKind_t SILDeclRef::getSerializedKind() const { // @objc thunks for top-level functions are serializable since they're // referenced from @convention(c) conversions inside inlinable // functions. - return IsSerialized; + if (isThunk()) + return IsSerialized; } // Declarations imported from Clang modules are serialized if diff --git a/test/Interop/Cxx/extern-c/decls.swift b/test/Interop/Cxx/extern-c/decls.swift index efa20d351c874..7b73af60b62d4 100644 --- a/test/Interop/Cxx/extern-c/decls.swift +++ b/test/Interop/Cxx/extern-c/decls.swift @@ -6,4 +6,4 @@ func zerome(ptr: UnsafeMutablePointer) { } // Verify that the asmname is "memset", not a C++-mangled version -// CHECK: sil [serialized] [asmname "memset"] [clang memset] @$sSo6memsetySvSgAB_s5Int32VSitFTo : $@convention(c) (Optional, Int32, Int) -> Optional +// CHECK: sil [asmname "memset"] [clang memset] @$sSo6memsetySvSgAB_s5Int32VSitFTo : $@convention(c) (Optional, Int32, Int) -> Optional diff --git a/test/Interop/Cxx/foreign-reference/singleton-silgen.swift b/test/Interop/Cxx/foreign-reference/singleton-silgen.swift index 52dcd75e704c9..80c27e7429d13 100644 --- a/test/Interop/Cxx/foreign-reference/singleton-silgen.swift +++ b/test/Interop/Cxx/foreign-reference/singleton-silgen.swift @@ -35,4 +35,4 @@ public func test() { // CHECK-LABEL: sil {{.*}}[asmname "{{.*}}test{{.*}}"] [clang DeletedSpecialMembers.test] @$sSo21DeletedSpecialMembersV4tests5Int32VyFTo : $@convention(cxx_method) (DeletedSpecialMembers) -> Int32 -// CHECK-LABEL: sil [serialized] [asmname "{{.*}}mutate{{.*}}"] [clang mutateIt] @$sSo8mutateItyySo21DeletedSpecialMembersVFTo : $@convention(c) (DeletedSpecialMembers) -> () +// CHECK-LABEL: sil [asmname "{{.*}}mutate{{.*}}"] [clang mutateIt] @$sSo8mutateItyySo21DeletedSpecialMembersVFTo : $@convention(c) (DeletedSpecialMembers) -> () diff --git a/test/SILGen/cdecl-official.swift b/test/SILGen/cdecl-official.swift index 34ec1666aa68d..dfe17dc673939 100644 --- a/test/SILGen/cdecl-official.swift +++ b/test/SILGen/cdecl-official.swift @@ -33,7 +33,7 @@ func requiresThunk() { acceptSwiftFunc(orange) } -// CHECK-LABEL: sil [serialized] [asmname "cauliflower"] [ossa] @$s5cdecl8broccoliyS2iFTo : $@convention(c) (Int) -> Int { +// CHECK-LABEL: sil [asmname "cauliflower"] [ossa] @$s5cdecl8broccoliyS2iFTo : $@convention(c) (Int) -> Int { // CHECK-NOT: apply // CHECK: return @c(cauliflower) diff --git a/test/SILGen/extern_c.swift b/test/SILGen/extern_c.swift index f863f8da5b6f4..5067493a9846e 100644 --- a/test/SILGen/extern_c.swift +++ b/test/SILGen/extern_c.swift @@ -10,7 +10,7 @@ func withCName(_ x: Int) -> Int @_extern(c, "take_c_func_ptr") func takeCFuncPtr(_ f: @convention(c) (Int) -> Int) -// CHECK-DAG: sil [serialized] [asmname "public_visible"] @$s8extern_c16publicVisibilityyS2iFTo : $@convention(c) (Int) -> Int +// CHECK-DAG: sil [asmname "public_visible"] @$s8extern_c16publicVisibilityyS2iFTo : $@convention(c) (Int) -> Int @_extern(c, "public_visible") public func publicVisibility(_ x: Int) -> Int diff --git a/test/embedded/linkage/leaf_application.swift b/test/embedded/linkage/leaf_application.swift index 14912315fff6d..70dddd5eeab6c 100644 --- a/test/embedded/linkage/leaf_application.swift +++ b/test/embedded/linkage/leaf_application.swift @@ -26,9 +26,6 @@ // Never referenced. // LIBRARY-IR-NOT: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = linkonce_odr {{(protected |dllexport )?}}global -// Note: referenced by swift_allocEmptyBox. -// LIBRARY-IR: @"$es16_emptyBoxStorageSi_Sitvp" = linkonce_odr {{(protected |dllexport )?}}global - // LIBRARY-IR-NOT: define {{.*}}@"$e7Library5helloSaySiGyF"() public func hello() -> [Int] { getArray() diff --git a/test/embedded/serialization.swift b/test/embedded/serialization.swift new file mode 100644 index 0000000000000..ac0160295d0e5 --- /dev/null +++ b/test/embedded/serialization.swift @@ -0,0 +1,32 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// Library module + +// SIL checking +// RUN: %target-swift-frontend %t/Library.swift -parse-as-library -entry-point-function-name Library_main -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -emit-sil -emit-module-path %t/Modules/Library.swiftmodule -o - | %FileCheck -check-prefix LIBRARY-SIL %s + +// RUN: %target-swift-frontend %t/Application.swift -I %t/Modules -parse-as-library -entry-point-function-name Application_main -enable-experimental-feature Embedded -emit-sil -o - | %FileCheck -check-prefix APPLICATION-SIL %s + +// REQUIRES: swift_in_compiler +// REQUIRES: swift_feature_Embedded +// REQUIRES: swift_feature_DeferredCodeGen + +//--- Library.swift + +func internalFunc() { } + +// LIBRARY-SIL: sil [asmname "swift_dosomething"] @$e7Library17swift_dosomethingyyFTo : $@convention(c) () -> () { +@c +public func swift_dosomething() { + internalFunc() +} + +//--- Application.swift +import Library + +// APPLICATION-SIL-LABEL: sil @$e11Application4testyyF : $@convention(thin) () -> () +public func test() { + // CHECK: function_ref @$e7Library17swift_dosomethingyyFTo : $@convention(c) () -> () + swift_dosomething() +} From 89af45c6fdf0dc3ef6145b55218bec8b6f413448 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 13 Nov 2025 18:33:03 -0800 Subject: [PATCH 4/6] Use @c instead of @_cdecl in the Embedded Swift runtime This change moves us toward the official feature, and eliminates the extra level of "thunk" that was implied by `@_cdecl`. Amusingly, this trips up the LLVM-level ARC optimizations, because we are trying to perform ARC optimizations within the retain/release runtime functions. Teach those optimization passes to leave swift_retainN et al alone. (cherry picked from commit 9ea8671c3f04f15c4fce4e66f2b43cacf4e15aaf) --- lib/LLVMPasses/LLVMARCContract.cpp | 5 ++ lib/LLVMPasses/LLVMARCOpts.cpp | 6 ++ lib/LLVMPasses/LLVMARCOpts.h | 80 +++++++++++++++++------- stdlib/public/core/EmbeddedRuntime.swift | 67 ++++++++++---------- 4 files changed, 100 insertions(+), 58 deletions(-) diff --git a/lib/LLVMPasses/LLVMARCContract.cpp b/lib/LLVMPasses/LLVMARCContract.cpp index dede6b1702452..85a4c032ea95a 100644 --- a/lib/LLVMPasses/LLVMARCContract.cpp +++ b/lib/LLVMPasses/LLVMARCContract.cpp @@ -403,6 +403,11 @@ void SwiftARCContract::getAnalysisUsage(llvm::AnalysisUsage &AU) const { llvm::PreservedAnalyses SwiftARCContractPass::run(llvm::Function &F, llvm::FunctionAnalysisManager &AM) { + // Don't touch those functions that implement reference counting in the + // runtime. + if (!allowArcOptimizations(F.getName())) + return PreservedAnalyses::all(); + bool changed = SwiftARCContractImpl(F).run(); if (!changed) return PreservedAnalyses::all(); diff --git a/lib/LLVMPasses/LLVMARCOpts.cpp b/lib/LLVMPasses/LLVMARCOpts.cpp index 16d9d2874ff3c..ff7f8d6fe56be 100644 --- a/lib/LLVMPasses/LLVMARCOpts.cpp +++ b/lib/LLVMPasses/LLVMARCOpts.cpp @@ -1015,6 +1015,12 @@ void SwiftARCOpt::getAnalysisUsage(llvm::AnalysisUsage &AU) const { static bool runSwiftARCOpts(Function &F, SwiftRCIdentity &RC) { bool Changed = false; + + // Don't touch those functions that implement reference counting in the + // runtime. + if (!allowArcOptimizations(F.getName())) + return Changed; + ARCEntryPointBuilder B(F); // First thing: canonicalize swift_retain and similar calls so that nothing diff --git a/lib/LLVMPasses/LLVMARCOpts.h b/lib/LLVMPasses/LLVMARCOpts.h index a740695d40056..a6a0cfad289f9 100644 --- a/lib/LLVMPasses/LLVMARCOpts.h +++ b/lib/LLVMPasses/LLVMARCOpts.h @@ -26,6 +26,62 @@ enum RT_Kind { #include "LLVMSwift.def" }; +inline RT_Kind classifyFunctionName(StringRef name) { + return llvm::StringSwitch(name) +#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ + .Case("swift_" #TextualName, RT_ ## Name) +#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) \ + .Case("__swift_" #TextualName, RT_ ## Name) +#include "LLVMSwift.def" + + // Identify "Client" versions of reference counting entry points. +#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ + .Case("swift_" #TextualName "Client", RT_ ## Name) +#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) \ + .Case("__swift_" #TextualName "Client", RT_ ## Name) +#include "LLVMSwift.def" + + // Support non-atomic versions of reference counting entry points. +#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ + .Case("swift_nonatomic_" #TextualName, RT_ ## Name) +#define OBJC_FUNC(Name, MemBehavior, TextualName) \ + .Case("objc_nonatomic_" #TextualName, RT_ ## Name) +#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) +#include "LLVMSwift.def" + + .Default(RT_Unknown); +} + +/// Whether to allow ARC optimizations for a function with the given name. +inline bool allowArcOptimizations(StringRef name) { + switch (classifyFunctionName(name)) { + case RT_UnknownObjectRetainN: + case RT_BridgeRetainN: + case RT_RetainN: + case RT_UnknownObjectReleaseN: + case RT_BridgeReleaseN: + case RT_ReleaseN: + case RT_UnknownObjectRetain: + case RT_UnknownObjectRelease: + case RT_Retain: + case RT_ObjCRetain: + case RT_ObjCRelease: + case RT_RetainUnowned: + case RT_Release: + case RT_BridgeRetain: + case RT_BridgeRelease: + return false; + + case RT_Unknown: + case RT_NoMemoryAccessed: + case RT_CheckUnowned: + case RT_AllocObject: + case RT_FixLifetime: + case RT_EndBorrow: + return true; + } +} + /// Take a look at the specified instruction and classify it into what kind of /// runtime entrypoint it is, if any. inline RT_Kind classifyInstruction(const llvm::Instruction &I) { @@ -57,29 +113,7 @@ inline RT_Kind classifyInstruction(const llvm::Instruction &I) { if (F == nullptr) return RT_Unknown; - return llvm::StringSwitch(F->getName()) -#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ - .Case("swift_" #TextualName, RT_ ## Name) -#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) \ - .Case("__swift_" #TextualName, RT_ ## Name) -#include "LLVMSwift.def" - - // Identify "Client" versions of reference counting entry points. -#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ - .Case("swift_" #TextualName "Client", RT_ ## Name) -#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) \ - .Case("__swift_" #TextualName "Client", RT_ ## Name) -#include "LLVMSwift.def" - - // Support non-atomic versions of reference counting entry points. -#define SWIFT_FUNC(Name, MemBehavior, TextualName) \ - .Case("swift_nonatomic_" #TextualName, RT_ ## Name) -#define OBJC_FUNC(Name, MemBehavior, TextualName) \ - .Case("objc_nonatomic_" #TextualName, RT_ ## Name) -#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) -#include "LLVMSwift.def" - - .Default(RT_Unknown); + return classifyFunctionName(F->getName()); } } // end namespace swift diff --git a/stdlib/public/core/EmbeddedRuntime.swift b/stdlib/public/core/EmbeddedRuntime.swift index d9804a8ab19c3..badedc60645b7 100644 --- a/stdlib/public/core/EmbeddedRuntime.swift +++ b/stdlib/public/core/EmbeddedRuntime.swift @@ -149,14 +149,14 @@ func alignedAlloc(size: Int, alignment: Int) -> UnsafeMutableRawPointer? { return unsafe r } -@_cdecl("swift_coroFrameAlloc") +@c public func swift_coroFrameAlloc(_ size: Int, _ type: UInt) -> UnsafeMutableRawPointer? { return unsafe alignedAlloc( size: size, alignment: _swift_MinAllocationAlignment) } -@_cdecl("swift_slowAlloc") +@c public func swift_slowAlloc(_ size: Int, _ alignMask: Int) -> UnsafeMutableRawPointer? { let alignment: Int if alignMask == -1 { @@ -167,12 +167,12 @@ public func swift_slowAlloc(_ size: Int, _ alignMask: Int) -> UnsafeMutableRawPo return unsafe alignedAlloc(size: size, alignment: alignment) } -@_cdecl("swift_slowDealloc") +@c public func swift_slowDealloc(_ ptr: UnsafeMutableRawPointer, _ size: Int, _ alignMask: Int) { unsafe free(ptr) } -@_cdecl("swift_allocObject") +@c public func swift_allocObject(metadata: Builtin.RawPointer, requiredSize: Int, requiredAlignmentMask: Int) -> Builtin.RawPointer { return unsafe swift_allocObject(metadata: UnsafeMutablePointer(metadata), requiredSize: requiredSize, requiredAlignmentMask: requiredAlignmentMask)._rawValue } @@ -185,7 +185,7 @@ func swift_allocObject(metadata: UnsafeMutablePointer, requiredSi return unsafe object } -@_cdecl("swift_deallocUninitializedObject") +@c public func swift_deallocUninitializedObject(object: Builtin.RawPointer, allocatedSize: Int, allocatedAlignMask: Int) { unsafe swift_deallocObject( object: UnsafeMutablePointer(object), @@ -193,7 +193,7 @@ public func swift_deallocUninitializedObject(object: Builtin.RawPointer, allocat allocatedAlignMask: allocatedAlignMask) } -@_cdecl("swift_deallocObject") +@c public func swift_deallocObject(object: Builtin.RawPointer, allocatedSize: Int, allocatedAlignMask: Int) { unsafe swift_deallocObject(object: UnsafeMutablePointer(object), allocatedSize: allocatedSize, allocatedAlignMask: allocatedAlignMask) } @@ -202,7 +202,7 @@ func swift_deallocObject(object: UnsafeMutablePointer, allocatedSize unsafe free(UnsafeMutableRawPointer(object)) } -@_cdecl("swift_deallocClassInstance") +@c public func swift_deallocClassInstance(object: Builtin.RawPointer, allocatedSize: Int, allocatedAlignMask: Int) { unsafe swift_deallocClassInstance(object: UnsafeMutablePointer(object), allocatedSize: allocatedSize, allocatedAlignMask: allocatedAlignMask) } @@ -215,7 +215,7 @@ func swift_deallocClassInstance(object: UnsafeMutablePointer, alloca unsafe free(UnsafeMutableRawPointer(object)) } -@_cdecl("swift_deallocPartialClassInstance") +@c public func swift_deallocPartialClassInstance(object: Builtin.RawPointer, metadata: Builtin.RawPointer, allocatedSize: Int, allocatedAlignMask: Int) { unsafe swift_deallocPartialClassInstance(object: UnsafeMutablePointer(object), metadata: UnsafeMutablePointer(metadata), allocatedSize: allocatedSize, allocatedAlignMask: allocatedAlignMask) } @@ -229,7 +229,7 @@ func swift_deallocPartialClassInstance(object: UnsafeMutablePointer, } } -@_cdecl("swift_initStaticObject") +@c public func swift_initStaticObject(metadata: Builtin.RawPointer, object: Builtin.RawPointer) -> Builtin.RawPointer { return unsafe swift_initStaticObject(metadata: UnsafeMutablePointer(metadata), object: UnsafeMutablePointer(object))._rawValue } @@ -240,7 +240,7 @@ func swift_initStaticObject(metadata: UnsafeMutablePointer, objec return unsafe object } -@_cdecl("swift_initStackObject") +@c public func swift_initStackObject(metadata: Builtin.RawPointer, object: Builtin.RawPointer) -> Builtin.RawPointer { return unsafe swift_initStackObject(metadata: UnsafeMutablePointer(metadata), object: UnsafeMutablePointer(object))._rawValue } @@ -254,7 +254,7 @@ func swift_initStackObject(metadata: UnsafeMutablePointer, object @unsafe public var _emptyBoxStorage: (Int, Int) = (/*isa*/0, /*refcount*/-1) -@_cdecl("swift_allocEmptyBox") +@c public func swift_allocEmptyBox() -> Builtin.RawPointer { let box = unsafe Builtin.addressof(&_emptyBoxStorage) swift_retain(object: box) @@ -275,11 +275,11 @@ func isValidPointerForNativeRetain(object: Builtin.RawPointer) -> Bool { return true } -@_cdecl("swift_setDeallocating") +@c public func swift_setDeallocating(object: Builtin.RawPointer) { } -@_cdecl("swift_dynamicCastClass") +@c public func swift_dynamicCastClass(object: UnsafeMutableRawPointer, targetMetadata: UnsafeRawPointer) -> UnsafeMutableRawPointer? { let sourceObj = unsafe object.assumingMemoryBound(to: HeapObject.self) var type = unsafe _swift_embedded_get_heap_object_metadata_pointer(sourceObj).assumingMemoryBound(to: ClassMetadata.self) @@ -293,7 +293,7 @@ public func swift_dynamicCastClass(object: UnsafeMutableRawPointer, targetMetada return unsafe object } -@_cdecl("swift_dynamicCastClassUnconditional") +@c public func swift_dynamicCastClassUnconditional(object: UnsafeMutableRawPointer, targetMetadata: UnsafeRawPointer, file: UnsafePointer, line: CUnsignedInt, column: CUnsignedInt) -> UnsafeMutableRawPointer { guard let result = unsafe swift_dynamicCastClass(object: object, targetMetadata: targetMetadata) else { @@ -302,7 +302,7 @@ public func swift_dynamicCastClassUnconditional(object: UnsafeMutableRawPointer, return unsafe result } -@_cdecl("swift_isEscapingClosureAtFileLocation") +@c public func swift_isEscapingClosureAtFileLocation(object: Builtin.RawPointer, filename: UnsafePointer, filenameLength: Int32, line: Int32, column: Int32, verificationType: CUnsignedInt) -> Bool { let objectBits = UInt(Builtin.ptrtoint_Word(object)) if objectBits == 0 { return false } @@ -313,14 +313,14 @@ public func swift_isEscapingClosureAtFileLocation(object: Builtin.RawPointer, fi return false } -@_cdecl("swift_isUniquelyReferenced_native") +@c public func swift_isUniquelyReferenced_native(object: Builtin.RawPointer) -> Bool { if !isValidPointerForNativeRetain(object: object) { return false } return unsafe swift_isUniquelyReferenced_nonNull_native(object: UnsafeMutablePointer(object)) } -@_cdecl("swift_isUniquelyReferenced_nonNull_native") +@c public func swift_isUniquelyReferenced_nonNull_native(object: Builtin.RawPointer) -> Bool { return unsafe swift_isUniquelyReferenced_nonNull_native(object: UnsafeMutablePointer(object)) } @@ -330,7 +330,7 @@ func swift_isUniquelyReferenced_nonNull_native(object: UnsafeMutablePointer Builtin.RawPointer { if !isValidPointerForNativeRetain(object: object) { return object } @@ -340,7 +340,7 @@ public func swift_retain(object: Builtin.RawPointer) -> Builtin.RawPointer { } // Cannot use UnsafeMutablePointer? directly in the function argument or return value as it causes IRGen crashes -@_cdecl("swift_retain_n") +@c public func swift_retain_n(object: Builtin.RawPointer, n: UInt32) -> Builtin.RawPointer { if !isValidPointerForNativeRetain(object: object) { return object } @@ -359,20 +359,20 @@ func swift_retain_n_(object: UnsafeMutablePointer, n: UInt32) -> Uns return unsafe object } -@_cdecl("swift_bridgeObjectRetain") +@c @discardableResult public func swift_bridgeObjectRetain(object: Builtin.RawPointer) -> Builtin.RawPointer { return swift_bridgeObjectRetain_n(object: object, n: 1) } -@_cdecl("swift_bridgeObjectRetain_n") +@c public func swift_bridgeObjectRetain_n(object: Builtin.RawPointer, n: UInt32) -> Builtin.RawPointer { let objectBits = UInt(Builtin.ptrtoint_Word(object)) let untaggedObject = unsafe Builtin.inttoptr_Word((objectBits & HeapObject.bridgeObjectToPlainObjectMask)._builtinWordValue) return swift_retain_n(object: untaggedObject, n: n) } -@_cdecl("swift_release") +@c public func swift_release(object: Builtin.RawPointer) { if !isValidPointerForNativeRetain(object: object) { return } @@ -380,7 +380,7 @@ public func swift_release(object: Builtin.RawPointer) { unsafe swift_release_n_(object: o, n: 1) } -@_cdecl("swift_release_n") +@c public func swift_release_n(object: Builtin.RawPointer, n: UInt32) { if !isValidPointerForNativeRetain(object: object) { return } @@ -419,19 +419,19 @@ func swift_release_n_(object: UnsafeMutablePointer?, n: UInt32) { } } -@_cdecl("swift_bridgeObjectRelease") +@c public func swift_bridgeObjectRelease(object: Builtin.RawPointer) { swift_bridgeObjectRelease_n(object: object, n: 1) } -@_cdecl("swift_bridgeObjectRelease_n") +@c public func swift_bridgeObjectRelease_n(object: Builtin.RawPointer, n: UInt32) { let objectBits = UInt(Builtin.ptrtoint_Word(object)) let untaggedObject = unsafe Builtin.inttoptr_Word((objectBits & HeapObject.bridgeObjectToPlainObjectMask)._builtinWordValue) swift_release_n(object: untaggedObject, n: n) } -@_cdecl("swift_retainCount") +@c public func swift_retainCount(object: Builtin.RawPointer) -> Int { if !isValidPointerForNativeRetain(object: object) { return 0 } let o = unsafe UnsafeMutablePointer(object) @@ -478,12 +478,12 @@ fileprivate func storeRelaxed(_ atomic: UnsafeMutablePointer, newValue: Int /// Exclusivity checking -@_cdecl("swift_beginAccess") +@c public func swift_beginAccess(pointer: UnsafeMutableRawPointer, buffer: UnsafeMutableRawPointer, flags: UInt, pc: UnsafeMutableRawPointer) { // TODO: Add actual exclusivity checking. } -@_cdecl("swift_endAccess") +@c public func swift_endAccess(buffer: UnsafeMutableRawPointer) { // TODO: Add actual exclusivity checking. } @@ -492,7 +492,7 @@ public func swift_endAccess(buffer: UnsafeMutableRawPointer) { // Once -@_cdecl("swift_once") +@c public func swift_once(predicate: UnsafeMutablePointer, fn: (@convention(c) (UnsafeMutableRawPointer)->()), context: UnsafeMutableRawPointer) { let checkedLoadAcquire = { predicate in let value = unsafe loadAcquire(predicate) @@ -519,12 +519,12 @@ public func swift_once(predicate: UnsafeMutablePointer, fn: (@convention(c) // Misc -@_cdecl("swift_deletedMethodError") +@c public func swift_deletedMethodError() -> Never { Builtin.int_trap() } -@_silgen_name("swift_willThrow") // This is actually expected to be swiftcc (@_silgen_name and not @_cdecl). +@_silgen_name("swift_willThrow") // This is actually expected to be swiftcc (@_silgen_name and not @c). public func swift_willThrow() throws { } @@ -540,7 +540,7 @@ public func swift_stdlib_random(_ buf: UnsafeMutableRawPointer, _ nbytes: Int) { unsafe arc4random_buf(buf: buf, nbytes: nbytes) } -@_cdecl("swift_clearSensitive") +@c @inline(never) public func swift_clearSensitive(buf: UnsafeMutableRawPointer, nbytes: Int) { // TODO: use memset_s if available @@ -552,7 +552,6 @@ public func swift_clearSensitive(buf: UnsafeMutableRawPointer, nbytes: Int) { } } -@usableFromInline @inline(never) func _embeddedReportFatalError(prefix: StaticString, message: StaticString) { print(prefix, terminator: "") @@ -560,7 +559,6 @@ func _embeddedReportFatalError(prefix: StaticString, message: StaticString) { print(message) } -@usableFromInline @inline(never) func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString, file: StaticString, line: UInt) { print(file, terminator: ":") @@ -570,7 +568,6 @@ func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString print(message) } -@usableFromInline @inline(never) func _embeddedReportFatalErrorInFile(prefix: StaticString, message: UnsafeBufferPointer, file: StaticString, line: UInt) { print(file, terminator: ":") From 27b8b357d1e4409e33f81bd470f158a338e5511d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 13 Nov 2025 11:34:01 -0800 Subject: [PATCH 5/6] Retain the Swift calling convention when it's on an imported C declaration Fixes #69264. --- lib/SIL/IR/SILFunctionType.cpp | 27 +++++++++++++++++++++----- test/IRGen/c_calling_conventions.swift | 26 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 test/IRGen/c_calling_conventions.swift diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index d98e2c83003bf..efcd2a3070560 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4424,12 +4424,29 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { if (!c.hasDecl()) return SILFunctionTypeRepresentation::CFunctionPointer; - if (auto method = - dyn_cast_or_null(c.getDecl()->getClangDecl())) - return isa(method) || method->isStatic() - ? SILFunctionTypeRepresentation::CFunctionPointer - : SILFunctionTypeRepresentation::CXXMethod; + if (auto clangDecl = c.getDecl()->getClangDecl()) { + if (auto method = dyn_cast(clangDecl)) { + return isa(method) || method->isStatic() + ? SILFunctionTypeRepresentation::CFunctionPointer + : SILFunctionTypeRepresentation::CXXMethod; + } + + if (auto function = dyn_cast(clangDecl)) { + if (auto fnType = function->getType()->getAs()) { + switch (fnType->getCallConv()) { + case clang::CC_Swift: + return SILFunctionTypeRepresentation::Thin; + case clang::CC_C: + return SILFunctionTypeRepresentation::CFunctionPointer; + + default: + // Fall back to heuristics below. + break; + } + } + } + } // For example, if we have a function in a namespace: if (c.getDecl()->isImportAsMember()) diff --git a/test/IRGen/c_calling_conventions.swift b/test/IRGen/c_calling_conventions.swift new file mode 100644 index 0000000000000..805cd148ee4cd --- /dev/null +++ b/test/IRGen/c_calling_conventions.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend -I%t %t/caller.swift -emit-ir | %FileCheck %s + +//--- c_funcs.h + +__attribute__((swiftcall)) +extern void with_swiftcc(void); + +//--- module.modulemap + +module c_funcs { + header "c_funcs.h" +} + +//--- caller.swift + +import c_funcs + +func test() { + // CHECK: call swiftcc void @with_swiftcc() + with_swiftcc() +} +// CHECK: declare swiftcc void @with_swiftcc() + +test() From 661f5422ecd3777f5e7c7a662da75582bb91e5bb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 13 Nov 2025 16:35:02 -0800 Subject: [PATCH 6/6] Update test for Windows (cherry picked from commit 7517425398968924320f026d938eb3d3c1fd2697) --- test/IRGen/c_calling_conventions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/IRGen/c_calling_conventions.swift b/test/IRGen/c_calling_conventions.swift index 805cd148ee4cd..f33580e86f623 100644 --- a/test/IRGen/c_calling_conventions.swift +++ b/test/IRGen/c_calling_conventions.swift @@ -21,6 +21,6 @@ func test() { // CHECK: call swiftcc void @with_swiftcc() with_swiftcc() } -// CHECK: declare swiftcc void @with_swiftcc() +// CHECK: declare {{.*}}swiftcc void @with_swiftcc() test()