| //===--- GenFunc.cpp - Swift IR Generation for Function Types -------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements IR generation for function types in Swift. This |
| // includes creating the IR type as well as capturing variables and |
| // performing calls. |
| // |
| // Swift supports three representations of functions: |
| // |
| // - thin, which are just a function pointer; |
| // |
| // - thick, which are a pair of a function pointer and |
| // an optional ref-counted opaque context pointer; and |
| // |
| // - block, which match the Apple blocks extension: a ref-counted |
| // pointer to a mostly-opaque structure with the function pointer |
| // stored at a fixed offset. |
| // |
| // The order of function parameters is as follows: |
| // |
| // - indirect return pointer |
| // - block context parameter, if applicable |
| // - expanded formal parameter types |
| // - implicit generic parameters |
| // - thick context parameter, if applicable |
| // - error result out-parameter, if applicable |
| // - witness_method generic parameters, if applicable |
| // |
| // The context and error parameters are last because they are |
| // optional: we'd like to be able to turn a thin function into a |
| // thick function, or a non-throwing function into a throwing one, |
| // without adding a thunk. A thick context parameter is required |
| // (but can be passed undef) if an error result is required. |
| // |
| // The additional generic parameters for witness methods follow the |
| // same logic: we'd like to be able to use non-generic method |
| // implementations directly as protocol witnesses if the rest of the |
| // ABI matches up. |
| // |
| // Note that some of this business with context parameters and error |
| // results is just IR formalism; on most of our targets, both of |
| // these are passed in registers. This is also why passing them |
| // as the final argument isn't bad for performance. |
| // |
| // For now, function pointer types are always stored as opaque |
| // pointers in LLVM IR; using a well-typed function type is |
| // very challenging because of issues with recursive type expansion, |
| // which can potentially introduce infinite types. For example: |
| // struct A { |
| // var fn: (A) -> () |
| // } |
| // Our CC lowering expands the fields of A into the argument list |
| // of A.fn, which is necessarily infinite. Attempting to use better |
| // types when not in a situation like this would just make the |
| // compiler complacent, leading to a long tail of undiscovered |
| // crashes. So instead we always store as i8* and require the |
| // bitcast whenever we change representations. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/Attr.h" |
| #include "swift/AST/Builtins.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/IRGenOptions.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/Pattern.h" |
| #include "swift/AST/PrettyStackTrace.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/Fallthrough.h" |
| #include "swift/SIL/SILModule.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/DeclGroup.h" |
| #include "clang/AST/RecordLayout.h" |
| #include "clang/CodeGen/CodeGenABITypes.h" |
| #include "clang/CodeGen/ModuleBuilder.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/IR/CallSite.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/ADT/StringSwitch.h" |
| |
| #include "IndirectTypeInfo.h" |
| #include "CallingConvention.h" |
| #include "CallEmission.h" |
| #include "EnumPayload.h" |
| #include "Explosion.h" |
| #include "GenClass.h" |
| #include "GenHeap.h" |
| #include "GenMeta.h" |
| #include "GenObjC.h" |
| #include "GenPoly.h" |
| #include "GenProto.h" |
| #include "GenType.h" |
| #include "HeapTypeInfo.h" |
| #include "IRGenDebugInfo.h" |
| #include "IRGenFunction.h" |
| #include "IRGenModule.h" |
| #include "FixedTypeInfo.h" |
| #include "ScalarTypeInfo.h" |
| #include "GenFunc.h" |
| |
| using namespace swift; |
| using namespace irgen; |
| |
| bool ExplosionSchema::requiresIndirectResult(IRGenModule &IGM) const { |
| return containsAggregate() || |
| size() > IGM.TargetInfo.MaxScalarsForDirectResult; |
| } |
| |
| bool ExplosionSchema::requiresIndirectParameter(IRGenModule &IGM) const { |
| // For now, use the same condition as requiresIndirectSchema. We may want |
| // to diverge at some point. |
| return requiresIndirectResult(IGM); |
| } |
| |
| llvm::Type *ExplosionSchema::getScalarResultType(IRGenModule &IGM) const { |
| if (size() == 0) { |
| return IGM.VoidTy; |
| } else if (size() == 1) { |
| return begin()->getScalarType(); |
| } else { |
| SmallVector<llvm::Type*, 16> elts; |
| for (auto &elt : *this) elts.push_back(elt.getScalarType()); |
| return llvm::StructType::get(IGM.getLLVMContext(), elts); |
| } |
| } |
| |
| static void addDereferenceableAttributeToBuilder(IRGenModule &IGM, |
| llvm::AttrBuilder &b, |
| const TypeInfo &ti) { |
| // The addresses of empty values are undefined, so we can't safely mark them |
| // dereferenceable. |
| if (ti.isKnownEmpty()) |
| return; |
| |
| // If we know the type to have a fixed nonempty size, then the pointer is |
| // dereferenceable to at least that size. |
| // TODO: Would be nice to have a "getMinimumKnownSize" on TypeInfo for |
| // dynamic-layout aggregates. |
| if (auto fixedTI = dyn_cast<FixedTypeInfo>(&ti)) { |
| b.addAttribute( |
| llvm::Attribute::getWithDereferenceableBytes(IGM.LLVMContext, |
| fixedTI->getFixedSize().getValue())); |
| } |
| } |
| |
| static void addIndirectValueParameterAttributes(IRGenModule &IGM, |
| llvm::AttributeSet &attrs, |
| const TypeInfo &ti, |
| unsigned argIndex) { |
| llvm::AttrBuilder b; |
| // Value parameter pointers can't alias or be captured. |
| b.addAttribute(llvm::Attribute::NoAlias); |
| b.addAttribute(llvm::Attribute::NoCapture); |
| // The parameter must reference dereferenceable memory of the type. |
| addDereferenceableAttributeToBuilder(IGM, b, ti); |
| |
| auto resultAttrs = llvm::AttributeSet::get(IGM.LLVMContext, argIndex+1, b); |
| attrs = attrs.addAttributes(IGM.LLVMContext, argIndex+1, resultAttrs); |
| } |
| |
| static void addInoutParameterAttributes(IRGenModule &IGM, |
| llvm::AttributeSet &attrs, |
| const TypeInfo &ti, |
| unsigned argIndex, |
| bool aliasable) { |
| llvm::AttrBuilder b; |
| // Aliasing inouts is unspecified, but we still want aliasing to be memory- |
| // safe, so we can't mark inouts as noalias at the LLVM level. |
| // They still can't be captured without doing unsafe stuff, though. |
| b.addAttribute(llvm::Attribute::NoCapture); |
| // The inout must reference dereferenceable memory of the type. |
| addDereferenceableAttributeToBuilder(IGM, b, ti); |
| |
| auto resultAttrs = llvm::AttributeSet::get(IGM.LLVMContext, argIndex+1, b); |
| attrs = attrs.addAttributes(IGM.LLVMContext, argIndex+1, resultAttrs); |
| } |
| |
| void ExplosionSchema::addToArgTypes(IRGenModule &IGM, |
| const TypeInfo &TI, |
| llvm::AttributeSet &Attrs, |
| SmallVectorImpl<llvm::Type*> &types) const { |
| // Pass large arguments as indirect value parameters. |
| if (requiresIndirectParameter(IGM)) { |
| addIndirectValueParameterAttributes(IGM, Attrs, TI, types.size()); |
| types.push_back(TI.getStorageType()->getPointerTo()); |
| return; |
| } |
| for (auto &elt : *this) { |
| if (elt.isAggregate()) |
| types.push_back(elt.getAggregateType()->getPointerTo()); |
| else |
| types.push_back(elt.getScalarType()); |
| } |
| } |
| |
| static llvm::CallingConv::ID getFreestandingConvention(IRGenModule &IGM) { |
| // TODO: use a custom CC that returns three scalars efficiently |
| return llvm::CallingConv::C; |
| } |
| |
| /// Expand the requirements of the given abstract calling convention |
| /// into a "physical" calling convention. |
| llvm::CallingConv::ID irgen::expandCallingConv(IRGenModule &IGM, |
| SILFunctionTypeRepresentation convention) { |
| switch (convention) { |
| case SILFunctionTypeRepresentation::CFunctionPointer: |
| case SILFunctionTypeRepresentation::ObjCMethod: |
| case SILFunctionTypeRepresentation::Block: |
| return llvm::CallingConv::C; |
| |
| case SILFunctionTypeRepresentation::Method: |
| case SILFunctionTypeRepresentation::WitnessMethod: |
| // TODO: maybe add 'inreg' to the first non-result argument. |
| SWIFT_FALLTHROUGH; |
| case SILFunctionTypeRepresentation::Thin: |
| case SILFunctionTypeRepresentation::Thick: |
| return getFreestandingConvention(IGM); |
| } |
| llvm_unreachable("bad calling convention!"); |
| } |
| |
| namespace { |
| /// The natural form of the result of performing a call. A call |
| /// result may be indirect, in which case it is returned in memory |
| /// whose address is passed as an implicit first argument, or it may |
| /// be direct. |
| class CallResult { |
| union Value { |
| /// The buffer for the set of direct values produced by the call. |
| /// This can be greater than the normal cap on scalar values if |
| /// the actual call is inlined or builtin. |
| Explosion Direct; |
| |
| /// The address into which to emit an indirect call. If this is |
| /// set, the call will be evaluated (as an initialization) into |
| /// this address; otherwise, memory will be allocated on the stack. |
| Address Indirect; |
| |
| Value() {} |
| ~Value() {} |
| }; |
| |
| enum class State { |
| Invalid, Indirect, Direct |
| }; |
| |
| Value CurValue; |
| State CurState; |
| |
| public: |
| CallResult() : CurState(State::Invalid) {} |
| ~CallResult() { reset(); } |
| |
| /// Configure this result to carry a number of direct values at |
| /// the given explosion level. |
| Explosion &initForDirectValues() { |
| assert(CurState == State::Invalid); |
| CurState = State::Direct; |
| return *new (&CurValue.Direct) Explosion(); |
| } |
| |
| /// As a potential efficiency, set that this is a direct result |
| /// with no values. |
| void setAsEmptyDirect() { |
| initForDirectValues(); |
| } |
| |
| /// Set this result so that it carries a single directly-returned |
| /// maximally-fragile value without management. |
| void setAsSingleDirectUnmanagedFragileValue(llvm::Value *value) { |
| initForDirectValues().add(value); |
| } |
| |
| void setAsIndirectAddress(Address address) { |
| assert(CurState == State::Invalid); |
| CurState = State::Indirect; |
| CurValue.Indirect = address; |
| } |
| |
| bool isInvalid() const { return CurState == State::Invalid; } |
| bool isDirect() const { return CurState == State::Direct; } |
| bool isIndirect() const { return CurState == State::Indirect; } |
| |
| Explosion &getDirectValues() { |
| assert(isDirect()); |
| return CurValue.Direct; |
| } |
| |
| Address getIndirectAddress() const { |
| assert(isIndirect()); |
| return CurValue.Indirect; |
| } |
| |
| void reset() { |
| if (CurState == State::Direct) |
| CurValue.Direct.~Explosion(); |
| CurState = State::Invalid; |
| } |
| }; |
| |
| /// A signature represents something which can actually be called. |
| class Signature { |
| llvm::PointerIntPair<llvm::FunctionType*, 1, bool> TypeAndHasIndirectReturn; |
| llvm::AttributeSet Attributes; |
| |
| public: |
| bool isValid() const { |
| return TypeAndHasIndirectReturn.getPointer() != nullptr; |
| } |
| |
| void set(llvm::FunctionType *type, bool hasIndirectReturn, |
| llvm::AttributeSet attrs) { |
| TypeAndHasIndirectReturn.setPointer(type); |
| TypeAndHasIndirectReturn.setInt(hasIndirectReturn); |
| Attributes = attrs; |
| assert(isValid()); |
| } |
| |
| llvm::FunctionType *getType() const { |
| assert(isValid()); |
| return TypeAndHasIndirectReturn.getPointer(); |
| } |
| |
| bool hasIndirectReturn() const { |
| assert(isValid()); |
| return TypeAndHasIndirectReturn.getInt(); |
| } |
| |
| llvm::AttributeSet getAttributes() const { |
| return Attributes; |
| } |
| }; |
| |
| /// Information about the IR-level signature of a function type. |
| class FuncSignatureInfo { |
| private: |
| /// The SIL function type being represented. |
| const CanSILFunctionType FormalType; |
| |
| mutable Signature TheSignature; |
| |
| public: |
| FuncSignatureInfo(CanSILFunctionType formalType) |
| : FormalType(formalType) {} |
| |
| Signature getSignature(IRGenModule &IGM) const; |
| }; |
| |
| /// The @thin function type-info class. |
| class ThinFuncTypeInfo : public PODSingleScalarTypeInfo<ThinFuncTypeInfo, |
| LoadableTypeInfo>, |
| public FuncSignatureInfo { |
| ThinFuncTypeInfo(CanSILFunctionType formalType, llvm::Type *storageType, |
| Size size, Alignment align, |
| const SpareBitVector &spareBits) |
| : PODSingleScalarTypeInfo(storageType, size, spareBits, align), |
| FuncSignatureInfo(formalType) |
| { |
| } |
| |
| public: |
| static const ThinFuncTypeInfo *create(CanSILFunctionType formalType, |
| llvm::Type *storageType, |
| Size size, Alignment align, |
| const SpareBitVector &spareBits) { |
| return new ThinFuncTypeInfo(formalType, storageType, size, align, |
| spareBits); |
| } |
| |
| bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { |
| return true; |
| } |
| |
| unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { |
| return getFunctionPointerExtraInhabitantCount(IGM); |
| } |
| |
| APInt getFixedExtraInhabitantValue(IRGenModule &IGM, |
| unsigned bits, |
| unsigned index) const override { |
| return getFunctionPointerFixedExtraInhabitantValue(IGM, bits, index, 0); |
| } |
| |
| llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, |
| SILType T) |
| const override { |
| return getFunctionPointerExtraInhabitantIndex(IGF, src); |
| } |
| |
| void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, |
| Address dest, SILType T) const override { |
| return storeFunctionPointerExtraInhabitant(IGF, index, dest); |
| } |
| }; |
| |
| /// The @thick function type-info class. |
| class FuncTypeInfo : public ScalarTypeInfo<FuncTypeInfo, ReferenceTypeInfo>, |
| public FuncSignatureInfo { |
| FuncTypeInfo(CanSILFunctionType formalType, llvm::StructType *storageType, |
| Size size, Alignment align, SpareBitVector &&spareBits) |
| : ScalarTypeInfo(storageType, size, std::move(spareBits), align), |
| FuncSignatureInfo(formalType) |
| { |
| } |
| |
| public: |
| static const FuncTypeInfo *create(CanSILFunctionType formalType, |
| llvm::StructType *storageType, |
| Size size, Alignment align, |
| SpareBitVector &&spareBits) { |
| return new FuncTypeInfo(formalType, storageType, size, align, |
| std::move(spareBits)); |
| } |
| |
| // Function types do not satisfy allowsOwnership. |
| const WeakTypeInfo * |
| createWeakStorageType(TypeConverter &TC) const override { |
| llvm_unreachable("[weak] function type"); |
| } |
| const TypeInfo * |
| createUnownedStorageType(TypeConverter &TC) const override { |
| llvm_unreachable("[unowned] function type"); |
| } |
| const TypeInfo * |
| createUnmanagedStorageType(TypeConverter &TC) const override { |
| llvm_unreachable("@unowned(unsafe) function type"); |
| } |
| |
| llvm::StructType *getStorageType() const { |
| return cast<llvm::StructType>(TypeInfo::getStorageType()); |
| } |
| |
| unsigned getExplosionSize() const override { |
| return 2; |
| } |
| |
| void getSchema(ExplosionSchema &schema) const override { |
| llvm::StructType *structTy = cast<llvm::StructType>(getStorageType()); |
| schema.add(ExplosionSchema::Element::forScalar(structTy->getElementType(0))); |
| schema.add(ExplosionSchema::Element::forScalar(structTy->getElementType(1))); |
| } |
| |
| Address projectFunction(IRGenFunction &IGF, Address address) const { |
| return IGF.Builder.CreateStructGEP(address, 0, Size(0), |
| address->getName() + ".fn"); |
| } |
| |
| Address projectData(IRGenFunction &IGF, Address address) const { |
| return IGF.Builder.CreateStructGEP(address, 1, IGF.IGM.getPointerSize(), |
| address->getName() + ".data"); |
| } |
| |
| void loadAsCopy(IRGenFunction &IGF, Address address, |
| Explosion &e) const override { |
| // Load the function. |
| Address fnAddr = projectFunction(IGF, address); |
| e.add(IGF.Builder.CreateLoad(fnAddr, fnAddr->getName()+".load")); |
| |
| Address dataAddr = projectData(IGF, address); |
| auto data = IGF.Builder.CreateLoad(dataAddr); |
| IGF.emitNativeStrongRetain(data); |
| e.add(data); |
| } |
| |
| void loadAsTake(IRGenFunction &IGF, Address addr, |
| Explosion &e) const override { |
| // Load the function. |
| Address fnAddr = projectFunction(IGF, addr); |
| e.add(IGF.Builder.CreateLoad(fnAddr)); |
| |
| Address dataAddr = projectData(IGF, addr); |
| e.add(IGF.Builder.CreateLoad(dataAddr)); |
| } |
| |
| void assign(IRGenFunction &IGF, Explosion &e, |
| Address address) const override { |
| // Store the function pointer. |
| Address fnAddr = projectFunction(IGF, address); |
| IGF.Builder.CreateStore(e.claimNext(), fnAddr); |
| |
| Address dataAddr = projectData(IGF, address); |
| IGF.emitNativeStrongAssign(e.claimNext(), dataAddr); |
| } |
| |
| void initialize(IRGenFunction &IGF, Explosion &e, |
| Address address) const override { |
| // Store the function pointer. |
| Address fnAddr = projectFunction(IGF, address); |
| IGF.Builder.CreateStore(e.claimNext(), fnAddr); |
| |
| // Store the data pointer, if any, transferring the +1. |
| Address dataAddr = projectData(IGF, address); |
| IGF.emitNativeStrongInit(e.claimNext(), dataAddr); |
| } |
| |
| void copy(IRGenFunction &IGF, Explosion &src, |
| Explosion &dest) const override { |
| src.transferInto(dest, 1); |
| auto data = src.claimNext(); |
| IGF.emitNativeStrongRetain(data); |
| dest.add(data); |
| } |
| |
| void consume(IRGenFunction &IGF, Explosion &src) const override { |
| src.claimNext(); |
| IGF.emitNativeStrongRelease(src.claimNext()); |
| } |
| |
| void fixLifetime(IRGenFunction &IGF, Explosion &src) const override { |
| src.claimNext(); |
| IGF.emitFixLifetime(src.claimNext()); |
| } |
| |
| void strongRetain(IRGenFunction &IGF, Explosion &e) const override { |
| e.claimNext(); |
| IGF.emitNativeStrongRetain(e.claimNext()); |
| } |
| |
| void strongRelease(IRGenFunction &IGF, Explosion &e) const override { |
| e.claimNext(); |
| IGF.emitNativeStrongRelease(e.claimNext()); |
| } |
| |
| void strongRetainUnowned(IRGenFunction &IGF, Explosion &e) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void strongRetainUnownedRelease(IRGenFunction &IGF, |
| Explosion &e) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedRetain(IRGenFunction &IGF, Explosion &e) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedRelease(IRGenFunction &IGF, Explosion &e) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedLoadStrong(IRGenFunction &IGF, Address src, |
| Explosion &out) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedTakeStrong(IRGenFunction &IGF, Address src, |
| Explosion &out) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedInit(IRGenFunction &IGF, Explosion &in, |
| Address dest) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void unownedAssign(IRGenFunction &IGF, Explosion &in, |
| Address dest) const override { |
| llvm_unreachable("unowned references to functions are not supported"); |
| } |
| |
| void destroy(IRGenFunction &IGF, Address addr, SILType T) const override { |
| auto data = IGF.Builder.CreateLoad(projectData(IGF, addr)); |
| IGF.emitNativeStrongRelease(data); |
| } |
| |
| void packIntoEnumPayload(IRGenFunction &IGF, |
| EnumPayload &payload, |
| Explosion &src, |
| unsigned offset) const override { |
| payload.insertValue(IGF, src.claimNext(), offset); |
| payload.insertValue(IGF, src.claimNext(), |
| offset + IGF.IGM.getPointerSize().getValueInBits()); |
| } |
| |
| void unpackFromEnumPayload(IRGenFunction &IGF, |
| const EnumPayload &payload, |
| Explosion &dest, |
| unsigned offset) const override { |
| auto storageTy = getStorageType(); |
| dest.add(payload.extractValue(IGF, storageTy->getElementType(0), offset)); |
| dest.add(payload.extractValue(IGF, storageTy->getElementType(1), |
| offset + IGF.IGM.getPointerSize().getValueInBits())); |
| } |
| |
| bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { |
| return true; |
| } |
| |
| unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { |
| return getFunctionPointerExtraInhabitantCount(IGM); |
| } |
| |
| APInt getFixedExtraInhabitantValue(IRGenModule &IGM, |
| unsigned bits, |
| unsigned index) const override { |
| return getFunctionPointerFixedExtraInhabitantValue(IGM, bits, index, 0); |
| } |
| |
| llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, |
| SILType T) const override { |
| src = projectFunction(IGF, src); |
| return getFunctionPointerExtraInhabitantIndex(IGF, src); |
| } |
| |
| APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override { |
| // Only the function pointer value is used for extra inhabitants. |
| auto pointerSize = IGM.getPointerSize().getValueInBits(); |
| APInt bits = APInt::getAllOnesValue(pointerSize); |
| bits = bits.zext(pointerSize * 2); |
| return bits; |
| } |
| |
| void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, |
| Address dest, SILType T) const override { |
| dest = projectFunction(IGF, dest); |
| return storeFunctionPointerExtraInhabitant(IGF, index, dest); |
| } |
| }; |
| |
| /// The type-info class for ObjC blocks, which are represented by an ObjC |
| /// heap pointer. |
| class BlockTypeInfo : public HeapTypeInfo<BlockTypeInfo>, |
| public FuncSignatureInfo |
| { |
| public: |
| BlockTypeInfo(CanSILFunctionType ty, |
| llvm::PointerType *storageType, |
| Size size, SpareBitVector spareBits, Alignment align) |
| : HeapTypeInfo(storageType, size, spareBits, align), |
| FuncSignatureInfo(ty) |
| { |
| } |
| |
| ReferenceCounting getReferenceCounting() const { |
| return ReferenceCounting::Block; |
| } |
| }; |
| |
| /// The type info class for the on-stack representation of an ObjC block. |
| /// |
| /// TODO: May not be fixed-layout if we capture generics. |
| class BlockStorageTypeInfo final |
| : public IndirectTypeInfo<BlockStorageTypeInfo, FixedTypeInfo> |
| { |
| Size CaptureOffset; |
| public: |
| BlockStorageTypeInfo(llvm::Type *type, Size size, Alignment align, |
| SpareBitVector &&spareBits, |
| IsPOD_t pod, IsBitwiseTakable_t bt, Size captureOffset) |
| : IndirectTypeInfo(type, size, std::move(spareBits), align, pod, bt, |
| IsFixedSize), |
| CaptureOffset(captureOffset) |
| {} |
| |
| // The lowered type should be an LLVM struct comprising the block header |
| // (IGM.ObjCBlockStructTy) as its first element and the capture as its |
| // second. |
| |
| Address projectBlockHeader(IRGenFunction &IGF, Address storage) const { |
| return IGF.Builder.CreateStructGEP(storage, 0, Size(0)); |
| } |
| |
| Address projectCapture(IRGenFunction &IGF, Address storage) const { |
| return IGF.Builder.CreateStructGEP(storage, 1, CaptureOffset); |
| } |
| |
| // TODO |
| // The frontend will currently never emit copy_addr or destroy_addr for |
| // block storage. |
| |
| void assignWithCopy(IRGenFunction &IGF, Address dest, |
| Address src, SILType T) const override { |
| IGF.unimplemented(SourceLoc(), "copying @block_storage"); |
| } |
| void initializeWithCopy(IRGenFunction &IGF, Address dest, |
| Address src, SILType T) const override { |
| IGF.unimplemented(SourceLoc(), "copying @block_storage"); |
| } |
| void destroy(IRGenFunction &IGF, Address addr, SILType T) const override { |
| IGF.unimplemented(SourceLoc(), "destroying @block_storage"); |
| } |
| }; |
| } |
| |
| const TypeInfo *TypeConverter::convertBlockStorageType(SILBlockStorageType *T) { |
| // The block storage consists of the block header (ObjCBlockStructTy) |
| // followed by the lowered type of the capture. |
| auto &capture = IGM.getTypeInfoForLowered(T->getCaptureType()); |
| |
| // TODO: Support dynamic-sized captures. |
| const FixedTypeInfo *fixedCapture = dyn_cast<FixedTypeInfo>(&capture); |
| llvm::Type *fixedCaptureTy; |
| // The block header is pointer aligned. The capture may be worse aligned. |
| Alignment align = IGM.getPointerAlignment(); |
| Size captureOffset( |
| IGM.DataLayout.getStructLayout(IGM.ObjCBlockStructTy)->getSizeInBytes()); |
| Size size = captureOffset; |
| SpareBitVector spareBits = |
| SpareBitVector::getConstant(size.getValueInBits(), false); |
| IsPOD_t pod = IsNotPOD; |
| IsBitwiseTakable_t bt = IsNotBitwiseTakable; |
| if (!fixedCapture) { |
| IGM.unimplemented(SourceLoc(), "dynamic @block_storage capture"); |
| fixedCaptureTy = llvm::StructType::get(IGM.getLLVMContext(), {}); |
| } else { |
| fixedCaptureTy = cast<FixedTypeInfo>(capture).getStorageType(); |
| align = std::max(align, fixedCapture->getFixedAlignment()); |
| captureOffset = captureOffset.roundUpToAlignment(align); |
| spareBits.extendWithSetBits(captureOffset.getValueInBits()); |
| size = captureOffset + fixedCapture->getFixedSize(); |
| spareBits.append(fixedCapture->getSpareBits()); |
| pod = fixedCapture->isPOD(ResilienceScope::Component); |
| bt = fixedCapture->isBitwiseTakable(ResilienceScope::Component); |
| } |
| |
| llvm::Type *storageElts[] = { |
| IGM.ObjCBlockStructTy, |
| fixedCaptureTy, |
| }; |
| |
| auto storageTy = llvm::StructType::get(IGM.getLLVMContext(), storageElts, |
| /*packed*/ false); |
| return new BlockStorageTypeInfo(storageTy, size, align, std::move(spareBits), |
| pod, bt, captureOffset); |
| } |
| |
| Address irgen::projectBlockStorageCapture(IRGenFunction &IGF, |
| Address storageAddr, |
| CanSILBlockStorageType storageTy) { |
| auto &tl = IGF.getTypeInfoForLowered(storageTy).as<BlockStorageTypeInfo>(); |
| return tl.projectCapture(IGF, storageAddr); |
| } |
| |
| const TypeInfo *TypeConverter::convertFunctionType(SILFunctionType *T) { |
| switch (T->getRepresentation()) { |
| case SILFunctionType::Representation::Block: |
| return new BlockTypeInfo(CanSILFunctionType(T), |
| IGM.ObjCBlockPtrTy, |
| IGM.getPointerSize(), |
| IGM.getHeapObjectSpareBits(), |
| IGM.getPointerAlignment()); |
| |
| case SILFunctionType::Representation::Thin: |
| case SILFunctionType::Representation::Method: |
| case SILFunctionType::Representation::WitnessMethod: |
| case SILFunctionType::Representation::ObjCMethod: |
| case SILFunctionType::Representation::CFunctionPointer: |
| return ThinFuncTypeInfo::create(CanSILFunctionType(T), |
| IGM.FunctionPtrTy, |
| IGM.getPointerSize(), |
| IGM.getPointerAlignment(), |
| IGM.getFunctionPointerSpareBits()); |
| |
| case SILFunctionType::Representation::Thick: { |
| SpareBitVector spareBits; |
| spareBits.append(IGM.getFunctionPointerSpareBits()); |
| spareBits.append(IGM.getHeapObjectSpareBits()); |
| |
| return FuncTypeInfo::create(CanSILFunctionType(T), |
| IGM.FunctionPairTy, |
| IGM.getPointerSize() * 2, |
| IGM.getPointerAlignment(), |
| std::move(spareBits)); |
| } |
| } |
| llvm_unreachable("bad function type representation"); |
| } |
| |
| void irgen::addIndirectReturnAttributes(IRGenModule &IGM, |
| llvm::AttributeSet &attrs) { |
| static const llvm::Attribute::AttrKind attrKinds[] = { |
| llvm::Attribute::StructRet, |
| llvm::Attribute::NoAlias, |
| llvm::Attribute::NoCapture, |
| }; |
| auto resultAttrs = llvm::AttributeSet::get(IGM.LLVMContext, 1, attrKinds); |
| attrs = attrs.addAttributes(IGM.LLVMContext, 1, resultAttrs); |
| } |
| |
| void irgen::addByvalArgumentAttributes(IRGenModule &IGM, |
| llvm::AttributeSet &attrs, |
| unsigned argIndex, |
| Alignment align) { |
| llvm::AttrBuilder b; |
| b.addAttribute(llvm::Attribute::ByVal); |
| b.addAttribute(llvm::Attribute::getWithAlignment(IGM.LLVMContext, |
| align.getValue())); |
| auto resultAttrs = llvm::AttributeSet::get(IGM.LLVMContext, argIndex+1, b); |
| attrs = attrs.addAttributes(IGM.LLVMContext, |
| argIndex+1, |
| resultAttrs); |
| } |
| |
| void irgen::addExtendAttribute(IRGenModule &IGM, |
| llvm::AttributeSet &attrs, |
| unsigned index, bool signExtend) { |
| llvm::AttrBuilder b; |
| if (signExtend) |
| b.addAttribute(llvm::Attribute::SExt); |
| else |
| b.addAttribute(llvm::Attribute::ZExt); |
| auto resultAttrs = llvm::AttributeSet::get(IGM.LLVMContext, index, b); |
| attrs = attrs.addAttributes(IGM.LLVMContext, index, resultAttrs); |
| } |
| |
| namespace { |
| class SignatureExpansion { |
| IRGenModule &IGM; |
| CanSILFunctionType FnType; |
| public: |
| SmallVector<llvm::Type*, 8> ParamIRTypes; |
| llvm::AttributeSet Attrs; |
| bool HasIndirectResult = false; |
| |
| SignatureExpansion(IRGenModule &IGM, CanSILFunctionType fnType) |
| : IGM(IGM), FnType(fnType) {} |
| |
| llvm::Type *expandSignatureTypes(); |
| |
| private: |
| void expand(SILParameterInfo param); |
| llvm::Type *addIndirectResult(); |
| |
| unsigned getCurParamIndex() { |
| return ParamIRTypes.size(); |
| } |
| |
| /// Add a pointer to the given type as the next parameter. |
| void addPointerParameter(llvm::Type *storageType) { |
| ParamIRTypes.push_back(storageType->getPointerTo()); |
| } |
| |
| llvm::Type *expandResult(); |
| void expandParameters(); |
| llvm::Type *expandExternalSignatureTypes(); |
| }; |
| } |
| |
| llvm::Type *SignatureExpansion::addIndirectResult() { |
| auto resultType = FnType->getResult().getSILType(); |
| const TypeInfo &resultTI = IGM.getTypeInfo(resultType); |
| addPointerParameter(resultTI.getStorageType()); |
| addIndirectReturnAttributes(IGM, Attrs); |
| return IGM.VoidTy; |
| } |
| |
| llvm::Type *SignatureExpansion::expandResult() { |
| // Handle the direct result type, checking for supposedly scalar |
| // result types that we actually want to return indirectly. |
| auto resultType = FnType->getResult().getSILType(); |
| |
| // Fast-path the empty tuple type. |
| if (auto tuple = resultType.getAs<TupleType>()) |
| if (tuple->getNumElements() == 0) |
| return IGM.VoidTy; |
| |
| ExplosionSchema schema = IGM.getSchema(resultType); |
| switch (FnType->getLanguage()) { |
| case SILFunctionLanguage::C: |
| llvm_unreachable("Expanding C/ObjC parameters in the wrong place!"); |
| break; |
| case SILFunctionLanguage::Swift: { |
| if (schema.requiresIndirectResult(IGM)) |
| return addIndirectResult(); |
| return schema.getScalarResultType(IGM); |
| } |
| } |
| } |
| |
| static const clang::FieldDecl * |
| getLargestUnionField(const clang::RecordDecl *record, |
| const clang::ASTContext &ctx) { |
| const clang::FieldDecl *largestField = nullptr; |
| clang::CharUnits unionSize = clang::CharUnits::Zero(); |
| |
| for (auto field : record->fields()) { |
| assert(!field->isBitField()); |
| clang::CharUnits fieldSize = ctx.getTypeSizeInChars(field->getType()); |
| if (unionSize < fieldSize) { |
| unionSize = fieldSize; |
| largestField = field; |
| } |
| } |
| assert(largestField && "empty union?"); |
| return largestField; |
| } |
| |
| namespace { |
| /// A CRTP class for working with Clang's ABIArgInfo::Expand |
| /// argument type expansions. |
| template <class Impl, class... Args> struct ClangExpand { |
| IRGenModule &IGM; |
| const clang::ASTContext &Ctx; |
| ClangExpand(IRGenModule &IGM) : IGM(IGM), Ctx(IGM.getClangASTContext()) {} |
| |
| Impl &asImpl() { return *static_cast<Impl*>(this); } |
| |
| void visit(clang::CanQualType type, Args... args) { |
| switch (type->getTypeClass()) { |
| #define TYPE(Class, Base) |
| #define NON_CANONICAL_TYPE(Class, Base) \ |
| case clang::Type::Class: |
| #define DEPENDENT_TYPE(Class, Base) \ |
| case clang::Type::Class: |
| #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) \ |
| case clang::Type::Class: |
| #include "clang/AST/TypeNodes.def" |
| llvm_unreachable("canonical or dependent type in ABI lowering"); |
| |
| // These shouldn't occur in expandable struct types. |
| case clang::Type::IncompleteArray: |
| case clang::Type::VariableArray: |
| llvm_unreachable("variable-sized or incomplete array in ABI lowering"); |
| |
| // We should only ever get ObjC pointers, not underlying objects. |
| case clang::Type::ObjCInterface: |
| case clang::Type::ObjCObject: |
| llvm_unreachable("ObjC object type in ABI lowering"); |
| |
| // We should only ever get function pointers. |
| case clang::Type::FunctionProto: |
| case clang::Type::FunctionNoProto: |
| llvm_unreachable("non-pointer function type in ABI lowering"); |
| |
| // We currently never import C++ code, and we should be able to |
| // kill Expand before we do. |
| case clang::Type::LValueReference: |
| case clang::Type::RValueReference: |
| case clang::Type::MemberPointer: |
| case clang::Type::Auto: |
| llvm_unreachable("C++ type in ABI lowering?"); |
| |
| case clang::Type::ConstantArray: { |
| auto array = Ctx.getAsConstantArrayType(type); |
| auto elt = Ctx.getCanonicalType(array->getElementType()); |
| auto &&context = asImpl().beginArrayElements(elt); |
| uint64_t n = array->getSize().getZExtValue(); |
| for (uint64_t i = 0; i != n; ++i) { |
| asImpl().visitArrayElement(elt, i, context, args...); |
| } |
| return; |
| } |
| |
| case clang::Type::Record: { |
| auto record = cast<clang::RecordType>(type)->getDecl(); |
| if (record->isUnion()) { |
| auto largest = getLargestUnionField(record, Ctx); |
| asImpl().visitUnionField(record, largest, args...); |
| } else { |
| auto &&context = asImpl().beginStructFields(record); |
| for (auto field : record->fields()) { |
| asImpl().visitStructField(record, field, context, args...); |
| } |
| } |
| return; |
| } |
| |
| case clang::Type::Complex: { |
| auto elt = type.castAs<clang::ComplexType>().getElementType(); |
| asImpl().visitComplexElement(elt, 0, args...); |
| asImpl().visitComplexElement(elt, 1, args...); |
| return; |
| } |
| |
| // Just handle this types as opaque integers. |
| case clang::Type::Enum: |
| case clang::Type::Atomic: |
| asImpl().visitScalar(convertTypeAsInteger(type), args...); |
| return; |
| |
| case clang::Type::Builtin: |
| asImpl().visitScalar( |
| convertBuiltinType(type.castAs<clang::BuiltinType>()), |
| args...); |
| return; |
| |
| case clang::Type::Vector: |
| case clang::Type::ExtVector: |
| asImpl().visitScalar( |
| convertVectorType(type.castAs<clang::VectorType>()), |
| args...); |
| return; |
| |
| case clang::Type::Pointer: |
| case clang::Type::BlockPointer: |
| case clang::Type::ObjCObjectPointer: |
| asImpl().visitScalar(IGM.Int8PtrTy, args...); |
| return; |
| } |
| llvm_unreachable("bad type kind"); |
| } |
| |
| Size getSizeOfType(clang::QualType type) { |
| auto clangSize = Ctx.getTypeSizeInChars(type); |
| return Size(clangSize.getQuantity()); |
| } |
| |
| private: |
| llvm::Type *convertVectorType(clang::CanQual<clang::VectorType> type) { |
| auto eltTy = |
| convertBuiltinType(type->getElementType().castAs<clang::BuiltinType>()); |
| return llvm::VectorType::get(eltTy, type->getNumElements()); |
| } |
| |
| llvm::Type *convertBuiltinType(clang::CanQual<clang::BuiltinType> type) { |
| switch (type.getTypePtr()->getKind()) { |
| #define BUILTIN_TYPE(Id, SingletonId) |
| #define PLACEHOLDER_TYPE(Id, SingletonId) \ |
| case clang::BuiltinType::Id: |
| #include "clang/AST/BuiltinTypes.def" |
| case clang::BuiltinType::Dependent: |
| llvm_unreachable("placeholder type in ABI lowering"); |
| |
| // We should never see these unadorned. |
| case clang::BuiltinType::ObjCId: |
| case clang::BuiltinType::ObjCClass: |
| case clang::BuiltinType::ObjCSel: |
| llvm_unreachable("bare Objective-C object type in ABI lowering"); |
| |
| // This should never be the type of an argument or field. |
| case clang::BuiltinType::Void: |
| llvm_unreachable("bare void type in ABI lowering"); |
| |
| // We should never see the OpenCL builtin types at all. |
| case clang::BuiltinType::OCLImage1d: |
| case clang::BuiltinType::OCLImage1dArray: |
| case clang::BuiltinType::OCLImage1dBuffer: |
| case clang::BuiltinType::OCLImage2d: |
| case clang::BuiltinType::OCLImage2dArray: |
| case clang::BuiltinType::OCLImage2dDepth: |
| case clang::BuiltinType::OCLImage2dArrayDepth: |
| case clang::BuiltinType::OCLImage2dMSAA: |
| case clang::BuiltinType::OCLImage2dArrayMSAA: |
| case clang::BuiltinType::OCLImage2dMSAADepth: |
| case clang::BuiltinType::OCLImage2dArrayMSAADepth: |
| case clang::BuiltinType::OCLImage3d: |
| case clang::BuiltinType::OCLSampler: |
| case clang::BuiltinType::OCLEvent: |
| case clang::BuiltinType::OCLClkEvent: |
| case clang::BuiltinType::OCLQueue: |
| case clang::BuiltinType::OCLNDRange: |
| case clang::BuiltinType::OCLReserveID: |
| llvm_unreachable("OpenCL type in ABI lowering"); |
| |
| // Handle all the integer types as opaque values. |
| #define BUILTIN_TYPE(Id, SingletonId) |
| #define SIGNED_TYPE(Id, SingletonId) \ |
| case clang::BuiltinType::Id: |
| #define UNSIGNED_TYPE(Id, SingletonId) \ |
| case clang::BuiltinType::Id: |
| #include "clang/AST/BuiltinTypes.def" |
| return convertTypeAsInteger(type); |
| |
| // Lower all the floating-point values by their semantics. |
| case clang::BuiltinType::Half: |
| return convertFloatingType(Ctx.getTargetInfo().getHalfFormat()); |
| case clang::BuiltinType::Float: |
| return convertFloatingType(Ctx.getTargetInfo().getFloatFormat()); |
| case clang::BuiltinType::Double: |
| return convertFloatingType(Ctx.getTargetInfo().getDoubleFormat()); |
| case clang::BuiltinType::LongDouble: |
| return convertFloatingType(Ctx.getTargetInfo().getLongDoubleFormat()); |
| |
| // nullptr_t -> void* |
| case clang::BuiltinType::NullPtr: |
| return IGM.Int8PtrTy; |
| } |
| llvm_unreachable("bad builtin type"); |
| } |
| |
| llvm::Type *convertFloatingType(const llvm::fltSemantics &format) { |
| if (&format == &llvm::APFloat::IEEEhalf) |
| return llvm::Type::getHalfTy(IGM.getLLVMContext()); |
| if (&format == &llvm::APFloat::IEEEsingle) |
| return llvm::Type::getFloatTy(IGM.getLLVMContext()); |
| if (&format == &llvm::APFloat::IEEEdouble) |
| return llvm::Type::getDoubleTy(IGM.getLLVMContext()); |
| if (&format == &llvm::APFloat::IEEEquad) |
| return llvm::Type::getFP128Ty(IGM.getLLVMContext()); |
| if (&format == &llvm::APFloat::PPCDoubleDouble) |
| return llvm::Type::getPPC_FP128Ty(IGM.getLLVMContext()); |
| if (&format == &llvm::APFloat::x87DoubleExtended) |
| return llvm::Type::getX86_FP80Ty(IGM.getLLVMContext()); |
| llvm_unreachable("bad float format"); |
| } |
| |
| llvm::Type *convertTypeAsInteger(clang::QualType type) { |
| auto size = getSizeOfType(type); |
| return llvm::IntegerType::get(IGM.getLLVMContext(), |
| size.getValueInBits()); |
| } |
| }; |
| |
| /// A CRTP specialization of ClangExpand which projects down to |
| /// various aggregate elements of an address. |
| /// |
| /// Subclasses should only have to define visitScalar. |
| template <class Impl> |
| class ClangExpandProjection : public ClangExpand<Impl, Address> { |
| using super = ClangExpand<Impl, Address>; |
| using super::asImpl; |
| using super::IGM; |
| using super::Ctx; |
| using super::getSizeOfType; |
| |
| protected: |
| IRGenFunction &IGF; |
| ClangExpandProjection(IRGenFunction &IGF) |
| : super(IGF.IGM), IGF(IGF) {} |
| |
| public: |
| void visit(clang::CanQualType type, Address addr) { |
| assert(addr.getType() == IGM.Int8PtrTy); |
| super::visit(type, addr); |
| } |
| |
| Size beginArrayElements(clang::CanQualType element) { |
| return getSizeOfType(element); |
| } |
| void visitArrayElement(clang::CanQualType element, unsigned i, |
| Size elementSize, Address arrayAddr) { |
| asImpl().visit(element, createGEPAtOffset(arrayAddr, elementSize * i)); |
| } |
| |
| void visitComplexElement(clang::CanQualType element, unsigned i, |
| Address complexAddr) { |
| Address addr = complexAddr; |
| if (i) { addr = createGEPAtOffset(complexAddr, getSizeOfType(element)); } |
| asImpl().visit(element, addr); |
| } |
| |
| void visitUnionField(const clang::RecordDecl *record, |
| const clang::FieldDecl *field, |
| Address structAddr) { |
| asImpl().visit(Ctx.getCanonicalType(field->getType()), structAddr); |
| } |
| |
| const clang::ASTRecordLayout & |
| beginStructFields(const clang::RecordDecl *record) { |
| return Ctx.getASTRecordLayout(record); |
| } |
| void visitStructField(const clang::RecordDecl *record, |
| const clang::FieldDecl *field, |
| const clang::ASTRecordLayout &layout, |
| Address structAddr) { |
| auto fieldIndex = field->getFieldIndex(); |
| assert(!field->isBitField()); |
| auto fieldOffset = Size(layout.getFieldOffset(fieldIndex) / 8); |
| asImpl().visit(Ctx.getCanonicalType(field->getType()), |
| createGEPAtOffset(structAddr, fieldOffset)); |
| } |
| |
| private: |
| Address createGEPAtOffset(Address addr, Size offset) { |
| if (offset.isZero()) { |
| return addr; |
| } else { |
| return IGF.Builder.CreateConstByteArrayGEP(addr, offset); |
| } |
| } |
| }; |
| |
| /// A class for collecting the types of a Clang ABIArgInfo::Expand |
| /// argument expansion. |
| struct ClangExpandTypeCollector : ClangExpand<ClangExpandTypeCollector> { |
| SmallVectorImpl<llvm::Type*> &Types; |
| ClangExpandTypeCollector(IRGenModule &IGM, |
| SmallVectorImpl<llvm::Type*> &types) |
| : ClangExpand(IGM), Types(types) {} |
| |
| bool beginArrayElements(clang::CanQualType element) { return true; } |
| void visitArrayElement(clang::CanQualType element, unsigned i, bool _) { |
| visit(element); |
| } |
| |
| void visitComplexElement(clang::CanQualType element, unsigned i) { |
| visit(element); |
| } |
| |
| void visitUnionField(const clang::RecordDecl *record, |
| const clang::FieldDecl *field) { |
| visit(Ctx.getCanonicalType(field->getType())); |
| } |
| |
| bool beginStructFields(const clang::RecordDecl *record) { return true; } |
| void visitStructField(const clang::RecordDecl *record, |
| const clang::FieldDecl *field, |
| bool _) { |
| visit(Ctx.getCanonicalType(field->getType())); |
| } |
| |
| void visitScalar(llvm::Type *type) { |
| Types.push_back(type); |
| } |
| }; |
| } |
| |
| static bool doesClangExpansionMatchSchema(IRGenModule &IGM, |
| clang::CanQualType type, |
| const ExplosionSchema &schema) { |
| assert(!schema.containsAggregate()); |
| SmallVector<llvm::Type *, 4> expansion; |
| ClangExpandTypeCollector(IGM, expansion).visit(type); |
| |
| if (expansion.size() != schema.size()) |
| return false; |
| |
| for (size_t i = 0, e = schema.size(); i != e; ++i) { |
| if (schema[i].getScalarType() != expansion[i]) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// Expand the result and parameter types to the appropriate LLVM IR |
| /// types for C and Objective-C signatures. |
| llvm::Type *SignatureExpansion::expandExternalSignatureTypes() { |
| assert(FnType->getLanguage() == SILFunctionLanguage::C); |
| |
| // Convert the SIL result type to a Clang type. |
| auto resultTy = FnType->getResult().getSILType(); |
| auto clangResultTy = IGM.getClangType(resultTy); |
| |
| // Now convert the parameters to Clang types. |
| auto params = FnType->getParameters(); |
| unsigned paramOffset = 0; |
| |
| SmallVector<clang::CanQualType,4> paramTys; |
| auto const &clangCtx = IGM.getClangASTContext(); |
| |
| if (FnType->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod){ |
| // ObjC methods take their 'self' argument first, followed by an |
| // implicit _cmd argument. |
| auto &self = params.back(); |
| auto clangTy = IGM.getClangType(self); |
| paramTys.push_back(clangTy); |
| paramTys.push_back(clangCtx.VoidPtrTy); |
| params = params.slice(0, params.size() - 1); |
| paramOffset = 2; |
| } |
| |
| // Convert each parameter to a Clang type. |
| for (auto param : params) { |
| auto clangTy = IGM.getClangType(param); |
| paramTys.push_back(clangTy); |
| } |
| |
| // Generate function info for this signature. |
| auto extInfo = clang::FunctionType::ExtInfo(); |
| auto &FI = IGM.ABITypes->arrangeFreeFunctionCall(clangResultTy, paramTys, |
| extInfo, |
| clang::CodeGen::RequiredArgs::All); |
| |
| assert(FI.arg_size() == paramTys.size() && |
| "Expected one ArgInfo for each parameter type!"); |
| |
| auto &returnInfo = FI.getReturnInfo(); |
| |
| // Does the result need an extension attribute? |
| if (returnInfo.isExtend()) { |
| bool signExt = clangResultTy->hasSignedIntegerRepresentation(); |
| assert((signExt || clangResultTy->hasUnsignedIntegerRepresentation()) && |
| "Invalid attempt to add extension attribute to argument!"); |
| addExtendAttribute(IGM, Attrs, llvm::AttributeSet::ReturnIndex, signExt); |
| } |
| |
| // If we return indirectly, that is the first parameter type. |
| if (returnInfo.isIndirect()) |
| addIndirectResult(); |
| |
| // Blocks are passed into themselves as their first (non-sret) argument. |
| if (FnType->getRepresentation() == SILFunctionTypeRepresentation::Block) |
| ParamIRTypes.push_back(IGM.ObjCBlockPtrTy); |
| |
| for (auto i : indices(paramTys)) { |
| auto &AI = FI.arg_begin()[i].info; |
| |
| // Add a padding argument if required. |
| if (auto *padType = AI.getPaddingType()) |
| ParamIRTypes.push_back(padType); |
| |
| switch (AI.getKind()) { |
| case clang::CodeGen::ABIArgInfo::Extend: { |
| bool signExt = paramTys[i]->hasSignedIntegerRepresentation(); |
| assert((signExt || paramTys[i]->hasUnsignedIntegerRepresentation()) && |
| "Invalid attempt to add extension attribute to argument!"); |
| addExtendAttribute(IGM, Attrs, getCurParamIndex()+1, signExt); |
| SWIFT_FALLTHROUGH; |
| } |
| case clang::CodeGen::ABIArgInfo::Direct: { |
| // If the coercion type is a struct, we need to expand it. |
| auto type = AI.getCoerceToType(); |
| if (auto expandedType = dyn_cast<llvm::StructType>(type)) { |
| for (size_t j = 0, e = expandedType->getNumElements(); j != e; ++j) |
| ParamIRTypes.push_back(expandedType->getElementType(j)); |
| } else { |
| ParamIRTypes.push_back(type); |
| } |
| break; |
| } |
| case clang::CodeGen::ABIArgInfo::Indirect: { |
| assert(i >= paramOffset && |
| "Unexpected index for indirect byval argument"); |
| auto ¶m = params[i - paramOffset]; |
| auto ¶mTI = cast<FixedTypeInfo>(IGM.getTypeInfo(param.getSILType())); |
| if (AI.getIndirectByVal()) |
| addByvalArgumentAttributes(IGM, Attrs, getCurParamIndex(), |
| paramTI.getFixedAlignment()); |
| addPointerParameter(paramTI.getStorageType()); |
| break; |
| } |
| case clang::CodeGen::ABIArgInfo::Expand: |
| ClangExpandTypeCollector(IGM, ParamIRTypes).visit(paramTys[i]); |
| break; |
| case clang::CodeGen::ABIArgInfo::Ignore: |
| break; |
| case clang::CodeGen::ABIArgInfo::InAlloca: |
| llvm_unreachable("Need to handle InAlloca during signature expansion"); |
| } |
| } |
| |
| if (returnInfo.isIndirect() || returnInfo.isIgnore()) |
| return IGM.VoidTy; |
| |
| return returnInfo.getCoerceToType(); |
| } |
| |
| void SignatureExpansion::expand(SILParameterInfo param) { |
| auto &ti = IGM.getTypeInfo(param.getSILType()); |
| switch (auto conv = param.getConvention()) { |
| case ParameterConvention::Indirect_Out: |
| assert(ParamIRTypes.empty()); |
| addIndirectReturnAttributes(IGM, Attrs); |
| HasIndirectResult = true; |
| addPointerParameter(IGM.getStorageType(param.getSILType())); |
| return; |
| |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Guaranteed: |
| addIndirectValueParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size()); |
| addPointerParameter(IGM.getStorageType(param.getSILType())); |
| return; |
| |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| addInoutParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size(), |
| conv == ParameterConvention::Indirect_InoutAliasable); |
| addPointerParameter(IGM.getStorageType(param.getSILType())); |
| return; |
| |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Direct_Unowned: |
| case ParameterConvention::Direct_Guaranteed: |
| // Go ahead and further decompose tuples. |
| if (auto tuple = dyn_cast<TupleType>(param.getType())) { |
| for (auto elt : tuple.getElementTypes()) { |
| // Propagate the same ownedness down to the element. |
| expand(SILParameterInfo(elt, param.getConvention())); |
| } |
| return; |
| } |
| SWIFT_FALLTHROUGH; |
| case ParameterConvention::Direct_Deallocating: |
| |
| switch (FnType->getLanguage()) { |
| case SILFunctionLanguage::C: { |
| llvm_unreachable("Unexpected C/ObjC method in parameter expansion!"); |
| return; |
| } |
| case SILFunctionLanguage::Swift: { |
| auto schema = ti.getSchema(); |
| schema.addToArgTypes(IGM, ti, Attrs, ParamIRTypes); |
| return; |
| } |
| } |
| llvm_unreachable("bad abstract CC"); |
| } |
| llvm_unreachable("bad parameter convention"); |
| } |
| |
| /// Should the given self parameter be given the special treatment |
| /// for self parameters? |
| /// |
| /// It's important that this only return true for things that are |
| /// passed as a single pointer. |
| bool irgen::isSelfContextParameter(SILParameterInfo param) { |
| // All the indirect conventions pass a single pointer. |
| if (param.isIndirect()) { |
| return true; |
| } |
| |
| // Direct conventions depends on the type. |
| CanType type = param.getType(); |
| |
| // Thick or @objc metatypes (but not existential metatypes). |
| if (auto metatype = dyn_cast<MetatypeType>(type)) { |
| return metatype->getRepresentation() != MetatypeRepresentation::Thin; |
| } |
| |
| // Classes and class-bounded archetypes. |
| // No need to apply this to existentials. |
| // The direct check for SubstitutableType works because only |
| // class-bounded generic types can be passed directly. |
| if (type->mayHaveSuperclass() || isa<SubstitutableType>(type)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Expand the abstract parameters of a SIL function type into the |
| /// physical parameters of an LLVM function type. |
| void SignatureExpansion::expandParameters() { |
| assert(FnType->getRepresentation() != SILFunctionTypeRepresentation::Block |
| && "block with non-C calling conv?!"); |
| |
| // First, the formal parameters. But 'self' is treated as the |
| // context if it has pointer representation. |
| auto params = FnType->getParameters(); |
| bool hasSelfContext = false; |
| if (FnType->hasSelfParam() && |
| isSelfContextParameter(FnType->getSelfParameter())) { |
| hasSelfContext = true; |
| params = params.drop_back(); |
| } |
| |
| for (auto param : params) { |
| expand(param); |
| } |
| |
| // Next, the generic signature. |
| if (hasPolymorphicParameters(FnType)) |
| expandPolymorphicSignature(IGM, FnType, ParamIRTypes); |
| |
| // Context is next. |
| if (hasSelfContext) { |
| auto curLength = ParamIRTypes.size(); (void) curLength; |
| |
| // TODO: 'swift_context' IR attribute |
| expand(FnType->getSelfParameter()); |
| |
| assert(ParamIRTypes.size() == curLength + 1 && |
| "adding 'self' added unexpected number of parameters"); |
| } else { |
| auto needsContext = [=]() -> bool { |
| switch (FnType->getRepresentation()) { |
| case SILFunctionType::Representation::Block: |
| llvm_unreachable("adding block parameter in Swift CC expansion?"); |
| |
| // Always leave space for a context argument if we have an error result. |
| case SILFunctionType::Representation::CFunctionPointer: |
| case SILFunctionType::Representation::Method: |
| case SILFunctionType::Representation::WitnessMethod: |
| case SILFunctionType::Representation::ObjCMethod: |
| case SILFunctionType::Representation::Thin: |
| return FnType->hasErrorResult(); |
| |
| case SILFunctionType::Representation::Thick: |
| return true; |
| } |
| llvm_unreachable("bad representation kind"); |
| }; |
| if (needsContext()) { |
| // TODO: 'swift_context' IR attribute |
| ParamIRTypes.push_back(IGM.RefCountedPtrTy); |
| } |
| } |
| |
| // Error results are last. We always pass them as a pointer to the |
| // formal error type; LLVM will magically turn this into a non-pointer |
| // if we set the right attribute. |
| if (FnType->hasErrorResult()) { |
| // TODO: 'swift_error' IR attribute |
| llvm::Type *errorType = |
| IGM.getStorageType(FnType->getErrorResult().getSILType()); |
| ParamIRTypes.push_back(errorType->getPointerTo()); |
| } |
| |
| // Witness methods have some extra parameter types. |
| if (FnType->getRepresentation() == |
| SILFunctionTypeRepresentation::WitnessMethod) { |
| expandTrailingWitnessSignature(IGM, FnType, ParamIRTypes); |
| } |
| } |
| |
| /// Expand the result and parameter types of a SIL function into the |
| /// physical parameter types of an LLVM function and return the result |
| /// type. |
| llvm::Type *SignatureExpansion::expandSignatureTypes() { |
| switch (FnType->getLanguage()) { |
| case SILFunctionLanguage::Swift: { |
| llvm::Type *resultType = expandResult(); |
| expandParameters(); |
| return resultType; |
| } |
| case SILFunctionLanguage::C: |
| return expandExternalSignatureTypes(); |
| } |
| llvm_unreachable("bad abstract calling convention"); |
| } |
| |
| Signature FuncSignatureInfo::getSignature(IRGenModule &IGM) const { |
| // If it's already been filled in, we're done. |
| if (TheSignature.isValid()) |
| return TheSignature; |
| |
| GenericContextScope scope(IGM, FormalType->getGenericSignature()); |
| SignatureExpansion expansion(IGM, FormalType); |
| llvm::Type *resultType = expansion.expandSignatureTypes(); |
| |
| // Create the appropriate LLVM type. |
| llvm::FunctionType *llvmType = |
| llvm::FunctionType::get(resultType, expansion.ParamIRTypes, |
| /*variadic*/ false); |
| |
| // Update the cache and return. |
| TheSignature.set(llvmType, expansion.HasIndirectResult, expansion.Attrs); |
| return TheSignature; |
| } |
| |
| static const FuncSignatureInfo & |
| getFuncSignatureInfoForLowered(IRGenModule &IGM, CanSILFunctionType type) { |
| auto &ti = IGM.getTypeInfoForLowered(type); |
| switch (type->getRepresentation()) { |
| case SILFunctionType::Representation::Block: |
| return ti.as<BlockTypeInfo>(); |
| case SILFunctionType::Representation::Thin: |
| case SILFunctionType::Representation::CFunctionPointer: |
| case SILFunctionType::Representation::Method: |
| case SILFunctionType::Representation::WitnessMethod: |
| case SILFunctionType::Representation::ObjCMethod: |
| return ti.as<ThinFuncTypeInfo>(); |
| case SILFunctionType::Representation::Thick: |
| return ti.as<FuncTypeInfo>(); |
| } |
| llvm_unreachable("bad function type representation"); |
| } |
| |
| llvm::FunctionType * |
| IRGenModule::getFunctionType(CanSILFunctionType type, |
| llvm::AttributeSet &attrs) { |
| auto &sigInfo = getFuncSignatureInfoForLowered(*this, type); |
| Signature sig = sigInfo.getSignature(*this); |
| attrs = sig.getAttributes(); |
| return sig.getType(); |
| } |
| |
| /// Return this function pointer, bitcasted to an i8*. |
| llvm::Value *Callee::getOpaqueFunctionPointer(IRGenFunction &IGF) const { |
| if (FnPtr->getType() == IGF.IGM.Int8PtrTy) |
| return FnPtr; |
| return IGF.Builder.CreateBitCast(FnPtr, IGF.IGM.Int8PtrTy); |
| } |
| |
| /// Return this data pointer. |
| llvm::Value *Callee::getDataPointer(IRGenFunction &IGF) const { |
| if (hasDataPointer()) return DataPtr; |
| return IGF.IGM.RefCountedNull; |
| } |
| |
| static void extractScalarResults(IRGenFunction &IGF, llvm::Type *bodyType, |
| llvm::Value *call, Explosion &out) { |
| assert(!bodyType->isVoidTy() && "Unexpected void result type!"); |
| |
| auto *returned = call; |
| auto *callType = call->getType(); |
| |
| // If the type of the result of the call differs from the type used |
| // elsewhere in the caller due to ABI type coercion, we need to |
| // coerce the result back from the ABI type before extracting the |
| // elements. |
| if (bodyType != callType) |
| returned = IGF.coerceValue(returned, bodyType, IGF.IGM.DataLayout); |
| |
| if (llvm::StructType *structType = dyn_cast<llvm::StructType>(bodyType)) |
| for (unsigned i = 0, e = structType->getNumElements(); i != e; ++i) |
| out.add(IGF.Builder.CreateExtractValue(returned, i)); |
| else |
| out.add(returned); |
| } |
| |
| static void emitCastBuiltin(IRGenFunction &IGF, SILType destType, |
| Explosion &result, |
| Explosion &args, |
| llvm::Instruction::CastOps opcode) { |
| llvm::Value *input = args.claimNext(); |
| assert(args.empty() && "wrong operands to cast operation"); |
| |
| llvm::Type *destTy = IGF.IGM.getStorageType(destType); |
| llvm::Value *output = IGF.Builder.CreateCast(opcode, input, destTy); |
| result.add(output); |
| } |
| |
| static void emitCastOrBitCastBuiltin(IRGenFunction &IGF, |
| SILType destType, |
| Explosion &result, |
| Explosion &args, |
| BuiltinValueKind BV) { |
| llvm::Value *input = args.claimNext(); |
| assert(args.empty() && "wrong operands to cast operation"); |
| |
| llvm::Type *destTy = IGF.IGM.getStorageType(destType); |
| llvm::Value *output; |
| switch (BV) { |
| default: llvm_unreachable("Not a cast-or-bitcast operation"); |
| case BuiltinValueKind::TruncOrBitCast: |
| output = IGF.Builder.CreateTruncOrBitCast(input, destTy); break; |
| case BuiltinValueKind::ZExtOrBitCast: |
| output = IGF.Builder.CreateZExtOrBitCast(input, destTy); break; |
| case BuiltinValueKind::SExtOrBitCast: |
| output = IGF.Builder.CreateSExtOrBitCast(input, destTy); break; |
| } |
| result.add(output); |
| } |
| |
| static void emitCompareBuiltin(IRGenFunction &IGF, Explosion &result, |
| Explosion &args, llvm::CmpInst::Predicate pred) { |
| llvm::Value *lhs = args.claimNext(); |
| llvm::Value *rhs = args.claimNext(); |
| |
| llvm::Value *v; |
| if (lhs->getType()->isFPOrFPVectorTy()) |
| v = IGF.Builder.CreateFCmp(pred, lhs, rhs); |
| else |
| v = IGF.Builder.CreateICmp(pred, lhs, rhs); |
| |
| result.add(v); |
| } |
| |
| /// decodeLLVMAtomicOrdering - turn a string like "release" into the LLVM enum. |
| static llvm::AtomicOrdering decodeLLVMAtomicOrdering(StringRef O) { |
| using namespace llvm; |
| return StringSwitch<AtomicOrdering>(O) |
| .Case("unordered", Unordered) |
| .Case("monotonic", Monotonic) |
| .Case("acquire", Acquire) |
| .Case("release", Release) |
| .Case("acqrel", AcquireRelease) |
| .Case("seqcst", SequentiallyConsistent); |
| } |
| |
| static void emitTypeTraitBuiltin(IRGenFunction &IGF, |
| Explosion &out, |
| Explosion &args, |
| ArrayRef<Substitution> substitutions, |
| TypeTraitResult (TypeBase::*trait)()) { |
| assert(substitutions.size() == 1 |
| && "type trait should have gotten single type parameter"); |
| args.claimNext(); |
| |
| // Lower away the trait to a tristate 0 = no, 1 = yes, 2 = maybe. |
| unsigned result; |
| switch ((substitutions[0].getReplacement().getPointer()->*trait)()) { |
| case TypeTraitResult::IsNot: |
| result = 0; |
| break; |
| case TypeTraitResult::Is: |
| result = 1; |
| break; |
| case TypeTraitResult::CanBe: |
| result = 2; |
| break; |
| } |
| |
| out.add(llvm::ConstantInt::get(IGF.IGM.Int8Ty, result)); |
| } |
| |
| static std::pair<SILType, const TypeInfo &> |
| getLoweredTypeAndTypeInfo(IRGenModule &IGM, Type unloweredType) { |
| auto lowered = IGM.SILMod->Types.getLoweredType( |
| unloweredType->getCanonicalType()); |
| return {lowered, IGM.getTypeInfo(lowered)}; |
| } |
| |
| /// emitBuiltinCall - Emit a call to a builtin function. |
| void irgen::emitBuiltinCall(IRGenFunction &IGF, Identifier FnId, |
| SILType resultType, |
| Explosion &args, Explosion &out, |
| ArrayRef<Substitution> substitutions) { |
| // Decompose the function's name into a builtin name and type list. |
| const BuiltinInfo &Builtin = IGF.IGM.SILMod->getBuiltinInfo(FnId); |
| |
| // These builtins don't care about their argument: |
| if (Builtin.ID == BuiltinValueKind::Sizeof) { |
| args.claimAll(); |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| out.add(valueTy.second.getSize(IGF, valueTy.first)); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::Strideof) { |
| args.claimAll(); |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| out.add(valueTy.second.getStride(IGF, valueTy.first)); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::Alignof) { |
| args.claimAll(); |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| // The alignof value is one greater than the alignment mask. |
| out.add(IGF.Builder.CreateAdd( |
| valueTy.second.getAlignmentMask(IGF, valueTy.first), |
| IGF.IGM.getSize(Size(1)))); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::StrideofNonZero) { |
| // Note this case must never return 0. |
| // It is implemented as max(strideof, 1) |
| args.claimAll(); |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| // Strideof should never return 0, so return 1 if the type has a 0 stride. |
| llvm::Value *StrideOf = valueTy.second.getStride(IGF, valueTy.first); |
| llvm::IntegerType *IntTy = cast<llvm::IntegerType>(StrideOf->getType()); |
| auto *Zero = llvm::ConstantInt::get(IntTy, 0); |
| auto *One = llvm::ConstantInt::get(IntTy, 1); |
| llvm::Value *Cmp = IGF.Builder.CreateICmpEQ(StrideOf, Zero); |
| out.add(IGF.Builder.CreateSelect(Cmp, One, StrideOf)); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::IsPOD) { |
| args.claimAll(); |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| out.add(valueTy.second.getIsPOD(IGF, valueTy.first)); |
| return; |
| } |
| |
| |
| // addressof expects an lvalue argument. |
| if (Builtin.ID == BuiltinValueKind::AddressOf) { |
| llvm::Value *address = args.claimNext(); |
| llvm::Value *value = IGF.Builder.CreateBitCast(address, |
| IGF.IGM.Int8PtrTy); |
| out.add(value); |
| return; |
| } |
| |
| // Everything else cares about the (rvalue) argument. |
| |
| // If this is an LLVM IR intrinsic, lower it to an intrinsic call. |
| const IntrinsicInfo &IInfo = IGF.IGM.SILMod->getIntrinsicInfo(FnId); |
| llvm::Intrinsic::ID IID = IInfo.ID; |
| if (IID != llvm::Intrinsic::not_intrinsic) { |
| SmallVector<llvm::Type*, 4> ArgTys; |
| for (auto T : IInfo.Types) |
| ArgTys.push_back(IGF.IGM.getStorageTypeForLowered(T->getCanonicalType())); |
| |
| auto F = llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| (llvm::Intrinsic::ID)IID, ArgTys); |
| llvm::FunctionType *FT = F->getFunctionType(); |
| SmallVector<llvm::Value*, 8> IRArgs; |
| for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) |
| IRArgs.push_back(args.claimNext()); |
| llvm::Value *TheCall = IGF.Builder.CreateCall(F, IRArgs); |
| |
| if (!TheCall->getType()->isVoidTy()) |
| extractScalarResults(IGF, TheCall->getType(), TheCall, out); |
| |
| return; |
| } |
| |
| // TODO: A linear series of ifs is suboptimal. |
| #define BUILTIN_SIL_OPERATION(id, name, overload) \ |
| if (Builtin.ID == BuiltinValueKind::id) \ |
| llvm_unreachable(name " builtin should be lowered away by SILGen!"); |
| |
| #define BUILTIN_CAST_OPERATION(id, name, attrs) \ |
| if (Builtin.ID == BuiltinValueKind::id) \ |
| return emitCastBuiltin(IGF, resultType, out, args, \ |
| llvm::Instruction::id); |
| |
| #define BUILTIN_CAST_OR_BITCAST_OPERATION(id, name, attrs) \ |
| if (Builtin.ID == BuiltinValueKind::id) \ |
| return emitCastOrBitCastBuiltin(IGF, resultType, out, args, \ |
| BuiltinValueKind::id); |
| |
| #define BUILTIN_BINARY_OPERATION(id, name, attrs, overload) \ |
| if (Builtin.ID == BuiltinValueKind::id) { \ |
| llvm::Value *lhs = args.claimNext(); \ |
| llvm::Value *rhs = args.claimNext(); \ |
| llvm::Value *v = IGF.Builder.Create##id(lhs, rhs); \ |
| return out.add(v); \ |
| } |
| |
| #define BUILTIN_RUNTIME_CALL(id, name, attrs) \ |
| if (Builtin.ID == BuiltinValueKind::id) { \ |
| llvm::CallInst *call = IGF.Builder.CreateCall(IGF.IGM.get##id##Fn(), \ |
| args.claimNext()); \ |
| call->setCallingConv(IGF.IGM.RuntimeCC); \ |
| call->setDoesNotThrow(); \ |
| return out.add(call); \ |
| } |
| |
| #define BUILTIN_BINARY_OPERATION_WITH_OVERFLOW(id, name, uncheckedID, attrs, overload) \ |
| if (Builtin.ID == BuiltinValueKind::id) { \ |
| SmallVector<llvm::Type*, 2> ArgTys; \ |
| auto opType = Builtin.Types[0]->getCanonicalType(); \ |
| ArgTys.push_back(IGF.IGM.getStorageTypeForLowered(opType)); \ |
| auto F = llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, \ |
| getLLVMIntrinsicIDForBuiltinWithOverflow(Builtin.ID), ArgTys); \ |
| SmallVector<llvm::Value*, 2> IRArgs; \ |
| IRArgs.push_back(args.claimNext()); \ |
| IRArgs.push_back(args.claimNext()); \ |
| args.claimNext();\ |
| llvm::Value *TheCall = IGF.Builder.CreateCall(F, IRArgs); \ |
| extractScalarResults(IGF, TheCall->getType(), TheCall, out); \ |
| return; \ |
| } |
| // FIXME: We could generate the code to dynamically report the overflow if the |
| // thrid argument is true. Now, we just ignore it. |
| |
| #define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload) \ |
| if (Builtin.ID == BuiltinValueKind::id) \ |
| return emitCompareBuiltin(IGF, out, args, llvm::CmpInst::id); |
| |
| #define BUILTIN_TYPE_TRAIT_OPERATION(id, name) \ |
| if (Builtin.ID == BuiltinValueKind::id) \ |
| return emitTypeTraitBuiltin(IGF, out, args, substitutions, &TypeBase::name); |
| |
| #define BUILTIN(ID, Name, Attrs) // Ignore the rest. |
| #include "swift/AST/Builtins.def" |
| |
| if (Builtin.ID == BuiltinValueKind::FNeg) { |
| llvm::Value *rhs = args.claimNext(); |
| llvm::Value *lhs = llvm::ConstantFP::get(rhs->getType(), "-0.0"); |
| llvm::Value *v = IGF.Builder.CreateFSub(lhs, rhs); |
| return out.add(v); |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::AssumeNonNegative) { |
| llvm::Value *v = args.claimNext(); |
| // Set a value range on the load instruction, which must be the argument of |
| // the builtin. |
| if (isa<llvm::LoadInst>(v) || isa<llvm::CallInst>(v)) { |
| // The load must be post-dominated by the builtin. Otherwise we would get |
| // a wrong assumption in the else-branch in this example: |
| // x = f() |
| // if condition { |
| // y = assumeNonNegative(x) |
| // } else { |
| // // x might be negative here! |
| // } |
| // For simplicity we just enforce that both the load and the builtin must |
| // be in the same block. |
| llvm::Instruction *I = static_cast<llvm::Instruction *>(v); |
| if (I->getParent() == IGF.Builder.GetInsertBlock()) { |
| llvm::LLVMContext &ctx = IGF.IGM.Module.getContext(); |
| llvm::IntegerType *intType = dyn_cast<llvm::IntegerType>(v->getType()); |
| llvm::Metadata *rangeElems[] = { |
| llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(intType, 0)), |
| llvm::ConstantAsMetadata::get( |
| llvm::ConstantInt::get(intType, |
| APInt::getSignedMaxValue(intType->getBitWidth()))) |
| }; |
| llvm::MDNode *range = llvm::MDNode::get(ctx, rangeElems); |
| I->setMetadata(llvm::LLVMContext::MD_range, range); |
| } |
| } |
| // Don't generate any code for the builtin. |
| return out.add(v); |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::AllocRaw) { |
| auto size = args.claimNext(); |
| auto align = args.claimNext(); |
| // Translate the alignment to a mask. |
| auto alignMask = IGF.Builder.CreateSub(align, IGF.IGM.getSize(Size(1))); |
| auto alloc = IGF.emitAllocRawCall(size, alignMask, "builtin-allocRaw"); |
| out.add(alloc); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::DeallocRaw) { |
| auto pointer = args.claimNext(); |
| auto size = args.claimNext(); |
| auto align = args.claimNext(); |
| // Translate the alignment to a mask. |
| auto alignMask = IGF.Builder.CreateSub(align, IGF.IGM.getSize(Size(1))); |
| IGF.emitDeallocRawCall(pointer, size, alignMask); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::Fence) { |
| SmallVector<Type, 4> Types; |
| StringRef BuiltinName = |
| getBuiltinBaseName(IGF.IGM.Context, FnId.str(), Types); |
| BuiltinName = BuiltinName.drop_front(strlen("fence_")); |
| // Decode the ordering argument, which is required. |
| auto underscore = BuiltinName.find('_'); |
| auto ordering = decodeLLVMAtomicOrdering(BuiltinName.substr(0, underscore)); |
| BuiltinName = BuiltinName.substr(underscore); |
| |
| // Accept singlethread if present. |
| bool isSingleThread = BuiltinName.startswith("_singlethread"); |
| if (isSingleThread) |
| BuiltinName = BuiltinName.drop_front(strlen("_singlethread")); |
| assert(BuiltinName.empty() && "Mismatch with sema"); |
| |
| IGF.Builder.CreateFence(ordering, |
| isSingleThread ? llvm::SingleThread : llvm::CrossThread); |
| return; |
| } |
| |
| |
| if (Builtin.ID == BuiltinValueKind::CmpXChg) { |
| SmallVector<Type, 4> Types; |
| StringRef BuiltinName = |
| getBuiltinBaseName(IGF.IGM.Context, FnId.str(), Types); |
| BuiltinName = BuiltinName.drop_front(strlen("cmpxchg_")); |
| |
| // Decode the success- and failure-ordering arguments, which are required. |
| SmallVector<StringRef, 4> Parts; |
| BuiltinName.split(Parts, "_"); |
| assert(Parts.size() >= 2 && "Mismatch with sema"); |
| auto successOrdering = decodeLLVMAtomicOrdering(Parts[0]); |
| auto failureOrdering = decodeLLVMAtomicOrdering(Parts[1]); |
| auto NextPart = Parts.begin() + 2; |
| |
| // Accept weak, volatile, and singlethread if present. |
| bool isWeak = false, isVolatile = false, isSingleThread = false; |
| if (NextPart != Parts.end() && *NextPart == "weak") { |
| isWeak = true; |
| NextPart++; |
| } |
| if (NextPart != Parts.end() && *NextPart == "volatile") { |
| isVolatile = true; |
| NextPart++; |
| } |
| if (NextPart != Parts.end() && *NextPart == "singlethread") { |
| isSingleThread = true; |
| NextPart++; |
| } |
| assert(NextPart == Parts.end() && "Mismatch with sema"); |
| |
| auto pointer = args.claimNext(); |
| auto cmp = args.claimNext(); |
| auto newval = args.claimNext(); |
| |
| llvm::Type *origTy = cmp->getType(); |
| if (origTy->isPointerTy()) { |
| cmp = IGF.Builder.CreatePtrToInt(cmp, IGF.IGM.IntPtrTy); |
| newval = IGF.Builder.CreatePtrToInt(newval, IGF.IGM.IntPtrTy); |
| } |
| |
| pointer = IGF.Builder.CreateBitCast(pointer, |
| llvm::PointerType::getUnqual(cmp->getType())); |
| llvm::Value *value = IGF.Builder.CreateAtomicCmpXchg(pointer, cmp, newval, |
| successOrdering, |
| failureOrdering, |
| isSingleThread ? llvm::SingleThread : llvm::CrossThread); |
| cast<llvm::AtomicCmpXchgInst>(value)->setVolatile(isVolatile); |
| cast<llvm::AtomicCmpXchgInst>(value)->setWeak(isWeak); |
| |
| auto valueLoaded = IGF.Builder.CreateExtractValue(value, {0}); |
| auto loadSuccessful = IGF.Builder.CreateExtractValue(value, {1}); |
| |
| if (origTy->isPointerTy()) |
| valueLoaded = IGF.Builder.CreateIntToPtr(valueLoaded, origTy); |
| |
| out.add(valueLoaded); |
| out.add(loadSuccessful); |
| |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::AtomicRMW) { |
| using namespace llvm; |
| |
| SmallVector<Type, 4> Types; |
| StringRef BuiltinName = getBuiltinBaseName(IGF.IGM.Context, |
| FnId.str(), Types); |
| BuiltinName = BuiltinName.drop_front(strlen("atomicrmw_")); |
| auto underscore = BuiltinName.find('_'); |
| StringRef SubOp = BuiltinName.substr(0, underscore); |
| |
| auto SubOpcode = StringSwitch<AtomicRMWInst::BinOp>(SubOp) |
| .Case("xchg", AtomicRMWInst::Xchg) |
| .Case("add", AtomicRMWInst::Add) |
| .Case("sub", AtomicRMWInst::Sub) |
| .Case("and", AtomicRMWInst::And) |
| .Case("nand", AtomicRMWInst::Nand) |
| .Case("or", AtomicRMWInst::Or) |
| .Case("xor", AtomicRMWInst::Xor) |
| .Case("max", AtomicRMWInst::Max) |
| .Case("min", AtomicRMWInst::Min) |
| .Case("umax", AtomicRMWInst::UMax) |
| .Case("umin", AtomicRMWInst::UMin); |
| BuiltinName = BuiltinName.drop_front(underscore+1); |
| |
| // Decode the ordering argument, which is required. |
| underscore = BuiltinName.find('_'); |
| auto ordering = decodeLLVMAtomicOrdering(BuiltinName.substr(0, underscore)); |
| BuiltinName = BuiltinName.substr(underscore); |
| |
| // Accept volatile and singlethread if present. |
| bool isVolatile = BuiltinName.startswith("_volatile"); |
| if (isVolatile) BuiltinName = BuiltinName.drop_front(strlen("_volatile")); |
| |
| bool isSingleThread = BuiltinName.startswith("_singlethread"); |
| if (isSingleThread) |
| BuiltinName = BuiltinName.drop_front(strlen("_singlethread")); |
| assert(BuiltinName.empty() && "Mismatch with sema"); |
| |
| auto pointer = args.claimNext(); |
| auto val = args.claimNext(); |
| |
| // Handle atomic ops on pointers by casting to intptr_t. |
| llvm::Type *origTy = val->getType(); |
| if (origTy->isPointerTy()) |
| val = IGF.Builder.CreatePtrToInt(val, IGF.IGM.IntPtrTy); |
| |
| pointer = IGF.Builder.CreateBitCast(pointer, |
| llvm::PointerType::getUnqual(val->getType())); |
| llvm::Value *value = IGF.Builder.CreateAtomicRMW(SubOpcode, pointer, val, |
| ordering, |
| isSingleThread ? llvm::SingleThread : llvm::CrossThread); |
| cast<AtomicRMWInst>(value)->setVolatile(isVolatile); |
| |
| if (origTy->isPointerTy()) |
| value = IGF.Builder.CreateIntToPtr(value, origTy); |
| |
| out.add(value); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::AtomicLoad |
| || Builtin.ID == BuiltinValueKind::AtomicStore) { |
| using namespace llvm; |
| |
| SmallVector<Type, 4> Types; |
| StringRef BuiltinName = getBuiltinBaseName(IGF.IGM.Context, |
| FnId.str(), Types); |
| auto underscore = BuiltinName.find('_'); |
| BuiltinName = BuiltinName.substr(underscore+1); |
| |
| underscore = BuiltinName.find('_'); |
| auto ordering = decodeLLVMAtomicOrdering(BuiltinName.substr(0, underscore)); |
| BuiltinName = BuiltinName.substr(underscore); |
| |
| // Accept volatile and singlethread if present. |
| bool isVolatile = BuiltinName.startswith("_volatile"); |
| if (isVolatile) BuiltinName = BuiltinName.drop_front(strlen("_volatile")); |
| |
| bool isSingleThread = BuiltinName.startswith("_singlethread"); |
| if (isSingleThread) |
| BuiltinName = BuiltinName.drop_front(strlen("_singlethread")); |
| assert(BuiltinName.empty() && "Mismatch with sema"); |
| |
| auto pointer = args.claimNext(); |
| auto &valueTI = IGF.getTypeInfoForUnlowered(Types[0]); |
| auto schema = valueTI.getSchema(); |
| assert(schema.size() == 1 && "not a scalar type?!"); |
| auto origValueTy = schema[0].getScalarType(); |
| |
| // If the type is floating-point, then we need to bitcast to integer. |
| auto valueTy = origValueTy; |
| if (valueTy->isFloatingPointTy()) { |
| valueTy = llvm::IntegerType::get(IGF.IGM.LLVMContext, |
| valueTy->getPrimitiveSizeInBits()); |
| } |
| |
| pointer = IGF.Builder.CreateBitCast(pointer, valueTy->getPointerTo()); |
| |
| if (Builtin.ID == BuiltinValueKind::AtomicLoad) { |
| auto load = IGF.Builder.CreateLoad(pointer, |
| valueTI.getBestKnownAlignment()); |
| load->setAtomic(ordering, |
| isSingleThread ? llvm::SingleThread : llvm::CrossThread); |
| load->setVolatile(isVolatile); |
| |
| llvm::Value *value = load; |
| if (valueTy != origValueTy) |
| value = IGF.Builder.CreateBitCast(value, origValueTy); |
| out.add(value); |
| return; |
| } else if (Builtin.ID == BuiltinValueKind::AtomicStore) { |
| llvm::Value *value = args.claimNext(); |
| if (valueTy != origValueTy) |
| value = IGF.Builder.CreateBitCast(value, valueTy); |
| auto store = IGF.Builder.CreateStore(value, pointer, |
| valueTI.getBestKnownAlignment()); |
| store->setAtomic(ordering, |
| isSingleThread ? llvm::SingleThread : llvm::CrossThread); |
| store->setVolatile(isVolatile); |
| return; |
| } else { |
| llvm_unreachable("out of sync with outer conditional"); |
| } |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::ExtractElement) { |
| using namespace llvm; |
| |
| auto vector = args.claimNext(); |
| auto index = args.claimNext(); |
| out.add(IGF.Builder.CreateExtractElement(vector, index)); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::InsertElement) { |
| using namespace llvm; |
| |
| auto vector = args.claimNext(); |
| auto newValue = args.claimNext(); |
| auto index = args.claimNext(); |
| out.add(IGF.Builder.CreateInsertElement(vector, newValue, index)); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc || |
| Builtin.ID == BuiltinValueKind::UToUCheckedTrunc || |
| Builtin.ID == BuiltinValueKind::SToUCheckedTrunc) { |
| auto FromTy = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[0]->getCanonicalType()); |
| auto ToTy = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[1]->getCanonicalType()); |
| |
| // Compute the result for SToSCheckedTrunc_IntFrom_IntTo(Arg): |
| // Res = trunc_IntTo(Arg) |
| // Ext = sext_IntFrom(Res) |
| // OverflowFlag = (Arg == Ext) ? 0 : 1 |
| // return (resultVal, OverflowFlag) |
| // |
| // Compute the result for UToUCheckedTrunc_IntFrom_IntTo(Arg) |
| // and SToUCheckedTrunc_IntFrom_IntTo(Arg): |
| // Res = trunc_IntTo(Arg) |
| // Ext = zext_IntFrom(Res) |
| // OverflowFlag = (Arg == Ext) ? 0 : 1 |
| // return (Res, OverflowFlag) |
| llvm::Value *Arg = args.claimNext(); |
| llvm::Value *Res = IGF.Builder.CreateTrunc(Arg, ToTy); |
| bool Signed = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc); |
| llvm::Value *Ext = Signed ? IGF.Builder.CreateSExt(Res, FromTy) : |
| IGF.Builder.CreateZExt(Res, FromTy); |
| llvm::Value *OverflowCond = IGF.Builder.CreateICmpEQ(Arg, Ext); |
| llvm::Value *OverflowFlag = IGF.Builder.CreateSelect(OverflowCond, |
| llvm::ConstantInt::get(IGF.IGM.Int1Ty, 0), |
| llvm::ConstantInt::get(IGF.IGM.Int1Ty, 1)); |
| // Return the tuple - the result + the overflow flag. |
| out.add(Res); |
| return out.add(OverflowFlag); |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::UToSCheckedTrunc) { |
| auto FromTy = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[0]->getCanonicalType()); |
| auto ToTy = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[1]->getCanonicalType()); |
| llvm::Type *ToMinusOneTy = |
| llvm::Type::getIntNTy(ToTy->getContext(), ToTy->getIntegerBitWidth() - 1); |
| |
| // Compute the result for UToSCheckedTrunc_IntFrom_IntTo(Arg): |
| // Res = trunc_IntTo(Arg) |
| // Trunc = trunc_'IntTo-1bit'(Arg) |
| // Ext = zext_IntFrom(Trunc) |
| // OverflowFlag = (Arg == Ext) ? 0 : 1 |
| // return (Res, OverflowFlag) |
| llvm::Value *Arg = args.claimNext(); |
| llvm::Value *Res = IGF.Builder.CreateTrunc(Arg, ToTy); |
| llvm::Value *Trunc = IGF.Builder.CreateTrunc(Arg, ToMinusOneTy); |
| llvm::Value *Ext = IGF.Builder.CreateZExt(Trunc, FromTy); |
| llvm::Value *OverflowCond = IGF.Builder.CreateICmpEQ(Arg, Ext); |
| llvm::Value *OverflowFlag = IGF.Builder.CreateSelect(OverflowCond, |
| llvm::ConstantInt::get(IGF.IGM.Int1Ty, 0), |
| llvm::ConstantInt::get(IGF.IGM.Int1Ty, 1)); |
| // Return the tuple: (the result, the overflow flag). |
| out.add(Res); |
| return out.add(OverflowFlag); |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::SUCheckedConversion || |
| Builtin.ID == BuiltinValueKind::USCheckedConversion) { |
| auto Ty = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[0]->getCanonicalType()); |
| |
| // Report a sign error if the input parameter is a negative number, when |
| // interpreted as signed. |
| llvm::Value *Arg = args.claimNext(); |
| llvm::Value *Zero = llvm::ConstantInt::get(Ty, 0); |
| llvm::Value *OverflowFlag = IGF.Builder.CreateICmpSLT(Arg, Zero); |
| |
| // Return the tuple: (the result (same as input), the overflow flag). |
| out.add(Arg); |
| return out.add(OverflowFlag); |
| } |
| |
| // We are currently emitting code for '_convertFromBuiltinIntegerLiteral', |
| // which will call the builtin and pass it a non-compile-time-const parameter. |
| if (Builtin.ID == BuiltinValueKind::IntToFPWithOverflow) { |
| auto ToTy = |
| IGF.IGM.getStorageTypeForLowered(Builtin.Types[1]->getCanonicalType()); |
| llvm::Value *Arg = args.claimNext(); |
| unsigned bitSize = Arg->getType()->getScalarSizeInBits(); |
| if (bitSize > 64) { |
| // TODO: the integer literal bit size is 2048, but we only have a 64-bit |
| // conversion function available (on all platforms). |
| Arg = IGF.Builder.CreateTrunc(Arg, IGF.IGM.Int64Ty); |
| } else if (bitSize < 64) { |
| // Just for completeness. IntToFPWithOverflow is currently only used to |
| // convert 2048 bit integer literals. |
| Arg = IGF.Builder.CreateSExt(Arg, IGF.IGM.Int64Ty); |
| } |
| llvm::Value *V = IGF.Builder.CreateSIToFP(Arg, ToTy); |
| return out.add(V); |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::Once) { |
| // The input type is statically (Builtin.RawPointer, @convention(thin) () -> ()). |
| llvm::Value *PredPtr = args.claimNext(); |
| // Cast the predicate to a OnceTy pointer. |
| PredPtr = IGF.Builder.CreateBitCast(PredPtr, IGF.IGM.OnceTy->getPointerTo()); |
| llvm::Value *FnCode = args.claimNext(); |
| |
| // If we know the platform runtime's "done" value, emit the check inline. |
| llvm::BasicBlock *notDoneBB, *doneBB; |
| |
| if (auto ExpectedPred = IGF.IGM.TargetInfo.OnceDonePredicateValue) { |
| auto PredValue = IGF.Builder.CreateLoad(PredPtr, |
| IGF.IGM.getPointerAlignment()); |
| auto ExpectedPredValue = llvm::ConstantInt::getSigned(IGF.IGM.OnceTy, |
| *ExpectedPred); |
| auto PredIsDone = IGF.Builder.CreateICmpEQ(PredValue, ExpectedPredValue); |
| |
| notDoneBB = IGF.createBasicBlock("once_not_done"); |
| doneBB = IGF.createBasicBlock("once_done"); |
| |
| IGF.Builder.CreateCondBr(PredIsDone, doneBB, notDoneBB); |
| IGF.Builder.emitBlock(notDoneBB); |
| } |
| |
| // Emit the runtime "once" call. |
| auto call |
| = IGF.Builder.CreateCall(IGF.IGM.getOnceFn(), {PredPtr, FnCode}); |
| call->setCallingConv(IGF.IGM.RuntimeCC); |
| |
| // If we emitted the "done" check inline, join the branches. |
| if (auto ExpectedPred = IGF.IGM.TargetInfo.OnceDonePredicateValue) { |
| IGF.Builder.CreateBr(doneBB); |
| IGF.Builder.emitBlock(doneBB); |
| // We can assume the once predicate is in the "done" state now. |
| auto PredValue = IGF.Builder.CreateLoad(PredPtr, |
| IGF.IGM.getPointerAlignment()); |
| auto ExpectedPredValue = llvm::ConstantInt::getSigned(IGF.IGM.OnceTy, |
| *ExpectedPred); |
| auto PredIsDone = IGF.Builder.CreateICmpEQ(PredValue, ExpectedPredValue); |
| |
| IGF.Builder.CreateAssumption(PredIsDone); |
| } |
| |
| // No return value. |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::AssertConf) { |
| // Replace the call to assert_configuration by the Debug configuration |
| // value. |
| // TODO: assert(IGF.IGM.getOptions().AssertConfig == |
| // SILOptions::DisableReplacement); |
| // Make sure this only happens in a mode where we build a library dylib. |
| |
| llvm::Value *DebugAssert = IGF.Builder.getInt32(SILOptions::Debug); |
| out.add(DebugAssert); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::DestroyArray) { |
| // The input type is (T.Type, Builtin.RawPointer, Builtin.Word). |
| /* metatype (which may be thin) */ |
| if (args.size() == 3) |
| args.claimNext(); |
| llvm::Value *ptr = args.claimNext(); |
| llvm::Value *count = args.claimNext(); |
| |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| |
| ptr = IGF.Builder.CreateBitCast(ptr, |
| valueTy.second.getStorageType()->getPointerTo()); |
| Address array = valueTy.second.getAddressForPointer(ptr); |
| valueTy.second.destroyArray(IGF, array, count, valueTy.first); |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::CopyArray |
| || Builtin.ID == BuiltinValueKind::TakeArrayFrontToBack |
| || Builtin.ID == BuiltinValueKind::TakeArrayBackToFront) { |
| // The input type is (T.Type, Builtin.RawPointer, Builtin.RawPointer, Builtin.Word). |
| /* metatype (which may be thin) */ |
| if (args.size() == 4) |
| args.claimNext(); |
| llvm::Value *dest = args.claimNext(); |
| llvm::Value *src = args.claimNext(); |
| llvm::Value *count = args.claimNext(); |
| |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| |
| dest = IGF.Builder.CreateBitCast(dest, |
| valueTy.second.getStorageType()->getPointerTo()); |
| src = IGF.Builder.CreateBitCast(src, |
| valueTy.second.getStorageType()->getPointerTo()); |
| Address destArray = valueTy.second.getAddressForPointer(dest); |
| Address srcArray = valueTy.second.getAddressForPointer(src); |
| |
| switch (Builtin.ID) { |
| case BuiltinValueKind::CopyArray: |
| valueTy.second.initializeArrayWithCopy(IGF, destArray, srcArray, count, |
| valueTy.first); |
| break; |
| case BuiltinValueKind::TakeArrayFrontToBack: |
| valueTy.second.initializeArrayWithTakeFrontToBack(IGF, destArray, srcArray, |
| count, valueTy.first); |
| break; |
| case BuiltinValueKind::TakeArrayBackToFront: |
| valueTy.second.initializeArrayWithTakeBackToFront(IGF, destArray, srcArray, |
| count, valueTy.first); |
| break; |
| default: |
| llvm_unreachable("out of sync with if condition"); |
| } |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::CondUnreachable) { |
| // conditionallyUnreachable is a no-op by itself. Since it's noreturn, there |
| // should be a true unreachable terminator right after. |
| return; |
| } |
| |
| if (Builtin.ID == BuiltinValueKind::ZeroInitializer) { |
| // Build a zero initializer of the result type. |
| auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM, |
| substitutions[0].getReplacement()); |
| auto schema = valueTy.second.getSchema(); |
| for (auto &elt : schema) { |
| out.add(llvm::Constant::getNullValue(elt.getScalarType())); |
| } |
| return; |
| } |
| |
| llvm_unreachable("IRGen unimplemented for this builtin!"); |
| } |
| |
| |
| /// Emit the unsubstituted result of this call into the given explosion. |
| /// The unsubstituted result must be naturally returned directly. |
| void CallEmission::emitToUnmappedExplosion(Explosion &out) { |
| assert(LastArgWritten == 0 && "emitting unnaturally to explosion"); |
| |
| auto call = emitCallSite(false); |
| |
| // Bail out immediately on a void result. |
| llvm::Value *result = call.getInstruction(); |
| if (result->getType()->isVoidTy()) return; |
| |
| CanSILFunctionType origFunctionType = getCallee().getOrigFunctionType(); |
| |
| // If the result was returned autoreleased, implicitly insert the reclaim. |
| if (origFunctionType->getResult().getConvention() == |
| ResultConvention::Autoreleased) { |
| result = emitObjCRetainAutoreleasedReturnValue(IGF, result); |
| } |
| |
| // Get the natural IR type in the body of the function that makes |
| // the call. This may be different than the IR type returned by the |
| // call itself due to ABI type coercion. |
| auto resultType = origFunctionType->getSILResult(); |
| auto &resultTI = IGF.IGM.getTypeInfo(resultType); |
| auto schema = resultTI.getSchema(); |
| auto *bodyType = schema.getScalarResultType(IGF.IGM); |
| |
| // Extract out the scalar results. |
| extractScalarResults(IGF, bodyType, result, out); |
| } |
| |
| /// Emit the unsubstituted result of this call to the given address. |
| /// The unsubstituted result must be naturally returned indirectly. |
| void CallEmission::emitToUnmappedMemory(Address result) { |
| assert(LastArgWritten == 1 && "emitting unnaturally to indirect result"); |
| |
| Args[0] = result.getAddress(); |
| addIndirectReturnAttributes(IGF.IGM, Attrs); |
| #ifndef NDEBUG |
| LastArgWritten = 0; // appease an assert |
| #endif |
| |
| emitCallSite(true); |
| } |
| |
| // FIXME: This doesn't belong on IGF. |
| llvm::CallSite CallEmission::emitInvoke(llvm::CallingConv::ID convention, |
| llvm::Value *fn, |
| ArrayRef<llvm::Value*> args, |
| const llvm::AttributeSet &attrs) { |
| // TODO: exceptions! |
| llvm::CallInst *call = IGF.Builder.CreateCall(fn, args); |
| call->setAttributes(attrs); |
| call->setCallingConv(convention); |
| return call; |
| } |
| |
| /// The private routine to ultimately emit a call or invoke instruction. |
| llvm::CallSite CallEmission::emitCallSite(bool hasIndirectResult) { |
| assert(LastArgWritten == 0); |
| assert(!EmittedCall); |
| EmittedCall = true; |
| |
| // Determine the calling convention. |
| // FIXME: collect attributes in the CallEmission. |
| auto cc = expandCallingConv(IGF.IGM, getCallee().getRepresentation()); |
| |
| // Make the call and clear the arguments array. |
| auto fnPtr = getCallee().getFunctionPointer(); |
| auto fnPtrTy = cast<llvm::PointerType>(fnPtr->getType()); |
| auto fnTy = cast<llvm::FunctionType>(fnPtrTy->getElementType()); |
| |
| // Coerce argument types for those cases where the IR type required |
| // by the ABI differs from the type used within the function body. |
| assert(fnTy->getNumParams() == Args.size()); |
| for (int i = 0, e = fnTy->getNumParams(); i != e; ++i) { |
| auto *paramTy = fnTy->getParamType(i); |
| auto *argTy = Args[i]->getType(); |
| if (paramTy != argTy) |
| Args[i] = IGF.coerceValue(Args[i], paramTy, IGF.IGM.DataLayout); |
| } |
| |
| llvm::CallSite call = emitInvoke(cc, fnPtr, Args, |
| llvm::AttributeSet::get(fnPtr->getContext(), |
| Attrs)); |
| Args.clear(); |
| |
| // Return. |
| return call; |
| } |
| |
| /// Emit the result of this call to memory. |
| void CallEmission::emitToMemory(Address addr, const TypeInfo &substResultTI) { |
| assert(LastArgWritten <= 1); |
| |
| // If the call is naturally to an explosion, emit it that way and |
| // then initialize the temporary. |
| if (LastArgWritten == 0) { |
| Explosion result; |
| emitToExplosion(result); |
| cast<LoadableTypeInfo>(substResultTI).initialize(IGF, result, addr); |
| return; |
| } |
| |
| // Okay, we're naturally emitting to memory. |
| Address origAddr = addr; |
| |
| auto origFnType = CurCallee.getOrigFunctionType(); |
| auto substFnType = CurCallee.getSubstFunctionType(); |
| assert(origFnType->hasIndirectResult() == substFnType->hasIndirectResult()); |
| |
| CanType origResultType, substResultType; |
| if (origFnType->hasIndirectResult()) { |
| origResultType = origFnType->getIndirectResult().getType(); |
| substResultType = substFnType->getIndirectResult().getType(); |
| } else { |
| origResultType = origFnType->getResult().getType(); |
| substResultType = substFnType->getResult().getType(); |
| } |
| |
| if (origResultType->hasTypeParameter()) |
| origResultType = IGF.IGM.getContextArchetypes() |
| .substDependentType(origResultType) |
| ->getCanonicalType(); |
| |
| if (origResultType != substResultType) { |
| auto origTy = IGF.IGM.getStoragePointerTypeForLowered(origResultType); |
| origAddr = IGF.Builder.CreateBitCast(origAddr, origTy); |
| } |
| |
| emitToUnmappedMemory(origAddr); |
| } |
| |
| /// Emit the result of this call to an explosion. |
| void CallEmission::emitToExplosion(Explosion &out) { |
| assert(LastArgWritten <= 1); |
| |
| SILType substResultType = |
| getCallee().getSubstFunctionType()->getSemanticResultSILType(); |
| |
| auto &substResultTI = |
| cast<LoadableTypeInfo>(IGF.getTypeInfo(substResultType)); |
| |
| // If the call is naturally to memory, emit it that way and then |
| // explode that temporary. |
| if (LastArgWritten == 1) { |
| // FIXME: we might still need to handle abstraction difference here? |
| |
| ContainedAddress ctemp = substResultTI.allocateStack(IGF, substResultType, |
| "call.aggresult"); |
| Address temp = ctemp.getAddress(); |
| emitToMemory(temp, substResultTI); |
| |
| // We can use a take. |
| substResultTI.loadAsTake(IGF, temp, out); |
| |
| substResultTI.deallocateStack(IGF, ctemp.getContainer(), substResultType); |
| return; |
| } |
| |
| CanType origResultType = |
| getCallee().getOrigFunctionType()->getResult().getType(); |
| if (origResultType->hasTypeParameter()) |
| origResultType = IGF.IGM.getContextArchetypes() |
| .substDependentType(origResultType) |
| ->getCanonicalType(); |
| |
| // Okay, we're naturally emitting to an explosion. |
| if (origResultType != substResultType.getSwiftRValueType()) { |
| Explosion temp; |
| emitToUnmappedExplosion(temp); |
| ExplosionSchema resultSchema = substResultTI.getSchema(); |
| assert(temp.size() == resultSchema.size()); |
| for (unsigned i = 0, e = temp.size(); i != e; ++i) { |
| llvm::Type *expectedType = resultSchema.begin()[i].getScalarType(); |
| llvm::Value *value = temp.claimNext(); |
| if (value->getType() != expectedType) |
| value = IGF.Builder.CreateBitCast(value, expectedType, |
| value->getName() + ".asSubstituted"); |
| out.add(value); |
| } |
| return; |
| } |
| |
| emitToUnmappedExplosion(out); |
| } |
| |
| CallEmission::CallEmission(CallEmission &&other) |
| : IGF(other.IGF), |
| Attrs(other.Attrs), |
| Args(std::move(other.Args)), |
| CurCallee(std::move(other.CurCallee)), |
| LastArgWritten(other.LastArgWritten), |
| EmittedCall(other.EmittedCall) { |
| // Prevent other's destructor from asserting. |
| other.invalidate(); |
| } |
| |
| CallEmission::~CallEmission() { |
| assert(LastArgWritten == 0); |
| assert(EmittedCall); |
| } |
| |
| void CallEmission::invalidate() { |
| LastArgWritten = 0; |
| EmittedCall = true; |
| } |
| |
| |
| /// Set up this emitter afresh from the current callee specs. |
| void CallEmission::setFromCallee() { |
| EmittedCall = false; |
| |
| unsigned numArgs = CurCallee.getLLVMFunctionType()->getNumParams(); |
| |
| // Set up the args array. |
| assert(Args.empty()); |
| Args.reserve(numArgs); |
| Args.set_size(numArgs); |
| LastArgWritten = numArgs; |
| |
| auto fnType = CurCallee.getOrigFunctionType(); |
| auto sig = FuncSignatureInfo(fnType); |
| Attrs = sig.getSignature(IGF.IGM).getAttributes(); |
| |
| if (fnType->getRepresentation() |
| == SILFunctionTypeRepresentation::WitnessMethod) { |
| unsigned n = getTrailingWitnessSignatureLength(IGF.IGM, fnType); |
| while (n--) { |
| Args[--LastArgWritten] = nullptr; |
| } |
| } |
| |
| llvm::Value *contextPtr = nullptr; |
| if (CurCallee.hasDataPointer()) |
| contextPtr = CurCallee.getDataPointer(IGF); |
| |
| // Add the error result if we have one. |
| if (fnType->hasErrorResult()) { |
| // The invariant is that this is always zero-initialized, so we |
| // don't need to do anything extra here. |
| Address errorResultSlot = |
| IGF.getErrorResultSlot(fnType->getErrorResult().getSILType()); |
| |
| // TODO: Add swift_error attribute. |
| assert(LastArgWritten > 0); |
| Args[--LastArgWritten] = errorResultSlot.getAddress(); |
| addAttribute(LastArgWritten + 1, llvm::Attribute::NoCapture); |
| |
| // Fill in the context pointer if necessary. |
| if (!contextPtr) { |
| contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy); |
| } |
| } |
| |
| // Add the data pointer if we have one. |
| // (Note that we're emitting backwards, so this correctly goes |
| // *before* the error pointer.) |
| if (contextPtr) { |
| assert(fnType->getRepresentation() != SILFunctionTypeRepresentation::Block |
| && "block function should not claimed to have data pointer"); |
| assert(LastArgWritten > 0); |
| Args[--LastArgWritten] = contextPtr; |
| } |
| } |
| |
| /// Does an ObjC method or C function returning the given type require an |
| /// sret indirect result? |
| llvm::PointerType * |
| irgen::requiresExternalIndirectResult(IRGenModule &IGM, |
| CanSILFunctionType fnType) { |
| if (fnType->hasIndirectResult()) { |
| return IGM.getStoragePointerType( |
| fnType->getIndirectResult().getSILType()); |
| } |
| |
| auto resultTy = fnType->getResult().getSILType(); |
| auto clangTy = IGM.getClangType(resultTy); |
| assert(clangTy && "Unexpected failure in Clang type generation!"); |
| |
| SmallVector<clang::CanQualType,1> args; |
| auto extInfo = clang::FunctionType::ExtInfo(); |
| auto &FI = IGM.ABITypes->arrangeFreeFunctionCall(clangTy, args, extInfo, |
| clang::CodeGen::RequiredArgs::All); |
| |
| auto &returnInfo = FI.getReturnInfo(); |
| if (!returnInfo.isIndirect()) |
| return nullptr; |
| |
| auto &ti = IGM.getTypeInfo(resultTy); |
| return ti.getStorageType()->getPointerTo(); |
| } |
| |
| bool irgen::canCoerceToSchema(IRGenModule &IGM, |
| ArrayRef<llvm::Type*> expandedTys, |
| const ExplosionSchema &schema) { |
| // If the schemas don't even match in number, we have to go |
| // through memory. |
| if (expandedTys.size() != schema.size()) |
| return false; |
| |
| // If there's just one element, we can always coerce as a scalar. |
| if (expandedTys.size() == 1) return true; |
| |
| // If there are multiple elements, the pairs of types need to |
| // match in size for the coercion to work. |
| for (size_t i = 0, e = expandedTys.size(); i != e; ++i) { |
| llvm::Type *inputTy = schema[i].getScalarType(); |
| llvm::Type *outputTy = expandedTys[i]; |
| if (inputTy != outputTy && |
| IGM.DataLayout.getTypeSizeInBits(inputTy) != |
| IGM.DataLayout.getTypeSizeInBits(outputTy)) |
| return false; |
| } |
| |
| // Okay, everything is fine. |
| return true; |
| } |
| |
| static void emitDirectExternalArgument(IRGenFunction &IGF, |
| SILType argType, llvm::Type *toTy, |
| Explosion &in, Explosion &out) { |
| // If we're supposed to pass directly as a struct type, that |
| // really means expanding out as multiple arguments. |
| ArrayRef<llvm::Type*> expandedTys; |
| if (auto expansionTy = dyn_cast<llvm::StructType>(toTy)) { |
| // Is there any good reason this isn't public API of llvm::StructType? |
| expandedTys = makeArrayRef(expansionTy->element_begin(), |
| expansionTy->getNumElements()); |
| } else { |
| expandedTys = toTy; |
| } |
| |
| auto &argTI = cast<LoadableTypeInfo>(IGF.getTypeInfo(argType)); |
| auto inputSchema = argTI.getSchema(); |
| |
| // Check to see if we can pairwise coerce Swift's exploded scalars |
| // to Clang's expanded elements. |
| if (canCoerceToSchema(IGF.IGM, expandedTys, inputSchema)) { |
| for (auto outputTy : expandedTys) { |
| llvm::Value *arg = in.claimNext(); |
| if (arg->getType() != outputTy) |
| arg = IGF.coerceValue(arg, outputTy, IGF.IGM.DataLayout); |
| out.add(arg); |
| } |
| return; |
| } |
| |
| // Otherwise, we need to coerce through memory. |
| |
| // Store to a temporary. |
| Address temporary = argTI.allocateStack(IGF, argType, |
| "coerced-arg").getAddress(); |
| argTI.initializeFromParams(IGF, in, temporary, argType); |
| |
| // Bitcast the temporary to the expected type. |
| Address coercedAddr = |
| IGF.Builder.CreateBitCast(temporary, toTy->getPointerTo()); |
| |
| // Project out individual elements if necessary. |
| if (auto expansionTy = dyn_cast<llvm::StructType>(toTy)) { |
| auto layout = IGF.IGM.DataLayout.getStructLayout(expansionTy); |
| for (unsigned i = 0, e = expansionTy->getNumElements(); i != e; ++i) { |
| auto fieldOffset = Size(layout->getElementOffset(i)); |
| auto fieldAddr = IGF.Builder.CreateStructGEP(coercedAddr, i, fieldOffset); |
| out.add(IGF.Builder.CreateLoad(fieldAddr)); |
| } |
| |
| // Otherwise, collect the single scalar. |
| } else { |
| out.add(IGF.Builder.CreateLoad(coercedAddr)); |
| } |
| |
| argTI.deallocateStack(IGF, temporary, argType); |
| } |
| |
| namespace { |
| /// Load a clang argument expansion from a buffer. |
| struct ClangExpandLoadEmitter : |
| ClangExpandProjection<ClangExpandLoadEmitter> { |
| |
| Explosion &Out; |
| ClangExpandLoadEmitter(IRGenFunction &IGF, Explosion &out) |
| : ClangExpandProjection(IGF), Out(out) {} |
| |
| void visitScalar(llvm::Type *scalarTy, Address addr) { |
| addr = IGF.Builder.CreateBitCast(addr, scalarTy->getPointerTo()); |
| auto value = IGF.Builder.CreateLoad(addr); |
| Out.add(value); |
| } |
| }; |
| |
| /// Store a clang argument expansion into a buffer. |
| struct ClangExpandStoreEmitter : |
| ClangExpandProjection<ClangExpandStoreEmitter> { |
| |
| Explosion &In; |
| ClangExpandStoreEmitter(IRGenFunction &IGF, Explosion &in) |
| : ClangExpandProjection(IGF), In(in) {} |
| |
| void visitScalar(llvm::Type *scalarTy, Address addr) { |
| auto value = In.claimNext(); |
| |
| addr = IGF.Builder.CreateBitCast(addr, scalarTy->getPointerTo()); |
| IGF.Builder.CreateStore(value, addr); |
| } |
| }; |
| } |
| |
| /// Given a Swift value explosion in 'in', produce a Clang expansion |
| /// (according to ABIArgInfo::Expand) in 'out'. |
| static void emitClangExpandedArgument(IRGenFunction &IGF, |
| Explosion &in, Explosion &out, |
| clang::CanQualType clangType, |
| SILType swiftType, |
| const LoadableTypeInfo &swiftTI) { |
| // If Clang's expansion schema matches Swift's, great. |
| auto swiftSchema = swiftTI.getSchema(); |
| if (doesClangExpansionMatchSchema(IGF.IGM, clangType, swiftSchema)) { |
| return in.transferInto(out, swiftSchema.size()); |
| } |
| |
| // Otherwise, materialize to a temporary. |
| Address temp = swiftTI.allocateStack(IGF, swiftType, |
| "clang-expand-arg.temp").getAddress(); |
| swiftTI.initialize(IGF, in, temp); |
| |
| Address castTemp = IGF.Builder.CreateBitCast(temp, IGF.IGM.Int8PtrTy); |
| ClangExpandLoadEmitter(IGF, out).visit(clangType, castTemp); |
| } |
| |
| /// Given a Clang-expanded (according to ABIArgInfo::Expand) parameter |
| /// in 'in', produce a Swift value explosion in 'out'. |
| void irgen::emitClangExpandedParameter(IRGenFunction &IGF, |
| Explosion &in, Explosion &out, |
| clang::CanQualType clangType, |
| SILType swiftType, |
| const LoadableTypeInfo &swiftTI) { |
| // If Clang's expansion schema matches Swift's, great. |
| auto swiftSchema = swiftTI.getSchema(); |
| if (doesClangExpansionMatchSchema(IGF.IGM, clangType, swiftSchema)) { |
| return in.transferInto(out, swiftSchema.size()); |
| } |
| |
| // Otherwise, materialize to a temporary. |
| Address temp = swiftTI.allocateStack(IGF, swiftType, |
| "clang-expand-param.temp").getAddress(); |
| Address castTemp = IGF.Builder.CreateBitCast(temp, IGF.IGM.Int8PtrTy); |
| ClangExpandStoreEmitter(IGF, in).visit(clangType, castTemp); |
| |
| // Then load out. |
| swiftTI.loadAsTake(IGF, temp, out); |
| } |
| |
| static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, |
| Explosion &in, Explosion &out) { |
| auto fnType = callee.getOrigFunctionType(); |
| auto params = fnType->getParameters(); |
| |
| SmallVector<clang::CanQualType,4> paramTys; |
| auto const &clangCtx = IGF.IGM.getClangASTContext(); |
| |
| // Objective-C methods take two implicit first parameters, 'self' |
| // and '_cmd'. |
| if (callee.getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) { |
| auto &self = params.back(); |
| auto clangTy = IGF.IGM.getClangType(self); |
| paramTys.push_back(clangTy); |
| paramTys.push_back(clangCtx.VoidPtrTy); |
| params = params.slice(0, params.size() - 1); |
| |
| // Blocks take an implicit first parameter of block-context type. |
| } else if (callee.getRepresentation() == SILFunctionTypeRepresentation::Block) { |
| paramTys.push_back(clangCtx.VoidPtrTy); |
| } |
| |
| for (auto param : params) { |
| auto clangTy = IGF.IGM.getClangType(param); |
| paramTys.push_back(clangTy); |
| } |
| |
| const auto &resultInfo = callee.getSubstFunctionType()->getResult(); |
| auto clangResultTy = IGF.IGM.getClangType(resultInfo.getSILType()); |
| |
| // Generate function info for this set of arguments. |
| auto extInfo = clang::FunctionType::ExtInfo(); |
| auto &FI = IGF.IGM.ABITypes->arrangeFreeFunctionCall(clangResultTy, |
| paramTys, extInfo, |
| clang::CodeGen::RequiredArgs::All); |
| |
| assert(FI.arg_size() == paramTys.size() && |
| "Expected one ArgInfo for each parameter type!"); |
| |
| // The index of the first "physical" parameter from paramTys/FI that |
| // corresponds to a logical parameter from params. |
| unsigned firstParam = 0; |
| |
| auto claimNextDirect = [&] { |
| assert(FI.arg_begin()[firstParam].info.isDirect()); |
| assert(!FI.arg_begin()[firstParam].info.getPaddingType()); |
| out.add(in.claimNext()); |
| firstParam++; |
| }; |
| |
| // Handle the ObjC prefix. |
| if (callee.getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) { |
| // The first two parameters are pointers, and we make some |
| // simplifying assumptions. |
| claimNextDirect(); |
| claimNextDirect(); |
| |
| // Or the block prefix. |
| } else if (fnType->getRepresentation() |
| == SILFunctionTypeRepresentation::Block) { |
| claimNextDirect(); |
| } |
| |
| // Get the argument index base for attributes. |
| // If there's an indirect return, it will come before any "in" arguments. |
| unsigned attrBase = 0; |
| auto &returnInfo = FI.getReturnInfo(); |
| if (returnInfo.isIndirect()) |
| attrBase += 1; |
| |
| // Does the result need an extension attribute? |
| if (returnInfo.isExtend()) { |
| bool signExt = clangResultTy->hasSignedIntegerRepresentation(); |
| assert((signExt || clangResultTy->hasUnsignedIntegerRepresentation()) && |
| "Invalid attempt to add extension attribute to argument!"); |
| (void) signExt; |
| } |
| |
| for (auto i : indices(paramTys).slice(firstParam)) { |
| auto &AI = FI.arg_begin()[i].info; |
| |
| // Add a padding argument if required. |
| if (auto *padType = AI.getPaddingType()) |
| out.add(llvm::UndefValue::get(padType)); |
| |
| SILType paramType = params[i - firstParam].getSILType(); |
| switch (AI.getKind()) { |
| case clang::CodeGen::ABIArgInfo::Extend: { |
| bool signExt = paramTys[i]->hasSignedIntegerRepresentation(); |
| assert((signExt || paramTys[i]->hasUnsignedIntegerRepresentation()) && |
| "Invalid attempt to add extension attribute to argument!"); |
| (void) signExt; |
| SWIFT_FALLTHROUGH; |
| } |
| case clang::CodeGen::ABIArgInfo::Direct: { |
| auto toTy = AI.getCoerceToType(); |
| |
| // Mutating parameters are bridged as Clang pointer types. For now, this |
| // only ever comes up with Clang-generated accessors. |
| if (params[i - firstParam].isIndirectMutating()) { |
| assert(paramType.isAddress() && "SIL type is not an address?"); |
| |
| auto addr = in.claimNext(); |
| if (addr->getType() != toTy) |
| addr = IGF.coerceValue(addr, toTy, IGF.IGM.DataLayout); |
| out.add(addr); |
| break; |
| } |
| |
| emitDirectExternalArgument(IGF, paramType, toTy, in, out); |
| break; |
| } |
| case clang::CodeGen::ABIArgInfo::Indirect: { |
| auto &ti = cast<LoadableTypeInfo>(IGF.getTypeInfo(paramType)); |
| Address addr = ti.allocateStack(IGF, paramType, |
| "indirect-temporary").getAddress(); |
| ti.initialize(IGF, in, addr); |
| |
| out.add(addr.getAddress()); |
| break; |
| } |
| case clang::CodeGen::ABIArgInfo::Expand: |
| emitClangExpandedArgument(IGF, in, out, paramTys[i], paramType, |
| cast<LoadableTypeInfo>(IGF.getTypeInfo(paramType))); |
| break; |
| case clang::CodeGen::ABIArgInfo::Ignore: |
| break; |
| case clang::CodeGen::ABIArgInfo::InAlloca: |
| llvm_unreachable("Need to handle InAlloca when externalizing arguments"); |
| break; |
| } |
| } |
| } |
| |
| namespace { |
| enum IsIndirectValueArgument_t: bool { |
| IsNotIndirectValueArgument = false, |
| IsIndirectValueArgument = true, |
| }; |
| } |
| |
| static IsIndirectValueArgument_t addNativeArgument(IRGenFunction &IGF, |
| Explosion &in, |
| const TypeInfo &ti, |
| ParameterConvention convention, |
| Explosion &out) { |
| // Addresses consist of a single pointer argument. |
| if (isIndirectParameter(convention)) { |
| out.add(in.claimNext()); |
| return IsNotIndirectValueArgument; |
| } |
| |
| auto &loadableTI = cast<LoadableTypeInfo>(ti); |
| auto schema = ti.getSchema(); |
| |
| if (schema.requiresIndirectParameter(IGF.IGM)) { |
| // Pass the argument indirectly. |
| auto buf = IGF.createAlloca(ti.getStorageType(), |
| loadableTI.getFixedAlignment(), ""); |
| loadableTI.initialize(IGF, in, buf); |
| out.add(buf.getAddress()); |
| return IsIndirectValueArgument; |
| } else { |
| // Pass the argument explosion directly. |
| loadableTI.reexplode(IGF, in, out); |
| return IsNotIndirectValueArgument; |
| } |
| } |
| |
| /// Add a new set of arguments to the function. |
| void CallEmission::setArgs(Explosion &arg, |
| ArrayRef<SILParameterInfo> params, |
| WitnessMetadata *witnessMetadata) { |
| // Convert arguments to a representation appropriate to the calling |
| // convention. |
| Explosion adjustedArg; |
| |
| switch (getCallee().getRepresentation()) { |
| case SILFunctionTypeRepresentation::CFunctionPointer: |
| case SILFunctionTypeRepresentation::ObjCMethod: |
| case SILFunctionTypeRepresentation::Block: { |
| externalizeArguments(IGF, getCallee(), arg, adjustedArg); |
| break; |
| } |
| |
| case SILFunctionTypeRepresentation::WitnessMethod: |
| // This is basically duplicating emitTrailingWitnessArguments. |
| assert(witnessMetadata); |
| assert(witnessMetadata->SelfMetadata); |
| Args.back() = witnessMetadata->SelfMetadata; |
| SWIFT_FALLTHROUGH; |
| |
| case SILFunctionTypeRepresentation::Method: |
| case SILFunctionTypeRepresentation::Thin: |
| case SILFunctionTypeRepresentation::Thick: |
| // Check for value arguments that need to be passed indirectly. |
| for (auto param : params) { |
| addNativeArgument(IGF, arg, |
| IGF.getTypeInfoForLowered(param.getType()), |
| param.getConvention(), |
| adjustedArg); |
| } |
| adjustedArg.add(arg.claimAll()); |
| break; |
| } |
| |
| // Add the given number of arguments. |
| assert(LastArgWritten >= adjustedArg.size()); |
| |
| size_t targetIndex = LastArgWritten - adjustedArg.size(); |
| assert(targetIndex <= 1); |
| LastArgWritten = targetIndex; |
| |
| auto argIterator = Args.begin() + targetIndex; |
| for (auto value : adjustedArg.claimAll()) { |
| *argIterator++ = value; |
| } |
| } |
| |
| void CallEmission::addAttribute(unsigned Index, llvm::Attribute::AttrKind Attr) { |
| Attrs = Attrs.addAttribute(IGF.IGM.LLVMContext, Index, Attr); |
| } |
| |
| /// Initialize an Explosion with the parameters of the current |
| /// function. All of the objects will be added unmanaged. This is |
| /// really only useful when writing prologue code. |
| Explosion IRGenFunction::collectParameters() { |
| Explosion params; |
| for (auto i = CurFn->arg_begin(), e = CurFn->arg_end(); i != e; ++i) |
| params.add(&*i); |
| return params; |
| } |
| |
| /// Fetch the error result slot. |
| Address IRGenFunction::getErrorResultSlot(SILType errorType) { |
| if (!ErrorResultSlot) { |
| auto &errorTI = cast<FixedTypeInfo>(getTypeInfo(errorType)); |
| |
| IRBuilder builder(IGM.getLLVMContext()); |
| builder.SetInsertPoint(AllocaIP->getParent(), AllocaIP->getIterator()); |
| |
| // Create the alloca. We don't use allocateStack because we're |
| // not allocating this in stack order. |
| auto addr = builder.CreateAlloca(errorTI.getStorageType(), nullptr, |
| "swifterror"); |
| addr->setAlignment(errorTI.getFixedAlignment().getValue()); |
| // TODO: add swift_error attribute |
| |
| // Initialize at the alloca point. |
| auto nullError = llvm::ConstantPointerNull::get( |
| cast<llvm::PointerType>(errorTI.getStorageType())); |
| builder.CreateStore(nullError, addr, errorTI.getFixedAlignment()); |
| |
| ErrorResultSlot = addr; |
| } |
| return Address(ErrorResultSlot, IGM.getPointerAlignment()); |
| } |
| |
| /// Fetch the error result slot received from the caller. |
| Address IRGenFunction::getCallerErrorResultSlot() { |
| assert(ErrorResultSlot && "no error result slot!"); |
| assert(isa<llvm::Argument>(ErrorResultSlot) && "error result slot is local!"); |
| return Address(ErrorResultSlot, IGM.getPointerAlignment()); |
| } |
| |
| // Set the error result slot. This should only be done in the prologue. |
| void IRGenFunction::setErrorResultSlot(llvm::Value *address) { |
| assert(!ErrorResultSlot && "already have error result slot!"); |
| assert(isa<llvm::PointerType>(address->getType())); |
| ErrorResultSlot = address; |
| } |
| |
| /// Emit the basic block that 'return' should branch to and insert it into |
| /// the current function. This creates a second |
| /// insertion point that most blocks should be inserted before. |
| void IRGenFunction::emitBBForReturn() { |
| ReturnBB = createBasicBlock("return"); |
| CurFn->getBasicBlockList().push_back(ReturnBB); |
| } |
| |
| /// Emit the prologue for the function. |
| void IRGenFunction::emitPrologue() { |
| // Set up the IRBuilder. |
| llvm::BasicBlock *EntryBB = createBasicBlock("entry"); |
| assert(CurFn->getBasicBlockList().empty() && "prologue already emitted?"); |
| CurFn->getBasicBlockList().push_back(EntryBB); |
| Builder.SetInsertPoint(EntryBB); |
| |
| // Set up the alloca insertion point. |
| AllocaIP = Builder.CreateAlloca(IGM.Int1Ty, /*array size*/ nullptr, |
| "alloca point"); |
| } |
| |
| /// Emit a branch to the return block and set the insert point there. |
| /// Returns true if the return block is reachable, false otherwise. |
| bool IRGenFunction::emitBranchToReturnBB() { |
| // If there are no edges to the return block, we never want to emit it. |
| if (ReturnBB->use_empty()) { |
| ReturnBB->eraseFromParent(); |
| |
| // Normally this means that we'll just insert the epilogue in the |
| // current block, but if the current IP is unreachable then so is |
| // the entire epilogue. |
| if (!Builder.hasValidIP()) |
| return false; |
| |
| // Otherwise, branch to it if the current IP is reachable. |
| } else if (Builder.hasValidIP()) { |
| Builder.CreateBr(ReturnBB); |
| Builder.SetInsertPoint(ReturnBB); |
| |
| // Otherwise, if there is exactly one use of the return block, merge |
| // it into its predecessor. |
| } else if (ReturnBB->hasOneUse()) { |
| // return statements are never emitted as conditional branches. |
| llvm::BranchInst *Br = cast<llvm::BranchInst>(*ReturnBB->use_begin()); |
| assert(Br->isUnconditional()); |
| Builder.SetInsertPoint(Br->getParent()); |
| Br->eraseFromParent(); |
| ReturnBB->eraseFromParent(); |
| |
| // Otherwise, just move the IP to the return block. |
| } else { |
| Builder.SetInsertPoint(ReturnBB); |
| } |
| return true; |
| } |
| |
| /// Emit the epilogue for the function. |
| void IRGenFunction::emitEpilogue() { |
| // Destroy the alloca insertion point. |
| AllocaIP->eraseFromParent(); |
| } |
| |
| Address irgen::allocateForCoercion(IRGenFunction &IGF, |
| llvm::Type *fromTy, |
| llvm::Type *toTy, |
| const llvm::Twine &basename) { |
| auto &DL = IGF.IGM.DataLayout; |
| |
| auto bufferTy = DL.getTypeSizeInBits(fromTy) >= DL.getTypeSizeInBits(toTy) |
| ? fromTy |
| : toTy; |
| |
| auto alignment = std::max(DL.getABITypeAlignment(fromTy), |
| DL.getABITypeAlignment(toTy)); |
| |
| return IGF.createAlloca(bufferTy, Alignment(alignment), |
| basename + ".coerced"); |
| } |
| |
| llvm::Value* IRGenFunction::coerceValue(llvm::Value *value, llvm::Type *toTy, |
| const llvm::DataLayout &DL) |
| { |
| llvm::Type *fromTy = value->getType(); |
| assert(fromTy != toTy && "Unexpected same types in type coercion!"); |
| assert(!fromTy->isVoidTy() |
| && "Unexpected void source type in type coercion!"); |
| assert(!toTy->isVoidTy() |
| && "Unexpected void destination type in type coercion!"); |
| |
| // Use the pointer/pointer and pointer/int casts if we can. |
| if (toTy->isPointerTy()) { |
| if (fromTy->isPointerTy()) |
| return Builder.CreateBitCast(value, toTy); |
| if (fromTy == IGM.IntPtrTy) |
| return Builder.CreateIntToPtr(value, toTy); |
| } else if (fromTy->isPointerTy()) { |
| if (toTy == IGM.IntPtrTy) { |
| return Builder.CreatePtrToInt(value, toTy); |
| } |
| } |
| |
| // Otherwise we need to store, bitcast, and load. |
| auto address = allocateForCoercion(*this, fromTy, toTy, |
| value->getName() + ".coercion"); |
| auto orig = Builder.CreateBitCast(address, fromTy->getPointerTo()); |
| Builder.CreateStore(value, orig); |
| auto coerced = Builder.CreateBitCast(address, toTy->getPointerTo()); |
| return Builder.CreateLoad(coerced); |
| } |
| |
| void IRGenFunction::emitScalarReturn(llvm::Type *resultType, |
| Explosion &result) { |
| if (result.size() == 0) { |
| Builder.CreateRetVoid(); |
| return; |
| } |
| |
| auto *ABIType = CurFn->getReturnType(); |
| |
| if (result.size() == 1) { |
| auto *returned = result.claimNext(); |
| if (ABIType != returned->getType()) |
| returned = coerceValue(returned, ABIType, IGM.DataLayout); |
| |
| Builder.CreateRet(returned); |
| return; |
| } |
| |
| // Multiple return values are returned as a struct. |
| assert(cast<llvm::StructType>(resultType)->getNumElements() == result.size()); |
| llvm::Value *resultAgg = llvm::UndefValue::get(resultType); |
| for (unsigned i = 0, e = result.size(); i != e; ++i) { |
| llvm::Value *elt = result.claimNext(); |
| resultAgg = Builder.CreateInsertValue(resultAgg, elt, i); |
| } |
| |
| if (ABIType != resultType) |
| resultAgg = coerceValue(resultAgg, ABIType, IGM.DataLayout); |
| |
| Builder.CreateRet(resultAgg); |
| } |
| |
| void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result) { |
| if (result.size() == 0) { |
| Builder.CreateRetVoid(); |
| return; |
| } |
| |
| auto *ABIType = CurFn->getReturnType(); |
| |
| if (result.size() == 1) { |
| auto *returned = result.claimNext(); |
| if (ABIType != returned->getType()) |
| returned = coerceValue(returned, ABIType, IGM.DataLayout); |
| |
| Builder.CreateRet(returned); |
| return; |
| } |
| |
| auto &resultTI = IGM.getTypeInfo(resultType); |
| auto schema = resultTI.getSchema(); |
| auto *bodyType = schema.getScalarResultType(IGM); |
| |
| // Multiple return values are returned as a struct. |
| assert(cast<llvm::StructType>(bodyType)->getNumElements() == result.size()); |
| llvm::Value *resultAgg = llvm::UndefValue::get(bodyType); |
| for (unsigned i = 0, e = result.size(); i != e; ++i) { |
| llvm::Value *elt = result.claimNext(); |
| resultAgg = Builder.CreateInsertValue(resultAgg, elt, i); |
| } |
| |
| if (ABIType != bodyType) |
| resultAgg = coerceValue(resultAgg, ABIType, IGM.DataLayout); |
| |
| Builder.CreateRet(resultAgg); |
| } |
| |
| static void emitApplyArgument(IRGenFunction &IGF, |
| SILParameterInfo origParam, |
| SILParameterInfo substParam, |
| Explosion &in, |
| Explosion &out) { |
| bool isSubstituted = (substParam.getSILType() != origParam.getSILType()); |
| |
| // For indirect arguments, we just need to pass a pointer. |
| if (origParam.isIndirect()) { |
| // This address is of the substituted type. |
| auto addr = in.claimNext(); |
| |
| // If a substitution is in play, just bitcast the address. |
| if (isSubstituted) { |
| auto origType = IGF.IGM.getStoragePointerType(origParam.getSILType()); |
| addr = IGF.Builder.CreateBitCast(addr, origType); |
| } |
| |
| out.add(addr); |
| return; |
| } |
| |
| // Otherwise, it's an explosion, which we may need to translate, |
| // both in terms of explosion level and substitution levels. |
| |
| // Handle the last unsubstituted case. |
| if (!isSubstituted) { |
| auto &substArgTI |
| = cast<LoadableTypeInfo>(IGF.getTypeInfo(substParam.getSILType())); |
| substArgTI.reexplode(IGF, in, out); |
| return; |
| } |
| |
| reemitAsUnsubstituted(IGF, origParam.getSILType(), |
| substParam.getSILType(), |
| in, out); |
| } |
| |
| /// Emit the forwarding stub function for a partial application. |
| /// |
| /// If 'layout' is null, there is a single captured value of |
| /// Swift-refcountable type that is being used directly as the |
| /// context object. |
| static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, |
| llvm::Function *staticFnPtr, |
| bool calleeHasContext, |
| llvm::Type *fnTy, |
| const llvm::AttributeSet &origAttrs, |
| CanSILFunctionType origType, |
| CanSILFunctionType substType, |
| CanSILFunctionType outType, |
| ArrayRef<Substitution> subs, |
| HeapLayout const *layout, |
| ArrayRef<ParameterConvention> conventions) { |
| llvm::AttributeSet outAttrs; |
| |
| llvm::FunctionType *fwdTy = IGM.getFunctionType(outType, outAttrs); |
| // Build a name for the thunk. If we're thunking a static function reference, |
| // include its symbol name in the thunk name. |
| llvm::SmallString<20> thunkName; |
| thunkName += "_TPA"; |
| if (staticFnPtr) { |
| thunkName += '_'; |
| thunkName += staticFnPtr->getName(); |
| } |
| |
| // FIXME: Maybe cache the thunk by function and closure types?. |
| llvm::Function *fwd = |
| llvm::Function::Create(fwdTy, llvm::Function::InternalLinkage, |
| llvm::StringRef(thunkName), &IGM.Module); |
| |
| auto initialAttrs = IGM.constructInitialAttributes(); |
| // Merge initialAttrs with outAttrs. |
| auto updatedAttrs = outAttrs.addAttributes(IGM.getLLVMContext(), |
| llvm::AttributeSet::FunctionIndex, initialAttrs); |
| fwd->setAttributes(updatedAttrs); |
| |
| IRGenFunction subIGF(IGM, fwd); |
| if (IGM.DebugInfo) |
| IGM.DebugInfo->emitArtificialFunction(subIGF, fwd); |
| |
| Explosion origParams = subIGF.collectParameters(); |
| |
| // Create a new explosion for potentially reabstracted parameters. |
| Explosion args; |
| |
| { |
| // Lower the forwarded arguments in the original function's generic context. |
| GenericContextScope scope(IGM, origType->getGenericSignature()); |
| |
| // Forward the indirect return value, if we have one. |
| auto &resultTI = IGM.getTypeInfo(outType->getResult().getSILType()); |
| if (resultTI.getSchema().requiresIndirectResult(IGM)) |
| args.add(origParams.claimNext()); |
| |
| // Reemit the parameters as unsubstituted. |
| for (unsigned i = 0; i < outType->getParameters().size(); ++i) { |
| Explosion arg; |
| auto origParamInfo = origType->getParameters()[i]; |
| auto &ti = IGM.getTypeInfoForLowered(origParamInfo.getType()); |
| auto schema = ti.getSchema(); |
| |
| // Forward the address of indirect value params. |
| if (!isIndirectParameter(origParamInfo.getConvention()) |
| && schema.requiresIndirectParameter(IGM)) { |
| auto addr = origParams.claimNext(); |
| if (addr->getType() != ti.getStorageType()->getPointerTo()) |
| addr = subIGF.Builder.CreateBitCast(addr, |
| ti.getStorageType()->getPointerTo()); |
| args.add(addr); |
| continue; |
| } |
| |
| emitApplyArgument(subIGF, origParamInfo, |
| outType->getParameters()[i], |
| origParams, args); |
| } |
| } |
| |
| struct AddressToDeallocate { |
| SILType Type; |
| const TypeInfo &TI; |
| Address Addr; |
| }; |
| SmallVector<AddressToDeallocate, 4> addressesToDeallocate; |
| |
| bool dependsOnContextLifetime = false; |
| bool consumesContext; |
| bool needsAllocas = false; |
| |
| switch (outType->getCalleeConvention()) { |
| case ParameterConvention::Direct_Owned: |
| consumesContext = true; |
| break; |
| case ParameterConvention::Direct_Unowned: |
| case ParameterConvention::Direct_Guaranteed: |
| consumesContext = false; |
| break; |
| case ParameterConvention::Direct_Deallocating: |
| llvm_unreachable("callables do not have destructors"); |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| case ParameterConvention::Indirect_Out: |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Guaranteed: |
| llvm_unreachable("indirect callables not supported"); |
| } |
| |
| // Lower the captured arguments in the original function's generic context. |
| GenericContextScope scope(IGM, origType->getGenericSignature()); |
| |
| // This is where the context parameter appears. |
| llvm::Value *rawData = nullptr; |
| Address data; |
| unsigned nextCapturedField = 0; |
| if (!layout) { |
| rawData = origParams.claimNext(); |
| } else if (!layout->isKnownEmpty()) { |
| rawData = origParams.claimNext(); |
| data = layout->emitCastTo(subIGF, rawData); |
| |
| // Restore type metadata bindings, if we have them. |
| if (layout->hasBindings()) { |
| auto bindingLayout = layout->getElement(nextCapturedField++); |
| // The bindings should be fixed-layout inside the object, so we can |
| // pass None here. If they weren't, we'd have a chicken-egg problem. |
| auto bindingsAddr = bindingLayout.project(subIGF, data, /*offsets*/ None); |
| layout->getBindings().restore(subIGF, bindingsAddr); |
| } |
| |
| // There's still a placeholder to claim if the target type is thick |
| // or there's an error result. |
| } else if (outType->getRepresentation()==SILFunctionTypeRepresentation::Thick |
| || outType->hasErrorResult()) { |
| llvm::Value *contextPtr = origParams.claimNext(); (void)contextPtr; |
| assert(contextPtr->getType() == IGM.RefCountedPtrTy); |
| } |
| |
| // If there's a data pointer required, but it's a swift-retainable |
| // value being passed as the context, just forward it down. |
| if (!layout) { |
| assert(conventions.size() == 1); |
| |
| // We need to retain the parameter if: |
| // - we received at +0 (either) and are passing as owned |
| // - we received as unowned and are passing as guaranteed |
| auto argConvention = conventions[nextCapturedField++]; |
| switch (argConvention) { |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Direct_Owned: |
| if (!consumesContext) subIGF.emitNativeStrongRetain(rawData); |
| break; |
| |
| case ParameterConvention::Indirect_In_Guaranteed: |
| case ParameterConvention::Direct_Guaranteed: |
| dependsOnContextLifetime = true; |
| if (outType->getCalleeConvention() == |
| ParameterConvention::Direct_Unowned) { |
| subIGF.emitNativeStrongRetain(rawData); |
| consumesContext = true; |
| } |
| break; |
| |
| case ParameterConvention::Direct_Unowned: |
| // Make sure we release later if we received at +1. |
| if (consumesContext) |
| dependsOnContextLifetime = true; |
| break; |
| |
| case ParameterConvention::Direct_Deallocating: |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| case ParameterConvention::Indirect_Out: |
| llvm_unreachable("should never happen!"); |
| } |
| |
| llvm::Type *expectedArgTy = |
| fnTy->getPointerElementType()->getFunctionParamType(args.size()); |
| llvm::Value *argValue; |
| if (isIndirectParameter(argConvention)) { |
| expectedArgTy = expectedArgTy->getPointerElementType(); |
| auto temporary = subIGF.createAlloca(expectedArgTy, |
| subIGF.IGM.getPointerAlignment(), |
| "partial-apply.context"); |
| argValue = subIGF.Builder.CreateBitCast(rawData, expectedArgTy); |
| subIGF.Builder.CreateStore(argValue, temporary); |
| argValue = temporary.getAddress(); |
| } else { |
| argValue = subIGF.Builder.CreateBitCast(rawData, expectedArgTy); |
| } |
| args.add(argValue); |
| |
| // If there's a data pointer required, grab it and load out the |
| // extra, previously-curried parameters. |
| } else if (!layout->isKnownEmpty()) { |
| unsigned origParamI = outType->getParameters().size(); |
| assert(layout->getElements().size() == conventions.size() |
| && "conventions don't match context layout"); |
| |
| // Calculate non-fixed field offsets. |
| HeapNonFixedOffsets offsets(subIGF, *layout); |
| |
| // Perform the loads. |
| for (unsigned n = layout->getElements().size(); |
| nextCapturedField < n; |
| ++nextCapturedField) { |
| auto &fieldLayout = layout->getElement(nextCapturedField); |
| auto &fieldTy = layout->getElementTypes()[nextCapturedField]; |
| auto fieldConvention = conventions[nextCapturedField]; |
| Address fieldAddr = fieldLayout.project(subIGF, data, offsets); |
| auto &fieldTI = fieldLayout.getType(); |
| auto fieldSchema = fieldTI.getSchema(); |
| |
| Explosion param; |
| switch (fieldConvention) { |
| case ParameterConvention::Indirect_In: { |
| // The +1 argument is passed indirectly, so we need to copy into a |
| // temporary. |
| needsAllocas = true; |
| auto caddr = fieldTI.allocateStack(subIGF, fieldTy, "arg.temp"); |
| fieldTI.initializeWithCopy(subIGF, caddr.getAddress(), fieldAddr, |
| fieldTy); |
| param.add(caddr.getAddressPointer()); |
| |
| // Remember to deallocate later. |
| addressesToDeallocate.push_back( |
| AddressToDeallocate{fieldTy, fieldTI, caddr.getContainer()}); |
| |
| break; |
| } |
| case ParameterConvention::Indirect_In_Guaranteed: |
| // The argument is +0, so we can use the address of the param in |
| // the context directly. |
| param.add(fieldAddr.getAddress()); |
| dependsOnContextLifetime = true; |
| break; |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| // Load the address of the inout parameter. |
| cast<LoadableTypeInfo>(fieldTI).loadAsCopy(subIGF, fieldAddr, param); |
| break; |
| case ParameterConvention::Direct_Guaranteed: |
| case ParameterConvention::Direct_Unowned: |
| // If the type is nontrivial, keep the context alive since the field |
| // depends on the context to not be deallocated. |
| if (!fieldTI.isPOD(ResilienceScope::Component)) |
| dependsOnContextLifetime = true; |
| SWIFT_FALLTHROUGH; |
| case ParameterConvention::Direct_Deallocating: |
| // Load these parameters directly. We can "take" since the parameter is |
| // +0. This can happen due to either: |
| // |
| // 1. The context keeping the parameter alive. |
| // 2. The object being a deallocating object. This means retains and |
| // releases do not affect the object since we do not support object |
| // resurrection. |
| cast<LoadableTypeInfo>(fieldTI).loadAsTake(subIGF, fieldAddr, param); |
| break; |
| case ParameterConvention::Direct_Owned: |
| // Copy the value out at +1. |
| cast<LoadableTypeInfo>(fieldTI).loadAsCopy(subIGF, fieldAddr, param); |
| break; |
| case ParameterConvention::Indirect_Out: |
| llvm_unreachable("can't partially apply out params"); |
| } |
| |
| // Reemit the capture params as unsubstituted. |
| if (origParamI < origType->getParameters().size()) { |
| Explosion origParam; |
| auto origParamInfo = origType->getParameters()[origParamI]; |
| emitApplyArgument(subIGF, origParamInfo, |
| substType->getParameters()[origParamI], |
| param, origParam); |
| |
| needsAllocas |= addNativeArgument(subIGF, origParam, |
| IGM.getTypeInfoForLowered(origParamInfo.getType()), |
| origParamInfo.getConvention(), |
| args); |
| ++origParamI; |
| } else { |
| args.add(param.claimAll()); |
| } |
| |
| } |
| |
| // If the parameters can live independent of the context, release it now |
| // so we can tail call. The safety of this assumes that neither this release |
| // nor any of the loads can throw. |
| if (consumesContext && !dependsOnContextLifetime) |
| subIGF.emitNativeStrongRelease(rawData); |
| } |
| |
| // Derive the callee function pointer. If we found a function |
| // pointer statically, great. |
| llvm::Value *fnPtr; |
| if (staticFnPtr) { |
| assert(staticFnPtr->getType() == fnTy && "static function type mismatch?!"); |
| fnPtr = staticFnPtr; |
| |
| // Otherwise, it was the last thing we added to the layout. |
| } else { |
| // The dynamic function pointer is packed "last" into the context, |
| // and we pulled it out as an argument. Just pop it off. |
| fnPtr = args.takeLast(); |
| |
| // It comes out of the context as an i8*. Cast to the function type. |
| fnPtr = subIGF.Builder.CreateBitCast(fnPtr, fnTy); |
| } |
| |
| // Derive the context argument if needed. This is either: |
| // - the saved context argument, in which case it was the last |
| // thing we added to the layout other than a possible non-static |
| // function pointer (which we already popped off of 'args'); or |
| // - 'self', in which case it was the last formal argument. |
| // In either case, it's the last thing in 'args'. |
| llvm::Value *fnContext = nullptr; |
| if (calleeHasContext || |
| (origType->hasSelfParam() && |
| isSelfContextParameter(origType->getSelfParameter()))) { |
| fnContext = args.takeLast(); |
| } |
| |
| // Emit the polymorphic arguments. |
| assert((subs.empty() != hasPolymorphicParameters(origType) || |
| (subs.empty() && origType->getRepresentation() == |
| SILFunctionTypeRepresentation::WitnessMethod)) |
| && "should have substitutions iff original function is generic"); |
| WitnessMetadata witnessMetadata; |
| if (hasPolymorphicParameters(origType)) { |
| emitPolymorphicArguments(subIGF, origType, substType, subs, |
| &witnessMetadata, args); |
| } |
| |
| // Okay, this is where the callee context goes. |
| if (fnContext) { |
| // TODO: swift_context marker. |
| args.add(fnContext); |
| |
| // Pass a placeholder if necessary. |
| } else if (origType->hasErrorResult()) { |
| args.add(llvm::UndefValue::get(IGM.RefCountedPtrTy)); |
| } |
| |
| // Pass down the error result. |
| if (origType->hasErrorResult()) { |
| llvm::Value *errorResultPtr = origParams.claimNext(); |
| // TODO: swift_error marker. |
| args.add(errorResultPtr); |
| } |
| |
| assert(origParams.empty()); |
| |
| if (origType->getRepresentation() == |
| SILFunctionTypeRepresentation::WitnessMethod) { |
| args.add(witnessMetadata.SelfMetadata); |
| } |
| |
| llvm::CallInst *call = subIGF.Builder.CreateCall(fnPtr, args.claimAll()); |
| |
| if (staticFnPtr) { |
| // Use the attributes and calling convention from the static definition if |
| // we have it. |
| call->setAttributes(staticFnPtr->getAttributes()); |
| call->setCallingConv(staticFnPtr->getCallingConv()); |
| } else { |
| // Otherwise, use the default attributes for the dynamic type. |
| // TODO: Currently all indirect function values use some variation of the |
| // "C" calling convention, but that may change. |
| call->setAttributes(origAttrs); |
| } |
| if (addressesToDeallocate.empty() && !needsAllocas && |
| (!consumesContext || !dependsOnContextLifetime)) |
| call->setTailCall(); |
| |
| // Deallocate everything we allocated above. |
| // FIXME: exceptions? |
| for (auto &entry : addressesToDeallocate) { |
| entry.TI.deallocateStack(subIGF, entry.Addr, entry.Type); |
| } |
| |
| // If the parameters depended on the context, consume the context now. |
| if (rawData && consumesContext && dependsOnContextLifetime) |
| subIGF.emitNativeStrongRelease(rawData); |
| |
| // FIXME: Reabstract the result value as substituted. |
| |
| if (call->getType()->isVoidTy()) |
| subIGF.Builder.CreateRetVoid(); |
| else { |
| llvm::Value *callResult = call; |
| // If the result type is dependent on a type parameter we might have to cast |
| // to the result type - it could be substituted. |
| if (origType->getSILResult().hasTypeParameter()) { |
| auto ResType = fwd->getReturnType(); |
| callResult = subIGF.Builder.CreateBitCast(callResult, ResType); |
| } |
| subIGF.Builder.CreateRet(callResult); |
| } |
| |
| return fwd; |
| } |
| |
| /// Emit a partial application thunk for a function pointer applied to a partial |
| /// set of argument values. |
| void irgen::emitFunctionPartialApplication(IRGenFunction &IGF, |
| llvm::Value *fnPtr, |
| llvm::Value *fnContext, |
| Explosion &args, |
| ArrayRef<SILParameterInfo> params, |
| ArrayRef<Substitution> subs, |
| CanSILFunctionType origType, |
| CanSILFunctionType substType, |
| CanSILFunctionType outType, |
| Explosion &out) { |
| // If we have a single Swift-refcounted context value, we can adopt it |
| // directly as our closure context without creating a box and thunk. |
| enum HasSingleSwiftRefcountedContext { Maybe, Yes, No, Thunkable } |
| hasSingleSwiftRefcountedContext = Maybe; |
| Optional<ParameterConvention> singleRefcountedConvention; |
| |
| SmallVector<const TypeInfo *, 4> argTypeInfos; |
| SmallVector<SILType, 4> argValTypes; |
| SmallVector<ParameterConvention, 4> argConventions; |
| |
| // Reserve space for polymorphic bindings. |
| auto bindings = NecessaryBindings::forFunctionInvocations(IGF.IGM, |
| origType, substType, subs); |
| if (!bindings.empty()) { |
| hasSingleSwiftRefcountedContext = No; |
| auto bindingsSize = bindings.getBufferSize(IGF.IGM); |
| auto &bindingsTI = IGF.IGM.getOpaqueStorageTypeInfo(bindingsSize, |
| IGF.IGM.getPointerAlignment()); |
| argValTypes.push_back(SILType()); |
| argTypeInfos.push_back(&bindingsTI); |
| argConventions.push_back(ParameterConvention::Direct_Unowned); |
| } |
| |
| // Collect the type infos for the context parameters. |
| for (auto param : params) { |
| SILType argType = param.getSILType(); |
| |
| argValTypes.push_back(argType); |
| argConventions.push_back(param.getConvention()); |
| |
| CanType argLoweringTy; |
| switch (param.getConvention()) { |
| // Capture value parameters by value, consuming them. |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Direct_Unowned: |
| case ParameterConvention::Direct_Guaranteed: |
| case ParameterConvention::Direct_Deallocating: |
| argLoweringTy = argType.getSwiftRValueType(); |
| break; |
| |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Guaranteed: |
| argLoweringTy = argType.getSwiftRValueType(); |
| break; |
| |
| // Capture inout parameters by pointer. |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| argLoweringTy = argType.getSwiftType(); |
| break; |
| |
| case ParameterConvention::Indirect_Out: |
| llvm_unreachable("can't partially apply out params"); |
| } |
| |
| auto &ti = IGF.getTypeInfoForLowered(argLoweringTy); |
| argTypeInfos.push_back(&ti); |
| |
| // Update the single-swift-refcounted check, unless we already ruled that |
| // out. |
| if (hasSingleSwiftRefcountedContext == No) |
| continue; |
| |
| // Empty values don't matter. |
| auto schema = ti.getSchema(); |
| if (schema.size() == 0) |
| continue; |
| |
| // Adding nonempty values when we already have a single refcounted pointer |
| // means we don't have a single value anymore. |
| if (hasSingleSwiftRefcountedContext != Maybe) { |
| hasSingleSwiftRefcountedContext = No; |
| continue; |
| } |
| |
| if (ti.isSingleSwiftRetainablePointer(ResilienceScope::Component)) { |
| hasSingleSwiftRefcountedContext = Yes; |
| singleRefcountedConvention = param.getConvention(); |
| } else { |
| hasSingleSwiftRefcountedContext = No; |
| } |
| } |
| |
| // We can't just bitcast if there's an error parameter to forward. |
| // This is an unfortunate restriction arising from the fact that a |
| // thin throwing function will have the signature: |
| // %result (%arg*, %context*, %error*) |
| // but the output signature needs to be |
| // %result (%context*, %error*) |
| // |
| // 'swifterror' fixes this physically, but there's still a risk of |
| // miscompiles because the LLVM optimizer may forward arguments |
| // positionally without considering 'swifterror'. |
| // |
| // Note, however, that we will override this decision below if the |
| // only thing we have to forward is already a context pointer. |
| // That's fine. |
| // |
| // The proper long-term fix is that closure functions should be |
| // emitted with a convention that takes the closure box as the |
| // context parameter. When we do that, all of this code will |
| // disappear. |
| if (hasSingleSwiftRefcountedContext == Yes && |
| origType->hasErrorResult()) { |
| hasSingleSwiftRefcountedContext = Thunkable; |
| } |
| |
| // Include the context pointer, if any, in the function arguments. |
| if (fnContext) { |
| args.add(fnContext); |
| argValTypes.push_back(SILType::getNativeObjectType(IGF.IGM.Context)); |
| argConventions.push_back(origType->getCalleeConvention()); |
| argTypeInfos.push_back( |
| &IGF.getTypeInfoForLowered(IGF.IGM.Context.TheNativeObjectType)); |
| // If this is the only context argument we end up with, we can just share |
| // it. |
| if (args.size() == 1) { |
| assert(bindings.empty()); |
| hasSingleSwiftRefcountedContext = Yes; |
| singleRefcountedConvention = origType->getCalleeConvention(); |
| } |
| } |
| |
| // If we have a single refcounted pointer context (and no polymorphic args |
| // to capture), and the dest ownership semantics match the parameter's, |
| // skip building the box and thunk and just take the pointer as |
| // context. |
| if (!origType->isPolymorphic() && |
| hasSingleSwiftRefcountedContext == Yes |
| && outType->getCalleeConvention() == *singleRefcountedConvention) { |
| assert(args.size() == 1); |
| fnPtr = IGF.Builder.CreateBitCast(fnPtr, IGF.IGM.Int8PtrTy); |
| out.add(fnPtr); |
| llvm::Value *ctx = args.claimNext(); |
| ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy); |
| out.add(ctx); |
| return; |
| } |
| |
| // If the function pointer is dynamic, include it in the context. |
| auto staticFn = dyn_cast<llvm::Function>(fnPtr); |
| if (!staticFn) { |
| llvm::Value *fnRawPtr = IGF.Builder.CreateBitCast(fnPtr, IGF.IGM.Int8PtrTy); |
| args.add(fnRawPtr); |
| argValTypes.push_back(SILType::getRawPointerType(IGF.IGM.Context)); |
| argTypeInfos.push_back( |
| &IGF.getTypeInfoForLowered(IGF.IGM.Context.TheRawPointerType)); |
| argConventions.push_back(ParameterConvention::Direct_Unowned); |
| hasSingleSwiftRefcountedContext = No; |
| } |
| |
| // If we only need to capture a single Swift-refcounted object, we |
| // still need to build a thunk, but we don't need to allocate anything. |
| if ((hasSingleSwiftRefcountedContext == Yes || |
| hasSingleSwiftRefcountedContext == Thunkable) && |
| *singleRefcountedConvention != ParameterConvention::Indirect_Inout && |
| *singleRefcountedConvention != |
| ParameterConvention::Indirect_InoutAliasable) { |
| assert(bindings.empty()); |
| assert(args.size() == 1); |
| |
| llvm::AttributeSet attrs; |
| auto fnPtrTy = IGF.IGM.getFunctionType(origType, attrs) |
| ->getPointerTo(); |
| |
| llvm::Function *forwarder = |
| emitPartialApplicationForwarder(IGF.IGM, staticFn, fnContext != nullptr, |
| fnPtrTy, attrs, origType, substType, |
| outType, subs, nullptr, argConventions); |
| llvm::Value *forwarderValue = |
| IGF.Builder.CreateBitCast(forwarder, IGF.IGM.Int8PtrTy); |
| out.add(forwarderValue); |
| |
| llvm::Value *ctx = args.claimNext(); |
| if (isIndirectParameter(*singleRefcountedConvention)) |
| ctx = IGF.Builder.CreateLoad(ctx, IGF.IGM.getPointerAlignment()); |
| ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy); |
| out.add(ctx); |
| return; |
| } |
| |
| // Store the context arguments on the heap. |
| assert(argValTypes.size() == argTypeInfos.size() |
| && argTypeInfos.size() == argConventions.size() |
| && "argument info lists out of sync"); |
| HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes, argTypeInfos, |
| /*typeToFill*/ nullptr, |
| std::move(bindings)); |
| llvm::Value *data; |
| if (layout.isKnownEmpty()) { |
| data = IGF.IGM.RefCountedNull; |
| } else { |
| // Allocate a new object. |
| HeapNonFixedOffsets offsets(IGF, layout); |
| |
| data = IGF.emitUnmanagedAlloc(layout, "closure", &offsets); |
| Address dataAddr = layout.emitCastTo(IGF, data); |
| |
| |
| unsigned i = 0; |
| |
| // Store necessary bindings, if we have them. |
| if (layout.hasBindings()) { |
| auto &bindingsLayout = layout.getElement(i); |
| Address bindingsAddr = bindingsLayout.project(IGF, dataAddr, offsets); |
| layout.getBindings().save(IGF, bindingsAddr); |
| ++i; |
| } |
| |
| // Store the context arguments. |
| for (unsigned end = layout.getElements().size(); i < end; ++i) { |
| auto &fieldLayout = layout.getElement(i); |
| auto &fieldTy = layout.getElementTypes()[i]; |
| Address fieldAddr = fieldLayout.project(IGF, dataAddr, offsets); |
| switch (argConventions[i]) { |
| // Take indirect value arguments out of memory. |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Guaranteed: { |
| auto addr = fieldLayout.getType().getAddressForPointer(args.claimNext()); |
| fieldLayout.getType().initializeWithTake(IGF, fieldAddr, addr, fieldTy); |
| break; |
| } |
| // Take direct value arguments and inout pointers by value. |
| case ParameterConvention::Direct_Unowned: |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Direct_Guaranteed: |
| case ParameterConvention::Direct_Deallocating: |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| cast<LoadableTypeInfo>(fieldLayout.getType()) |
| .initialize(IGF, args, fieldAddr); |
| break; |
| case ParameterConvention::Indirect_Out: |
| llvm_unreachable("can't capture out params"); |
| } |
| } |
| } |
| assert(args.empty() && "unused args in partial application?!"); |
| |
| // Create the forwarding stub. |
| llvm::AttributeSet attrs; |
| auto fnPtrTy = IGF.IGM.getFunctionType(origType, attrs) |
| ->getPointerTo(); |
| |
| llvm::Function *forwarder = emitPartialApplicationForwarder(IGF.IGM, |
| staticFn, |
| fnContext != nullptr, |
| fnPtrTy, |
| attrs, |
| origType, |
| substType, |
| outType, |
| subs, |
| &layout, |
| argConventions); |
| llvm::Value *forwarderValue = IGF.Builder.CreateBitCast(forwarder, |
| IGF.IGM.Int8PtrTy); |
| out.add(forwarderValue); |
| out.add(data); |
| } |
| |
| /// Emit the block copy helper for a block. |
| static llvm::Function *emitBlockCopyHelper(IRGenModule &IGM, |
| CanSILBlockStorageType blockTy, |
| const BlockStorageTypeInfo &blockTL){ |
| // See if we've produced a block copy helper for this type before. |
| // TODO |
| |
| // Create the helper. |
| llvm::Type *args[] = { |
| blockTL.getStorageType()->getPointerTo(), |
| blockTL.getStorageType()->getPointerTo(), |
| }; |
| auto copyTy = llvm::FunctionType::get(IGM.VoidTy, args, /*vararg*/ false); |
| // TODO: Give these predictable mangled names and shared linkage. |
| auto func = llvm::Function::Create(copyTy, llvm::GlobalValue::InternalLinkage, |
| "block_copy_helper", |
| IGM.getModule()); |
| func->setAttributes(IGM.constructInitialAttributes()); |
| IRGenFunction IGF(IGM, func); |
| |
| // Copy the captures from the source to the destination. |
| Explosion params = IGF.collectParameters(); |
| auto dest = Address(params.claimNext(), blockTL.getFixedAlignment()); |
| auto src = Address(params.claimNext(), blockTL.getFixedAlignment()); |
| |
| auto destCapture = blockTL.projectCapture(IGF, dest); |
| auto srcCapture = blockTL.projectCapture(IGF, src); |
| auto &captureTL = IGM.getTypeInfoForLowered(blockTy->getCaptureType()); |
| captureTL.initializeWithCopy(IGF, destCapture, srcCapture, |
| blockTy->getCaptureAddressType()); |
| |
| IGF.Builder.CreateRetVoid(); |
| |
| return func; |
| } |
| |
| /// Emit the block copy helper for a block. |
| static llvm::Function *emitBlockDisposeHelper(IRGenModule &IGM, |
| CanSILBlockStorageType blockTy, |
| const BlockStorageTypeInfo &blockTL){ |
| // See if we've produced a block destroy helper for this type before. |
| // TODO |
| |
| // Create the helper. |
| auto destroyTy = llvm::FunctionType::get(IGM.VoidTy, |
| blockTL.getStorageType()->getPointerTo(), |
| /*vararg*/ false); |
| // TODO: Give these predictable mangled names and shared linkage. |
| auto func = llvm::Function::Create(destroyTy, |
| llvm::GlobalValue::InternalLinkage, |
| "block_destroy_helper", |
| IGM.getModule()); |
| func->setAttributes(IGM.constructInitialAttributes()); |
| IRGenFunction IGF(IGM, func); |
| |
| // Destroy the captures. |
| Explosion params = IGF.collectParameters(); |
| auto storage = Address(params.claimNext(), blockTL.getFixedAlignment()); |
| auto capture = blockTL.projectCapture(IGF, storage); |
| auto &captureTL = IGM.getTypeInfoForLowered(blockTy->getCaptureType()); |
| captureTL.destroy(IGF, capture, blockTy->getCaptureAddressType()); |
| IGF.Builder.CreateRetVoid(); |
| |
| return func; |
| } |
| |
| /// Emit the block header into a block storage slot. |
| void irgen::emitBlockHeader(IRGenFunction &IGF, |
| Address storage, |
| CanSILBlockStorageType blockTy, |
| llvm::Function *invokeFunction, |
| CanSILFunctionType invokeTy) { |
| auto &storageTL |
| = IGF.getTypeInfoForLowered(blockTy).as<BlockStorageTypeInfo>(); |
| |
| Address headerAddr = storageTL.projectBlockHeader(IGF, storage); |
| |
| // |
| // Initialize the "isa" pointer, which is _NSConcreteStackBlock. |
| auto NSConcreteStackBlock |
| = IGF.IGM.getModule()->getOrInsertGlobal("_NSConcreteStackBlock", |
| IGF.IGM.ObjCClassStructTy); |
| // |
| // Set the flags. |
| // - HAS_COPY_DISPOSE unless the capture type is POD |
| uint32_t flags = 0; |
| auto &captureTL |
| = IGF.getTypeInfoForLowered(blockTy->getCaptureType()); |
| bool isPOD = captureTL.isPOD(ResilienceScope::Component); |
| if (!isPOD) |
| flags |= 1 << 25; |
| |
| // - HAS_STRET, if the invoke function is sret |
| if (requiresExternalIndirectResult(IGF.IGM, invokeTy)) |
| flags |= 1 << 29; |
| |
| // - HAS_SIGNATURE |
| flags |= 1 << 30; |
| |
| auto flagsVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, flags); |
| |
| // |
| // Collect the reserved and invoke pointer fields. |
| auto reserved = llvm::ConstantInt::get(IGF.IGM.Int32Ty, 0); |
| auto invokeVal = llvm::ConstantExpr::getBitCast(invokeFunction, |
| IGF.IGM.FunctionPtrTy); |
| |
| // |
| // Build the block descriptor. |
| SmallVector<llvm::Constant*, 5> descriptorFields; |
| descriptorFields.push_back(llvm::ConstantInt::get(IGF.IGM.IntPtrTy, 0)); |
| descriptorFields.push_back(llvm::ConstantInt::get(IGF.IGM.IntPtrTy, |
| storageTL.getFixedSize().getValue())); |
| |
| if (!isPOD) { |
| // Define the copy and dispose helpers. |
| descriptorFields.push_back(emitBlockCopyHelper(IGF.IGM, blockTy, storageTL)); |
| descriptorFields.push_back(emitBlockDisposeHelper(IGF.IGM, blockTy, storageTL)); |
| } |
| |
| // |
| // Build the descriptor signature. |
| // TODO |
| descriptorFields.push_back(getBlockTypeExtendedEncoding(IGF.IGM, invokeTy)); |
| |
| // |
| // Create the descriptor. |
| auto descriptorInit = llvm::ConstantStruct::getAnon(descriptorFields); |
| auto descriptor = new llvm::GlobalVariable(*IGF.IGM.getModule(), |
| descriptorInit->getType(), |
| /*constant*/ true, |
| llvm::GlobalValue::InternalLinkage, |
| descriptorInit, |
| "block_descriptor"); |
| auto descriptorVal = llvm::ConstantExpr::getBitCast(descriptor, |
| IGF.IGM.Int8PtrTy); |
| |
| // |
| // Store the block header literal. |
| llvm::Constant *blockFields[] = { |
| NSConcreteStackBlock, |
| flagsVal, |
| reserved, |
| invokeVal, |
| descriptorVal, |
| }; |
| auto blockHeader = llvm::ConstantStruct::get(IGF.IGM.ObjCBlockStructTy, |
| blockFields); |
| IGF.Builder.CreateStore(blockHeader, headerAddr); |
| } |