//===--- GenOpaque.cpp - Swift IR-generation for opaque values ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements IR generation for opaque values and value // witness operations. // // In the comments throughout this file, three type names are used: // 'B' is the type of a fixed-size buffer // 'T' is the type which implements a protocol // 'W' is the type of a witness to the protocol // //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/DerivedTypes.h" #include "swift/AST/IRGenOptions.h" #include "swift/IRGen/ValueWitness.h" #include "Callee.h" #include "FixedTypeInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "ProtocolInfo.h" #include "GenOpaque.h" using namespace swift; using namespace irgen; /// A fixed-size buffer is always 16 bytes and pointer-aligned. /// If we align them more, we'll need to introduce padding to /// make protocol types work. Size irgen::getFixedBufferSize(IRGenModule &IGM) { return 3 * IGM.getPointerSize(); } Alignment irgen::getFixedBufferAlignment(IRGenModule &IGM) { return IGM.getPointerAlignment(); } /// Lazily create the standard fixed-buffer type. llvm::Type *IRGenModule::getFixedBufferTy() { if (FixedBufferTy) return FixedBufferTy; auto size = getFixedBufferSize(*this).getValue(); FixedBufferTy = llvm::ArrayType::get(Int8Ty, size); return FixedBufferTy; } static llvm::Type *createWitnessType(IRGenModule &IGM, ValueWitness index) { switch (index) { // void (*destroy)(T *object, witness_t *self); case ValueWitness::Destroy: { llvm::Type *args[] = { IGM.OpaquePtrTy, IGM.TypeMetadataPtrTy }; return llvm::FunctionType::get(IGM.VoidTy, args, /*isVarArg*/ false); } // T *(*initializeBufferWithCopyOfBuffer)(B *dest, B *src, M *self); // T *(*initializeBufferWithTakeOfBuffer)(B *dest, B *src, M *self); case ValueWitness::InitializeBufferWithCopyOfBuffer: case ValueWitness::InitializeBufferWithTakeOfBuffer: { llvm::Type *bufPtrTy = IGM.getFixedBufferTy()->getPointerTo(0); llvm::Type *args[] = { bufPtrTy, bufPtrTy, IGM.TypeMetadataPtrTy }; return llvm::FunctionType::get(IGM.OpaquePtrTy, args, /*isVarArg*/ false); } // T *(*assignWithCopy)(T *dest, T *src, M *self); // T *(*assignWithTake)(T *dest, T *src, M *self); // T *(*initializeWithCopy)(T *dest, T *src, M *self); // T *(*initializeWithTake)(T *dest, T *src, M *self); case ValueWitness::AssignWithCopy: case ValueWitness::AssignWithTake: case ValueWitness::InitializeWithCopy: case ValueWitness::InitializeWithTake: { llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *args[] = { ptrTy, ptrTy, IGM.TypeMetadataPtrTy }; return llvm::FunctionType::get(ptrTy, args, /*isVarArg*/ false); } /// void (*storeExtraInhabitant)(T *obj, unsigned index, M *self); case ValueWitness::StoreExtraInhabitant: { llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *voidTy = IGM.VoidTy; llvm::Type *args[] = {ptrTy, indexTy, metaTy}; return llvm::FunctionType::get(voidTy, args, /*isVarArg*/ false); } /// int (*getExtraInhabitantIndex)(T *obj, M *self); case ValueWitness::GetExtraInhabitantIndex: { llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *args[] = {ptrTy, metaTy}; return llvm::FunctionType::get(indexTy, args, /*isVarArg*/ false); } /// int (*getEnumTag)(T *obj, M *self); case ValueWitness::GetEnumTag: { llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *args[] = {ptrTy, metaTy}; return llvm::FunctionType::get(indexTy, args, /*isVarArg*/ false); } /// void (*destructiveProjectEnumData)(T *obj, M *self); case ValueWitness::DestructiveProjectEnumData: { llvm::Type *voidTy = IGM.VoidTy; llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *args[] = {ptrTy, metaTy}; return llvm::FunctionType::get(voidTy, args, /*isVarArg*/ false); } /// void (*destructiveInjectEnumTag)(T *obj, int tag, M *self); case ValueWitness::DestructiveInjectEnumTag: { llvm::Type *voidTy = IGM.VoidTy; llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *args[] = {ptrTy, indexTy, metaTy}; return llvm::FunctionType::get(voidTy, args, /*isVarArg*/ false); } /// int (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases, /// M *self) case ValueWitness::GetEnumTagSinglePayload: { llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *args[] = { ptrTy, indexTy, metaTy }; return llvm::FunctionType::get(indexTy, args, false); } /// void (*storeEnumTagSinglePayload)(T* enum, INT_TYPE whichCase, /// UINT_TYPE emptyCases, /// M *self) case ValueWitness::StoreEnumTagSinglePayload: { llvm::Type *voidTy = IGM.VoidTy; llvm::Type *ptrTy = IGM.OpaquePtrTy; llvm::Type *indexTy = IGM.Int32Ty; llvm::Type *metaTy = IGM.TypeMetadataPtrTy; llvm::Type *args[] = { ptrTy, indexTy, indexTy, metaTy }; return llvm::FunctionType::get(voidTy, args, false); } case ValueWitness::Size: case ValueWitness::Flags: case ValueWitness::Stride: case ValueWitness::ExtraInhabitantFlags: // Non-function witnesses all have type size_t. return IGM.SizeTy; } llvm_unreachable("bad value witness!"); } static llvm::AttributeList getValueWitnessAttrs(IRGenModule &IGM, ValueWitness index) { assert(isValueWitnessFunction(index)); auto &ctx = IGM.getLLVMContext(); // All value witnesses are nounwind. auto attrs = llvm::AttributeList::get(ctx, llvm::AttributeList::FunctionIndex, llvm::Attribute::NoUnwind); switch (index) { // These have two arguments, but they can alias. case ValueWitness::AssignWithCopy: return attrs; // These have one argument. case ValueWitness::Destroy: case ValueWitness::DestructiveInjectEnumTag: case ValueWitness::DestructiveProjectEnumData: case ValueWitness::GetEnumTag: case ValueWitness::GetExtraInhabitantIndex: case ValueWitness::StoreExtraInhabitant: case ValueWitness::StoreEnumTagSinglePayload: return attrs.addAttribute(ctx, 1, llvm::Attribute::NoAlias); case ValueWitness::GetEnumTagSinglePayload: return attrs .addAttribute(ctx, llvm::AttributeList::FunctionIndex, llvm::Attribute::ReadOnly) .addAttribute(ctx, 1, llvm::Attribute::NoAlias); // These have two arguments and they don't alias each other. case ValueWitness::AssignWithTake: case ValueWitness::InitializeBufferWithCopyOfBuffer: case ValueWitness::InitializeBufferWithTakeOfBuffer: case ValueWitness::InitializeWithCopy: case ValueWitness::InitializeWithTake: return attrs.addAttribute(ctx, 1, llvm::Attribute::NoAlias) .addAttribute(ctx, 2, llvm::Attribute::NoAlias); case ValueWitness::Size: case ValueWitness::Flags: case ValueWitness::Stride: case ValueWitness::ExtraInhabitantFlags: llvm_unreachable("not a function value witness"); } llvm_unreachable("bad witness"); } /// Return the cached pointer-to-function type for the given value /// witness index. llvm::Type *IRGenModule::getValueWitnessTy(ValueWitness index) { assert(unsigned(index) < MaxNumValueWitnesses); auto &ty = ValueWitnessTys[unsigned(index)]; if (ty) return ty; ty = createWitnessType(*this, index); return ty; } Signature IRGenModule::getValueWitnessSignature(ValueWitness index) { assert(isValueWitnessFunction(index)); auto fnTy = cast(getValueWitnessTy(index)); auto attrs = getValueWitnessAttrs(*this, index); return Signature(fnTy, attrs, DefaultCC); } static StringRef getValueWitnessLabel(ValueWitness index) { switch (index) { case ValueWitness::Destroy: return "destroy"; case ValueWitness::InitializeBufferWithCopyOfBuffer: return "initializeBufferWithCopyOfBuffer"; case ValueWitness::AssignWithCopy: return "assignWithCopy"; case ValueWitness::AssignWithTake: return "assignWithTake"; case ValueWitness::InitializeWithCopy: return "initializeWithCopy"; case ValueWitness::InitializeWithTake: return "initializeWithTake"; case ValueWitness::InitializeBufferWithTakeOfBuffer: return "initializeBufferWithTakeOfBuffer"; case ValueWitness::Size: return "size"; case ValueWitness::Flags: return "flags"; case ValueWitness::Stride: return "stride"; case ValueWitness::StoreExtraInhabitant: return "storeExtraInhabitant"; case ValueWitness::GetExtraInhabitantIndex: return "getExtraInhabitantIndex"; case ValueWitness::ExtraInhabitantFlags: return "extraInhabitantFlags"; case ValueWitness::GetEnumTag: return "getEnumTag"; case ValueWitness::DestructiveProjectEnumData: return "destructiveProjectEnumData"; case ValueWitness::DestructiveInjectEnumTag: return "destructiveInjectEnumTag"; case ValueWitness::GetEnumTagSinglePayload: return "getEnumTagSinglePayload"; case ValueWitness::StoreEnumTagSinglePayload: return "storeEnumTagSinglePayload"; } llvm_unreachable("bad value witness index"); } /// Load a specific witness from a known table. The result is /// always an i8*. llvm::Value *irgen::emitInvariantLoadOfOpaqueWitness(IRGenFunction &IGF, llvm::Value *table, WitnessIndex index) { assert(table->getType() == IGF.IGM.WitnessTablePtrTy); // GEP to the appropriate index, avoiding spurious IR in the trivial case. llvm::Value *slot = table; if (index.getValue() != 0) slot = IGF.Builder.CreateConstInBoundsGEP1_32( /*Ty=*/nullptr, table, index.getValue()); auto witness = IGF.Builder.CreateLoad(Address(slot, IGF.IGM.getPointerAlignment())); IGF.setInvariantLoad(witness); return witness; } /// Given a value witness table, load one of the value witnesses. /// The result has the appropriate type for the witness. static llvm::Value *emitLoadOfValueWitnessValue(IRGenFunction &IGF, llvm::Value *table, ValueWitness index) { assert(!isValueWitnessFunction(index)); llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, table, index); auto label = getValueWitnessLabel(index); auto type = IGF.IGM.getValueWitnessTy(index); return IGF.Builder.CreatePtrToInt(witness, type, label); } /// Given a type metadata pointer, load one of the value witnesses from its /// value witness table. static llvm::Value * emitLoadOfValueWitnessValueFromMetadata(IRGenFunction &IGF, llvm::Value *metadata, ValueWitness index) { llvm::Value *vwtable = IGF.emitValueWitnessTableRefForMetadata(metadata); return emitLoadOfValueWitnessValue(IGF, vwtable, index); } /// Given a value witness table, load one of the value witnesses. /// The result has the appropriate type for the witness. static FunctionPointer emitLoadOfValueWitnessFunction(IRGenFunction &IGF, llvm::Value *table, ValueWitness index) { assert(isValueWitnessFunction(index)); llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, table, index); auto label = getValueWitnessLabel(index); auto signature = IGF.IGM.getValueWitnessSignature(index); auto type = signature.getType()->getPointerTo(); witness = IGF.Builder.CreateBitCast(witness, type, label); return FunctionPointer(witness, signature); } /// Given a type metadata pointer, load one of the function /// value witnesses from its value witness table. static FunctionPointer emitLoadOfValueWitnessFunctionFromMetadata(IRGenFunction &IGF, llvm::Value *metadata, ValueWitness index) { llvm::Value *vwtable = IGF.emitValueWitnessTableRefForMetadata(metadata); return emitLoadOfValueWitnessFunction(IGF, vwtable, index); } llvm::Value * IRGenFunction::emitValueWitnessValue(SILType type, ValueWitness index) { assert(!isValueWitnessFunction(index)); if (auto witness = tryGetLocalTypeDataForLayout(type, LocalTypeDataKind::forValueWitness(index))) { return witness; } auto vwtable = emitValueWitnessTableRef(type); auto witness = emitLoadOfValueWitnessValue(*this, vwtable, index); setScopedLocalTypeDataForLayout(type, LocalTypeDataKind::forValueWitness(index), witness); return witness; } FunctionPointer IRGenFunction::emitValueWitnessFunctionRef(SILType type, llvm::Value *&metadataSlot, ValueWitness index) { assert(isValueWitnessFunction(index)); if (auto witness = tryGetLocalTypeDataForLayout(type, LocalTypeDataKind::forValueWitness(index))) { metadataSlot = emitTypeMetadataRefForLayout(type); auto signature = IGM.getValueWitnessSignature(index); return FunctionPointer(witness, signature); } auto vwtable = emitValueWitnessTableRef(type, &metadataSlot); auto witness = emitLoadOfValueWitnessFunction(*this, vwtable, index); setScopedLocalTypeDataForLayout(type, LocalTypeDataKind::forValueWitness(index), witness.getPointer()); return witness; } llvm::Value *irgen::emitInitializeBufferWithCopyOfBufferCall(IRGenFunction &IGF, SILType T, Address destBuffer, Address srcBuffer) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); return emitInitializeBufferWithCopyOfBufferCall(IGF, metadata, destBuffer, srcBuffer); } /// Emit a call to do an 'initializeBufferWithCopyOfBuffer' operation. llvm::Value * irgen::emitInitializeBufferWithCopyOfBufferCall(IRGenFunction &IGF, llvm::Value *metadata, Address destBuffer, Address srcBuffer) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::InitializeBufferWithCopyOfBuffer); llvm::CallInst *call = IGF.Builder.CreateCall(copyFn, {destBuffer.getAddress(), srcBuffer.getAddress(), metadata}); return call; } llvm::Value * irgen::emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, SILType T, Address destBuffer, Address srcBuffer) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); return emitInitializeBufferWithTakeOfBufferCall(IGF, metadata, destBuffer, srcBuffer); } /// Emit a call to do an 'initializeBufferWithTakeOfBuffer' operation. llvm::Value * irgen::emitInitializeBufferWithTakeOfBufferCall(IRGenFunction &IGF, llvm::Value *metadata, Address destBuffer, Address srcBuffer) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::InitializeBufferWithTakeOfBuffer); llvm::CallInst *call = IGF.Builder.CreateCall(copyFn, {destBuffer.getAddress(), srcBuffer.getAddress(), metadata}); return call; } /// Emit a dynamic alloca call to allocate enough memory to hold an object of /// type 'T' and an optional llvm.stackrestore point if 'isInEntryBlock' is /// false. StackAddress IRGenFunction::emitDynamicAlloca(SILType T, const llvm::Twine &name) { llvm::Value *size = emitLoadOfSize(*this, T); return emitDynamicAlloca(IGM.Int8Ty, size, Alignment(16), name); } StackAddress IRGenFunction::emitDynamicAlloca(llvm::Type *eltTy, llvm::Value *arraySize, Alignment align, const llvm::Twine &name) { llvm::Value *stackRestorePoint = nullptr; // Save the stack pointer if we are not in the entry block (we could be // executed more than once). bool isInEntryBlock = (Builder.GetInsertBlock() == &*CurFn->begin()); if (!isInEntryBlock) { auto *stackSaveFn = llvm::Intrinsic::getDeclaration( &IGM.Module, llvm::Intrinsic::ID::stacksave); stackRestorePoint = Builder.CreateCall(stackSaveFn, {}, "spsave"); } // Emit the dynamic alloca. auto *alloca = Builder.IRBuilderBase::CreateAlloca(eltTy, arraySize, name); alloca->setAlignment(align.getValue()); assert(!isInEntryBlock || getActiveDominancePoint().isUniversal() && "Must be in entry block if we insert dynamic alloca's without " "stackrestores"); return {Address(alloca, align), stackRestorePoint}; } /// Deallocate dynamic alloca's memory if requested by restoring the stack /// location before the dynamic alloca's call. void IRGenFunction::emitDeallocateDynamicAlloca(StackAddress address) { if (!address.needsSPRestore()) return; auto *stackRestoreFn = llvm::Intrinsic::getDeclaration( &IGM.Module, llvm::Intrinsic::ID::stackrestore); Builder.CreateCall(stackRestoreFn, address.getSavedSP()); } /// Emit a call to do an 'initializeArrayWithCopy' operation. void irgen::emitInitializeArrayWithCopyCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithCopyFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'initializeArrayWithTakeNoAlias' operation. void irgen::emitInitializeArrayWithTakeNoAliasCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeNoAliasFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'initializeArrayWithTakeFrontToBack' operation. void irgen::emitInitializeArrayWithTakeFrontToBackCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeFrontToBackFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'initializeArrayWithTakeBackToFront' operation. void irgen::emitInitializeArrayWithTakeBackToFrontCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayInitWithTakeBackToFrontFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'assignWithCopy' operation. void irgen::emitAssignWithCopyCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject) { llvm::Value *metadata; auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::AssignWithCopy); IGF.Builder.CreateCall(copyFn, {destObject.getAddress(), srcObject.getAddress(), metadata}); } /// Emit a call to do an 'assignWithCopy' operation. void irgen::emitAssignWithCopyCall(IRGenFunction &IGF, llvm::Value *metadata, Address destObject, Address srcObject) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::AssignWithCopy); IGF.Builder.CreateCall(copyFn, {destObject.getAddress(), srcObject.getAddress(), metadata}); } /// Emit a call to do an 'arrayAssignWithCopyNoAlias' operation. void irgen::emitAssignArrayWithCopyNoAliasCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyNoAliasFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'arrayAssignWithCopyFrontToBack' operation. void irgen::emitAssignArrayWithCopyFrontToBackCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyFrontToBackFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'arrayAssignWithCopyBackToFront' operation. void irgen::emitAssignArrayWithCopyBackToFrontCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithCopyBackToFrontFn(), {dest, src, count, metadata}); } /// Emit a call to do an 'assignWithTake' operation. void irgen::emitAssignWithTakeCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject) { llvm::Value *metadata; auto copyFn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::AssignWithTake); IGF.Builder.CreateCall(copyFn, {destObject.getAddress(), srcObject.getAddress(), metadata}); } /// Emit a call to do an 'arrayAssignWithTake' operation. void irgen::emitAssignArrayWithTakeCall(IRGenFunction &IGF, SILType T, Address destObject, Address srcObject, llvm::Value *count) { auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto dest = IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy); auto src = IGF.Builder.CreateBitCast(srcObject.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayAssignWithTakeFn(), {dest, src, count, metadata}); } /// Emit a call to do a 'destroyArray' operation. void irgen::emitDestroyArrayCall(IRGenFunction &IGF, SILType T, Address object, llvm::Value *count) { // If T is a trivial/POD type, nothing needs to be done. if (T.getObjectType().isTrivial(IGF.getSILModule())) return; auto metadata = IGF.emitTypeMetadataRefForLayout(T); auto obj = IGF.Builder.CreateBitCast(object.getAddress(), IGF.IGM.OpaquePtrTy); IGF.Builder.CreateCall(IGF.IGM.getArrayDestroyFn(), {obj, count, metadata}); } /// Emit a call to the 'getExtraInhabitantIndex' operation. /// The type must be dynamically known to have extra inhabitant witnesses. llvm::Value *irgen::emitGetExtraInhabitantIndexCall(IRGenFunction &IGF, SILType T, Address srcObject) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::GetExtraInhabitantIndex); llvm::CallInst *call = IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); return call; } /// Emit a call to the 'storeExtraInhabitant' operation. /// The type must be dynamically known to have extra inhabitant witnesses. llvm::Value *irgen::emitStoreExtraInhabitantCall(IRGenFunction &IGF, SILType T, llvm::Value *index, Address destObject) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::StoreExtraInhabitant); llvm::CallInst *call = IGF.Builder.CreateCall(fn, {destObject.getAddress(), index, metadata}); return call; } /// Emit a trampoline to call the getEnumTagSinglePayload witness. API: /// INT_TYPE (const T* enum, UINT_TYPE emptyCases, M *self) static llvm::Constant * getGetEnumTagSinglePayloadTrampolineFn(IRGenModule &IGM) { llvm::Type *argTys[] = {IGM.OpaquePtrTy, IGM.Int32Ty, IGM.TypeMetadataPtrTy}; llvm::SmallString<40> fnName("__swift_getEnumTagSinglePayload"); auto func = IGM.getOrCreateHelperFunction( fnName, IGM.Int32Ty, argTys, [&](IRGenFunction &IGF) { auto it = IGF.CurFn->arg_begin(); auto *enumAddr = &*(it++); auto *numEmptyCases = &*(it++); auto *metadata = &*(it++); auto &Builder = IGF.Builder; auto witnessFunc = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::GetEnumTagSinglePayload); auto *result = Builder.CreateCall(witnessFunc, {enumAddr, numEmptyCases, metadata}); Builder.CreateRet(result); }, true /*noinline*/); // This function is readonly. cast(func)->addFnAttr(llvm::Attribute::ReadOnly); return func; } /// Emit a trampoline to call the storeEnumTagSinglePayload witness. API: /// VOID_TYPE (const T* enum, INT_TYPE whichCase, UINT_TYPE emptyCases, /// M *self) static llvm::Constant * getStoreEnumTagSinglePayloadTrampolineFn(IRGenModule &IGM) { llvm::Type *argTys[] = {IGM.OpaquePtrTy, IGM.Int32Ty, IGM.Int32Ty, IGM.TypeMetadataPtrTy}; llvm::SmallString<40> fnName("__swift_storeEnumTagSinglePayload"); return IGM.getOrCreateHelperFunction( fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) { auto it = IGF.CurFn->arg_begin(); auto *enumAddr = &*(it++); auto *whichCase = &*(it++); auto *numEmptyCases = &*(it++); auto *metadata = &*(it++); auto &Builder = IGF.Builder; auto witnessFunc = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::StoreEnumTagSinglePayload); Builder.CreateCall(witnessFunc, {enumAddr, whichCase, numEmptyCases, metadata}); Builder.CreateRetVoid(); }, true /*noinline*/); } llvm::Value *irgen::emitGetEnumTagSinglePayloadCall(IRGenFunction &IGF, SILType T, llvm::Value *numEmptyCases, Address destObject) { if (!IGF.optimizeForSize()) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef( T, metadata, ValueWitness::GetEnumTagSinglePayload); llvm::CallInst *call = IGF.Builder.CreateCall( fn, {destObject.getAddress(), numEmptyCases, metadata}); return call; } auto *metadata = IGF.emitTypeMetadataRefForLayout(T); auto *func = getGetEnumTagSinglePayloadTrampolineFn(IGF.IGM); auto *result = IGF.Builder.CreateCall( func, {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy), numEmptyCases, metadata}); return result; } llvm::Value *irgen::emitStoreEnumTagSinglePayloadCall( IRGenFunction &IGF, SILType T, llvm::Value *whichCase, llvm::Value *numEmptyCases, Address destObject) { if (!IGF.optimizeForSize()) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef( T, metadata, ValueWitness::StoreEnumTagSinglePayload); llvm::CallInst *call = IGF.Builder.CreateCall( fn, {destObject.getAddress(), whichCase, numEmptyCases, metadata}); return call; } auto *metadata = IGF.emitTypeMetadataRefForLayout(T); auto *func = getStoreEnumTagSinglePayloadTrampolineFn(IGF.IGM); auto *result = IGF.Builder.CreateCall( func, {IGF.Builder.CreateBitCast(destObject.getAddress(), IGF.IGM.OpaquePtrTy), whichCase, numEmptyCases, metadata}); return result; } /// Emit a call to the 'getEnumTag' operation. llvm::Value *irgen::emitGetEnumTagCall(IRGenFunction &IGF, SILType T, Address srcObject) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::GetEnumTag); llvm::CallInst *call = IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); return call; } /// Emit a call to the 'destructiveProjectEnumData' operation. /// The type must be dynamically known to have enum witnesses. void irgen::emitDestructiveProjectEnumDataCall(IRGenFunction &IGF, SILType T, Address srcObject) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::DestructiveProjectEnumData); IGF.Builder.CreateCall(fn, {srcObject.getAddress(), metadata}); } /// Emit a call to the 'destructiveInjectEnumTag' operation. /// The type must be dynamically known to have enum witnesses. void irgen::emitDestructiveInjectEnumTagCall(IRGenFunction &IGF, SILType T, unsigned tag, Address srcObject) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::DestructiveInjectEnumTag); llvm::Value *tagValue = llvm::ConstantInt::get(IGF.IGM.Int32Ty, tag); IGF.Builder.CreateCall(fn, {srcObject.getAddress(), tagValue, metadata}); } /// Load the 'size' value witness from the given table as a size_t. llvm::Value *irgen::emitLoadOfSize(IRGenFunction &IGF, SILType T) { return IGF.emitValueWitnessValue(T, ValueWitness::Size); } /// Load the 'alignmentMask' value witness from the given table as a size_t. llvm::Value *irgen::emitLoadOfAlignmentMask(IRGenFunction &IGF, SILType T) { auto flags = IGF.emitValueWitnessValue(T, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::AlignmentMask)); return IGF.Builder.CreateAnd(flags, mask, flags->getName() + ".alignmentMask"); } /// Load the 'isPOD' valueWitness from the given table as an i1. llvm::Value *irgen::emitLoadOfIsPOD(IRGenFunction &IGF, SILType T) { auto flags = IGF.emitValueWitnessValue(T, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::IsNonPOD)); auto masked = IGF.Builder.CreateAnd(flags, mask); return IGF.Builder.CreateICmpEQ(masked, IGF.IGM.getSize(Size(0)), flags->getName() + ".isPOD"); } /// Load the 'isBitwiseTakable' valueWitness from the given table as an i1. llvm::Value *irgen::emitLoadOfIsBitwiseTakable(IRGenFunction &IGF, SILType T) { auto flags = IGF.emitValueWitnessValue(T, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::IsNonBitwiseTakable)); auto masked = IGF.Builder.CreateAnd(flags, mask); return IGF.Builder.CreateICmpEQ(masked, IGF.IGM.getSize(Size(0)), flags->getName() + ".isBitwiseTakable"); } /// Load the 'isInline' valueWitness from the given table as an i1. llvm::Value *irgen::emitLoadOfIsInline(IRGenFunction &IGF, SILType T) { auto flags = IGF.emitValueWitnessValue(T, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::IsNonInline)); auto masked = IGF.Builder.CreateAnd(flags, mask); return IGF.Builder.CreateICmpEQ(masked, IGF.IGM.getSize(Size(0)), flags->getName() + ".isInline"); } /// Load the 'hasExtraInhabitants' valueWitness from the given table as an i1. llvm::Value *irgen::emitLoadOfHasExtraInhabitants(IRGenFunction &IGF, SILType T) { auto flags = IGF.emitValueWitnessValue(T, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::Enum_HasExtraInhabitants)); auto masked = IGF.Builder.CreateAnd(flags, mask); return IGF.Builder.CreateICmpNE(masked, IGF.IGM.getSize(Size(0)), flags->getName() + ".hasExtraInhabitants"); } /// Load the 'stride' value witness from the given table as a size_t. llvm::Value *irgen::emitLoadOfStride(IRGenFunction &IGF, SILType T) { return IGF.emitValueWitnessValue(T, ValueWitness::Stride); } llvm::Value *irgen::emitLoadOfExtraInhabitantCount(IRGenFunction &IGF, SILType T) { auto xiFlags = IGF.emitValueWitnessValue(T, ValueWitness::ExtraInhabitantFlags); auto mask = IGF.IGM.getSize( Size(ExtraInhabitantFlags::NumExtraInhabitantsMask)); return IGF.Builder.CreateAnd(xiFlags, mask, xiFlags->getName() + ".extraInhabitantCount"); } std::pair irgen::emitLoadOfIsInline(IRGenFunction &IGF, llvm::Value *metadata) { auto *flags = emitLoadOfValueWitnessValueFromMetadata(IGF, metadata, ValueWitness::Flags); auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::IsNonInline)); auto masked = IGF.Builder.CreateAnd(flags, mask); return std::make_pair( IGF.Builder.CreateICmpEQ(masked, IGF.IGM.getSize(Size(0)), flags->getName() + ".isInline"), flags); } llvm::Value *irgen::emitLoadOfSize(IRGenFunction &IGF, llvm::Value *metadata) { auto *size = emitLoadOfValueWitnessValueFromMetadata(IGF, metadata, ValueWitness::Size); return size; } llvm::Value *irgen::emitAlignMaskFromFlags(IRGenFunction &IGF, llvm::Value *flags) { auto *alignMask = IGF.IGM.getSize(Size(ValueWitnessFlags::AlignmentMask)); return IGF.Builder.CreateAnd(flags, alignMask, flags->getName() + ".alignmentMask"); } /// Emit a call to do an 'initializeWithCopy' operation. void irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, SILType T, Address dest, Address src) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithCopy); IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata}); } llvm::Value *irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, llvm::Value *metadata, Address dest, Address src) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::InitializeWithCopy); llvm::CallInst *call = IGF.Builder.CreateCall( copyFn, {dest.getAddress(), src.getAddress(), metadata}); return call; } /// Emit a call to do an 'initializeWithTake' operation. void irgen::emitInitializeWithTakeCall(IRGenFunction &IGF, SILType T, Address dest, Address src) { llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithTake); IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress(), metadata}); } llvm::Value *irgen::emitInitializeWithTakeCall(IRGenFunction &IGF, llvm::Value *metadata, Address dest, Address src) { auto copyFn = emitLoadOfValueWitnessFunctionFromMetadata( IGF, metadata, ValueWitness::InitializeWithTake); llvm::CallInst *call = IGF.Builder.CreateCall( copyFn, {dest.getAddress(), src.getAddress(), metadata}); return call; } /// Emit a call to do a 'destroy' operation. void irgen::emitDestroyCall(IRGenFunction &IGF, SILType T, Address object) { // If T is a trivial/POD type, nothing needs to be done. if (T.getObjectType().isTrivial(IGF.getSILModule())) return; llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::Destroy); IGF.Builder.CreateCall(fn, {object.getAddress(), metadata}); } void irgen::emitDestroyCall(IRGenFunction &IGF, llvm::Value *metadata, Address object) { auto fn = emitLoadOfValueWitnessFunctionFromMetadata(IGF, metadata, ValueWitness::Destroy); IGF.Builder.CreateCall(fn, {object.getAddress(), metadata}); } static llvm::Constant *getAllocateValueBufferFunction(IRGenModule &IGM) { llvm::Type *argTys[] = {IGM.TypeMetadataPtrTy, IGM.OpaquePtrTy}; llvm::SmallString<40> fnName("__swift_allocate_value_buffer"); return IGM.getOrCreateHelperFunction( fnName, IGM.OpaquePtrTy, argTys, [&](IRGenFunction &IGF) { auto it = IGF.CurFn->arg_begin(); auto *metadata = &*(it++); auto buffer = Address(&*(it++), Alignment(1)); // Dynamically check whether this type is inline or needs an allocation. llvm::Value *isInline, *flags; std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata); auto *outlineBB = IGF.createBasicBlock("outline.allocateValueInBuffer"); auto *doneBB = IGF.createBasicBlock("done"); llvm::Value *addressInline, *addressOutline; addressInline = buffer.getAddress(); auto *origBB = IGF.Builder.GetInsertBlock(); IGF.Builder.CreateCondBr(isInline, doneBB, outlineBB); IGF.Builder.emitBlock(outlineBB); { auto *size = emitLoadOfSize(IGF, metadata); auto *alignMask = emitAlignMaskFromFlags(IGF, flags); auto valueAddr = IGF.emitAllocRawCall(size, alignMask, "outline.ValueBuffer"); IGF.Builder.CreateStore( valueAddr, Address(IGF.Builder.CreateBitCast( buffer.getAddress(), valueAddr->getType()->getPointerTo()), Alignment(1))); addressOutline = IGF.Builder.CreateBitCast(valueAddr, IGM.OpaquePtrTy); IGF.Builder.CreateBr(doneBB); } IGF.Builder.emitBlock(doneBB); auto *addressOfValue = IGF.Builder.CreatePHI(IGM.OpaquePtrTy, 2); addressOfValue->addIncoming(addressInline, origBB); addressOfValue->addIncoming(addressOutline, outlineBB); IGF.Builder.CreateRet(addressOfValue); }, true /*noinline*/); } Address irgen::emitAllocateValueInBuffer(IRGenFunction &IGF, SILType type, Address buffer) { // Handle FixedSize types. auto &IGM = IGF.IGM; auto storagePtrTy = IGM.getStoragePointerType(type); auto &Builder = IGF.Builder; if (auto *fixedTI = dyn_cast(&IGF.getTypeInfo(type))) { auto packing = fixedTI->getFixedPacking(IGM); // Inline representation. if (packing == FixedPacking::OffsetZero) { return Address(Builder.CreateBitCast(buffer.getAddress(), storagePtrTy), buffer.getAlignment()); } // Outline representation. assert(packing == FixedPacking::Allocate && "Expect non dynamic packing"); auto size = fixedTI->getStaticSize(IGM); auto alignMask = fixedTI->getStaticAlignmentMask(IGM); auto valueAddr = IGF.emitAllocRawCall(size, alignMask, "outline.ValueBuffer"); Builder.CreateStore( valueAddr, Address(Builder.CreateBitCast(buffer.getAddress(), valueAddr->getType()->getPointerTo()), buffer.getAlignment())); return Address(Builder.CreateBitCast(valueAddr, storagePtrTy), buffer.getAlignment()); } // Dynamic packing. /// Call a function to handle the non-fixed case. auto *allocateFun = getAllocateValueBufferFunction(IGF.IGM); auto *metadata = IGF.emitTypeMetadataRefForLayout(type); auto *call = Builder.CreateCall( allocateFun, {metadata, Builder.CreateBitCast(buffer.getAddress(), IGM.OpaquePtrTy)}); call->setCallingConv(IGF.IGM.DefaultCC); call->setDoesNotThrow(); auto addressOfValue = Builder.CreateBitCast(call, storagePtrTy); return Address(addressOfValue, Alignment(1)); } static llvm::Constant *getProjectValueInBufferFunction(IRGenModule &IGM) { llvm::Type *argTys[] = {IGM.TypeMetadataPtrTy, IGM.OpaquePtrTy}; llvm::SmallString<40> fnName("__swift_project_value_buffer"); return IGM.getOrCreateHelperFunction( fnName, IGM.OpaquePtrTy, argTys, [&](IRGenFunction &IGF) { auto it = IGF.CurFn->arg_begin(); auto *metadata = &*(it++); auto buffer = Address(&*(it++), Alignment(1)); auto &Builder = IGF.Builder; // Dynamically check whether this type is inline or needs an allocation. llvm::Value *isInline, *flags; std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata); auto *outlineBB = IGF.createBasicBlock("outline.projectValueInBuffer"); auto *doneBB = IGF.createBasicBlock("done"); llvm::Value *addressInline, *addressOutline; auto *origBB = Builder.GetInsertBlock(); addressInline = buffer.getAddress(); Builder.CreateCondBr(isInline, doneBB, outlineBB); Builder.emitBlock(outlineBB); { addressOutline = Builder.CreateLoad( Address(Builder.CreateBitCast(buffer.getAddress(), IGM.OpaquePtrTy->getPointerTo()), Alignment(1))); Builder.CreateBr(doneBB); } Builder.emitBlock(doneBB); auto *addressOfValue = Builder.CreatePHI(IGM.OpaquePtrTy, 2); addressOfValue->addIncoming(addressInline, origBB); addressOfValue->addIncoming(addressOutline, outlineBB); Builder.CreateRet(addressOfValue); }, true /*noinline*/); } Address irgen::emitProjectValueInBuffer(IRGenFunction &IGF, SILType type, Address buffer) { // Handle FixedSize types. auto &IGM = IGF.IGM; auto storagePtrTy = IGM.getStoragePointerType(type); auto &Builder = IGF.Builder; if (auto *fixedTI = dyn_cast(&IGF.getTypeInfo(type))) { auto packing = fixedTI->getFixedPacking(IGM); // Inline representation. if (packing == FixedPacking::OffsetZero) { return Address(Builder.CreateBitCast(buffer.getAddress(), storagePtrTy), buffer.getAlignment()); } // Outline representation. assert(packing == FixedPacking::Allocate && "Expect non dynamic packing"); auto valueAddr = Builder.CreateLoad( Address(Builder.CreateBitCast(buffer.getAddress(), storagePtrTy->getPointerTo()), buffer.getAlignment())); return Address(Builder.CreateBitCast(valueAddr, storagePtrTy), buffer.getAlignment()); } // Dynamic packing. auto *projectFun = getProjectValueInBufferFunction(IGF.IGM); auto *metadata = IGF.emitTypeMetadataRefForLayout(type); auto *call = Builder.CreateCall( projectFun, {metadata, Builder.CreateBitCast(buffer.getAddress(), IGM.OpaquePtrTy)}); call->setCallingConv(IGF.IGM.DefaultCC); call->setDoesNotThrow(); auto addressOfValue = Builder.CreateBitCast(call, storagePtrTy); return Address(addressOfValue, Alignment(1)); } static llvm::Constant *getDeallocateValueInBufferFunction(IRGenModule &IGM) { llvm::Type *argTys[] = {IGM.TypeMetadataPtrTy, IGM.OpaquePtrTy}; llvm::SmallString<40> fnName("__swift_deallocate_value_buffer"); return IGM.getOrCreateHelperFunction( fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) { auto it = IGF.CurFn->arg_begin(); auto *metadata = &*(it++); auto buffer = Address(&*(it++), Alignment(1)); auto &Builder = IGF.Builder; // Dynamically check whether this type is inline or needs an allocation. llvm::Value *isInline, *flags; std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata); auto *outlineBB = IGF.createBasicBlock("outline.deallocateValueInBuffer"); auto *doneBB = IGF.createBasicBlock("done"); Builder.CreateCondBr(isInline, doneBB, outlineBB); Builder.emitBlock(outlineBB); { auto *size = emitLoadOfSize(IGF, metadata); auto *alignMask = emitAlignMaskFromFlags(IGF, flags); auto *ptr = Builder.CreateLoad(Address( Builder.CreateBitCast(buffer.getAddress(), IGM.Int8PtrPtrTy), buffer.getAlignment())); IGF.emitDeallocRawCall(ptr, size, alignMask); Builder.CreateBr(doneBB); } Builder.emitBlock(doneBB); Builder.CreateRetVoid(); }, true /*noinline*/); } void irgen::emitDeallocateValueInBuffer(IRGenFunction &IGF, SILType type, Address buffer) { // Handle FixedSize types. auto &IGM = IGF.IGM; auto &Builder = IGF.Builder; if (auto *fixedTI = dyn_cast(&IGF.getTypeInfo(type))) { auto packing = fixedTI->getFixedPacking(IGM); // Inline representation. if (packing == FixedPacking::OffsetZero) return; // Outline representation. assert(packing == FixedPacking::Allocate && "Expect non dynamic packing"); auto size = fixedTI->getStaticSize(IGM); auto alignMask = fixedTI->getStaticAlignmentMask(IGM); auto *ptr = Builder.CreateLoad(Address( Builder.CreateBitCast(buffer.getAddress(), IGM.Int8PtrPtrTy), buffer.getAlignment())); IGF.emitDeallocRawCall(ptr, size, alignMask); return; } // Dynamic packing. auto *projectFun = getDeallocateValueInBufferFunction(IGF.IGM); auto *metadata = IGF.emitTypeMetadataRefForLayout(type); auto *call = Builder.CreateCall( projectFun, {metadata, Builder.CreateBitCast(buffer.getAddress(), IGM.OpaquePtrTy)}); call->setCallingConv(IGF.IGM.DefaultCC); call->setDoesNotThrow(); }