blob: 2fbdb1bc2ce5dc0249ed6cc3b2491c29992042c2 [file] [log] [blame]
//===--- ConstantBuilder.h - IR generation for constant structs -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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 of constant packed LLVM structs.
//===----------------------------------------------------------------------===//
#include "swift/AST/Mangle.h"
#include "swift/ABI/MetadataValues.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "Address.h"
#include "IRGenModule.h"
#include "IRGenFunction.h"
namespace swift {
namespace irgen {
class ConstantBuilderBase {
protected:
IRGenModule &IGM;
ConstantBuilderBase(IRGenModule &IGM) : IGM(IGM) {}
};
template <class Base = ConstantBuilderBase>
class ConstantBuilder : public Base {
protected:
template <class... T>
ConstantBuilder(T &&...args) : Base(std::forward<T>(args)...) {}
IRGenModule &IGM = Base::IGM;
private:
llvm::GlobalVariable *relativeAddressBase = nullptr;
llvm::SmallVector<llvm::Constant*, 16> Fields;
Size NextOffset = Size(0);
protected:
Size getNextOffset() const { return NextOffset; }
void addStruct(llvm::Constant *st) {
assert(st->getType()->isStructTy());
Fields.push_back(st);
NextOffset += Size(IGM.DataLayout.getTypeStoreSize(st->getType()));
}
/// Add a constant word-sized value.
void addConstantWord(int64_t value) {
addWord(llvm::ConstantInt::get(IGM.SizeTy, value));
}
/// Add a word-sized value.
void addWord(llvm::Constant *value) {
assert(value->getType() == IGM.IntPtrTy ||
value->getType()->isPointerTy());
Fields.push_back(value);
NextOffset += IGM.getPointerSize();
}
void setRelativeAddressBase(llvm::GlobalVariable *base) {
relativeAddressBase = base;
}
llvm::Constant *getRelativeAddressFromNextField(ConstantReference referent,
llvm::IntegerType *addressTy) {
assert(relativeAddressBase && "no relative address base set");
// Determine the address of the next field in the initializer.
llvm::Constant *fieldAddr =
llvm::ConstantExpr::getPtrToInt(relativeAddressBase, IGM.IntPtrTy);
fieldAddr = llvm::ConstantExpr::getAdd(fieldAddr,
llvm::ConstantInt::get(IGM.SizeTy,
getNextOffset().getValue()));
llvm::Constant *referentValue =
llvm::ConstantExpr::getPtrToInt(referent.getValue(), IGM.IntPtrTy);
llvm::Constant *relative
= llvm::ConstantExpr::getSub(referentValue, fieldAddr);
if (relative->getType() != addressTy)
relative = llvm::ConstantExpr::getTrunc(relative, addressTy);
if (referent.isIndirect()) {
relative = llvm::ConstantExpr::getAdd(relative,
llvm::ConstantInt::get(addressTy, 1));
}
return relative;
}
/// Add a 32-bit relative address from the current location in the local
/// being built to another global variable.
void addRelativeAddress(llvm::Constant *referent) {
addRelativeAddress({referent, ConstantReference::Direct});
}
void addRelativeAddress(ConstantReference referent) {
addInt32(getRelativeAddressFromNextField(referent, IGM.RelativeAddressTy));
}
/// Add a pointer-sized relative address from the current location in the
/// local being built to another global variable.
void addFarRelativeAddress(llvm::Constant *referent) {
addFarRelativeAddress({referent, ConstantReference::Direct});
}
void addFarRelativeAddress(ConstantReference referent) {
addWord(getRelativeAddressFromNextField(referent,
IGM.FarRelativeAddressTy));
}
/// Add a 32-bit relative address from the current location in the local
/// being built to another global variable, or null if a null referent
/// is passed.
void addRelativeAddressOrNull(llvm::Constant *referent) {
addRelativeAddressOrNull({referent, ConstantReference::Direct});
}
void addRelativeAddressOrNull(ConstantReference referent) {
if (referent)
addRelativeAddress(referent);
else
addConstantInt32(0);
}
/// Add a pointer-sized relative address from the current location in the
/// local being built to another global variable, or null if a null referent
/// is passed.
void addFarRelativeAddressOrNull(llvm::Constant *referent) {
addFarRelativeAddressOrNull({referent, ConstantReference::Direct});
}
void addFarRelativeAddressOrNull(ConstantReference referent) {
if (referent)
addFarRelativeAddress(referent);
else
addConstantWord(0);
}
/// Add a 32-bit relative address from the current location in the local
/// being built to another global variable. Pack a constant integer into
/// the alignment bits of the pointer.
void addRelativeAddressWithTag(llvm::Constant *referent,
unsigned tag) {
assert(tag < 4 && "tag too big to pack in relative address");
llvm::Constant *relativeAddr =
getRelativeAddressFromNextField({referent, ConstantReference::Direct},
IGM.RelativeAddressTy);
relativeAddr = llvm::ConstantExpr::getAdd(relativeAddr,
llvm::ConstantInt::get(IGM.RelativeAddressTy, tag));
addInt32(relativeAddr);
}
/// Add a pointer-size relative address from the current location in the
/// local being built to another global variable. Pack a constant integer
/// into the alignment bits of the pointer.
void addFarRelativeAddressWithTag(llvm::Constant *referent,
unsigned tag) {
// FIXME: could be 8 when targeting 64-bit platforms
assert(tag < 4 && "tag too big to pack in relative address");
llvm::Constant *relativeAddr =
getRelativeAddressFromNextField(referent, IGM.FarRelativeAddressTy);
relativeAddr = llvm::ConstantExpr::getAdd(relativeAddr,
llvm::ConstantInt::get(IGM.FarRelativeAddressTy, tag));
addWord(relativeAddr);
}
/// Add a uint32_t value that represents the given offset
/// scaled to a number of words.
void addConstantInt32InWords(Size value) {
addConstantInt32(IGM.getOffsetInWords(value));
}
/// Add a constant 32-bit value.
void addConstantInt32(int32_t value) {
addInt32(llvm::ConstantInt::get(IGM.Int32Ty, value));
}
/// Add a 32-bit value.
void addInt32(llvm::Constant *value) {
assert(value->getType() == IGM.Int32Ty);
Fields.push_back(value);
NextOffset += Size(4);
}
/// Add a constant 16-bit value.
void addConstantInt16(int16_t value) {
addInt16(llvm::ConstantInt::get(IGM.Int16Ty, value));
}
/// Add a 16-bit value.
void addInt16(llvm::Constant *value) {
assert(value->getType() == IGM.Int16Ty);
Fields.push_back(value);
NextOffset += Size(2);
}
/// Add a constant 8-bit value.
void addConstantInt8(int8_t value) {
addInt8(llvm::ConstantInt::get(IGM.Int8Ty, value));
}
/// Add an 8-bit value.
void addInt8(llvm::Constant *value) {
assert(value->getType() == IGM.Int8Ty);
Fields.push_back(value);
NextOffset += Size(1);
}
/// Add a constant of the given size.
void addStruct(llvm::Constant *value, Size size) {
assert(size.getValue()
== IGM.DataLayout.getTypeStoreSize(value->getType()));
Fields.push_back(value);
NextOffset += size;
}
class ReservationToken {
size_t Index;
ReservationToken(size_t index) : Index(index) {}
friend ConstantBuilder<Base>;
};
ReservationToken reserveFields(unsigned numFields, Size size) {
unsigned index = Fields.size();
Fields.append(numFields, nullptr);
NextOffset += size;
return ReservationToken(index);
}
MutableArrayRef<llvm::Constant*> claimReservation(ReservationToken token,
unsigned numFields) {
return MutableArrayRef<llvm::Constant*>(&Fields[0] + token.Index,
numFields);
}
public:
llvm::Constant *getInit() const {
if (Fields.empty())
return nullptr;
return llvm::ConstantStruct::getAnon(Fields, /*packed*/ true);
}
/// An optimization of getInit for when we have a known type we
/// can use when there aren't any extra fields.
llvm::Constant *getInitWithSuggestedType(unsigned numFields,
llvm::StructType *type) {
if (Fields.size() == numFields) {
return llvm::ConstantStruct::get(type, Fields);
} else {
return getInit();
}
}
};
} // end namespace irgen
} // end namespace swift