Skip to content

Commit

Permalink
IRGen: Use clang's codegen for protocol decls
Browse files Browse the repository at this point in the history
There are certain protocol method decls types that swift does not import
today.

```
@protocol Incomplete
- (id)getObjectFromVarArgs:(id)first, ...;
@EnD
```

Furthermore, the old method also emitted duplicate entries for protocols
methods when Swift synthesized methods for diagnosics.

We won't import this method into Swift. So if we emit protocol metadata
from swift delcs we would generate incomplete records.

rdar://60888524
  • Loading branch information
aschwaighofer committed Oct 26, 2022
1 parent e9520e1 commit ba9578d
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 20 deletions.
9 changes: 9 additions & 0 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/DeclObjC.h"

#include "Callee.h"
#include "ClassLayout.h"
Expand Down Expand Up @@ -2706,6 +2707,14 @@ llvm::Constant *irgen::emitObjCProtocolData(IRGenModule &IGM,
ProtocolDecl *proto) {
assert(proto->isObjC() && "not an objc protocol");
PrettyStackTraceDecl stackTraceRAII("emitting ObjC metadata for", proto);
if (llvm::Triple(IGM.Module.getTargetTriple()).isOSDarwin()) {
// Use the clang to generate the protocol metadata if there is a clang node.
if (auto clangDecl = proto->getClangDecl()) {
if (auto objcMethodDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl)) {
return IGM.emitClangProtocolObject(objcMethodDecl);
}
}
}
ClassDataBuilder builder(IGM, proto);
return builder.emitProtocol();
}
Expand Down
78 changes: 78 additions & 0 deletions lib/IRGen/GenObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "clang/AST/GlobalDecl.h"
#include "clang/Basic/CharInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/CodeGenABITypes.h"

#include "swift/AST/Decl.h"
#include "swift/AST/IRGenOptions.h"
Expand Down Expand Up @@ -395,6 +396,83 @@ IRGenModule::getObjCProtocolGlobalVars(ProtocolDecl *proto) {
return pair;
}

static std::pair<uint64_t, llvm::ConstantArray *>
getProtocolRefsList(llvm::Constant *protocol) {
// We expect to see a structure like this.
// @"_OBJC_PROTOCOL_$_MyProto" = weak hidden global %struct._protocol_t {
// i8* null,
// i8* getelementptr inbounds ([8 x i8],
// [8 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0),
// %struct._objc_protocol_list* bitcast (
// { i64, [2 x %struct._protocol_t*] }*
// @"_OBJC_$_PROTOCOL_REFS_MyProto" to %struct._objc_protocol_list*),
// %struct.__method_list_t* null,
// %struct.__method_list_t* null,
// %struct.__method_list_t* null,
// %struct.__method_list_t* null,
// %struct._prop_list_t* null, i32 96, i32 0,
// i8** getelementptr inbounds ([1 x i8*],
// [1 x i8*]* @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProto", i32 0, i32 0),
// i8* null, %struct._prop_list_t* null }, align 8
auto protocolVar = cast<llvm::GlobalVariable>(protocol);
auto protocolStruct =
cast<llvm::ConstantStruct>(protocolVar->getInitializer());
auto objCProtocolList = cast<llvm::Constant>(protocolStruct->getOperand(2));
if (objCProtocolList->isNullValue()) {
return std::make_pair(0, nullptr);
}
auto bitcast = cast<llvm::ConstantExpr>(objCProtocolList);
assert(bitcast->getOpcode() == llvm::Instruction::BitCast);
auto protocolRefsVar = cast<llvm::GlobalVariable>(bitcast->getOperand(0));
auto sizeListPair =
cast<llvm::ConstantStruct>(protocolRefsVar->getInitializer());
auto size =
cast<llvm::ConstantInt>(sizeListPair->getOperand(0))->getZExtValue();
auto protocolRefsList =
cast<llvm::ConstantArray>(sizeListPair->getOperand(1));
return std::make_pair(size, protocolRefsList);
}

