Skip to content

IRGen: Use clang's codegen for protocol decls #61750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
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