Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3107,7 +3107,7 @@ class ExternAttr : public DeclAttribute {
StringRef getCName(const FuncDecl *forDecl) const;

/// Find an ExternAttr with the given kind in the given DeclAttributes.
static ExternAttr *find(DeclAttributes &attrs, ExternKind kind);
static const ExternAttr *find(const DeclAttributes &attrs, ExternKind kind);

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DeclAttrKind::Extern;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
std::optional<ObjCSelector>
getExplicitObjCName(bool allowInvalid = false) const;

/// Whether this declaration is exposed to C and does not expose a Swift
/// counterpart.
bool hasOnlyCEntryPoint() const;

/// True if this declaration provides an implementation for an imported
/// Objective-C declaration. This implies various restrictions and special
/// behaviors for it and, if it's an extension, its members.
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3270,8 +3270,8 @@ StringRef ExternAttr::getCName(const FuncDecl *D) const {
return D->getBaseIdentifier().str();
}

ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) {
for (DeclAttribute *attr : attrs) {
const ExternAttr *ExternAttr::find(const DeclAttributes &attrs, ExternKind kind) {
for (const DeclAttribute *attr : attrs) {
if (auto *externAttr = dyn_cast<ExternAttr>(attr)) {
if (externAttr->getExternKind() == kind)
return externAttr;
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2256,6 +2256,20 @@ ExtensionDecl::isAddingConformanceToInvertible() const {
return std::nullopt;
}

bool Decl::hasOnlyCEntryPoint() const {
if (ExternAttr::find(getAttrs(), ExternKind::C))
return true;

if (auto cdeclAttr = getAttrs().getAttribute<CDeclAttr>()) {
// The @c syntax only provides C entrypoints, not Swift ones. The historical
// @_cdecl introduces both Swift and C entrypoints.
if (!cdeclAttr->Underscored)
return true;
}

return false;
}

bool Decl::isObjCImplementation() const {
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
}
Expand Down
20 changes: 16 additions & 4 deletions lib/SIL/IR/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ bool swift::requiresForeignToNativeThunk(ValueDecl *vd) {
if (proto->isObjC())
return true;

// If there is only a C entrypoint from a Swift function, we will need
// foreign-to-native thunks to deal with them.
if (vd->hasOnlyCEntryPoint())
return true;

if (auto fd = dyn_cast<FuncDecl>(vd))
return fd->hasClangNode();

Expand All @@ -111,7 +116,7 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) {
if (vd->hasClangNode())
return true;

if (ExternAttr::find(vd->getAttrs(), ExternKind::C))
if (vd->hasOnlyCEntryPoint())
return true;

if (auto *accessor = dyn_cast<AccessorDecl>(vd)) {
Expand All @@ -131,7 +136,9 @@ SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign,
bool isRuntimeAccessible,
SILDeclRef::BackDeploymentKind backDeploymentKind,
AutoDiffDerivativeFunctionIdentifier *derivativeId)
: loc(vd), kind(kind), isForeign(isForeign), distributedThunk(isDistributedThunk),
: loc(vd), kind(kind),
isForeign(isForeign),
distributedThunk(isDistributedThunk),
isKnownToBeLocal(isKnownToBeLocal),
isRuntimeAccessible(isRuntimeAccessible),
backDeploymentKind(backDeploymentKind), defaultArgIndex(0),
Expand Down Expand Up @@ -470,7 +477,8 @@ static LinkageLimit getLinkageLimit(SILDeclRef constant) {
// Native-to-foreign thunks for methods are always just private, since
// they're anchored by Objective-C metadata.
auto &attrs = fn->getAttrs();
if (constant.isNativeToForeignThunk() && !attrs.hasAttribute<CDeclAttr>()) {
if (constant.isNativeToForeignThunk() &&
!(attrs.hasAttribute<CDeclAttr>() && !fn->hasOnlyCEntryPoint())) {
auto isTopLevel = fn->getDeclContext()->isModuleScopeContext();
return isTopLevel ? Limit::OnDemand : Limit::Private;
}
Expand Down Expand Up @@ -1268,6 +1276,10 @@ bool SILDeclRef::isNativeToForeignThunk() const {
if (getDecl()->getAttrs().hasAttribute<ExternAttr>())
return false;

// No thunk is required if the decl directly exposes a C entry point.
if (getDecl()->hasOnlyCEntryPoint())
return false;

// Only certain kinds of SILDeclRef can expose native-to-foreign thunks.
return kind == Kind::Func || kind == Kind::Initializer ||
kind == Kind::Deallocator;
Expand Down Expand Up @@ -1433,7 +1445,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {

// Use a given cdecl name for native-to-foreign thunks.
if (getDecl()->getAttrs().hasAttribute<CDeclAttr>())
if (isNativeToForeignThunk()) {
if (isNativeToForeignThunk() || isForeign) {
// If this is an @implementation @_cdecl, mangle it like the clang
// function it implements.
if (auto objcInterface = getDecl()->getImplementedObjCDecl()) {
Expand Down
12 changes: 11 additions & 1 deletion lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4899,8 +4899,18 @@ getAbstractionPatternForConstant(TypeConverter &converter, ASTContext &ctx,
return AbstractionPattern(fnType);

const clang::Decl *clangDecl = bridgedFn->getClangDecl();
if (!clangDecl)
if (!clangDecl) {
// If this function only has a C entrypoint, create a Clang type to
// use when referencing it.
if (bridgedFn->hasOnlyCEntryPoint()) {
auto clangType = ctx.getClangFunctionType(
fnType->getParams(), fnType->getResult(),
FunctionTypeRepresentation::CFunctionPointer);
return AbstractionPattern(fnType, clangType);
}

return AbstractionPattern(fnType);
}

// Don't implicitly turn non-optional results to optional if
// we're going to apply a foreign error convention that checks
Expand Down
12 changes: 7 additions & 5 deletions lib/SIL/IR/SILSymbolVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
}
}

addFunction(SILDeclRef(AFD));
addFunction(SILDeclRef(AFD).asForeign(AFD->hasOnlyCEntryPoint()));

ASSERT(ABIRoleInfo(AFD).providesAPI()
&& "SILSymbolVisitorImpl visiting ABI-only decl?");
Expand All @@ -499,10 +499,12 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
Visitor.addDynamicFunction(AFD, *dynKind);
}

if (AFD->getAttrs().hasAttribute<CDeclAttr>()) {
// A @_cdecl("...") function has an extra symbol, with the name from the
// attribute.
addFunction(SILDeclRef(AFD).asForeign());
if (auto cdeclAttr = AFD->getAttrs().getAttribute<CDeclAttr>()) {
if (cdeclAttr->Underscored) {
// A @_cdecl("...") function has an extra symbol, with the name from the
// attribute.
addFunction(SILDeclRef(AFD).asForeign());
}
}

if (auto distributedThunk = AFD->getDistributedThunk()) {
Expand Down
12 changes: 7 additions & 5 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1559,10 +1559,12 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {

// If the declaration is exported as a C function, emit its native-to-foreign
// thunk too, if it wasn't already forced.
if (AFD->getAttrs().hasAttribute<CDeclAttr>()) {
auto thunk = SILDeclRef(AFD).asForeign();
if (!hasFunction(thunk))
emitNativeToForeignThunk(thunk);
if (auto cdeclAttr = AFD->getAttrs().getAttribute<CDeclAttr>()) {
if (cdeclAttr->Underscored) {
auto thunk = SILDeclRef(AFD).asForeign();
if (!hasFunction(thunk))
emitNativeToForeignThunk(thunk);
}
}

emitDistributedThunkForDecl(AFD);
Expand Down Expand Up @@ -1591,7 +1593,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) {

if (shouldEmitFunctionBody(fd)) {
Types.setCaptureTypeExpansionContext(SILDeclRef(fd), M);
emitOrDelayFunction(SILDeclRef(decl));
emitOrDelayFunction(SILDeclRef(decl, fd->hasOnlyCEntryPoint()));
}
}

Expand Down
3 changes: 0 additions & 3 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,9 +996,6 @@ emitRValueForDecl(SILLocation loc, ConcreteDeclRef declRef, Type ncRefType,
// If the referenced decl isn't a VarDecl, it should be a constant of some
// sort.
SILDeclRef silDeclRef(decl);
if (ExternAttr::find(decl->getAttrs(), ExternKind::C))
silDeclRef = silDeclRef.asForeign();

assert(silDeclRef.getParameterListCount() == 1);
auto substType = cast<AnyFunctionType>(refType);
auto typeContext = getFunctionTypeInfo(substType);
Expand Down
5 changes: 0 additions & 5 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2565,11 +2565,6 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const {
infoBuilder = infoBuilder.withSendingResult();
}

if (ExternAttr::find(D->getAttrs(), ExternKind::C)) {
infoBuilder = infoBuilder.withRepresentation(
FunctionTypeRepresentation::CFunctionPointer);
}

// Lifetime dependencies only apply to the outer function type.
if (!hasSelf && lifetimeDependenceInfo.has_value()) {
infoBuilder =
Expand Down
6 changes: 2 additions & 4 deletions test/IRGen/cdecl_implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ public func fn() {
/// implFunc(_:)
// CHECK-LABEL: define{{.*}} void @implFunc

// FIXME: We'd like this to be internal or hidden, not public.
// CHECK: define{{.*}} swiftcc void @"$s20cdecl_implementation8implFuncyys5Int32VF"
// CHECK-NOT: define{{.*}} swiftcc void @"$s20cdecl_implementation8implFuncyys5Int32VF"

/// inplFuncCName(_:)
// CHECK-LABEL: define{{.*}} void @"\01_implFuncAsmName"

// FIXME: We'd like this to be internal or hidden, not public.
// CHECK: define{{.*}} swiftcc void @"$s20cdecl_implementation13implFuncCNameyys5Int32VF"
// CHECK-NOT: define{{.*}} swiftcc void @"$s20cdecl_implementation13implFuncCNameyys5Int32VF"

/// fn()
// CHECK-LABEL: define{{.*}} swiftcc void @"$s20cdecl_implementation2fnyyF"
Expand Down
46 changes: 26 additions & 20 deletions test/SILGen/cdecl-official.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,55 @@

// REQUIRES: swift_feature_CDecl

// CHECK-LABEL: sil hidden [thunk] [ossa] @pear : $@convention(c)
// CHECK: function_ref @$s5cdecl5apple{{[_0-9a-zA-Z]*}}F
// CHECK-LABEL: sil hidden [ossa] @$s5cdecl5apple{{[_0-9a-zA-Z]*}}F
// CHECK-LABEL: sil hidden [ossa] @pear : $@convention(c) (@convention(c) (Int) -> Int) -> () {
@c(pear)
func apple(_ f: @convention(c) (Int) -> Int) { }

func acceptSwiftFunc(_ f: (Int) -> Int) { }

// CHECK-LABEL: sil hidden [ossa] @$s5cdecl16forceCEntryPoint{{[_0-9a-zA-Z]*}}F
// CHECK: function_ref @grapefruit : $@convention(c) (Int) -> Int
// CHECK: function_ref apple(_:)
// CHECK: function_ref @$s5cdecl5appleyyS2iXCF : $@convention(thin) (@convention(c) (Int) -> Int) -> ()
// FIXME should it be function_ref @apple?
// CHECK: [[GRAPEFRUIT:%[0-9]+]] = function_ref @grapefruit : $@convention(c) (Int) -> Int
// CHECK: [[PEAR:%[0-9]+]] = function_ref @pear : $@convention(c) (@convention(c) (Int) -> Int) -> ()
// CHECK: apply [[PEAR]]([[GRAPEFRUIT]])
func forceCEntryPoint() {
apple(orange)
}

// CHECK-LABEL: sil hidden [thunk] [ossa] @grapefruit : $@convention(c)
// CHECK: function_ref @$s5cdecl6orange{{[_0-9a-zA-Z]*}}F
// CHECK-LABEL: sil hidden [ossa] @$s5cdecl6orange{{[_0-9a-zA-Z]*}}F
// CHECK-LABEL: sil hidden [ossa] @grapefruit : $@convention(c) (Int) -> Int {
@c(grapefruit)
func orange(_ x: Int) -> Int {
return x
}

// CHECK-LABEL: sil [serialized] [thunk] [ossa] @cauliflower : $@convention(c)
// CHECK: function_ref @$s5cdecl8broccoli{{[_0-9a-zA-Z]*}}F
// CHECK-LABEL: sil [ossa] @$s5cdecl8broccoli{{[_0-9a-zA-Z]*}}F
// FIXME should it be `sil hidden`?
// CHECK-LABEL: sil hidden [ossa] @$s5cdecl13requiresThunkyyF : $@convention(thin) () -> () {
// CHECK: [[ORANGE:%[0-9]+]] = function_ref @$s5cdecl6orangeyS2iFTO : $@convention(thin) (Int) -> Int
// CHECK: [[THICK_ORANGE:%[0-9]+]] = thin_to_thick_function [[ORANGE]] to $@callee_guaranteed (Int) -> Int
// CHECK: [[NOESCAPE_ORANGE:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[THICK_ORANGE]] to $@noescape @callee_guaranteed (Int) -> Int
// CHECK: [[ACCEPT:%[0-9]+]] = function_ref @$s5cdecl15acceptSwiftFuncyyS2iXEF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> ()
// CHECK: apply [[ACCEPT]]([[NOESCAPE_ORANGE]])
func requiresThunk() {
acceptSwiftFunc(orange)
}

// CHECK-LABEL: sil [serialized] [ossa] @cauliflower : $@convention(c) (Int) -> Int {
// CHECK-NOT: apply
// CHECK: return
@c(cauliflower)
public func broccoli(_ x: Int) -> Int {
return x
}

// CHECK-LABEL: sil private [thunk] [ossa] @collard_greens : $@convention(c)
// CHECK: function_ref @$s5cdecl4kale[[PRIVATE:.*]]
// CHECK: sil private [ossa] @$s5cdecl4kale[[PRIVATE:.*]]
// CHECK-LABEL: sil private [ossa] @collard_greens : $@convention(c) (Int) -> Int {
// CHECK-NOT: apply
// CHECK: return
@c(collard_greens)
private func kale(_ x: Int) -> Int {
return x
}

// CHECK-LABEL: sil private [thunk] [ossa] @defaultName : $@convention(c)
// CHECK: function_ref @$s5cdecl11defaultName[[PRIVATE:.*]]
// CHECK: sil private [ossa] @$s5cdecl11defaultName[[PRIVATE:.*]]
// CHECK-LABEL: sil private [ossa] @defaultName : $@convention(c) (Int) -> Int {
// CHECK-NOT: apply
// CHECK: return
@c
private func defaultName(_ x: Int) -> Int {
return x
Expand Down
9 changes: 4 additions & 5 deletions test/SILGen/extern_c.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,11 @@ func main() {
// CHECK-DAG: apply [[F7]]([[DEFAULT_V]]) : $@convention(c) (Int) -> ()
defaultArg()

// CHECK-DAG: [[CREF:%[0-9]+]] = function_ref @$s8extern_c16publicVisibilityyS2iFTo : $@convention(c) (Int) -> Int
// CHECK-DAG: [[THUNK:%[0-9]+]] = function_ref @$sS2iIetCyd_S2iIegyd_TR : $@convention(thin) (Int, @convention(c) (Int) -> Int) -> Int
// CHECK-DAG: [[APPLIED:%[0-9]+]] = partial_apply [callee_guaranteed] [[THUNK]]([[CREF]]) : $@convention(thin) (Int, @convention(c) (Int) -> Int) -> Int
// CHECK-DAG: [[NOESCAPE_APPLIED:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[APPLIED]] to $@noescape @callee_guaranteed (Int) -> Int
// CHECK-DAG: [[CREF:%[0-9]+]] = function_ref @$s8extern_c16publicVisibilityyS2iFTO : $@convention(thin) (Int) -> Int
// CHECK-DAG: [[THICK_CREF:%[0-9]+]] = thin_to_thick_function [[CREF]] to $@callee_guaranteed (Int) -> Int
// CHECK-DAG: [[NOESCAPE_CREF:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[THICK_CREF]] to $@noescape @callee_guaranteed (Int) -> Int
// CHECK-DAG: [[CALL_ME:%[0-9]+]] = function_ref @$s8extern_c6callMe4bodyS3iXE_tF
// CHECK-DAG: apply [[CALL_ME]]([[NOESCAPE_APPLIED]]) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> Int
// CHECK-DAG: apply [[CALL_ME]]([[NOESCAPE_CREF]]) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> Int
_ = callMe(body: publicVisibility)
}

Expand Down