static void updateProtocolRefs(IRGenModule &IGM,
const clang::ObjCProtocolDecl *objcProtocol,
llvm::Constant *protocol) {

// Get the clang importer to map ObjCProtocolDecl to ProtocolDecl.
auto &astContext = IGM.getSwiftModule()->getASTContext();
auto *clangImporter =
static_cast<ClangImporter *>(astContext.getClangModuleLoader());
assert(clangImporter && "Must have a clang importer");

// Get the array containining the protocol refs.
unsigned protocolRefsSize;
llvm::ConstantArray *protocolRefs;
std::tie(protocolRefsSize, protocolRefs) = getProtocolRefsList(protocol);
unsigned currentIdx = 0;
for (auto inheritedObjCProtocol : objcProtocol->protocols()) {
assert(currentIdx < protocolRefsSize);
auto oldVar = protocolRefs->getOperand(currentIdx);
// Map the objc protocol to swift protocol.
auto optionalDecl = clangImporter->importDeclCached(inheritedObjCProtocol);
auto inheritedSwiftProtocol = cast<ProtocolDecl>(*optionalDecl);
// Get the objc protocol record we use in Swift.
auto record = IGM.getAddrOfObjCProtocolRecord(inheritedSwiftProtocol,
NotForDefinition);
auto newOpd = llvm::ConstantExpr::getBitCast(record, oldVar->getType());
if (newOpd != oldVar)
oldVar->replaceAllUsesWith(newOpd);
++currentIdx;
}
assert(currentIdx == protocolRefsSize);
}

llvm::Constant *IRGenModule::emitClangProtocolObject(
const clang::ObjCProtocolDecl *objcProtocol) {
auto clangProto =
clang::CodeGen::emitObjCProtocolObject(getClangCGM(), objcProtocol);
updateProtocolRefs(*this, objcProtocol, clangProto);
return clangProto;
}

