Skip to content

Commit ba9578d

Browse files
committed
IRGen: Use clang's codegen for protocol decls
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
1 parent e9520e1 commit ba9578d

File tree

9 files changed

+140
-20
lines changed

9 files changed

+140
-20
lines changed

lib/IRGen/GenClass.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "llvm/IR/Function.h"
4444
#include "llvm/IR/GlobalVariable.h"
4545
#include "llvm/Support/raw_ostream.h"
46+
#include "clang/AST/DeclObjC.h"
4647

4748
#include "Callee.h"
4849
#include "ClassLayout.h"
@@ -2706,6 +2707,14 @@ llvm::Constant *irgen::emitObjCProtocolData(IRGenModule &IGM,
27062707
ProtocolDecl *proto) {
27072708
assert(proto->isObjC() && "not an objc protocol");
27082709
PrettyStackTraceDecl stackTraceRAII("emitting ObjC metadata for", proto);
2710+
if (llvm::Triple(IGM.Module.getTargetTriple()).isOSDarwin()) {
2711+
// Use the clang to generate the protocol metadata if there is a clang node.
2712+
if (auto clangDecl = proto->getClangDecl()) {
2713+
if (auto objcMethodDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl)) {
2714+
return IGM.emitClangProtocolObject(objcMethodDecl);
2715+
}
2716+
}
2717+
}
27092718
ClassDataBuilder builder(IGM, proto);
27102719
return builder.emitProtocol();
27112720
}

lib/IRGen/GenObjC.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "clang/AST/GlobalDecl.h"
2525
#include "clang/Basic/CharInfo.h"
2626
#include "clang/CodeGen/CGFunctionInfo.h"
27+
#include "clang/CodeGen/CodeGenABITypes.h"
2728

2829
#include "swift/AST/Decl.h"
2930
#include "swift/AST/IRGenOptions.h"
@@ -395,6 +396,83 @@ IRGenModule::getObjCProtocolGlobalVars(ProtocolDecl *proto) {
395396
return pair;
396397
}
397398

