Further fixes and improvements to the ConstantInitBuilder API.

llvm-svn: 297050
This commit is contained in:
John McCall 2017-03-06 19:04:16 +00:00
parent 692b2f88d3
commit 262f962252
3 changed files with 267 additions and 25 deletions

View File

@ -21,6 +21,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "clang/AST/CharUnits.h"
#include "clang/CodeGen/ConstantInitFuture.h"
#include <vector>
@ -60,18 +61,17 @@ class ConstantInitBuilderBase {
std::vector<SelfReference> SelfReferences;
bool Frozen = false;
friend class ConstantInitFuture;
friend class ConstantAggregateBuilderBase;
template <class, class>
friend class ConstantAggregateBuilderTemplateBase;
// The rule for CachedOffset is that anything which removes elements
// from the Buffer
protected:
explicit ConstantInitBuilderBase(CodeGenModule &CGM) : CGM(CGM) {}
~ConstantInitBuilderBase() {
assert(Buffer.empty() && "didn't claim all values out of buffer");
assert(SelfReferences.empty() && "didn't apply all self-references");
}
private:
@ -83,10 +83,14 @@ private:
= llvm::GlobalValue::InternalLinkage,
unsigned addressSpace = 0);
ConstantInitFuture createFuture(llvm::Constant *initializer);
void setGlobalInitializer(llvm::GlobalVariable *GV,
llvm::Constant *initializer);
void resolveSelfReferences(llvm::GlobalVariable *GV);
void abandon(size_t newEnd);
};
/// A concrete base class for struct and array aggregate
@ -99,6 +103,7 @@ protected:
mutable size_t CachedOffsetEnd = 0;
bool Finished = false;
bool Frozen = false;
bool Packed = false;
mutable CharUnits CachedOffsetFromGlobal;
llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() {
@ -150,17 +155,32 @@ public:
// properly to satisfy the assert in the destructor.
ConstantAggregateBuilderBase(ConstantAggregateBuilderBase &&other)
: Builder(other.Builder), Parent(other.Parent), Begin(other.Begin),
Finished(other.Finished), Frozen(other.Frozen) {
CachedOffsetEnd(other.CachedOffsetEnd),
Finished(other.Finished), Frozen(other.Frozen), Packed(other.Packed),
CachedOffsetFromGlobal(other.CachedOffsetFromGlobal) {
other.Finished = true;
}
ConstantAggregateBuilderBase &operator=(ConstantAggregateBuilderBase &&other)
= delete;
/// Return the number of elements that have been added to
/// this struct or array.
size_t size() const {
assert(!this->Finished && "cannot query after finishing builder");
assert(!this->Frozen && "cannot query while sub-builder is active");
assert(this->Begin <= this->getBuffer().size());
return this->getBuffer().size() - this->Begin;
}
/// Return true if no elements have yet been added to this struct or array.
bool empty() const {
return size() == 0;
}
/// Abandon this builder completely.
void abandon() {
markFinished();
auto &buffer = Builder.Buffer;
buffer.erase(buffer.begin() + Begin, buffer.end());
Builder.abandon(Begin);
}
/// Add a new value to this initializer.
@ -224,6 +244,9 @@ public:
/// Return the offset from the start of the initializer to the
/// next position, assuming no padding is required prior to it.
///
/// This operation will not succeed if any unsized placeholders are
/// currently in place in the initializer.
CharUnits getNextOffsetFromGlobal() const {
assert(!Finished && "cannot add more values after finishing builder");
assert(!Frozen && "cannot add values while subbuilder is active");
@ -253,6 +276,9 @@ public:
return Builder.Buffer.size() - 1;
}
/// Add a placeholder, giving the expected type that will be filled in.
PlaceholderPosition addPlaceholderWithSize(llvm::Type *expectedType);
/// Fill a previously-added placeholder.
void fillPlaceholderWithInt(PlaceholderPosition position,
llvm::IntegerType *type, uint64_t value,
@ -354,6 +380,19 @@ public:
assert(!this->Parent && "finishing non-root builder");
return this->Builder.setGlobalInitializer(global, asImpl().finishImpl());
}
/// Given that this builder was created by beginning an array or struct
/// directly on a ConstantInitBuilder, finish the array/struct and
/// return a future which can be used to install the initializer in
/// a global later.
///
/// This is useful for allowing a finished initializer to passed to
/// an API which will build the global. However, the "future" preserves
/// a dependency on the original builder; it is an error to pass it aside.
ConstantInitFuture finishAndCreateFuture() {
assert(!this->Parent && "finishing non-root builder");
return this->Builder.createFuture(asImpl().finishImpl());
}
};
template <class Traits>
@ -379,18 +418,6 @@ protected:
llvm::Type *eltTy)
: super(builder, parent), EltTy(eltTy) {}
public:
size_t size() const {
assert(!this->Finished && "cannot query after finishing builder");
assert(!this->Frozen && "cannot query while sub-builder is active");
assert(this->Begin <= this->getBuffer().size());
return this->getBuffer().size() - this->Begin;
}
bool empty() const {
return size() == 0;
}
private:
/// Form an array constant from the values that have been added to this
/// builder.
@ -425,7 +452,22 @@ protected:
ConstantStructBuilderTemplateBase(InitBuilder &builder,
AggregateBuilderBase *parent,
llvm::StructType *structTy)
: super(builder, parent), StructTy(structTy) {}
: super(builder, parent), StructTy(structTy) {
if (structTy) this->Packed = structTy->isPacked();
}
public:
void setPacked(bool packed) {
this->Packed = packed;
}
/// Use the given type for the struct if its element count is correct.
/// Don't add more elements after calling this.
void suggestType(llvm::StructType *structTy) {
if (this->size() == structTy->getNumElements()) {
StructTy = structTy;
}
}
private:
/// Form an array constant from the values that have been added to this