void IRGenModule::emitLazyObjCProtocolDefinition(ProtocolDecl *proto) {
// Emit the real definition.
auto record = cast<llvm::GlobalVariable>(emitObjCProtocolData(*this, proto));
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace clang {
class Decl;
class GlobalDecl;
class Type;
class ObjCProtocolDecl;
class PointerAuthSchema;
namespace CodeGen {
class CGFunctionInfo;
Expand Down Expand Up @@ -1083,6 +1084,9 @@ class IRGenModule {
SILLocation diagLoc);
llvm::Constant *getAddrOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaqueType,
ConstantInit forDefinition);
llvm::Constant *
emitClangProtocolObject(const clang::ObjCProtocolDecl *objcProtocol);

ConstantReference getConstantReferenceForProtocolDescriptor(ProtocolDecl *proto);

ConstantIntegerLiteral getConstantIntegerLiteral(APInt value);
Expand Down
17 changes: 12 additions & 5 deletions test/Concurrency/objc_async_protocol_irgen.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-availability-checking -import-objc-header %S/Inputs/Delegate.h %s -emit-ir -o - | %FileCheck %s -DALIGNMENT=%target-alignment
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-availability-checking -import-objc-header %S/Inputs/Delegate.h %s -emit-ir -o - | %FileCheck %s -DALIGNMENT=%target-alignment --check-prefix=CHECK-%is-darwin
// REQUIRES: concurrency
// REQUIRES: objc_interop

Expand All @@ -9,7 +9,14 @@ let anyObject: AnyObject = (MyAsyncProtocol.self as AnyObject) // or something l
// Make sure we don't emit 2 copies of methods, due to a completion-handler
// version and another due to an async based version.

// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol = weak hidden constant
// CHECK-SAME: selector_data(myAsyncMethod:)
// CHECK-NOT: selector_data(myAsyncMethod:)
// CHECK-SAME: align [[ALIGNMENT]]
// CHECK-isNotDarwin-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol = weak hidden constant
// CHECK-isNotDarwin-SAME: selector_data(myAsyncMethod:)
// CHECK-isNotDarwin-NOT: selector_data(myAsyncMethod:)
// CHECK-isNotDarwin-SAME: align [[ALIGNMENT]]


// CHECK-isDarwin: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [15 x i8] c"myAsyncMethod:\00"
// CHECK-isDarwin: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol" =
// CHECK-isDarwin-SAME: [1 x %struct._objc_method]
// CHECK-isDarwin-SAME: @OBJC_METH_VAR_NAME_
// CHECK-isDarwin-SAME: align [[ALIGNMENT]]
13 changes: 9 additions & 4 deletions test/IRGen/generic_casts.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %build-irgen-test-overlays
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -enable-objc-interop -disable-objc-attr-requires-foundation-module | %FileCheck %s
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -enable-objc-interop -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-os

// REQUIRES: CPU=x86_64

Expand All @@ -12,9 +12,14 @@ import gizmo
// CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$__TtP13generic_casts10ObjCProto1_" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL__TtP13generic_casts10ObjCProto1_ to i8*), section {{"__DATA,__objc_protolist,coalesced,no_dead_strip"|"objc_protolist"|".objc_protolist\$B"}}
// CHECK: @"\01l_OBJC_PROTOCOL_REFERENCE_$__TtP13generic_casts10ObjCProto1_" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL__TtP13generic_casts10ObjCProto1_ to i8*), section {{"__DATA,__objc_protorefs,coalesced,no_dead_strip"|"objc_protorefs"|".objc_protorefs\$B"}}

// CHECK: @_PROTOCOL_NSRuncing = weak hidden constant
// CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL_NSRuncing to i8*), section {{"__DATA,__objc_protolist,coalesced,no_dead_strip"|"objc_protolist"|".objc_protolist\$B"}}
// CHECK: @"\01l_OBJC_PROTOCOL_REFERENCE_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL_NSRuncing to i8*), section {{"__DATA,__objc_protorefs,coalesced,no_dead_strip"|"objc_protorefs"|".objc_protorefs\$B"}}
// CHECK-macosx: @"_OBJC_PROTOCOL_$_NSRuncing" = weak hidden global
// CHECK-macosx: @"\01l_OBJC_LABEL_PROTOCOL_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @"_OBJC_PROTOCOL_$_NSRuncing" to i8*), section {{"__DATA,__objc_protolist,coalesced,no_dead_strip"|"objc_protolist"|".objc_protolist\$B"}}
// CHECK-macosx: @"\01l_OBJC_PROTOCOL_REFERENCE_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @"_OBJC_PROTOCOL_$_NSRuncing" to i8*), section {{"__DATA,__objc_protorefs,coalesced,no_dead_strip"|"objc_protorefs"|".objc_protorefs\$B"}}


// CHECK-linux: @_PROTOCOL_NSRuncing = weak hidden constant
// CHECK-linux: @"\01l_OBJC_LABEL_PROTOCOL_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL_NSRuncing to i8*), section {{"__DATA,__objc_protolist,coalesced,no_dead_strip"|"objc_protolist"|".objc_protolist\$B"}}
// CHECK-linux: @"\01l_OBJC_PROTOCOL_REFERENCE_$_NSRuncing" = weak hidden global i8* bitcast ({{.*}} @_PROTOCOL_NSRuncing to i8*), section {{"__DATA,__objc_protorefs,coalesced,no_dead_strip"|"objc_protorefs"|".objc_protorefs\$B"}}

// CHECK: @_PROTOCOLS__TtC13generic_casts10ObjCClass2 = internal constant { i64, [1 x i8*] } {
// CHECK: i64 1,
Expand Down
5 changes: 3 additions & 2 deletions test/IRGen/objc_protocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %build-irgen-test-overlays
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -emit-module -o %t %S/Inputs/objc_protocols_Bas.swift
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module > %t/out.ir
// RUN: %FileCheck --input-file=%t/out.ir %s
// RUN: %FileCheck --input-file=%t/out.ir %s --check-prefix=CHECK --check-prefix=CHECK-%target-os

// REQUIRES: PTRSIZE=64
// REQUIRES: objc_interop
Expand Down Expand Up @@ -121,7 +121,8 @@ protocol InheritingProtocol : BaseProtocol { }
// CHECK: @_PROTOCOLS__TtC14objc_protocols17ImplementingClass {{.*}} @_PROTOCOL__TtP14objc_protocols12BaseProtocol_
class ImplementingClass : InheritingProtocol { }

// CHECK: @_PROTOCOL_PROTOCOLS_NSDoubleInheritedFunging = weak hidden constant{{.*}}i64 2{{.*}} @_PROTOCOL_NSFungingAndRuncing {{.*}}@_PROTOCOL_NSFunging
// CHECK-linux: @_PROTOCOL_PROTOCOLS_NSDoubleInheritedFunging = weak hidden constant{{.*}}i64 2{{.*}} @_PROTOCOL_NSFungingAndRuncing {{.*}}@_PROTOCOL_NSFunging
// CHECK-macosx: @"_OBJC_$_PROTOCOL_REFS_NSDoubleInheritedFunging" = internal global{{.*}}i64 2{{.*}} @"_OBJC_PROTOCOL_$_NSFungingAndRuncing"{{.*}} @"_OBJC_PROTOCOL_$_NSFunging"

// -- Force generation of witness for Zim.
// CHECK: define hidden swiftcc { %objc_object*, i8** } @"$s14objc_protocols22mixed_heritage_erasure{{[_0-9a-zA-Z]*}}F"
Expand Down
5 changes: 3 additions & 2 deletions test/IRGen/objc_type_encoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,6 @@ class C: P {
func stuff() {}
}

// CHECK-macosx: [[ENC5:@.*]] = private unnamed_addr constant [9 x i8] c"Vv16@0:8\00"
// CHECK-macosx: @_PROTOCOL_INSTANCE_METHODS_P = {{.*}}@"\01L_selector_data(stuff)"{{.*}}[[ENC5]]{{.*}}
// CHECK-macosx: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [6 x i8] c"stuff\00"
// CHECK-macosx: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [9 x i8] c"Vv16@0:8\00"
// CHECK-macosx: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_P" = {{.*}}@OBJC_METH_VAR_NAME_{{.*}}@OBJC_METH_VAR_TYPE_{{.*}}
5 changes: 5 additions & 0 deletions test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,11 @@ config.substitutions.append(('%target-ptrsize', run_ptrsize))
config.substitutions.append(('%target-vendor', run_vendor))
config.substitutions.append(('%target-alignment', "%d" % (int(run_ptrsize)/8)))

if platform.system() == 'Darwin':
config.substitutions.append(('%is-darwin', 'isDarwin'))
else:
config.substitutions.append(('%is-darwin', 'isNotDarwin'))

# Enable Darwin SDK-dependent tests if we have an SDK.
# On Linux, assume that SDK path does not point to the Darwin SDK.
if config.variant_sdk != "":
Expand Down
24 changes: 17 additions & 7 deletions validation-test/IRGen/issue-49393.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
// RUN: %target-swift-frontend -emit-ir %s -module-name M -import-objc-header %S/Inputs/issue-49393.h | %FileCheck %s
// RUN: %target-swift-frontend -emit-ir %s -module-name M -import-objc-header %S/Inputs/issue-49393.h | %FileCheck %s --check-prefix=CHECK-%is-darwin --check-prefix=CHECK
// REQUIRES: objc_interop

// https://github.com/apple/swift/issues/49393

import Foundation

// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey)"
// CHECK: [[OBJC_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey\00"
// CHECK: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
// CHECK: @_PROTOCOL_PROPERTIES_MyJSONSerializing =
// CHECK-SAME: [[OBJC_PROPNAME]]
// CHECK-SAME: [[PROPKIND]]
// CHECK-isDarwin: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [26 x i8] c"JSONKeyPathsByPropertyKey\00"
// CHECK-isDarwin: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing" = {{.*}} @OBJC_METH_VAR_NAME_
// CHECK-isDarwin: @OBJC_PROP_NAME_ATTR_ = private unnamed_addr constant [26 x i8] c"JSONKeyPathsByPropertyKey\00"
// CHECK-isDarwin: @OBJC_PROP_NAME_ATTR_.1 = private unnamed_addr constant [21 x i8] c"T@\22NSDictionary\22,R,C\00"
// CHECK-isDarwin: @"_OBJC_$_PROP_LIST_MyJSONSerializing" {{.*}} [1 x %struct._prop_t] }
// CHECK-isDarwin-SAME: @OBJC_PROP_NAME_ATTR_
// CHECK-isDarwin-SAME: @OBJC_PROP_NAME_ATTR_.1

// CHECK-isNotDarwin-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey)"
// CHECK-isNotDarwin: [[OBJC_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey\00"
// CHECK-isNotDarwin: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
// CHECK-isNotDarwin: @_PROTOCOL_PROPERTIES_MyJSONSerializing =
// CHECK-isNotDarwin-SAME: [[OBJC_PROPNAME]]
// CHECK-isNotDarwin-SAME: [[PROPKIND]]

// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS__TtP1M18MyJSONSerializing2_ = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey2)"
// CHECK: [[SWIFT_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey2\00"

// CHECK-isDarwin: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
// CHECK: @_PROTOCOL_PROPERTIES__TtP1M18MyJSONSerializing2_ =
// CHECK-SAME: [[SWIFT_PROPNAME]]
// CHECK-SAME: [[PROPKIND]]
Expand Down

0 comments on commit ba9578d

Please sign in to comment.