blob: e5d75d12d4ec558db335b8d03e19354dd2efdcc5 [file] [log] [blame]
//===--- 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();
}