Skip to content

Commit 411b3d2

Browse files
Merge pull request #61750 from aschwaighofer/use_clang_for_protocol_irgen
IRGen: Use clang's codegen for protocol decls
2 parents beaa975 + ba9578d commit 411b3d2

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)