Skip to content

Commit

Permalink
Merge pull request swiftlang#61750 from aschwaighofer/use_clang_for_p…
Browse files Browse the repository at this point in the history
…rotocol_irgen

IRGen: Use clang's codegen for protocol decls
  • Loading branch information
aschwaighofer authored Oct 28, 2022
2 parents beaa975 + ba9578d commit 411b3d2
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 411b3d2

Please sign in to comment.