399+
static std::pair<uint64_t, llvm::ConstantArray *>
400+
getProtocolRefsList(llvm::Constant *protocol) {
401+
// We expect to see a structure like this.
402+
// @"_OBJC_PROTOCOL_$_MyProto" = weak hidden global %struct._protocol_t {
403+
// i8* null,
404+
// i8* getelementptr inbounds ([8 x i8],
405+
// [8 x i8]* @OBJC_CLASS_NAME_, i32 0, i32 0),
406+
// %struct._objc_protocol_list* bitcast (
407+
// { i64, [2 x %struct._protocol_t*] }*
408+
// @"_OBJC_$_PROTOCOL_REFS_MyProto" to %struct._objc_protocol_list*),
409+
// %struct.__method_list_t* null,
410+
// %struct.__method_list_t* null,
411+
// %struct.__method_list_t* null,
412+
// %struct.__method_list_t* null,
413+
// %struct._prop_list_t* null, i32 96, i32 0,
414+
// i8** getelementptr inbounds ([1 x i8*],
415+
// [1 x i8*]* @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProto", i32 0, i32 0),
416+
// i8* null, %struct._prop_list_t* null }, align 8
417+
auto protocolVar = cast<llvm::GlobalVariable>(protocol);
418+
auto protocolStruct =
419+
cast<llvm::ConstantStruct>(protocolVar->getInitializer());
420+
auto objCProtocolList = cast<llvm::Constant>(protocolStruct->getOperand(2));
421+
if (objCProtocolList->isNullValue()) {
422+
return std::make_pair(0, nullptr);
423+
}
424+
auto bitcast = cast<llvm::ConstantExpr>(objCProtocolList);
425+
assert(bitcast->getOpcode() == llvm::Instruction::BitCast);
426+
auto protocolRefsVar = cast<llvm::GlobalVariable>(bitcast->getOperand(0));
427+
auto sizeListPair =
428+
cast<llvm::ConstantStruct>(protocolRefsVar->getInitializer());
429+
auto size =
430+
cast<llvm::ConstantInt>(sizeListPair->getOperand(0))->getZExtValue();
431+
auto protocolRefsList =
432+
cast<llvm::ConstantArray>(sizeListPair->getOperand(1));
433+
return std::make_pair(size, protocolRefsList);
434+
}
435+
436+
static void updateProtocolRefs(IRGenModule &IGM,
437+
const clang::ObjCProtocolDecl *objcProtocol,
438+
llvm::Constant *protocol) {
439+
440+
// Get the clang importer to map ObjCProtocolDecl to ProtocolDecl.
441+
auto &astContext = IGM.getSwiftModule()->getASTContext();
442+
auto *clangImporter =
443+
static_cast<ClangImporter *>(astContext.getClangModuleLoader());
444+
assert(clangImporter && "Must have a clang importer");
445+
446+
// Get the array containining the protocol refs.
447+
unsigned protocolRefsSize;
448+
llvm::ConstantArray *protocolRefs;
449+
std::tie(protocolRefsSize, protocolRefs) = getProtocolRefsList(protocol);
450+
unsigned currentIdx = 0;
451+
for (auto inheritedObjCProtocol : objcProtocol->protocols()) {
452+
assert(currentIdx < protocolRefsSize);
453+
auto oldVar = protocolRefs->getOperand(currentIdx);
454+
// Map the objc protocol to swift protocol.
455+
auto optionalDecl = clangImporter->importDeclCached(inheritedObjCProtocol);
456+
auto inheritedSwiftProtocol = cast<ProtocolDecl>(*optionalDecl);
457+
// Get the objc protocol record we use in Swift.
458+
auto record = IGM.getAddrOfObjCProtocolRecord(inheritedSwiftProtocol,
459+
NotForDefinition);
460+
auto newOpd = llvm::ConstantExpr::getBitCast(record, oldVar->getType());
461+
if (newOpd != oldVar)
462+
oldVar->replaceAllUsesWith(newOpd);
463+
++currentIdx;
464+
}
465+
assert(currentIdx == protocolRefsSize);
466+
}
467+
468+
llvm::Constant *IRGenModule::emitClangProtocolObject(
469+
const clang::ObjCProtocolDecl *objcProtocol) {
470+
auto clangProto =
471+
clang::CodeGen::emitObjCProtocolObject(getClangCGM(), objcProtocol);
472+
updateProtocolRefs(*this, objcProtocol, clangProto);
473+
return clangProto;
474+
}
475+
398476
void IRGenModule::emitLazyObjCProtocolDefinition(ProtocolDecl *proto) {
399477
// Emit the real definition.
400478
auto record = cast<llvm::GlobalVariable>(emitObjCProtocolData(*this, proto));

lib/IRGen/IRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace clang {
7979
class Decl;
8080
class GlobalDecl;
8181
class Type;
82+
class ObjCProtocolDecl;
8283
class PointerAuthSchema;
8384
namespace CodeGen {
8485
class CGFunctionInfo;
@@ -1083,6 +1084,9 @@ class IRGenModule {
10831084
SILLocation diagLoc);
10841085
llvm::Constant *getAddrOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaqueType,
10851086
ConstantInit forDefinition);
1087+
llvm::Constant *
1088+
emitClangProtocolObject(const clang::ObjCProtocolDecl *objcProtocol);
1089+
10861090
ConstantReference getConstantReferenceForProtocolDescriptor(ProtocolDecl *proto);
10871091

10881092
ConstantIntegerLiteral getConstantIntegerLiteral(APInt value);
Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// 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
1+
// 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
22
// REQUIRES: concurrency
33
// REQUIRES: objc_interop
44

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

12-
// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol = weak hidden constant
13-
// CHECK-SAME: selector_data(myAsyncMethod:)
14-
// CHECK-NOT: selector_data(myAsyncMethod:)
15-
// CHECK-SAME: align [[ALIGNMENT]]
12+
// CHECK-isNotDarwin-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol = weak hidden constant
13+
// CHECK-isNotDarwin-SAME: selector_data(myAsyncMethod:)
14+
// CHECK-isNotDarwin-NOT: selector_data(myAsyncMethod:)
15+
// CHECK-isNotDarwin-SAME: align [[ALIGNMENT]]
16+
17+
18+
// CHECK-isDarwin: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [15 x i8] c"myAsyncMethod:\00"
19+
// CHECK-isDarwin: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyAsyncProtocol" =
20+
// CHECK-isDarwin-SAME: [1 x %struct._objc_method]
21+
// CHECK-isDarwin-SAME: @OBJC_METH_VAR_NAME_
22+
// CHECK-isDarwin-SAME: align [[ALIGNMENT]]

test/IRGen/generic_casts.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %build-irgen-test-overlays
3-
// 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
3+
// 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
44

55
// REQUIRES: CPU=x86_64
66

@@ -12,9 +12,14 @@ import gizmo
1212
// 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"}}
1313
// 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"}}
1414

15-
// CHECK: @_PROTOCOL_NSRuncing = weak hidden constant
16-
// 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"}}
17-
// 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"}}
15+
// CHECK-macosx: @"_OBJC_PROTOCOL_$_NSRuncing" = weak hidden global
16+
// 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"}}
17+
// 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"}}
18+
19+
20+
// CHECK-linux: @_PROTOCOL_NSRuncing = weak hidden constant
21+
// 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"}}
22+
// 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"}}
1823

1924
// CHECK: @_PROTOCOLS__TtC13generic_casts10ObjCClass2 = internal constant { i64, [1 x i8*] } {
2025
// CHECK: i64 1,

test/IRGen/objc_protocols.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// RUN: %build-irgen-test-overlays
33
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -emit-module -o %t %S/Inputs/objc_protocols_Bas.swift
44
// 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
5-
// RUN: %FileCheck --input-file=%t/out.ir %s
5+
// RUN: %FileCheck --input-file=%t/out.ir %s --check-prefix=CHECK --check-prefix=CHECK-%target-os
66

77
// REQUIRES: PTRSIZE=64
88
// REQUIRES: objc_interop
@@ -121,7 +121,8 @@ protocol InheritingProtocol : BaseProtocol { }
121121
// CHECK: @_PROTOCOLS__TtC14objc_protocols17ImplementingClass {{.*}} @_PROTOCOL__TtP14objc_protocols12BaseProtocol_
122122
class ImplementingClass : InheritingProtocol { }
123123

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

126127
// -- Force generation of witness for Zim.
127128
// CHECK: define hidden swiftcc { %objc_object*, i8** } @"$s14objc_protocols22mixed_heritage_erasure{{[_0-9a-zA-Z]*}}F"

test/IRGen/objc_type_encoding.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,5 +220,6 @@ class C: P {
220220
func stuff() {}
221221
}
222222

223-
// CHECK-macosx: [[ENC5:@.*]] = private unnamed_addr constant [9 x i8] c"Vv16@0:8\00"
224-
// CHECK-macosx: @_PROTOCOL_INSTANCE_METHODS_P = {{.*}}@"\01L_selector_data(stuff)"{{.*}}[[ENC5]]{{.*}}
223+
// CHECK-macosx: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [6 x i8] c"stuff\00"
224+
// CHECK-macosx: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [9 x i8] c"Vv16@0:8\00"
225+
// CHECK-macosx: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_P" = {{.*}}@OBJC_METH_VAR_NAME_{{.*}}@OBJC_METH_VAR_TYPE_{{.*}}

test/lit.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,11 @@ config.substitutions.append(('%target-ptrsize', run_ptrsize))
902902
config.substitutions.append(('%target-vendor', run_vendor))
903903
config.substitutions.append(('%target-alignment', "%d" % (int(run_ptrsize)/8)))
904904

905+
if platform.system() == 'Darwin':
906+
config.substitutions.append(('%is-darwin', 'isDarwin'))
907+
else:
908+
config.substitutions.append(('%is-darwin', 'isNotDarwin'))
909+
905910
# Enable Darwin SDK-dependent tests if we have an SDK.
906911
# On Linux, assume that SDK path does not point to the Darwin SDK.
907912
if config.variant_sdk != "":

validation-test/IRGen/issue-49393.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
1-
// RUN: %target-swift-frontend -emit-ir %s -module-name M -import-objc-header %S/Inputs/issue-49393.h | %FileCheck %s
1+
// 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
22
// REQUIRES: objc_interop
33

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

66
import Foundation
77

8-
// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey)"
9-
// CHECK: [[OBJC_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey\00"
10-
// CHECK: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
11-
// CHECK: @_PROTOCOL_PROPERTIES_MyJSONSerializing =
12-
// CHECK-SAME: [[OBJC_PROPNAME]]
13-
// CHECK-SAME: [[PROPKIND]]
8+
// CHECK-isDarwin: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [26 x i8] c"JSONKeyPathsByPropertyKey\00"
9+
// CHECK-isDarwin: @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing" = {{.*}} @OBJC_METH_VAR_NAME_
10+
// CHECK-isDarwin: @OBJC_PROP_NAME_ATTR_ = private unnamed_addr constant [26 x i8] c"JSONKeyPathsByPropertyKey\00"
11+
// CHECK-isDarwin: @OBJC_PROP_NAME_ATTR_.1 = private unnamed_addr constant [21 x i8] c"T@\22NSDictionary\22,R,C\00"
12+
// CHECK-isDarwin: @"_OBJC_$_PROP_LIST_MyJSONSerializing" {{.*}} [1 x %struct._prop_t] }
13+
// CHECK-isDarwin-SAME: @OBJC_PROP_NAME_ATTR_
14+
// CHECK-isDarwin-SAME: @OBJC_PROP_NAME_ATTR_.1
15+
16+
// CHECK-isNotDarwin-LABEL: @_PROTOCOL_INSTANCE_METHODS_MyJSONSerializing = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey)"
17+
// CHECK-isNotDarwin: [[OBJC_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey\00"
18+
// CHECK-isNotDarwin: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
19+
// CHECK-isNotDarwin: @_PROTOCOL_PROPERTIES_MyJSONSerializing =
20+
// CHECK-isNotDarwin-SAME: [[OBJC_PROPNAME]]
21+
// CHECK-isNotDarwin-SAME: [[PROPKIND]]
1422

1523
// CHECK-LABEL: @_PROTOCOL_INSTANCE_METHODS__TtP1M18MyJSONSerializing2_ = {{.+}} @"\01L_selector_data(JSONKeyPathsByPropertyKey2)"
1624
// CHECK: [[SWIFT_PROPNAME:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"JSONKeyPathsByPropertyKey2\00"
25+
26+
// CHECK-isDarwin: [[PROPKIND:@.+]] = private unnamed_addr constant [{{.+}} x i8] c"T@\22NSDictionary\22,N,R\00"
1727
// CHECK: @_PROTOCOL_PROPERTIES__TtP1M18MyJSONSerializing2_ =
1828
// CHECK-SAME: [[SWIFT_PROPNAME]]
1929
// CHECK-SAME: [[PROPKIND]]

0 commit comments

Comments
 (0)