Files
swift-mirror/lib/IRGen/GenTuple.cpp
John McCall c57dac63ae Use the first element of structs and tuples as a source
for extra inhabitants.

For structs in particular, this eliminates a major source
of abstraction penatlies.  For example, an optional struct
containing an object pointer is now represented the same
way as an optional object pointer, which is critical for
correctly importing CF types as Unmanaged<T>!.

In time, we should generalize this to consider all elements
as sources for extra inhabitants, as well as exploiting
spare bits in the representation, but getting the
single-element case right really provides the bulk of the
benefit.

This commit restores r17242 and r17243 with a fix to use
value witnesses that actually forward the right type metadata
down.  We were already generating these value witnesses in
the dependent struct VWT pattern, but I was being too clever
and trying to use the underlying value witness directly.

Swift SVN r17267
2014-05-02 20:17:14 +00:00

364 lines
15 KiB
C++

//===--- 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<TupleFieldInfo> {
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<TupleType>(t);
return tup->getFields()[Index];
}
CanType getType(IRGenModule&, CanType t) const {
auto tup = cast<TupleType>(t);
return tup.getElementType(Index);
}
};
/// Adapter for tuple types.
template <class Impl, class Base>
class TupleTypeInfoBase
: public SequentialTypeInfo<Impl, Base, TupleFieldInfo> {
typedef SequentialTypeInfo<Impl, Base, TupleFieldInfo> super;
protected:
template <class... As>
TupleTypeInfoBase(As &&...args) : super(std::forward<As>(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<llvm::Value *> 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 &params,
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<FixedTypeInfo>(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<FixedTypeInfo>(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<FixedTypeInfo>(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<LoadableTupleTypeInfo, LoadableTypeInfo> {
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<FixedTupleTypeInfo,
IndirectTypeInfo<FixedTupleTypeInfo,
FixedTypeInfo>>
{
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<TupleType>(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<NonFixedTupleTypeInfo,
WitnessSizedTypeInfo<NonFixedTupleTypeInfo>>
{
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<TupleTypeBuilder, TupleFieldInfo,
TupleTypeElt> {
CanType TheTuple;
public:
TupleTypeBuilder(IRGenModule &IGM, CanType theTuple)
: SequentialTypeBuilder(IGM), TheTuple(theTuple) {}
FixedTupleTypeInfo *createFixed(ArrayRef<TupleFieldInfo> fields,
const StructLayout &layout) {
return create<FixedTupleTypeInfo>(fields, layout.getType(),
layout.getSize(),
layout.getSpareBits(),
layout.getAlignment(),
layout.isKnownPOD(),
layout.isKnownBitwiseTakable());
}
LoadableTupleTypeInfo *createLoadable(ArrayRef<TupleFieldInfo> fields,
const StructLayout &layout) {
return create<LoadableTupleTypeInfo>(fields, layout.getType(),
layout.getSize(),
layout.getSpareBits(),
layout.getAlignment(),
layout.isKnownPOD());
}
NonFixedTupleTypeInfo *createNonFixed(ArrayRef<TupleFieldInfo> fields,
const StructLayout &layout) {
return create<NonFixedTupleTypeInfo>(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<const TypeInfo *> 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<LoadableTypeInfo>(tupleTI)) { \
return tupleTI.as<LoadableTupleTypeInfo>().op(IGF, __VA_ARGS__); \
} else if (isa<FixedTypeInfo>(tupleTI)) { \
return tupleTI.as<FixedTupleTypeInfo>().op(IGF, __VA_ARGS__); \
} else { \
return tupleTI.as<NonFixedTupleTypeInfo>().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);
}