mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
466 lines
17 KiB
C++
466 lines
17 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.
|
|
//
|
|
// Currently we do no optimization of tuples.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/Basic/Optional.h"
|
|
#include "llvm/DerivedTypes.h"
|
|
#include "llvm/Target/TargetData.h"
|
|
|
|
#include "GenType.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenModule.h"
|
|
#include "LValue.h"
|
|
#include "RValue.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
static StringRef getFieldName(const TupleTypeElt &field) {
|
|
if (!field.Name.empty())
|
|
return field.Name.str();
|
|
return "elt";
|
|
}
|
|
|
|
namespace {
|
|
/// A class describing the IR layout for a particular field.
|
|
struct TupleFieldInfo {
|
|
TupleFieldInfo(const TupleTypeElt &Field, const TypeInfo &FieldInfo)
|
|
: Field(Field), FieldInfo(FieldInfo) {}
|
|
|
|
/// The field.
|
|
const TupleTypeElt &Field;
|
|
|
|
/// The TypeInfo for the field.
|
|
const TypeInfo &FieldInfo;
|
|
|
|
/// The offset of this field into the storage type.
|
|
Size StorageOffset;
|
|
|
|
/// The index of this field into the storage type, or NoStorage
|
|
/// if the field requires no storage.
|
|
unsigned StorageIndex : 24;
|
|
enum : unsigned { NoStorage = 0xFFFFFFU };
|
|
|
|
/// The range of this field within the scalars for the tuple.
|
|
unsigned ScalarBegin : 4;
|
|
unsigned ScalarEnd : 4;
|
|
|
|
/// Given an l-value for the base address, produce an l-value for
|
|
/// this field.
|
|
/// TODO: this is only actually meaningful for certain possible
|
|
/// representations of tuples; a bit-packing representation
|
|
/// would not be adequate here.
|
|
LValue getElementPtr(IRGenFunction &IGF, const LValue &LV) const {
|
|
llvm::Value *Addr =
|
|
IGF.Builder.CreateStructGEP(LV.getAddress(), StorageIndex);
|
|
Alignment Alignment = LV.getAlignment().alignmentAtOffset(StorageOffset);
|
|
return LValue::forAddress(Addr, Alignment);
|
|
}
|
|
};
|
|
|
|
/// An abstract base class for tuple types.
|
|
class TupleTypeInfo : public TypeInfo {
|
|
/// The number of TupleFieldInfos for this type. Equal to the
|
|
/// number of fields in the tuple. The actual data is stored
|
|
/// after the TypeInfo.
|
|
unsigned NumFields : 31;
|
|
unsigned IsAggregate : 1;
|
|
|
|
TupleFieldInfo *getFieldInfoStorage();
|
|
const TupleFieldInfo *getFieldInfoStorage() const {
|
|
return const_cast<TupleTypeInfo*>(this)->getFieldInfoStorage();
|
|
}
|
|
|
|
public:
|
|
TupleTypeInfo(llvm::Type *T, Size S, Alignment A,
|
|
bool IsAggregate, ArrayRef<TupleFieldInfo> Fields)
|
|
: TypeInfo(T, S, A), NumFields(Fields.size()), IsAggregate(IsAggregate) {
|
|
|
|
TupleFieldInfo *FieldStorage = getFieldInfoStorage();
|
|
for (unsigned I = 0, E = Fields.size(); I != E; ++I)
|
|
new(&FieldStorage[I]) TupleFieldInfo(Fields[I]);
|
|
}
|
|
|
|
ArrayRef<TupleFieldInfo> getFieldInfos() const {
|
|
return llvm::makeArrayRef(getFieldInfoStorage(), NumFields);
|
|
}
|
|
|
|
/// Perform an l-value projection of a member of this tuple.
|
|
LValue project(IRGenFunction &IGF, const LValue &tuple,
|
|
const TupleFieldInfo &field) const {
|
|
llvm::Value *addr = tuple.getAddress();
|
|
addr = IGF.Builder.CreateStructGEP(addr, field.StorageIndex,
|
|
addr->getName() + "." + getFieldName(field.Field));
|
|
|
|
// Compute the adjusted alignment.
|
|
Alignment align =
|
|
tuple.getAlignment().alignmentAtOffset(field.StorageOffset);
|
|
|
|
return LValue::forAddress(addr, align);
|
|
}
|
|
|
|
/// Perform an r-value projection of a member of this tuple.
|
|
virtual RValue project(IRGenFunction &IGF, const RValue &tuple,
|
|
const TupleFieldInfo &field) const = 0;
|
|
};
|
|
|
|
/// A TypeInfo implementation for tuples that are broken down into scalars.
|
|
class ScalarTupleTypeInfo : public TupleTypeInfo {
|
|
RValueSchema Schema;
|
|
|
|
ScalarTupleTypeInfo(llvm::Type *T, Size S, Alignment A,
|
|
ArrayRef<TupleFieldInfo> Fields,
|
|
const RValueSchema &Schema)
|
|
: TupleTypeInfo(T, S, A, /*agg*/ false, Fields), Schema(Schema) {}
|
|
|
|
public:
|
|
static ScalarTupleTypeInfo *create(llvm::Type *T, Size S, Alignment A,
|
|
ArrayRef<llvm::Type*> ScalarTypes,
|
|
ArrayRef<TupleFieldInfo> FieldInfos) {
|
|
void *Storage = operator new(sizeof(ScalarTupleTypeInfo) +
|
|
sizeof(TupleFieldInfo) * FieldInfos.size());
|
|
return new(Storage) ScalarTupleTypeInfo(T, S, A, FieldInfos,
|
|
RValueSchema::forScalars(ScalarTypes));
|
|
}
|
|
|
|
RValueSchema getSchema() const {
|
|
return Schema;
|
|
}
|
|
|
|
RValue project(IRGenFunction &IGF, const RValue &tuple,
|
|
const TupleFieldInfo &field) const {
|
|
ArrayRef<llvm::Value*> scalars = tuple.getScalars();
|
|
return RValue::forScalars(scalars.slice(field.ScalarBegin,
|
|
field.ScalarEnd - field.ScalarBegin));
|
|
}
|
|
|
|
RValue load(IRGenFunction &IGF, const LValue &LV) const {
|
|
SmallVector<llvm::Value*, RValue::MaxScalars> Scalars;
|
|
|
|
// Load by loading the elements.
|
|
for (const TupleFieldInfo &Field : getFieldInfos()) {
|
|
assert(Scalars.size() == Field.ScalarBegin);
|
|
|
|
// Skip fields that contribute no scalars.
|
|
if (Field.ScalarBegin == Field.ScalarEnd) continue;
|
|
|
|
// Load the field and extract the scalars.
|
|
LValue FieldLV = Field.getElementPtr(IGF, LV);
|
|
RValue FieldRV = Field.FieldInfo.load(IGF, FieldLV);
|
|
Scalars.append(FieldRV.getScalars().begin(),
|
|
FieldRV.getScalars().end());
|
|
|
|
assert(Scalars.size() == Field.ScalarEnd);
|
|
}
|
|
|
|
return RValue::forScalars(Scalars);
|
|
}
|
|
|
|
void store(IRGenFunction &IGF, const RValue &RV, const LValue &LV) const {
|
|
assert(RV.isScalar());
|
|
|
|
for (const TupleFieldInfo &Field : getFieldInfos()) {
|
|
// Skip fields that contribute no scalars.
|
|
if (Field.ScalarBegin == Field.ScalarEnd) continue;
|
|
|
|
// Project out the appropriate r-value.
|
|
ArrayRef<llvm::Value*> Scalars =
|
|
RV.getScalars().slice(Field.ScalarBegin,
|
|
Field.ScalarEnd - Field.ScalarBegin);
|
|
RValue FieldRV = RValue::forScalars(Scalars);
|
|
|
|
// Write the extracted r-value into a projected l-value.
|
|
LValue FieldLV = Field.getElementPtr(IGF, LV);
|
|
Field.FieldInfo.store(IGF, FieldRV, FieldLV);
|
|
}
|
|
}
|
|
};
|
|
|
|
/// A TypeInfo implementation for tuples that are passed around as
|
|
/// aggregates.
|
|
class AggregateTupleTypeInfo : public TupleTypeInfo {
|
|
AggregateTupleTypeInfo(llvm::Type *T, Size S, Alignment A,
|
|
ArrayRef<TupleFieldInfo> Fields)
|
|
: TupleTypeInfo(T, S, A, /*agg*/ true, Fields) {}
|
|
|
|
public:
|
|
static AggregateTupleTypeInfo *create(llvm::StructType *T,
|
|
Size S, Alignment A,
|
|
ArrayRef<TupleFieldInfo> FieldInfos) {
|
|
void *Storage = operator new(sizeof(AggregateTupleTypeInfo) +
|
|
sizeof(TupleFieldInfo) * FieldInfos.size());
|
|
return new(Storage) AggregateTupleTypeInfo(T, S, A, FieldInfos);
|
|
}
|
|
|
|
llvm::StructType *getStorageType() const {
|
|
return cast<llvm::StructType>(TypeInfo::getStorageType());
|
|
}
|
|
|
|
RValueSchema getSchema() const {
|
|
return RValueSchema::forAggregate(getStorageType(), StorageAlignment);
|
|
}
|
|
|
|
/// Given an r-value of this type, form an l-value referring to
|
|
/// the temporary.
|
|
LValue getLValueForAggregateRValue(const RValue &rvalue) const {
|
|
assert(rvalue.isAggregate());
|
|
|
|
// The alignment of a temporary is always the alignment of the type.
|
|
return LValue::forAddress(rvalue.getAggregateAddress(),
|
|
StorageAlignment);
|
|
}
|
|
|
|
using TupleTypeInfo::project;
|
|
RValue project(IRGenFunction &IGF, const RValue &tuple,
|
|
const TupleFieldInfo &field) const {
|
|
assert(tuple.isAggregate());
|
|
LValue tupleLV = getLValueForAggregateRValue(tuple);
|
|
return field.FieldInfo.load(IGF, project(IGF, tupleLV, field));
|
|
}
|
|
|
|
RValue load(IRGenFunction &IGF, const LValue &LV) const {
|
|
LValue temp = IGF.createFullExprAlloca(StorageType, StorageAlignment,
|
|
"lvalue-load");
|
|
// FIXME: a memcpy isn't right if any of the fields require
|
|
// special logic for loads or stores.
|
|
IGF.emitMemCpy(temp.getAddress(), LV.getAddress(), StorageSize,
|
|
std::min(StorageAlignment, LV.getAlignment()));
|
|
return RValue::forAggregate(temp.getAddress());
|
|
}
|
|
|
|
void store(IRGenFunction &IGF, const RValue &RV, const LValue &LV) const {
|
|
// FIXME: a memcpy isn't right if any of the fields require
|
|
// special logic for loads or stores.
|
|
IGF.emitMemCpy(LV.getAddress(), RV.getAggregateAddress(), StorageSize,
|
|
std::min(StorageAlignment, LV.getAlignment()));
|
|
}
|
|
};
|
|
}
|
|
|
|
TupleFieldInfo *TupleTypeInfo::getFieldInfoStorage() {
|
|
void *Ptr;
|
|
if (IsAggregate)
|
|
Ptr = static_cast<AggregateTupleTypeInfo*>(this)+1;
|
|
else
|
|
Ptr = static_cast<ScalarTupleTypeInfo*>(this)+1;
|
|
return reinterpret_cast<TupleFieldInfo*>(Ptr);
|
|
}
|
|
|
|
const TypeInfo *
|
|
TypeConverter::convertTupleType(IRGenModule &IGM, TupleType *T) {
|
|
SmallVector<TupleFieldInfo, 8> FieldInfos;
|
|
SmallVector<llvm::Type*, 8> ScalarTypes;
|
|
SmallVector<llvm::Type*, 8> StorageTypes;
|
|
FieldInfos.reserve(T->Fields.size());
|
|
StorageTypes.reserve(T->Fields.size());
|
|
|
|
bool HasAggregateField = false;
|
|
|
|
Size StorageSize;
|
|
Alignment StorageAlignment;
|
|
|
|
// TODO: rearrange the tuple for optimal packing.
|
|
for (const TupleTypeElt &Field : T->Fields) {
|
|
const TypeInfo &TInfo = getFragileTypeInfo(IGM, Field.Ty);
|
|
assert(TInfo.isComplete());
|
|
|
|
FieldInfos.push_back(TupleFieldInfo(Field, TInfo));
|
|
TupleFieldInfo &FieldInfo = FieldInfos.back();
|
|
|
|
// Ignore zero-sized fields.
|
|
if (TInfo.StorageSize.isZero()) {
|
|
FieldInfo.StorageIndex = TupleFieldInfo::NoStorage;
|
|
FieldInfo.ScalarBegin = FieldInfo.ScalarEnd = ScalarTypes.size();
|
|
continue;
|
|
}
|
|
|
|
if (!HasAggregateField) {
|
|
RValueSchema Schema = TInfo.getSchema();
|
|
if (Schema.isAggregate()) {
|
|
HasAggregateField = true;
|
|
} else {
|
|
FieldInfo.ScalarBegin = ScalarTypes.size();
|
|
ScalarTypes.append(Schema.getScalarTypes().begin(),
|
|
Schema.getScalarTypes().end());
|
|
FieldInfo.ScalarEnd = ScalarTypes.size();
|
|
}
|
|
}
|
|
|
|
StorageAlignment = std::max(StorageAlignment, TInfo.StorageAlignment);
|
|
|
|
// If the current tuple size isn't a multiple of the tuple
|
|
// alignment, we need padding.
|
|
if (Size OffsetFromAlignment = StorageSize % StorageAlignment) {
|
|
unsigned PaddingRequired
|
|
= StorageAlignment.getValue() - OffsetFromAlignment.getValue();
|
|
|
|
// We don't actually need to uglify the IR unless the natural
|
|
// alignment of the IR type for the field isn't good enough.
|
|
Alignment FieldIRAlignment(
|
|
IGM.TargetData.getABITypeAlignment(TInfo.StorageType));
|
|
assert(FieldIRAlignment <= TInfo.StorageAlignment);
|
|
if (FieldIRAlignment != TInfo.StorageAlignment) {
|
|
StorageTypes.push_back(llvm::ArrayType::get(IGM.Int8Ty,
|
|
PaddingRequired));
|
|
}
|
|
|
|
// Regardless, the storage size goes up.
|
|
StorageSize += Size(PaddingRequired);
|
|
}
|
|
|
|
FieldInfo.StorageIndex = StorageTypes.size();
|
|
FieldInfo.StorageOffset = StorageSize;
|
|
StorageTypes.push_back(TInfo.getStorageType());
|
|
StorageSize += TInfo.StorageSize;
|
|
}
|
|
|
|
// If the tuple requires no storage at all, just use i8. Most
|
|
// clients will just ignore zero-size types, but those that care can
|
|
// have a sensible one-byte type.
|
|
if (StorageSize.isZero()) {
|
|
assert(ScalarTypes.empty());
|
|
assert(StorageTypes.empty());
|
|
return ScalarTupleTypeInfo::create(IGM.Int8Ty, Size(0), Alignment(1),
|
|
ScalarTypes, FieldInfos);
|
|
}
|
|
|
|
// Otherwise, build a new, structural type.
|
|
llvm::StructType *Converted
|
|
= llvm::StructType::get(IGM.getLLVMContext(), StorageTypes);
|
|
|
|
// If the tuple has no aggregate fields, and the number of scalars
|
|
// doesn't exceed the maximum, pass it as scalars.
|
|
if (!HasAggregateField && ScalarTypes.size() <= RValueSchema::MaxScalars) {
|
|
return ScalarTupleTypeInfo::create(Converted, StorageSize, StorageAlignment,
|
|
ScalarTypes, FieldInfos);
|
|
}
|
|
|
|
// Otherwise, pass the entire thing as an aggregate. This is a bit
|
|
// wasteful; we could really pass some of the fields as aggregates
|
|
// and some as scalars.
|
|
return AggregateTupleTypeInfo::create(Converted, StorageSize,
|
|
StorageAlignment, FieldInfos);
|
|
}
|
|
|
|
/// Emit a tuple literal expression.
|
|
RValue IRGenFunction::emitTupleExpr(TupleExpr *Tuple, const TypeInfo &TI) {
|
|
// Extract information about the tuple. Note that type-checking
|
|
// should ensure that the literal's elements are exactly parallel
|
|
// with the field infos.
|
|
const TupleTypeInfo &TInfo = static_cast<const TupleTypeInfo&>(TI);
|
|
ArrayRef<TupleFieldInfo> FieldInfos = TInfo.getFieldInfos();
|
|
RValueSchema Schema = TInfo.getSchema();
|
|
|
|
// Set up for the emission.
|
|
llvm::SmallVector<llvm::Value*, RValue::MaxScalars> Scalars;
|
|
LValue Aggregate;
|
|
if (Schema.isAggregate()) {
|
|
Aggregate = createFullExprAlloca(Schema.getAggregateType(),
|
|
Schema.getAggregateAlignment(),
|
|
"tuple-literal");
|
|
}
|
|
|
|
// Emit all the sub-expressions.
|
|
for (unsigned I = 0, E = Tuple->getNumElements(); I != E; ++I) {
|
|
const TupleFieldInfo &FieldInfo = FieldInfos[I];
|
|
RValue Field = emitRValue(Tuple->getElement(I), FieldInfo.FieldInfo);
|
|
|
|
// If the outer schema is scalar, then all the element schemas
|
|
// are, too, and we should just their scalars into field scalars.
|
|
if (Schema.isScalar()) {
|
|
assert(Field.isScalar());
|
|
Scalars.append(Field.getScalars().begin(), Field.getScalars().end());
|
|
|
|
// The reverse is not necessarily true, so we need to store an
|
|
// arbitrary r-value into our temporary.
|
|
} else {
|
|
LValue FieldLV = FieldInfo.getElementPtr(*this, Aggregate);
|
|
FieldInfo.FieldInfo.store(*this, Field, FieldLV);
|
|
}
|
|
}
|
|
|
|
// Finally, construct the temporary.
|
|
if (Schema.isAggregate()) {
|
|
return RValue::forAggregate(Aggregate.getAddress());
|
|
} else {
|
|
return RValue::forScalars(Scalars);
|
|
}
|
|
}
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
RValue IRGenFunction::emitTupleElementRValue(TupleElementExpr *E,
|
|
const TypeInfo &fieldType) {
|
|
Expr *tuple = E->getBase();
|
|
const TupleTypeInfo &tupleType =
|
|
static_cast<const TupleTypeInfo&>(getFragileTypeInfo(tuple->getType()));
|
|
|
|
llvm::errs() << E->getFieldNumber() << "\n";
|
|
|
|
const TupleFieldInfo &field =
|
|
tupleType.getFieldInfos()[E->getFieldNumber()];
|
|
|
|
// If the field requires no storage, there's nothing to do.
|
|
if (field.StorageIndex == TupleFieldInfo::NoStorage) {
|
|
// Emit the base in case it has side-effects.
|
|
emitIgnored(tuple);
|
|
return emitFakeRValue(field.FieldInfo);
|
|
}
|
|
|
|
// If we can emit the base as an l-value, we can avoid a lot
|
|
// of unnecessary work.
|
|
if (Optional<LValue> tupleLV = tryEmitAsLValue(tuple, tupleType)) {
|
|
LValue fieldLV = tupleType.project(*this, tupleLV.getValue(), field);
|
|
return field.FieldInfo.load(*this, fieldLV);
|
|
}
|
|
|
|
// Otherwise, emit the base as an r-value and project.
|
|
RValue tupleRV = emitRValue(tuple, tupleType);
|
|
return tupleType.project(*this, tupleRV, field);
|
|
}
|
|
|
|
LValue IRGenFunction::emitTupleElementLValue(TupleElementExpr *E,
|
|
const TypeInfo &fieldType) {
|
|
// Emit the base l-value.
|
|
Expr *tuple = E->getBase();
|
|
const TupleTypeInfo &tupleType =
|
|
static_cast<const TupleTypeInfo&>(getFragileTypeInfo(tuple->getType()));
|
|
LValue tupleLV = emitLValue(tuple, tupleType);
|
|
|
|
const TupleFieldInfo &field =
|
|
tupleType.getFieldInfos()[E->getFieldNumber()];
|
|
|
|
// If the field requires no storage, there's nothing to do.
|
|
if (field.StorageIndex == TupleFieldInfo::NoStorage) {
|
|
return tupleLV; // as good as anything
|
|
}
|
|
|
|
// Project.
|
|
return tupleType.project(*this, tupleLV, field);
|
|
}
|
|
|
|
RValue IRGenFunction::emitTupleShuffleExpr(TupleShuffleExpr *E,
|
|
const TypeInfo &TI) {
|
|
unimplemented(E->getLoc(), "tuple shuffles");
|
|
return emitFakeRValue(TI);
|
|
}
|