//===--- GenTuple.cpp - Swift IR Generation For Tuple 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 tuple types in Swift. This // includes creating the IR type as well as emitting the primitive access // operations. // // It is assumed in several places in IR-generation that the // explosion schema of a tuple type is always equal to the appended // explosion schemas of the component types. // //===----------------------------------------------------------------------===// #include "swift/AST/Types.h" #include "swift/AST/Decl.h" #include "swift/AST/Pattern.h" #include "swift/SIL/SILType.h" #include "swift/Basic/Optional.h" #include "llvm/IR/DerivedTypes.h" #include "GenHeap.h" #include "GenSequential.h" #include "GenType.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "Explosion.h" #include "IndirectTypeInfo.h" #include "NonFixedTypeInfo.h" #include "GenTuple.h" using namespace swift; using namespace irgen; namespace { class TupleFieldInfo : public SequentialField { public: TupleFieldInfo(unsigned index, StringRef name, const TypeInfo &type) : SequentialField(type), Index(index), Name(name) {} /// The field index. const unsigned Index; const StringRef Name; StringRef getFieldName() const { return Name; } const TupleTypeElt &getField(CanType t) const { auto tup = cast(t); return tup->getFields()[Index]; } CanType getType(IRGenModule&, CanType t) const { auto tup = cast(t); return tup.getElementType(Index); } }; /// Adapter for tuple types. template class TupleTypeInfoBase : public SequentialTypeInfo { typedef SequentialTypeInfo super; protected: template TupleTypeInfoBase(As &&...args) : super(std::forward(args)...) {} using super::asImpl; public: /// Given a full tuple explosion, project out a single element. void projectElementFromExplosion(IRGenFunction &IGF, Explosion &tuple, unsigned fieldNo, Explosion &out) const { assert(tuple.getKind() == out.getKind()); const TupleFieldInfo &field = asImpl().getFields()[fieldNo]; // If the field requires no storage, there's nothing to do. if (field.isEmpty()) return IGF.emitFakeExplosion(field.getTypeInfo(), out); // Otherwise, project from the base. auto fieldRange = field.getProjectionRange(out.getKind()); ArrayRef element = tuple.getRange(fieldRange.first, fieldRange.second); out.add(element); } /// Given the address of a tuple, project out the address of a /// single element. Address projectElementAddress(IRGenFunction &IGF, Address tuple, CanType T, unsigned fieldNo) const { const TupleFieldInfo &field = asImpl().getFields()[fieldNo]; if (field.isEmpty()) return field.getTypeInfo().getUndefAddress(); auto offsets = asImpl().getNonFixedOffsets(IGF, T); return field.projectAddress(IGF, tuple, offsets); } bool isIndirectArgument(ResilienceExpansion kind) const override { llvm_unreachable("unexploded tuple as argument?"); } void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, Address src, CanType T) const override { llvm_unreachable("unexploded tuple as argument?"); } // For now, just use extra inhabitants from the first element. // FIXME: generalize bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { if (asImpl().getFields().empty()) return false; return asImpl().getFields()[0].getTypeInfo().mayHaveExtraInhabitants(IGM); } // This is dead code in NonFixedTupleTypeInfo. unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const { if (asImpl().getFields().empty()) return 0; auto &eltTI = cast(asImpl().getFields()[0].getTypeInfo()); return eltTI.getFixedExtraInhabitantCount(IGM); } // This is dead code in NonFixedTupleTypeInfo. llvm::ConstantInt *getFixedExtraInhabitantValue(IRGenModule &IGM, unsigned bits, unsigned index) const { auto &eltTI = cast(asImpl().getFields()[0].getTypeInfo()); return eltTI.getFixedExtraInhabitantValue(IGM, bits, index); } // This is dead code in NonFixedTupleTypeInfo. llvm::Value *maskFixedExtraInhabitant(IRGenFunction &IGF, llvm::Value *tupleValue) const { // Truncate down to the width of the element, mask it recursively, // and then zext back out to the payload size. auto &eltTI = cast(asImpl().getFields()[0].getTypeInfo()); unsigned eltWidth = eltTI.getFixedSize().getValueInBits(); auto eltTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), eltWidth); auto eltValue = IGF.Builder.CreateTrunc(tupleValue, eltTy); eltValue = eltTI.maskFixedExtraInhabitant(IGF, eltValue); return IGF.Builder.CreateZExt(eltValue, tupleValue->getType()); } llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address tupleAddr, CanType tupleType) const override { Address eltAddr = asImpl().projectElementAddress(IGF, tupleAddr, tupleType, 0); auto &elt = asImpl().getFields()[0]; return elt.getTypeInfo().getExtraInhabitantIndex(IGF, eltAddr, elt.getType(IGF.IGM, tupleType)); } void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, Address tupleAddr, CanType tupleType) const override { Address eltAddr = asImpl().projectElementAddress(IGF, tupleAddr, tupleType, 0); auto &elt = asImpl().getFields()[0]; elt.getTypeInfo().storeExtraInhabitant(IGF, index, eltAddr, elt.getType(IGF.IGM, tupleType)); } }; /// Type implementation for loadable tuples. class LoadableTupleTypeInfo : public TupleTypeInfoBase { public: // FIXME: Spare bits between tuple elements. LoadableTupleTypeInfo(unsigned numFields, llvm::Type *ty, Size size, llvm::BitVector spareBits, Alignment align, IsPOD_t isPOD) : TupleTypeInfoBase(numFields, ty, size, std::move(spareBits), align, isPOD) {} Nothing_t getNonFixedOffsets(IRGenFunction &IGF) const { return Nothing; } Nothing_t getNonFixedOffsets(IRGenFunction &IGF, CanType T) const { return Nothing; } }; /// Type implementation for fixed-size but non-loadable tuples. class FixedTupleTypeInfo : public TupleTypeInfoBase> { public: // FIXME: Spare bits between tuple elements. FixedTupleTypeInfo(unsigned numFields, llvm::Type *ty, Size size, llvm::BitVector spareBits, Alignment align, IsPOD_t isPOD, IsBitwiseTakable_t isBT) : TupleTypeInfoBase(numFields, ty, size, std::move(spareBits), align, isPOD, isBT) {} Nothing_t getNonFixedOffsets(IRGenFunction &IGF) const { return Nothing; } Nothing_t getNonFixedOffsets(IRGenFunction &IGF, CanType T) const { return Nothing; } }; /// An accessor for the non-fixed offsets for a tuple type. class TupleNonFixedOffsets : public NonFixedOffsetsImpl { CanType TheType; public: TupleNonFixedOffsets(CanType type) : TheType(type) { assert(isa(TheType)); } llvm::Value *getOffsetForIndex(IRGenFunction &IGF, unsigned index) { // Fetch the metadata as a tuple type. We cache this because // we might repeatedly need the bitcast. auto metadata = IGF.emitTypeMetadataRef(TheType); auto asTuple = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TupleTypeMetadataPtrTy); llvm::Value *indices[] = { IGF.IGM.getSize(Size(0)), // (*tupleType) llvm::ConstantInt::get(IGF.IGM.Int32Ty, 3), // .Elements IGF.IGM.getSize(Size(index)), // [index] llvm::ConstantInt::get(IGF.IGM.Int32Ty, 1) // .Offset }; auto slot = IGF.Builder.CreateInBoundsGEP(asTuple, indices); return IGF.Builder.CreateLoad(slot, IGF.IGM.getPointerAlignment(), metadata->getName() + "." + Twine(index) + ".offset"); } }; /// Type implementation for non-fixed-size tuples. class NonFixedTupleTypeInfo : public TupleTypeInfoBase> { public: NonFixedTupleTypeInfo(unsigned numFields, llvm::Type *T, Alignment minAlign, IsPOD_t isPOD, IsBitwiseTakable_t isBT) : TupleTypeInfoBase(numFields, T, minAlign, isPOD, isBT) {} TupleNonFixedOffsets getNonFixedOffsets(IRGenFunction &IGF, CanType T) const { return TupleNonFixedOffsets(T); } void initializeMetadata(IRGenFunction &IGF, llvm::Value *metadata, llvm::Value *vwtable, CanType T) const override { // Tuple value witness tables are instantiated by the runtime along with // their metadata. We should never try to initialize one in the compiler. llvm_unreachable("initializing value witness table for tuple?!"); } }; class TupleTypeBuilder : public SequentialTypeBuilder { CanType TheTuple; public: TupleTypeBuilder(IRGenModule &IGM, CanType theTuple) : SequentialTypeBuilder(IGM), TheTuple(theTuple) {} FixedTupleTypeInfo *createFixed(ArrayRef fields, const StructLayout &layout) { return create(fields, layout.getType(), layout.getSize(), layout.getSpareBits(), layout.getAlignment(), layout.isKnownPOD(), layout.isKnownBitwiseTakable()); } LoadableTupleTypeInfo *createLoadable(ArrayRef fields, const StructLayout &layout) { return create(fields, layout.getType(), layout.getSize(), layout.getSpareBits(), layout.getAlignment(), layout.isKnownPOD()); } NonFixedTupleTypeInfo *createNonFixed(ArrayRef fields, const StructLayout &layout) { return create(fields, layout.getType(), layout.getAlignment(), layout.isKnownPOD(), layout.isKnownBitwiseTakable()); } TupleFieldInfo getFieldInfo(unsigned index, const TupleTypeElt &field, const TypeInfo &fieldTI) { StringRef name = field.hasName() ? field.getName().str() : "elt"; return TupleFieldInfo(index, name, fieldTI); } SILType getType(const TupleTypeElt &field) { // We know we're working with a lowered type here. return SILType::getPrimitiveObjectType(CanType(field.getType())); } StructLayout performLayout(ArrayRef fieldTypes) { return StructLayout(IGM, LayoutKind::NonHeapObject, LayoutStrategy::Universal, fieldTypes); } }; } const TypeInfo *TypeConverter::convertTupleType(TupleType *tuple) { TupleTypeBuilder builder(IGM, CanType(tuple)); return builder.layout(tuple->getFields()); } /// A convenient macro for delegating an operation to all of the /// various tuple implementations. #define FOR_TUPLE_IMPL(IGF, type, op, ...) do { \ auto &tupleTI = IGF.getTypeInfo(type); \ if (isa(tupleTI)) { \ return tupleTI.as().op(IGF, __VA_ARGS__); \ } else if (isa(tupleTI)) { \ return tupleTI.as().op(IGF, __VA_ARGS__); \ } else { \ return tupleTI.as().op(IGF, __VA_ARGS__); \ } \ } while(0) void irgen::projectTupleElementFromExplosion(IRGenFunction &IGF, SILType tupleType, Explosion &tuple, unsigned fieldNo, Explosion &out) { FOR_TUPLE_IMPL(IGF, tupleType, projectElementFromExplosion, tuple, fieldNo, out); } Address irgen::projectTupleElementAddress(IRGenFunction &IGF, Address tuple, SILType tupleType, unsigned fieldNo) { FOR_TUPLE_IMPL(IGF, tupleType, projectElementAddress, tuple, tupleType.getSwiftRValueType(), fieldNo); }