mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Adjust the IRGen for ObjC interop to ensure that the section that metadata is emitted into the correct section for non-MachO targets. This also adds a more comprehensive test for ensuring that the IRGen can now be tested on all targets. Since the ObjC interop is now controllable via the driver, this test does not require that the objc_interop feature is present as it is a IRGen test. This is the first step to remove the `REQUIRES: objc_interop` from the IRGen tests.
1490 lines
57 KiB
C++
1490 lines
57 KiB
C++
//===--- GenObjC.cpp - Objective-C interaction ----------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements bridging to Objective-C.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/InlineAsm.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/Basic/CharInfo.h"
|
|
#include "clang/CodeGen/CGFunctionInfo.h"
|
|
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/IRGenOptions.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/ClangImporter/ClangImporter.h"
|
|
#include "swift/Demangling/ManglingMacros.h"
|
|
#include "swift/IRGen/Linking.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
|
|
#include "CallEmission.h"
|
|
#include "ConstantBuilder.h"
|
|
#include "Explosion.h"
|
|
#include "GenCall.h"
|
|
#include "GenClass.h"
|
|
#include "GenFunc.h"
|
|
#include "GenHeap.h"
|
|
#include "GenMeta.h"
|
|
#include "GenProto.h"
|
|
#include "GenType.h"
|
|
#include "HeapTypeInfo.h"
|
|
#include "IRGenDebugInfo.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenModule.h"
|
|
#include "NativeConventionSchema.h"
|
|
#include "ScalarTypeInfo.h"
|
|
#include "StructLayout.h"
|
|
|
|
#include "GenObjC.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
void IRGenFunction::emitObjCStrongRelease(llvm::Value *value) {
|
|
// Get an appropriately-cast function pointer.
|
|
auto fn = IGM.getObjCReleaseFn();
|
|
auto cc = IGM.C_CC;
|
|
if (auto fun = dyn_cast<llvm::Function>(fn))
|
|
cc = fun->getCallingConv();
|
|
|
|
if (value->getType() != IGM.ObjCPtrTy) {
|
|
auto fnTy = llvm::FunctionType::get(IGM.VoidTy, value->getType(),
|
|
false)->getPointerTo();
|
|
fn = llvm::ConstantExpr::getBitCast(fn, fnTy);
|
|
}
|
|
|
|
auto call = Builder.CreateCall(fn, value);
|
|
call->setCallingConv(cc);
|
|
call->setDoesNotThrow();
|
|
}
|
|
|
|
/// Given a function of type %objc* (%objc*)*, cast it as appropriate
|
|
/// to be used with values of type T.
|
|
static llvm::Constant *getCastOfRetainFn(IRGenModule &IGM,
|
|
llvm::Constant *fn,
|
|
llvm::Type *valueTy) {
|
|
#ifndef NDEBUG
|
|
auto origFnTy = cast<llvm::FunctionType>(fn->getType()->getPointerElementType());
|
|
assert(origFnTy->getReturnType() == IGM.ObjCPtrTy);
|
|
assert(origFnTy->getNumParams() == 1);
|
|
assert(origFnTy->getParamType(0) == IGM.ObjCPtrTy);
|
|
assert(isa<llvm::PointerType>(valueTy) ||
|
|
valueTy == IGM.IntPtrTy); // happens with optional types
|
|
#endif
|
|
if (valueTy == IGM.ObjCPtrTy)
|
|
return fn;
|
|
|
|
auto fnTy = llvm::FunctionType::get(valueTy, valueTy, false);
|
|
return llvm::ConstantExpr::getBitCast(fn, fnTy->getPointerTo(0));
|
|
}
|
|
|
|
void IRGenFunction::emitObjCStrongRetain(llvm::Value *v) {
|
|
emitObjCRetainCall(v);
|
|
}
|
|
|
|
llvm::Value *IRGenFunction::emitObjCRetainCall(llvm::Value *value) {
|
|
// Get an appropriately cast function pointer.
|
|
auto fn = IGM.getObjCRetainFn();
|
|
auto cc = IGM.C_CC;
|
|
if (auto fun = dyn_cast<llvm::Function>(fn))
|
|
cc = fun->getCallingConv();
|
|
fn = getCastOfRetainFn(IGM, fn, value->getType());
|
|
|
|
auto call = Builder.CreateCall(fn, value);
|
|
call->setCallingConv(cc);
|
|
call->setDoesNotThrow();
|
|
return call;
|
|
}
|
|
|
|
llvm::Value *IRGenFunction::emitObjCAutoreleaseCall(llvm::Value *val) {
|
|
if (val->getType()->isPointerTy())
|
|
val = Builder.CreateBitCast(val, IGM.ObjCPtrTy);
|
|
else
|
|
val = Builder.CreateIntToPtr(val, IGM.ObjCPtrTy);
|
|
|
|
auto call = Builder.CreateCall(IGM.getObjCAutoreleaseFn(), val);
|
|
call->setDoesNotThrow();
|
|
return call;
|
|
}
|
|
|
|
llvm::InlineAsm *IRGenModule::getObjCRetainAutoreleasedReturnValueMarker() {
|
|
// Check to see if we've already computed the market. Note that we
|
|
// might have cached a null marker, and that's fine.
|
|
auto &cache = ObjCRetainAutoreleasedReturnValueMarker;
|
|
if (cache.hasValue())
|
|
return cache.getValue();
|
|
|
|
// Ask the target for the string.
|
|
StringRef asmString = TargetInfo.ObjCRetainAutoreleasedReturnValueMarker;
|
|
|
|
// If the string is empty, just leave, remembering that we did all this.
|
|
if (asmString.empty()) {
|
|
cache = nullptr;
|
|
return nullptr;
|
|
}
|
|
|
|
// If we're emitting optimized code, record the string in the module
|
|
// and let the late ARC pass insert it, but don't generate any calls
|
|
// right now.
|
|
if (IRGen.Opts.shouldOptimize()) {
|
|
llvm::NamedMDNode *metadata =
|
|
Module.getOrInsertNamedMetadata(
|
|
"clang.arc.retainAutoreleasedReturnValueMarker");
|
|
assert(metadata->getNumOperands() <= 1);
|
|
if (metadata->getNumOperands() == 0) {
|
|
auto *string = llvm::MDString::get(LLVMContext, asmString);
|
|
metadata->addOperand(llvm::MDNode::get(LLVMContext, string));
|
|
}
|
|
|
|
cache = nullptr;
|
|
|
|
// Otherwise, create the module
|
|
} else {
|
|
llvm::FunctionType *type =
|
|
llvm::FunctionType::get(VoidTy, /*variadic*/false);
|
|
cache = llvm::InlineAsm::get(type, asmString, "", /*sideeffects*/ true);
|
|
}
|
|
|
|
return cache.getValue();
|
|
}
|
|
|
|
/// Reclaim an autoreleased return value.
|
|
llvm::Value *irgen::emitObjCRetainAutoreleasedReturnValue(IRGenFunction &IGF,
|
|
llvm::Value *value) {
|
|
// Call the inline-assembly marker if we need one.
|
|
if (auto marker = IGF.IGM.getObjCRetainAutoreleasedReturnValueMarker()) {
|
|
IGF.Builder.CreateAsmCall(marker, {});
|
|
}
|
|
|
|
auto fn = IGF.IGM.getObjCRetainAutoreleasedReturnValueFn();
|
|
|
|
// We don't want to cast the function here because it interferes with
|
|
// LLVM's ability to recognize and special-case this function.
|
|
// Note that the parameter and result must also have type i8*.
|
|
llvm::Type *valueType = value->getType();
|
|
if (isa<llvm::PointerType>(valueType)) {
|
|
value = IGF.Builder.CreateBitCast(value, IGF.IGM.Int8PtrTy);
|
|
} else {
|
|
value = IGF.Builder.CreateIntToPtr(value, IGF.IGM.Int8PtrTy);
|
|
}
|
|
|
|
auto call = IGF.Builder.CreateCall(fn, value);
|
|
call->setDoesNotThrow();
|
|
|
|
const llvm::Triple &triple = IGF.IGM.Context.LangOpts.Target;
|
|
if (triple.getArch() == llvm::Triple::x86_64) {
|
|
// Don't tail call objc_retainAutoreleasedReturnValue. This blocks the
|
|
// autoreleased return optimization.
|
|
// callq 0x01ec08 ; symbol stub for: objc_msgSend
|
|
// movq %rax, %rdi
|
|
// popq %rbp ;<== Blocks the handshake from objc_autoreleaseReturnValue
|
|
// jmp 0x01ec20 ; symbol stub for: objc_retainAutoreleasedReturnValue
|
|
call->setTailCallKind(llvm::CallInst::TCK_NoTail);
|
|
}
|
|
|
|
llvm::Value *result = call;
|
|
if (isa<llvm::PointerType>(valueType)) {
|
|
result = IGF.Builder.CreateBitCast(result, valueType);
|
|
} else {
|
|
result = IGF.Builder.CreatePtrToInt(result, valueType);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// Autorelease a return value.
|
|
llvm::Value *irgen::emitObjCAutoreleaseReturnValue(IRGenFunction &IGF,
|
|
llvm::Value *value) {
|
|
auto fn = IGF.IGM.getObjCAutoreleaseReturnValueFn();
|
|
fn = getCastOfRetainFn(IGF.IGM, fn, value->getType());
|
|
|
|
auto call = IGF.Builder.CreateCall(fn, value);
|
|
call->setDoesNotThrow();
|
|
call->setTailCall(); // force tail calls at -O0
|
|
return call;
|
|
}
|
|
|
|
namespace {
|
|
/// A type-info implementation suitable for Builtin.UnknownObject.
|
|
class UnknownTypeInfo : public HeapTypeInfo<UnknownTypeInfo> {
|
|
public:
|
|
UnknownTypeInfo(llvm::PointerType *storageType, Size size,
|
|
SpareBitVector spareBits, Alignment align)
|
|
: HeapTypeInfo(storageType, size, spareBits, align) {
|
|
}
|
|
|
|
/// Builtin.UnknownObject requires ObjC reference-counting.
|
|
ReferenceCounting getReferenceCounting() const {
|
|
return ReferenceCounting::Unknown;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
const LoadableTypeInfo *TypeConverter::convertBuiltinUnknownObject() {
|
|
// UnknownObject is only interestingly different from NativeObject on
|
|
// platforms with ObjC interop.
|
|
if (IGM.Context.LangOpts.EnableObjCInterop) {
|
|
return new UnknownTypeInfo(IGM.ObjCPtrTy, IGM.getPointerSize(),
|
|
IGM.getHeapObjectSpareBits(),
|
|
IGM.getPointerAlignment());
|
|
}
|
|
|
|
// Without ObjC interop, UnknownObject handles just like a NativeObject.
|
|
return convertBuiltinNativeObject();
|
|
}
|
|
|
|
namespace {
|
|
/// A type info implementation for BridgeObject
|
|
class BridgeObjectTypeInfo : public HeapTypeInfo<BridgeObjectTypeInfo> {
|
|
public:
|
|
BridgeObjectTypeInfo(llvm::PointerType *storageType, Size size,
|
|
SpareBitVector spareBits, Alignment align)
|
|
: HeapTypeInfo(storageType, size, spareBits, align) {
|
|
}
|
|
|
|
/// Builtin.BridgeObject uses its own specialized refcounting implementation.
|
|
ReferenceCounting getReferenceCounting() const {
|
|
return ReferenceCounting::Bridge;
|
|
}
|
|
|
|
// BridgeObject exposes only null as an extra inhabitant for enum layout.
|
|
// Other representations are reserved for future use by the stdlib.
|
|
|
|
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
|
|
return true;
|
|
}
|
|
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
|
|
return 1;
|
|
}
|
|
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
|
|
unsigned bits,
|
|
unsigned index) const override {
|
|
return APInt(bits, 0);
|
|
}
|
|
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src,
|
|
SILType T) const override {
|
|
src = IGF.Builder.CreateBitCast(src, IGF.IGM.SizeTy->getPointerTo());
|
|
auto val = IGF.Builder.CreateLoad(src);
|
|
auto isNonzero = IGF.Builder.CreateICmpNE(val,
|
|
llvm::ConstantInt::get(IGF.IGM.SizeTy, 0));
|
|
// We either have extra inhabitant 0 or no extra inhabitant (-1).
|
|
// Conveniently, this is just a sext i1 -> i32 away.
|
|
return IGF.Builder.CreateSExt(isNonzero, IGF.IGM.Int32Ty);
|
|
}
|
|
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
|
|
Address dest, SILType T) const override {
|
|
// There's only one extra inhabitant, 0.
|
|
dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.SizeTy->getPointerTo());
|
|
IGF.Builder.CreateStore(llvm::ConstantInt::get(IGF.IGM.SizeTy, 0), dest);
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
|
|
const LoadableTypeInfo *TypeConverter::convertBuiltinBridgeObject() {
|
|
return new BridgeObjectTypeInfo(IGM.BridgeObjectPtrTy, IGM.getPointerSize(),
|
|
SpareBitVector::getConstant(IGM.getPointerSize().getValueInBits(), false),
|
|
IGM.getPointerAlignment());
|
|
}
|
|
|
|
const TypeInfo &IRGenModule::getObjCClassPtrTypeInfo() {
|
|
return Types.getObjCClassPtrTypeInfo();
|
|
}
|
|
|
|
const LoadableTypeInfo &TypeConverter::getObjCClassPtrTypeInfo() {
|
|
// ObjC class pointers look like unmanaged (untagged) object references.
|
|
if (ObjCClassPtrTI) return *ObjCClassPtrTI;
|
|
ObjCClassPtrTI =
|
|
createUnmanagedStorageType(IGM.ObjCClassPtrTy);
|
|
ObjCClassPtrTI->NextConverted = FirstType;
|
|
FirstType = ObjCClassPtrTI;
|
|
return *ObjCClassPtrTI;
|
|
}
|
|
|
|
/// Get or create a global Objective-C method name. Always returns an i8*.
|
|
llvm::Constant *IRGenModule::getAddrOfObjCMethodName(StringRef selector) {
|
|
// Check whether this selector already exists.
|
|
auto &entry = ObjCMethodNames[selector];
|
|
if (entry) return entry;
|
|
|
|
// If not, create it. This implicitly adds a trailing null.
|
|
auto init = llvm::ConstantDataArray::getString(LLVMContext, selector);
|
|
auto global = new llvm::GlobalVariable(Module, init->getType(), false,
|
|
llvm::GlobalValue::PrivateLinkage,
|
|
init,
|
|
llvm::Twine("\01L_selector_data(") + selector + ")");
|
|
SetCStringLiteralSection(global, ObjCLabelType::MethodVarName);
|
|
global->setAlignment(1);
|
|
addCompilerUsedGlobal(global);
|
|
|
|
// Drill down to make an i8*.
|
|
auto zero = llvm::ConstantInt::get(SizeTy, 0);
|
|
llvm::Constant *indices[] = { zero, zero };
|
|
auto address = llvm::ConstantExpr::getInBoundsGetElementPtr(
|
|
init->getType(), global, indices);
|
|
|
|
// Cache and return.
|
|
entry = address;
|
|
return address;
|
|
}
|
|
|
|
/// Get or create an Objective-C selector reference. Always returns
|
|
/// an i8**. The design is that the compiler will emit a load of this
|
|
/// pointer, and the linker will ensure that that pointer is unique.
|
|
llvm::Constant *IRGenModule::getAddrOfObjCSelectorRef(StringRef selector) {
|
|
// Check whether a reference for this selector already exists.
|
|
auto &entry = ObjCSelectorRefs[selector];
|
|
if (entry) return entry;
|
|
|
|
// If not, create it. The initializer is just a pointer to the
|
|
// method name. Note that the label here is unimportant, so we
|
|
// choose something descriptive to make the IR readable.
|
|
auto init = getAddrOfObjCMethodName(selector);
|
|
auto global = new llvm::GlobalVariable(Module, init->getType(), false,
|
|
llvm::GlobalValue::PrivateLinkage,
|
|
init,
|
|
llvm::Twine("\01L_selector(") + selector + ")");
|
|
global->setExternallyInitialized(true);
|
|
global->setAlignment(getPointerAlignment().getValue());
|
|
|
|
// This section name is magical for the Darwin static and dynamic linkers.
|
|
global->setSection(GetObjCSectionName("__objc_selrefs",
|
|
"literal_pointers,no_dead_strip"));
|
|
|
|
// Make sure that this reference does not get optimized away.
|
|
addCompilerUsedGlobal(global);
|
|
|
|
// Cache and return.
|
|
entry = global;
|
|
return global;
|
|
}
|
|
|
|
/// Get or create an ObjC protocol record. Always returns an i8*. We lazily
|
|
/// create ObjC protocol_t records for protocols, storing references to the
|
|
/// record into the __objc_protolist and __objc_protorefs sections to be
|
|
/// fixed up by the runtime.
|
|
///
|
|
/// It is not correct to use this value as a Protocol* reference directly. The
|
|
/// ObjC runtime requires protocol references to be loaded from an
|
|
/// indirect variable, the address of which is given by
|
|
/// getAddrOfObjCProtocolRef.
|
|
llvm::Constant *
|
|
IRGenModule::getAddrOfObjCProtocolRecord(ProtocolDecl *proto,
|
|
ForDefinition_t forDefinition) {
|
|
return const_cast<llvm::Constant*>
|
|
(cast<llvm::Constant>(getObjCProtocolGlobalVars(proto).record));
|
|
}
|
|
|
|
/// Get or create an ObjC protocol reference. Always returns an i8**. We lazily
|
|
/// create ObjC protocol_t records for protocols, storing references to the
|
|
/// record into the __objc_protolist and __objc_protorefs sections to be
|
|
/// fixed up by the runtime.
|
|
llvm::Constant *IRGenModule::getAddrOfObjCProtocolRef(ProtocolDecl *proto,
|
|
ForDefinition_t forDefinition) {
|
|
return const_cast<llvm::Constant*>
|
|
(cast<llvm::Constant>(getObjCProtocolGlobalVars(proto).ref));
|
|
}
|
|
|
|
IRGenModule::ObjCProtocolPair
|
|
IRGenModule::getObjCProtocolGlobalVars(ProtocolDecl *proto) {
|
|
// See whether we already emitted this protocol reference.
|
|
auto found = ObjCProtocols.find(proto);
|
|
if (found != ObjCProtocols.end()) {
|
|
return found->second;
|
|
}
|
|
|
|
// Create a placeholder protocol record.
|
|
llvm::Constant *protocolRecord =
|
|
new llvm::GlobalVariable(Module, Int8Ty, /*constant*/ false,
|
|
llvm::GlobalValue::PrivateLinkage, nullptr);
|
|
LazyObjCProtocolDefinitions.push_back(proto);
|
|
|
|
// Introduce a variable to label the protocol.
|
|
llvm::SmallString<64> nameBuffer;
|
|
StringRef protocolName = proto->getObjCRuntimeName(nameBuffer);
|
|
auto *protocolLabel
|
|
= new llvm::GlobalVariable(Module, Int8PtrTy,
|
|
/*constant*/ false,
|
|
llvm::GlobalValue::WeakAnyLinkage,
|
|
protocolRecord,
|
|
llvm::Twine("\01l_OBJC_LABEL_PROTOCOL_$_")
|
|
+ protocolName);
|
|
protocolLabel->setAlignment(getPointerAlignment().getValue());
|
|
protocolLabel->setVisibility(llvm::GlobalValue::HiddenVisibility);
|
|
protocolLabel->setSection(GetObjCSectionName("__objc_protolist",
|
|
"coalesced,no_dead_strip"));
|
|
|
|
// Introduce a variable to reference the protocol.
|
|
auto *protocolRef =
|
|
new llvm::GlobalVariable(Module, Int8PtrTy, /*constant*/ false,
|
|
llvm::GlobalValue::WeakAnyLinkage,
|
|
protocolRecord,
|
|
llvm::Twine("\01l_OBJC_PROTOCOL_REFERENCE_$_") + protocolName);
|
|
protocolRef->setAlignment(getPointerAlignment().getValue());
|
|
protocolRef->setVisibility(llvm::GlobalValue::HiddenVisibility);
|
|
protocolRef->setSection(GetObjCSectionName("__objc_protorefs",
|
|
"coalesced,no_dead_strip"));
|
|
|
|
ObjCProtocolPair pair{protocolRecord, protocolRef};
|
|
ObjCProtocols.insert({proto, pair});
|
|
|
|
return pair;
|
|
}
|
|
|
|
void IRGenModule::emitLazyObjCProtocolDefinition(ProtocolDecl *proto) {
|
|
// Emit the real definition.
|
|
auto record = cast<llvm::GlobalVariable>(emitObjCProtocolData(*this, proto));
|
|
|
|
// Find the placeholder. It should always still be a placeholder,
|
|
// because it was created as an anonymous symbol and nobody should
|
|
// ever be randomly messing with those.
|
|
auto placeholder =
|
|
cast<llvm::GlobalVariable>(ObjCProtocols.find(proto)->second.record);
|
|
|
|
// Move the new record to the placeholder's position.
|
|
Module.getGlobalList().remove(record);
|
|
Module.getGlobalList().insertAfter(placeholder->getIterator(), record);
|
|
|
|
// Replace and destroy the placeholder.
|
|
placeholder->replaceAllUsesWith(
|
|
llvm::ConstantExpr::getBitCast(record, Int8PtrTy));
|
|
placeholder->eraseFromParent();
|
|
}
|
|
|
|
void IRGenModule::emitLazyObjCProtocolDefinitions() {
|
|
// Emit any lazy ObjC protocol definitions we require. Try to do
|
|
// this in the order in which we needed them, since they can require
|
|
// other protocol definitions recursively.
|
|
for (size_t i = 0; i != LazyObjCProtocolDefinitions.size(); ++i) {
|
|
ProtocolDecl *protocol = LazyObjCProtocolDefinitions[i];
|
|
emitLazyObjCProtocolDefinition(protocol);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class Selector {
|
|
|
|
llvm::SmallString<80> Buffer;
|
|
StringRef Text;
|
|
|
|
public:
|
|
|
|
static constexpr struct ForGetter_t { } ForGetter{};
|
|
static constexpr struct ForSetter_t { } ForSetter{};
|
|
|
|
#define FOREACH_FAMILY(FAMILY) \
|
|
FAMILY(Alloc, "alloc") \
|
|
FAMILY(Copy, "copy") \
|
|
FAMILY(Init, "init") \
|
|
FAMILY(MutableCopy, "mutableCopy") \
|
|
FAMILY(New, "new")
|
|
|
|
// Note that these are in parallel with 'prefixes', below.
|
|
enum class Family {
|
|
None,
|
|
#define GET_LABEL(LABEL, PREFIX) LABEL,
|
|
FOREACH_FAMILY(GET_LABEL)
|
|
#undef GET_LABEL
|
|
};
|
|
|
|
Selector() = default;
|
|
|
|
Selector(FuncDecl *method) {
|
|
Text = method->getObjCSelector().getString(Buffer);
|
|
}
|
|
|
|
Selector(ConstructorDecl *ctor) {
|
|
Text = ctor->getObjCSelector().getString(Buffer);
|
|
}
|
|
|
|
Selector(ValueDecl *methodOrCtorOrDtor) {
|
|
if (auto *method = dyn_cast<FuncDecl>(methodOrCtorOrDtor)) {
|
|
Text = method->getObjCSelector().getString(Buffer);
|
|
} else if (auto *ctor = dyn_cast<ConstructorDecl>(methodOrCtorOrDtor)) {
|
|
Text = ctor->getObjCSelector().getString(Buffer);
|
|
} else if (isa<DestructorDecl>(methodOrCtorOrDtor)) {
|
|
Text = "dealloc";
|
|
} else {
|
|
llvm_unreachable("property or subscript selector should be generated "
|
|
"using ForGetter or ForSetter constructors");
|
|
}
|
|
}
|
|
|
|
Selector(AbstractStorageDecl *asd, ForGetter_t) {
|
|
Text = asd->getObjCGetterSelector().getString(Buffer);
|
|
}
|
|
|
|
Selector(AbstractStorageDecl *asd, ForSetter_t) {
|
|
Text = asd->getObjCSetterSelector().getString(Buffer);
|
|
}
|
|
|
|
Selector(SILDeclRef ref) {
|
|
switch (ref.kind) {
|
|
case SILDeclRef::Kind::DefaultArgGenerator:
|
|
case SILDeclRef::Kind::StoredPropertyInitializer:
|
|
case SILDeclRef::Kind::EnumElement:
|
|
case SILDeclRef::Kind::GlobalAccessor:
|
|
case SILDeclRef::Kind::GlobalGetter:
|
|
llvm_unreachable("Method does not have a selector");
|
|
|
|
case SILDeclRef::Kind::Destroyer:
|
|
case SILDeclRef::Kind::Deallocator:
|
|
Text = "dealloc";
|
|
break;
|
|
|
|
case SILDeclRef::Kind::Func:
|
|
Text = cast<FuncDecl>(ref.getDecl())->getObjCSelector()
|
|
.getString(Buffer);
|
|
break;
|
|
|
|
case SILDeclRef::Kind::Allocator:
|
|
case SILDeclRef::Kind::Initializer:
|
|
Text = cast<ConstructorDecl>(ref.getDecl())->getObjCSelector()
|
|
.getString(Buffer);
|
|
break;
|
|
|
|
case SILDeclRef::Kind::IVarInitializer:
|
|
Text = ".cxx_construct";
|
|
break;
|
|
|
|
case SILDeclRef::Kind::IVarDestroyer:
|
|
Text = ".cxx_destruct";
|
|
break;
|
|
}
|
|
}
|
|
|
|
StringRef str() const {
|
|
return Text;
|
|
}
|
|
|
|
/// Return the family string of this selector.
|
|
Family getFamily() const {
|
|
StringRef text = str();
|
|
while (!text.empty() && text[0] == '_') text = text.substr(1);
|
|
|
|
#define CHECK_PREFIX(LABEL, PREFIX) \
|
|
if (hasPrefix(text, PREFIX)) return Family::LABEL;
|
|
FOREACH_FAMILY(CHECK_PREFIX)
|
|
#undef CHECK_PREFIX
|
|
|
|
return Family::None;
|
|
}
|
|
|
|
private:
|
|
/// Does the given selector start with the given string as a
|
|
/// prefix, in the sense of the selector naming conventions?
|
|
static bool hasPrefix(StringRef text, StringRef prefix) {
|
|
if (!text.startswith(prefix)) return false;
|
|
if (text.size() == prefix.size()) return true;
|
|
assert(text.size() > prefix.size());
|
|
return !clang::isLowercase(text[prefix.size()]);
|
|
}
|
|
|
|
#undef FOREACH_FAMILY
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
llvm::Constant *IRGenModule::getAddrOfObjCSelectorRef(SILDeclRef method) {
|
|
assert(method.isForeign);
|
|
return getAddrOfObjCSelectorRef(Selector(method).str());
|
|
}
|
|
|
|
static llvm::Value *emitSuperArgument(IRGenFunction &IGF,
|
|
bool isInstanceMethod,
|
|
llvm::Value *selfValue,
|
|
CanType searchClass) {
|
|
// Allocate an objc_super struct.
|
|
Address super = IGF.createAlloca(IGF.IGM.ObjCSuperStructTy,
|
|
IGF.IGM.getPointerAlignment(),
|
|
"objc_super");
|
|
// TODO: Track lifetime markers for function args.
|
|
llvm::Value *self = IGF.Builder.CreateBitCast(selfValue,
|
|
IGF.IGM.ObjCPtrTy);
|
|
|
|
// Generate the search class object reference.
|
|
llvm::Value *searchValue;
|
|
if (isInstanceMethod) {
|
|
searchValue = emitClassHeapMetadataRef(IGF, searchClass,
|
|
MetadataValueType::ObjCClass,
|
|
/*allow uninitialized*/ true);
|
|
} else {
|
|
searchClass = cast<MetatypeType>(searchClass).getInstanceType();
|
|
ClassDecl *searchClassDecl = searchClass.getClassOrBoundGenericClass();
|
|
if (doesClassMetadataRequireDynamicInitialization(IGF.IGM, searchClassDecl)) {
|
|
searchValue = emitClassHeapMetadataRef(IGF, searchClass,
|
|
MetadataValueType::ObjCClass,
|
|
/*allow uninitialized*/ true);
|
|
searchValue = emitLoadOfObjCHeapMetadataRef(IGF, searchValue);
|
|
searchValue = IGF.Builder.CreateBitCast(searchValue, IGF.IGM.ObjCClassPtrTy);
|
|
} else {
|
|
searchValue = IGF.IGM.getAddrOfMetaclassObject(searchClassDecl,
|
|
NotForDefinition);
|
|
}
|
|
}
|
|
|
|
// Store the receiver and class to the struct.
|
|
Address selfAddr = IGF.Builder.CreateStructGEP(super, 0, Size(0));
|
|
IGF.Builder.CreateStore(self, selfAddr);
|
|
|
|
Address searchAddr =
|
|
IGF.Builder.CreateStructGEP(super, 1, IGF.IGM.getPointerSize());
|
|
IGF.Builder.CreateStore(searchValue, searchAddr);
|
|
|
|
// Pass a pointer to the objc_super struct to the messenger.
|
|
// Project the ownership semantics of 'self' to the super argument.
|
|
return super.getAddress();
|
|
}
|
|
|
|
static llvm::FunctionType *getMsgSendSuperTy(IRGenModule &IGM,
|
|
llvm::FunctionType *fnTy,
|
|
bool indirectResult) {
|
|
SmallVector<llvm::Type*, 4> args(fnTy->param_begin(), fnTy->param_end());
|
|
if (indirectResult)
|
|
args[1] = IGM.ObjCSuperPtrTy;
|
|
else
|
|
args[0] = IGM.ObjCSuperPtrTy;
|
|
return llvm::FunctionType::get(fnTy->getReturnType(), args, fnTy->isVarArg());
|
|
}
|
|
|
|
Callee irgen::getObjCMethodCallee(IRGenFunction &IGF,
|
|
const ObjCMethod &methodInfo,
|
|
llvm::Value *selfValue,
|
|
CalleeInfo &&info) {
|
|
SILDeclRef method = methodInfo.getMethod();
|
|
assert((method.kind == SILDeclRef::Kind::Initializer
|
|
|| method.kind == SILDeclRef::Kind::Allocator
|
|
|| method.kind == SILDeclRef::Kind::Func
|
|
|| method.kind == SILDeclRef::Kind::Destroyer
|
|
|| method.kind == SILDeclRef::Kind::Deallocator) &&
|
|
"objc method call must be to a func/initializer/getter/setter/dtor");
|
|
|
|
auto kind = methodInfo.getMessageKind();
|
|
|
|
Signature sig = IGF.IGM.getSignature(info.OrigFnType);
|
|
bool indirectResult =
|
|
sig.getForeignInfo().ClangInfo->getReturnInfo().isIndirect();
|
|
if (kind != ObjCMessageKind::Normal) {
|
|
sig.setType(getMsgSendSuperTy(IGF.IGM, sig.getType(), indirectResult));
|
|
}
|
|
|
|
// Create the appropriate messenger function.
|
|
// FIXME: this needs to be target-specific. Ask Clang for it!
|
|
llvm::Constant *messenger = [&]() -> llvm::Constant* {
|
|
if (indirectResult && IGF.IGM.TargetInfo.ObjCUseStret) {
|
|
switch (kind) {
|
|
case ObjCMessageKind::Normal:
|
|
return IGF.IGM.getObjCMsgSendStretFn();
|
|
|
|
case ObjCMessageKind::Peer:
|
|
return IGF.IGM.getObjCMsgSendSuperStretFn();
|
|
|
|
case ObjCMessageKind::Super:
|
|
return IGF.IGM.getObjCMsgSendSuperStret2Fn();
|
|
}
|
|
} else {
|
|
switch (kind) {
|
|
case ObjCMessageKind::Normal:
|
|
return IGF.IGM.getObjCMsgSendFn();
|
|
|
|
case ObjCMessageKind::Peer:
|
|
return IGF.IGM.getObjCMsgSendSuperFn();
|
|
|
|
case ObjCMessageKind::Super:
|
|
return IGF.IGM.getObjCMsgSendSuper2Fn();
|
|
}
|
|
}
|
|
}();
|
|
|
|
messenger = llvm::ConstantExpr::getBitCast(messenger,
|
|
sig.getType()->getPointerTo());
|
|
|
|
// super.constructor references an instance method (even though the
|
|
// decl is really a 'static' member). Similarly, destructors refer
|
|
// to the instance method -dealloc.
|
|
bool isInstanceMethod
|
|
= method.kind == SILDeclRef::Kind::Initializer
|
|
|| method.kind == SILDeclRef::Kind::Deallocator
|
|
|| method.getDecl()->isInstanceMember();
|
|
|
|
llvm::Value *receiverValue;
|
|
if (auto searchType = methodInfo.getSearchType()) {
|
|
receiverValue =
|
|
emitSuperArgument(IGF, isInstanceMethod, selfValue,
|
|
searchType.getSwiftRValueType());
|
|
} else {
|
|
receiverValue = selfValue;
|
|
}
|
|
|
|
// Compute the selector.
|
|
Selector selector(method);
|
|
llvm::Value *selectorValue = IGF.emitObjCSelectorRefLoad(selector.str());
|
|
|
|
auto fn = FunctionPointer::forDirect(messenger, sig);
|
|
return Callee(std::move(info), fn, receiverValue, selectorValue);
|
|
}
|
|
|
|
/// Call [self allocWithZone: nil].
|
|
llvm::Value *irgen::emitObjCAllocObjectCall(IRGenFunction &IGF,
|
|
llvm::Value *self,
|
|
SILType selfType) {
|
|
// Get an appropriately-cast function pointer.
|
|
auto fn = IGF.IGM.getObjCAllocWithZoneFn();
|
|
|
|
if (self->getType() != IGF.IGM.ObjCClassPtrTy) {
|
|
auto fnTy = llvm::FunctionType::get(self->getType(), self->getType(),
|
|
false)->getPointerTo();
|
|
fn = llvm::ConstantExpr::getBitCast(fn, fnTy);
|
|
}
|
|
|
|
auto call = IGF.Builder.CreateCall(fn, self);
|
|
|
|
// Cast the returned pointer to the right type.
|
|
auto &classTI = IGF.getTypeInfo(selfType);
|
|
llvm::Type *destType = classTI.getStorageType();
|
|
return IGF.Builder.CreateBitCast(call, destType);
|
|
}
|
|
|
|
static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM,
|
|
ObjCMethod method,
|
|
CanSILFunctionType origMethodType,
|
|
CanSILFunctionType resultType,
|
|
const HeapLayout &layout,
|
|
SILType selfType) {
|
|
auto &selfTI = IGM.getTypeInfo(selfType);
|
|
|
|
assert(resultType->getRepresentation()
|
|
== SILFunctionType::Representation::Thick);
|
|
|
|
llvm::AttributeList attrs;
|
|
llvm::FunctionType *fwdTy = IGM.getFunctionType(resultType, attrs);
|
|
// FIXME: Give the thunk a real name.
|
|
// FIXME: Maybe cache the thunk by function and closure types?
|
|
llvm::Function *fwd =
|
|
llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage,
|
|
MANGLE_AS_STRING(OBJC_PARTIAL_APPLY_THUNK_SYM),
|
|
&IGM.Module);
|
|
fwd->setCallingConv(
|
|
expandCallingConv(IGM, SILFunctionTypeRepresentation::Thick));
|
|
|
|
fwd->setAttributes(attrs);
|
|
// Merge initial attributes with attrs.
|
|
llvm::AttrBuilder b;
|
|
IGM.constructInitialFnAttributes(b);
|
|
fwd->addAttributes(llvm::AttributeList::FunctionIndex, b);
|
|
|
|
IRGenFunction subIGF(IGM, fwd);
|
|
if (IGM.DebugInfo)
|
|
IGM.DebugInfo->emitArtificialFunction(subIGF, fwd);
|
|
|
|
// Do we need to lifetime-extend self?
|
|
bool lifetimeExtendsSelf;
|
|
auto results = origMethodType->getResults();
|
|
if (results.size() == 1) {
|
|
switch (results[0].getConvention()) {
|
|
case ResultConvention::UnownedInnerPointer:
|
|
lifetimeExtendsSelf = true;
|
|
break;
|
|
|
|
case ResultConvention::Indirect:
|
|
case ResultConvention::Unowned:
|
|
case ResultConvention::Owned:
|
|
case ResultConvention::Autoreleased:
|
|
lifetimeExtendsSelf = false;
|
|
break;
|
|
}
|
|
} else {
|
|
lifetimeExtendsSelf = false;
|
|
}
|
|
|
|
// Do we need to retain self before calling, and/or release it after?
|
|
bool retainsSelf;
|
|
switch (origMethodType->getParameters().back().getConvention()) {
|
|
case ParameterConvention::Direct_Unowned:
|
|
retainsSelf = false;
|
|
break;
|
|
case ParameterConvention::Direct_Guaranteed:
|
|
case ParameterConvention::Direct_Owned:
|
|
retainsSelf = true;
|
|
break;
|
|
case ParameterConvention::Indirect_In_Guaranteed:
|
|
case ParameterConvention::Indirect_In:
|
|
case ParameterConvention::Indirect_In_Constant:
|
|
case ParameterConvention::Indirect_Inout:
|
|
case ParameterConvention::Indirect_InoutAliasable:
|
|
llvm_unreachable("self passed indirectly?!");
|
|
}
|
|
|
|
// Recover 'self' from the context.
|
|
Explosion params = subIGF.collectParameters();
|
|
llvm::Value *context = params.takeLast();
|
|
Address dataAddr = layout.emitCastTo(subIGF, context);
|
|
auto &fieldLayout = layout.getElement(0);
|
|
Address selfAddr = fieldLayout.project(subIGF, dataAddr, None);
|
|
Explosion selfParams;
|
|
if (retainsSelf)
|
|
cast<LoadableTypeInfo>(selfTI).loadAsCopy(subIGF, selfAddr, selfParams);
|
|
else
|
|
cast<LoadableTypeInfo>(selfTI).loadAsTake(subIGF, selfAddr, selfParams);
|
|
llvm::Value *self = selfParams.claimNext();
|
|
|
|
// Save off the forwarded indirect return address if we have one.
|
|
llvm::Value *formalIndirectResult = nullptr;
|
|
llvm::Value *indirectedDirectResult = nullptr;
|
|
const LoadableTypeInfo *indirectedResultTI = nullptr;
|
|
if (origMethodType->hasIndirectFormalResults()) {
|
|
// We should never import an ObjC method as returning a tuple which
|
|
// would get broken up into multiple results like this.
|
|
assert(origMethodType->getNumIndirectFormalResults() == 1);
|
|
formalIndirectResult = params.claimNext();
|
|
} else {
|
|
SILType appliedResultTy = origMethodType->getDirectFormalResultsType();
|
|
indirectedResultTI =
|
|
&cast<LoadableTypeInfo>(IGM.getTypeInfo(appliedResultTy));
|
|
auto &nativeSchema = indirectedResultTI->nativeReturnValueSchema(IGM);
|
|
if (nativeSchema.requiresIndirect()) {
|
|
indirectedDirectResult = params.claimNext();
|
|
}
|
|
}
|
|
|
|
// Translate direct parameters passed indirectly.
|
|
Explosion translatedParams;
|
|
|
|
// Add the formal indirect return here.
|
|
if (formalIndirectResult)
|
|
translatedParams.add(formalIndirectResult);
|
|
|
|
// We already handled self.
|
|
assert(origMethodType->hasSelfParam());
|
|
auto origParamInfos = origMethodType->getParameters();
|
|
origParamInfos = origParamInfos.drop_back();
|
|
|
|
for (auto info : origParamInfos) {
|
|
// Addresses consist of a single pointer argument.
|
|
if (isIndirectFormalParameter(info.getConvention())) {
|
|
translatedParams.add(params.claimNext());
|
|
continue;
|
|
}
|
|
// Otherwise, we have a loadable type that can either be passed directly or
|
|
// indirectly.
|
|
assert(info.getSILStorageType().isObject());
|
|
auto curSILType = info.getSILStorageType();
|
|
auto &ti = cast<LoadableTypeInfo>(IGM.getTypeInfo(curSILType));
|
|
|
|
// Load the indirectly passed parameter.
|
|
auto &nativeSchema = ti.nativeParameterValueSchema(IGM);
|
|
if (nativeSchema.requiresIndirect()) {
|
|
Address paramAddr = ti.getAddressForPointer(params.claimNext());
|
|
ti.loadAsTake(subIGF, paramAddr, translatedParams);
|
|
continue;
|
|
}
|
|
// Map from the native calling convention into the explosion schema.
|
|
auto &nativeParamSchema = ti.nativeParameterValueSchema(IGM);
|
|
Explosion nativeParam;
|
|
params.transferInto(nativeParam, nativeParamSchema.size());
|
|
Explosion nonNativeParam = nativeParamSchema.mapFromNative(
|
|
subIGF.IGM, subIGF, nativeParam, curSILType);
|
|
assert(nativeParam.empty());
|
|
|
|
// Pass along the value.
|
|
ti.reexplode(subIGF, nonNativeParam, translatedParams);
|
|
}
|
|
|
|
// Prepare the call to the underlying method.
|
|
CallEmission emission(subIGF,
|
|
getObjCMethodCallee(subIGF, method, self,
|
|
CalleeInfo(origMethodType, origMethodType, {})));
|
|
|
|
emission.setArgs(translatedParams, false);
|
|
|
|
// Cleanup that always has to occur after the function call.
|
|
auto cleanup = [&]{
|
|
// Lifetime-extend 'self' by sending it to the autorelease pool if need be.
|
|
if (lifetimeExtendsSelf) {
|
|
subIGF.emitObjCRetainCall(self);
|
|
subIGF.emitObjCAutoreleaseCall(self);
|
|
}
|
|
// Release the context.
|
|
if (!resultType->isCalleeGuaranteed())
|
|
subIGF.emitNativeStrongRelease(context, subIGF.getDefaultAtomicity());
|
|
};
|
|
|
|
// Emit the call and produce the return value.
|
|
if (indirectedDirectResult) {
|
|
Address addr =
|
|
indirectedResultTI->getAddressForPointer(indirectedDirectResult);
|
|
emission.emitToMemory(addr, *indirectedResultTI, false);
|
|
cleanup();
|
|
subIGF.Builder.CreateRetVoid();
|
|
} else {
|
|
Explosion result;
|
|
emission.emitToExplosion(result, false);
|
|
cleanup();
|
|
auto &callee = emission.getCallee();
|
|
auto resultType =
|
|
callee.getOrigFunctionType()->getDirectFormalResultsType();
|
|
subIGF.emitScalarReturn(resultType, result, true /*isSwiftCCReturn*/,
|
|
false);
|
|
}
|
|
|
|
return fwd;
|
|
}
|
|
|
|
void irgen::emitObjCPartialApplication(IRGenFunction &IGF,
|
|
ObjCMethod method,
|
|
CanSILFunctionType origMethodType,
|
|
CanSILFunctionType resultType,
|
|
llvm::Value *self,
|
|
SILType selfType,
|
|
Explosion &out) {
|
|
// Create a heap object to contain the self argument.
|
|
// TODO: If function context arguments were given objc retain counts,
|
|
// we wouldn't need to create a separate heap object here.
|
|
auto *selfTypeInfo = &IGF.getTypeInfo(selfType);
|
|
HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal,
|
|
selfType, selfTypeInfo);
|
|
|
|
// FIXME: Either emit a descriptor for this or create a metadata kind
|
|
// that indicates its trivial layout.
|
|
auto Descriptor
|
|
= llvm::ConstantPointerNull::get(IGF.IGM.CaptureDescriptorPtrTy);
|
|
llvm::Value *data = IGF.emitUnmanagedAlloc(layout, "closure",
|
|
Descriptor);
|
|
// FIXME: non-fixed offsets
|
|
NonFixedOffsets offsets = None;
|
|
Address dataAddr = layout.emitCastTo(IGF, data);
|
|
auto &fieldLayout = layout.getElement(0);
|
|
auto &fieldType = layout.getElementTypes()[0];
|
|
Address fieldAddr = fieldLayout.project(IGF, dataAddr, offsets);
|
|
Explosion selfParams;
|
|
selfParams.add(self);
|
|
fieldLayout.getType().initializeFromParams(IGF, selfParams, fieldAddr,
|
|
fieldType, false);
|
|
|
|
// Create the forwarding stub.
|
|
llvm::Function *forwarder = emitObjCPartialApplicationForwarder(IGF.IGM,
|
|
method,
|
|
origMethodType,
|
|
resultType,
|
|
layout,
|
|
selfType);
|
|
llvm::Value *forwarderValue = IGF.Builder.CreateBitCast(forwarder,
|
|
IGF.IGM.Int8PtrTy);
|
|
|
|
// Emit the result explosion.
|
|
out.add(forwarderValue);
|
|
out.add(data);
|
|
}
|
|
|
|
/// Create the LLVM function declaration for a thunk that acts like
|
|
/// an Objective-C method for a Swift method implementation.
|
|
static llvm::Constant *findSwiftAsObjCThunk(IRGenModule &IGM, SILDeclRef ref) {
|
|
SILFunction *SILFn = IGM.getSILModule().lookUpFunction(ref);
|
|
assert(SILFn && "no IR function for swift-as-objc thunk");
|
|
auto fn = IGM.getAddrOfSILFunction(SILFn, NotForDefinition);
|
|
fn->setVisibility(llvm::GlobalValue::DefaultVisibility);
|
|
fn->setLinkage(llvm::GlobalValue::InternalLinkage);
|
|
fn->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
|
|
|
return llvm::ConstantExpr::getBitCast(fn, IGM.Int8PtrTy);
|
|
}
|
|
|
|
/// Produce a function pointer, suitable for invocation by
|
|
/// objc_msgSend, for the given property's getter method implementation.
|
|
///
|
|
/// Returns a value of type i8*.
|
|
static llvm::Constant *getObjCGetterPointer(IRGenModule &IGM,
|
|
AbstractStorageDecl *property) {
|
|
// Protocol properties have no impl.
|
|
if (isa<ProtocolDecl>(property->getDeclContext()))
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
SILDeclRef getter = SILDeclRef(property->getGetter(), SILDeclRef::Kind::Func)
|
|
.asForeign();
|
|
|
|
return findSwiftAsObjCThunk(IGM, getter);
|
|
}
|
|
|
|
/// Produce a function pointer, suitable for invocation by
|
|
/// objc_msgSend, for the given property's setter method implementation.
|
|
///
|
|
/// Returns a value of type i8*.
|
|
static llvm::Constant *getObjCSetterPointer(IRGenModule &IGM,
|
|
AbstractStorageDecl *property) {
|
|
// Protocol properties have no impl.
|
|
if (isa<ProtocolDecl>(property->getDeclContext()))
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
assert(property->isSettable(property->getDeclContext()) &&
|
|
"property is not settable?!");
|
|
|
|
SILDeclRef setter = SILDeclRef(property->getSetter(), SILDeclRef::Kind::Func)
|
|
.asForeign();
|
|
|
|
return findSwiftAsObjCThunk(IGM, setter);
|
|
}
|
|
|
|
/// Produce a function pointer, suitable for invocation by
|
|
/// objc_msgSend, for the given method implementation.
|
|
///
|
|
/// Returns a value of type i8*.
|
|
static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM,
|
|
FuncDecl *method) {
|
|
// Protocol methods have no impl.
|
|
if (isa<ProtocolDecl>(method->getDeclContext()))
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
SILDeclRef declRef = SILDeclRef(method, SILDeclRef::Kind::Func)
|
|
.asForeign();
|
|
|
|
return findSwiftAsObjCThunk(IGM, declRef);
|
|
}
|
|
|
|
/// Produce a function pointer, suitable for invocation by
|
|
/// objc_msgSend, for the given constructor implementation.
|
|
///
|
|
/// Returns a value of type i8*.
|
|
static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM,
|
|
ConstructorDecl *constructor) {
|
|
// Protocol methods have no impl.
|
|
if (isa<ProtocolDecl>(constructor->getDeclContext()))
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
SILDeclRef declRef = SILDeclRef(constructor, SILDeclRef::Kind::Initializer)
|
|
.asForeign();
|
|
|
|
return findSwiftAsObjCThunk(IGM, declRef);
|
|
}
|
|
|
|
/// Produce a function pointer, suitable for invocation by
|
|
/// objc_msgSend, for the given destructor implementation.
|
|
///
|
|
/// Returns a value of type i8*.
|
|
static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM,
|
|
DestructorDecl *destructor) {
|
|
SILDeclRef declRef = SILDeclRef(destructor, SILDeclRef::Kind::Deallocator)
|
|
.asForeign();
|
|
|
|
return findSwiftAsObjCThunk(IGM, declRef);
|
|
}
|
|
|
|
static SILDeclRef getObjCMethodRef(AbstractFunctionDecl *method) {
|
|
if (isa<ConstructorDecl>(method))
|
|
return SILDeclRef(method, SILDeclRef::Kind::Initializer).asForeign();
|
|
if (isa<DestructorDecl>(method))
|
|
return SILDeclRef(method, SILDeclRef::Kind::Deallocator).asForeign();
|
|
return SILDeclRef(method, SILDeclRef::Kind::Func).asForeign();
|
|
}
|
|
|
|
static CanSILFunctionType getObjCMethodType(IRGenModule &IGM,
|
|
AbstractFunctionDecl *method) {
|
|
return IGM.getSILTypes().getConstantFunctionType(getObjCMethodRef(method));
|
|
}
|
|
|
|
static clang::CanQualType getObjCPropertyType(IRGenModule &IGM,
|
|
VarDecl *property) {
|
|
// Use the lowered return type of the foreign getter.
|
|
auto getter = property->getGetter();
|
|
assert(getter);
|
|
CanSILFunctionType methodTy = getObjCMethodType(IGM, getter);
|
|
return IGM.getClangType(
|
|
methodTy->getFormalCSemanticResult().getSwiftRValueType());
|
|
}
|
|
|
|
void irgen::getObjCEncodingForPropertyType(IRGenModule &IGM,
|
|
VarDecl *property, std::string &s) {
|
|
// FIXME: Property encoding differs in slight ways that aren't publicly
|
|
// exposed from Clang.
|
|
IGM.getClangASTContext()
|
|
.getObjCEncodingForPropertyType(getObjCPropertyType(IGM, property), s);
|
|
}
|
|
|
|
static void
|
|
HelperGetObjCEncodingForType(const clang::ASTContext &Context,
|
|
clang::CanQualType T,
|
|
std::string &S, bool Extended) {
|
|
|
|
Context.getObjCEncodingForMethodParameter(clang::Decl::OBJC_TQ_None,
|
|
T, S, Extended);
|
|
}
|
|
|
|
static llvm::Constant *getObjCEncodingForTypes(IRGenModule &IGM,
|
|
SILType resultType,
|
|
ArrayRef<SILParameterInfo> params,
|
|
StringRef fixedParamsString,
|
|
Size::int_type parmOffset,
|
|
bool useExtendedEncoding) {
|
|
auto &clangASTContext = IGM.getClangASTContext();
|
|
|
|
std::string encodingString;
|
|
|
|
// Return type.
|
|
{
|
|
auto clangType = IGM.getClangType(resultType.getSwiftRValueType());
|
|
if (clangType.isNull())
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
HelperGetObjCEncodingForType(clangASTContext, clangType, encodingString,
|
|
useExtendedEncoding);
|
|
}
|
|
|
|
// Parameter types.
|
|
// TODO. Encode type qualifier, 'in', 'inout', etc. for the parameter.
|
|
std::string paramsString;
|
|
for (auto param : params) {
|
|
auto clangType = IGM.getClangType(param.getType());
|
|
if (clangType.isNull())
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
// TODO. Some stuff related to Array and Function type is missing.
|
|
// TODO. Encode type qualifier, 'in', 'inout', etc. for the parameter.
|
|
HelperGetObjCEncodingForType(clangASTContext, clangType, paramsString,
|
|
useExtendedEncoding);
|
|
paramsString += llvm::itostr(parmOffset);
|
|
clang::CharUnits sz = clangASTContext.getObjCEncodingTypeSize(clangType);
|
|
parmOffset += sz.getQuantity();
|
|
}
|
|
|
|
encodingString += llvm::itostr(parmOffset);
|
|
encodingString += fixedParamsString;
|
|
encodingString += paramsString;
|
|
return IGM.getAddrOfGlobalString(encodingString);
|
|
}
|
|
|
|
static llvm::Constant *getObjCEncodingForMethodType(IRGenModule &IGM,
|
|
CanSILFunctionType fnType,
|
|
bool useExtendedEncoding) {
|
|
SILType resultType = fnType->getFormalCSemanticResult();
|
|
|
|
// Get the inputs without 'self'.
|
|
auto inputs = fnType->getParameters().drop_back();
|
|
|
|
// Include the encoding for 'self' and '_cmd'.
|
|
llvm::SmallString<8> specialParams;
|
|
specialParams += "@0:";
|
|
auto ptrSize = IGM.getPointerSize().getValue();
|
|
specialParams += llvm::itostr(ptrSize);
|
|
GenericContextScope scope(IGM, fnType->getGenericSignature());
|
|
return getObjCEncodingForTypes(IGM, resultType, inputs, specialParams,
|
|
ptrSize * 2, useExtendedEncoding);
|
|
}
|
|
|
|
/// Emit the components of an Objective-C method descriptor: its selector,
|
|
/// type encoding, and IMP pointer.
|
|
void irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM,
|
|
AbstractFunctionDecl *method,
|
|
bool extendedEncoding,
|
|
bool concrete,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
Selector selector(method);
|
|
|
|
/// The first element is the selector.
|
|
selectorRef = IGM.getAddrOfObjCMethodName(selector.str());
|
|
|
|
/// The second element is the type @encoding.
|
|
CanSILFunctionType methodType = getObjCMethodType(IGM, method);
|
|
atEncoding = getObjCEncodingForMethodType(IGM, methodType, extendedEncoding);
|
|
|
|
/// The third element is the method implementation pointer.
|
|
if (!concrete) {
|
|
impl = nullptr;
|
|
return;
|
|
}
|
|
|
|
if (auto func = dyn_cast<FuncDecl>(method))
|
|
impl = getObjCMethodPointer(IGM, func);
|
|
else if (auto ctor = dyn_cast<ConstructorDecl>(method))
|
|
impl = getObjCMethodPointer(IGM, ctor);
|
|
else
|
|
impl = getObjCMethodPointer(IGM, cast<DestructorDecl>(method));
|
|
}
|
|
|
|
/// Emit the components of an Objective-C method descriptor for a
|
|
/// property getter method.
|
|
void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM,
|
|
VarDecl *property,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
Selector getterSel(property, Selector::ForGetter);
|
|
selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str());
|
|
|
|
auto clangType = getObjCPropertyType(IGM, property);
|
|
if (clangType.isNull()) {
|
|
atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
return;
|
|
}
|
|
|
|
auto &clangASTContext = IGM.getClangASTContext();
|
|
std::string TypeStr;
|
|
clangASTContext.getObjCEncodingForType(clangType, TypeStr);
|
|
|
|
Size PtrSize = IGM.getPointerSize();
|
|
Size::int_type ParmOffset = 2 * PtrSize.getValue();
|
|
|
|
TypeStr += llvm::itostr(ParmOffset);
|
|
TypeStr += "@0:";
|
|
TypeStr += llvm::itostr(PtrSize.getValue());
|
|
atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str());
|
|
impl = getObjCGetterPointer(IGM, property);
|
|
}
|
|
|
|
/// Emit the components of an Objective-C method descriptor for a
|
|
/// subscript getter method.
|
|
void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM,
|
|
SubscriptDecl *subscript,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
Selector getterSel(subscript, Selector::ForGetter);
|
|
selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str());
|
|
atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
impl = getObjCGetterPointer(IGM, subscript);
|
|
}
|
|
|
|
void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM,
|
|
AbstractStorageDecl *decl,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
if (auto sub = dyn_cast<SubscriptDecl>(decl)) {
|
|
return emitObjCGetterDescriptorParts(IGM, sub,
|
|
selectorRef, atEncoding, impl);
|
|
}
|
|
if (auto var = dyn_cast<VarDecl>(decl)) {
|
|
return emitObjCGetterDescriptorParts(IGM, var,
|
|
selectorRef, atEncoding, impl);
|
|
}
|
|
llvm_unreachable("unknown storage!");
|
|
}
|
|
|
|
/// Emit the components of an Objective-C method descriptor for a
|
|
/// property getter method.
|
|
void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM,
|
|
VarDecl *property,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
assert(property->isSettable(property->getDeclContext()) &&
|
|
"not a settable property?!");
|
|
|
|
Selector setterSel(property, Selector::ForSetter);
|
|
selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str());
|
|
|
|
auto &clangASTContext = IGM.getClangASTContext();
|
|
std::string TypeStr;
|
|
auto clangType = clangASTContext.VoidTy;
|
|
clangASTContext.getObjCEncodingForType(clangType, TypeStr);
|
|
|
|
Size PtrSize = IGM.getPointerSize();
|
|
Size::int_type ParmOffset = 2 * PtrSize.getValue();
|
|
|
|
clangType = getObjCPropertyType(IGM, property);
|
|
if (clangType.isNull()) {
|
|
atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
return;
|
|
}
|
|
clang::CharUnits sz = clangASTContext.getObjCEncodingTypeSize(clangType);
|
|
if (!sz.isZero())
|
|
ParmOffset += sz.getQuantity();
|
|
TypeStr += llvm::itostr(ParmOffset);
|
|
TypeStr += "@0:";
|
|
TypeStr += llvm::itostr(PtrSize.getValue());
|
|
ParmOffset = 2 * PtrSize.getValue();
|
|
clangASTContext.getObjCEncodingForType(clangType, TypeStr);
|
|
TypeStr += llvm::itostr(ParmOffset);
|
|
atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str());
|
|
|
|
impl = getObjCSetterPointer(IGM, property);
|
|
}
|
|
|
|
/// Emit the components of an Objective-C method descriptor for a
|
|
/// subscript getter method.
|
|
void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM,
|
|
SubscriptDecl *subscript,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
assert(subscript->isSettable() && "not a settable subscript?!");
|
|
|
|
Selector setterSel(subscript, Selector::ForSetter);
|
|
selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str());
|
|
atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
impl = getObjCSetterPointer(IGM, subscript);
|
|
}
|
|
|
|
void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM,
|
|
AbstractStorageDecl *decl,
|
|
llvm::Constant *&selectorRef,
|
|
llvm::Constant *&atEncoding,
|
|
llvm::Constant *&impl) {
|
|
if (auto sub = dyn_cast<SubscriptDecl>(decl)) {
|
|
return emitObjCSetterDescriptorParts(IGM, sub,
|
|
selectorRef, atEncoding, impl);
|
|
}
|
|
if (auto var = dyn_cast<VarDecl>(decl)) {
|
|
return emitObjCSetterDescriptorParts(IGM, var,
|
|
selectorRef, atEncoding, impl);
|
|
}
|
|
llvm_unreachable("unknown storage!");
|
|
}
|
|
|
|
static void buildMethodDescriptor(ConstantArrayBuilder &descriptors,
|
|
llvm::Constant *selectorRef,
|
|
llvm::Constant *atEncoding,
|
|
llvm::Constant *impl) {
|
|
auto descriptor = descriptors.beginStruct();
|
|
descriptor.add(selectorRef);
|
|
descriptor.add(atEncoding);
|
|
descriptor.add(impl);
|
|
descriptor.finishAndAddTo(descriptors);
|
|
}
|
|
|
|
/// Emit an Objective-C method descriptor for the given method.
|
|
/// struct method_t {
|
|
/// SEL name;
|
|
/// const char *types;
|
|
/// IMP imp;
|
|
/// };
|
|
void irgen::emitObjCMethodDescriptor(IRGenModule &IGM,
|
|
ConstantArrayBuilder &descriptors,
|
|
AbstractFunctionDecl *method) {
|
|
llvm::Constant *selectorRef, *atEncoding, *impl;
|
|
emitObjCMethodDescriptorParts(IGM, method,
|
|
/*extended*/ false,
|
|
/*concrete*/ true,
|
|
selectorRef, atEncoding, impl);
|
|
buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl);
|
|
}
|
|
|
|
void irgen::emitObjCIVarInitDestroyDescriptor(IRGenModule &IGM,
|
|
ConstantArrayBuilder &descriptors,
|
|
ClassDecl *cd,
|
|
llvm::Function *objcImpl,
|
|
bool isDestroyer) {
|
|
/// The first element is the selector.
|
|
SILDeclRef declRef = SILDeclRef(cd,
|
|
isDestroyer? SILDeclRef::Kind::IVarDestroyer
|
|
: SILDeclRef::Kind::IVarInitializer,
|
|
ResilienceExpansion::Minimal,
|
|
1,
|
|
/*foreign*/ true);
|
|
Selector selector(declRef);
|
|
auto selectorRef = IGM.getAddrOfObjCMethodName(selector.str());
|
|
|
|
/// The second element is the type @encoding, which is always "@?"
|
|
/// for a function type.
|
|
auto atEncoding = IGM.getAddrOfGlobalString("@?");
|
|
|
|
/// The third element is the method implementation pointer.
|
|
auto impl = llvm::ConstantExpr::getBitCast(objcImpl, IGM.Int8PtrTy);
|
|
|
|
// Form the method_t instance.
|
|
buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl);
|
|
}
|
|
|
|
llvm::Constant *
|
|
irgen::getMethodTypeExtendedEncoding(IRGenModule &IGM,
|
|
AbstractFunctionDecl *method) {
|
|
CanSILFunctionType methodType = getObjCMethodType(IGM, method);
|
|
return getObjCEncodingForMethodType(IGM, methodType, true/*Extended*/);
|
|
}
|
|
|
|
llvm::Constant *
|
|
irgen::getBlockTypeExtendedEncoding(IRGenModule &IGM,
|
|
CanSILFunctionType invokeTy) {
|
|
SILType resultType = invokeTy->getFormalCSemanticResult();
|
|
|
|
// Skip the storage pointer, which is encoded as '@?' to avoid the infinite
|
|
// recursion of the usual '@?<...>' rule for blocks.
|
|
auto paramTypes = invokeTy->getParameters().slice(1);
|
|
|
|
return getObjCEncodingForTypes(IGM, resultType, paramTypes,
|
|
"@?0", IGM.getPointerSize().getValue(),
|
|
/*extended*/ true);
|
|
}
|
|
|
|
void irgen::emitObjCGetterDescriptor(IRGenModule &IGM,
|
|
ConstantArrayBuilder &descriptors,
|
|
AbstractStorageDecl *storage) {
|
|
llvm::Constant *selectorRef, *atEncoding, *impl;
|
|
emitObjCGetterDescriptorParts(IGM, storage,
|
|
selectorRef, atEncoding, impl);
|
|
buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl);
|
|
}
|
|
|
|
void irgen::emitObjCSetterDescriptor(IRGenModule &IGM,
|
|
ConstantArrayBuilder &descriptors,
|
|
AbstractStorageDecl *storage) {
|
|
llvm::Constant *selectorRef, *atEncoding, *impl;
|
|
emitObjCSetterDescriptorParts(IGM, storage,
|
|
selectorRef, atEncoding, impl);
|
|
buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl);
|
|
}
|
|
|
|
bool irgen::requiresObjCMethodDescriptor(FuncDecl *method) {
|
|
// Property accessors should be generated alongside the property.
|
|
if (method->isAccessor())
|
|
return false;
|
|
|
|
return method->isObjC() || method->getAttrs().hasAttribute<IBActionAttr>();
|
|
}
|
|
|
|
bool irgen::requiresObjCMethodDescriptor(ConstructorDecl *constructor) {
|
|
return constructor->isObjC();
|
|
}
|
|
|
|
bool irgen::requiresObjCPropertyDescriptor(IRGenModule &IGM,
|
|
VarDecl *property) {
|
|
// Don't generate a descriptor for a property without any accessors.
|
|
// This is only possible in SIL files because Sema will normally
|
|
// implicitly synthesize accessors for @objc properties.
|
|
return property->isObjC() && property->getGetter();
|
|
}
|
|
|
|
bool irgen::requiresObjCSubscriptDescriptor(IRGenModule &IGM,
|
|
SubscriptDecl *subscript) {
|
|
return subscript->isObjC();
|
|
}
|
|
|
|
llvm::Value *IRGenFunction::emitBlockCopyCall(llvm::Value *value) {
|
|
// Get an appropriately-cast function pointer.
|
|
auto fn = IGM.getBlockCopyFn();
|
|
if (value->getType() != IGM.ObjCBlockPtrTy) {
|
|
auto fnTy = llvm::FunctionType::get(value->getType(), value->getType(),
|
|
false)->getPointerTo();
|
|
fn = llvm::ConstantExpr::getBitCast(fn, fnTy);
|
|
}
|
|
|
|
auto call = Builder.CreateCall(fn, value);
|
|
return call;
|
|
}
|
|
|
|
void IRGenFunction::emitBlockRelease(llvm::Value *value) {
|
|
// Get an appropriately-cast function pointer.
|
|
auto fn = IGM.getBlockReleaseFn();
|
|
if (value->getType() != IGM.ObjCBlockPtrTy) {
|
|
auto fnTy = llvm::FunctionType::get(IGM.VoidTy, value->getType(),
|
|
false)->getPointerTo();
|
|
fn = llvm::ConstantExpr::getBitCast(fn, fnTy);
|
|
}
|
|
auto call = Builder.CreateCall(fn, value);
|
|
call->setDoesNotThrow();
|
|
}
|