View File

@ -0,0 +1,111 @@
//===- ConstantInitFuture.h - "Future" constant initializers ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class defines the ConstantInitFuture class. This is split out
// from ConstantInitBuilder.h in order to allow APIs to work with it
// without having to include that entire header. This is particularly
// important because it is often useful to be able to default-construct
// a future in, say, a default argument.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_CODEGEN_CONSTANTINITFUTURE_H
#define LLVM_CLANG_CODEGEN_CONSTANTINITFUTURE_H
#include "llvm/ADT/PointerUnion.h"
#include "llvm/IR/Constant.h"
// Forward-declare ConstantInitBuilderBase and give it a
// PointerLikeTypeTraits specialization so that we can safely use it
// in a PointerUnion below.
namespace clang {
namespace CodeGen {
class ConstantInitBuilderBase;
}
}
namespace llvm {
template <>
class PointerLikeTypeTraits< ::clang::CodeGen::ConstantInitBuilderBase*> {
public:
using T = ::clang::CodeGen::ConstantInitBuilderBase*;
static inline void *getAsVoidPointer(T p) { return p; }
static inline T getFromVoidPointer(void *p) {return static_cast<T>(p);}
enum { NumLowBitsAvailable = 2 };
};
}
namespace clang {
namespace CodeGen {
/// A "future" for a completed constant initializer, which can be passed
/// around independently of any sub-builders (but not the original parent).
class ConstantInitFuture {
using PairTy = llvm::PointerUnion<ConstantInitBuilderBase*, llvm::Constant*>;
PairTy Data;
friend class ConstantInitBuilderBase;
explicit ConstantInitFuture(ConstantInitBuilderBase *builder);
public:
ConstantInitFuture() {}
/// A future can be explicitly created from a fixed initializer.
explicit ConstantInitFuture(llvm::Constant *initializer) : Data(initializer) {
assert(initializer && "creating null future");
}
/// Is this future non-null?
explicit operator bool() const { return bool(Data); }
/// Return the type of the initializer.
llvm::Type *getType() const;
/// Abandon this initializer.
void abandon();
/// Install the initializer into a global variable. This cannot
/// be called multiple times.
void installInGlobal(llvm::GlobalVariable *global);
void *getOpaqueValue() const { return Data.getOpaqueValue(); }
static ConstantInitFuture getFromOpaqueValue(void *value) {
ConstantInitFuture result;
result.Data = PairTy::getFromOpaqueValue(value);
return result;
}
enum {
NumLowBitsAvailable =
llvm::PointerLikeTypeTraits<PairTy>::NumLowBitsAvailable
};
};
} // end namespace CodeGen
} // end namespace clang
namespace llvm {
template <>
class PointerLikeTypeTraits< ::clang::CodeGen::ConstantInitFuture> {
public:
using T = ::clang::CodeGen::ConstantInitFuture;
static inline void *getAsVoidPointer(T future) {
return future.getOpaqueValue();
}
static inline T getFromVoidPointer(void *p) {
return T::getFromOpaqueValue(p);
}
enum { NumLowBitsAvailable = T::NumLowBitsAvailable };
};
} // end namespace llvm
#endif

View File

@ -19,6 +19,51 @@
using namespace clang;
using namespace CodeGen;
llvm::Type *ConstantInitFuture::getType() const {
assert(Data && "dereferencing null future");
if (Data.is<llvm::Constant*>()) {
return Data.get<llvm::Constant*>()->getType();
} else {
return Data.get<ConstantInitBuilderBase*>()->Buffer[0]->getType();
}
}
void ConstantInitFuture::abandon() {
assert(Data && "abandoning null future");
if (auto builder = Data.dyn_cast<ConstantInitBuilderBase*>()) {
builder->abandon(0);
}
Data = nullptr;
}
void ConstantInitFuture::installInGlobal(llvm::GlobalVariable *GV) {
assert(Data && "installing null future");
if (Data.is<llvm::Constant*>()) {
GV->setInitializer(Data.get<llvm::Constant*>());
} else {
auto &builder = *Data.get<ConstantInitBuilderBase*>();
assert(builder.Buffer.size() == 1);
builder.setGlobalInitializer(GV, builder.Buffer[0]);
builder.Buffer.clear();
Data = nullptr;
}
}
ConstantInitFuture
ConstantInitBuilderBase::createFuture(llvm::Constant *initializer) {
assert(Buffer.empty() && "buffer not current empty");
Buffer.push_back(initializer);
return ConstantInitFuture(this);
}
// Only used in this file.
inline ConstantInitFuture::ConstantInitFuture(ConstantInitBuilderBase *builder)
: Data(builder) {
assert(!builder->Frozen);
assert(builder->Buffer.size() == 1);
assert(builder->Buffer[0] != nullptr);
}
llvm::GlobalVariable *
ConstantInitBuilderBase::createGlobal(llvm::Constant *initializer,
const llvm::Twine &name,
@ -53,8 +98,27 @@ void ConstantInitBuilderBase::resolveSelfReferences(llvm::GlobalVariable *GV) {
llvm::Constant *resolvedReference =
llvm::ConstantExpr::getInBoundsGetElementPtr(
GV->getValueType(), GV, entry.Indices);
entry.Dummy->replaceAllUsesWith(resolvedReference);
entry.Dummy->eraseFromParent();
auto dummy = entry.Dummy;
dummy->replaceAllUsesWith(resolvedReference);
dummy->eraseFromParent();
}
SelfReferences.clear();
}
void ConstantInitBuilderBase::abandon(size_t newEnd) {
// Remove all the entries we've added.
Buffer.erase(Buffer.begin() + newEnd, Buffer.end());
// If we're abandoning all the way to the beginning, destroy
// all the self-references, because we might not get another
// opportunity.
if (newEnd == 0) {
for (auto &entry : SelfReferences) {
auto dummy = entry.Dummy;
dummy->replaceAllUsesWith(llvm::UndefValue::get(dummy->getType()));
dummy->eraseFromParent();
}
SelfReferences.clear();
}
}
@ -115,6 +179,27 @@ void ConstantAggregateBuilderBase::getGEPIndicesTo(
position - Begin));
}
ConstantAggregateBuilderBase::PlaceholderPosition
ConstantAggregateBuilderBase::addPlaceholderWithSize(llvm::Type *type) {
// Bring the offset up to the last field.
CharUnits offset = getNextOffsetFromGlobal();
// Create the placeholder.
auto position = addPlaceholder();
// Advance the offset past that field.
auto &layout = Builder.CGM.getDataLayout();
if (!Packed)
offset = offset.alignTo(CharUnits::fromQuantity(
layout.getABITypeAlignment(type)));
offset += CharUnits::fromQuantity(layout.getTypeStoreSize(type));
CachedOffsetEnd = Builder.Buffer.size();
CachedOffsetFromGlobal = offset;
return position;
}
CharUnits ConstantAggregateBuilderBase::getOffsetFromGlobalTo(size_t end) const{
size_t cacheEnd = CachedOffsetEnd;
assert(cacheEnd <= end);
@ -144,6 +229,7 @@ CharUnits ConstantAggregateBuilderBase::getOffsetFromGlobalTo(size_t end) const{
assert(element != nullptr &&
"cannot compute offset when a placeholder is present");
llvm::Type *elementType = element->getType();
if (!Packed)
offset = offset.alignTo(CharUnits::fromQuantity(
layout.getABITypeAlignment(elementType)));
offset += CharUnits::fromQuantity(layout.getTypeStoreSize(elementType));
@ -176,14 +262,17 @@ ConstantAggregateBuilderBase::finishStruct(llvm::StructType *ty) {
markFinished();
auto &buffer = getBuffer();
assert(Begin < buffer.size() && "didn't add any struct elements?");
auto elts = llvm::makeArrayRef(buffer).slice(Begin);
if (ty == nullptr && elts.empty())
ty = llvm::StructType::get(Builder.CGM.getLLVMContext(), {}, Packed);
llvm::Constant *constant;
if (ty) {
assert(ty->isPacked() == Packed);
constant = llvm::ConstantStruct::get(ty, elts);
} else {
constant = llvm::ConstantStruct::getAnon(elts, /*packed*/ false);
constant = llvm::ConstantStruct::getAnon(elts, Packed);
}
buffer.erase(buffer.begin() + Begin, buffer.end());