llvm-project/clang/lib/CodeGen/ConstantBuilder.h

445 lines
16 KiB
C++

//===----- ConstantBuilder.h - Builder for LLVM IR constants ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class provides a convenient interface for building complex
// global initializers.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_CODEGEN_CONSTANTBUILDER_H
#define LLVM_CLANG_LIB_CODEGEN_CONSTANTBUILDER_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Constants.h"
#include "CodeGenModule.h"
#include <vector>
namespace clang {
namespace CodeGen {
class ConstantStructBuilder;
class ConstantArrayBuilder;
/// A convenience builder class for complex constant initializers,
/// especially for anonymous global structures used by various language
/// runtimes.
///
/// The basic usage pattern is expected to be something like:
/// ConstantInitBuilder builder(CGM);
/// auto toplevel = builder.beginStruct();
/// toplevel.addInt(CGM.SizeTy, widgets.size());
/// auto widgetArray = builder.beginArray();
/// for (auto &widget : widgets) {
/// auto widgetDesc = widgetArray.beginStruct();
/// widgetDesc.addInt(CGM.SizeTy, widget.getPower());
/// widgetDesc.add(CGM.GetAddrOfConstantString(widget.getName()));
/// widgetDesc.add(CGM.GetAddrOfGlobal(widget.getInitializerDecl()));
/// widgetArray.add(widgetDesc.finish());
/// }
/// toplevel.add(widgetArray.finish());
/// auto global = toplevel.finishAndCreateGlobal("WIDGET_LIST", Align,
/// /*constant*/ true);
class ConstantInitBuilder {
struct SelfReference {
llvm::GlobalVariable *Dummy;
llvm::SmallVector<llvm::Constant*, 4> Indices;
SelfReference(llvm::GlobalVariable *dummy) : Dummy(dummy) {}
};
CodeGenModule &CGM;
llvm::SmallVector<llvm::Constant*, 16> Buffer;
std::vector<SelfReference> SelfReferences;
bool Frozen = false;
public:
explicit ConstantInitBuilder(CodeGenModule &CGM) : CGM(CGM) {}
~ConstantInitBuilder() {
assert(Buffer.empty() && "didn't claim all values out of buffer");
}
class AggregateBuilderBase {
protected:
ConstantInitBuilder &Builder;
AggregateBuilderBase *Parent;
size_t Begin;
bool Finished = false;
bool Frozen = false;
llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() {
return Builder.Buffer;
}
const llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() const {
return Builder.Buffer;
}
AggregateBuilderBase(ConstantInitBuilder &builder,
AggregateBuilderBase *parent)
: Builder(builder), Parent(parent), Begin(builder.Buffer.size()) {
if (parent) {
assert(!parent->Frozen && "parent already has child builder active");
parent->Frozen = true;
} else {
assert(!builder.Frozen && "builder already has child builder active");
builder.Frozen = true;
}
}
~AggregateBuilderBase() {
assert(Finished && "didn't finish aggregate builder");
}
void markFinished() {
assert(!Frozen && "child builder still active");
assert(!Finished && "builder already finished");
Finished = true;
if (Parent) {
assert(Parent->Frozen &&
"parent not frozen while child builder active");
Parent->Frozen = false;
} else {
assert(Builder.Frozen &&
"builder not frozen while child builder active");
Builder.Frozen = false;
}
}
public:
// Not copyable.
AggregateBuilderBase(const AggregateBuilderBase &) = delete;
AggregateBuilderBase &operator=(const AggregateBuilderBase &) = delete;
// Movable, mostly to allow returning. But we have to write this out
// properly to satisfy the assert in the destructor.
AggregateBuilderBase(AggregateBuilderBase &&other)
: Builder(other.Builder), Parent(other.Parent), Begin(other.Begin),
Finished(other.Finished), Frozen(other.Frozen) {
other.Finished = false;
}
AggregateBuilderBase &operator=(AggregateBuilderBase &&other) = delete;
/// Abandon this builder completely.
void abandon() {
markFinished();
auto &buffer = Builder.Buffer;
buffer.erase(buffer.begin() + Begin, buffer.end());
}
/// Add a new value to this initializer.
void add(llvm::Constant *value) {
assert(value && "adding null value to constant initializer");
assert(!Finished && "cannot add more values after finishing builder");
assert(!Frozen && "cannot add values while subbuilder is active");
Builder.Buffer.push_back(value);
}
/// Add an integer value of type size_t.
void addSize(CharUnits size) {
add(Builder.CGM.getSize(size));
}
/// Add an integer value of a specific type.
void addInt(llvm::IntegerType *intTy, uint64_t value,
bool isSigned = false) {
add(llvm::ConstantInt::get(intTy, value, isSigned));
}
/// Add a null pointer of a specific type.
void addNullPointer(llvm::PointerType *ptrTy) {
add(llvm::ConstantPointerNull::get(ptrTy));
}
/// Add a bitcast of a value to a specific type.
void addBitCast(llvm::Constant *value, llvm::Type *type) {
add(llvm::ConstantExpr::getBitCast(value, type));
}
/// Add a bunch of new values to this initializer.
void addAll(ArrayRef<llvm::Constant *> values) {
assert(!Finished && "cannot add more values after finishing builder");
assert(!Frozen && "cannot add values while subbuilder is active");
Builder.Buffer.append(values.begin(), values.end());
}
/// An opaque class to hold the abstract position of a placeholder.
class PlaceholderPosition {
size_t Index;
friend class AggregateBuilderBase;
PlaceholderPosition(size_t index) : Index(index) {}
};
/// Add a placeholder value to the structure. The returned position
/// can be used to set the value later; it will not be invalidated by
/// any intermediate operations except (1) filling the same position or
/// (2) finishing the entire builder.
///
/// This is useful for emitting certain kinds of structure which
/// contain some sort of summary field, generaly a count, before any
/// of the data. By emitting a placeholder first, the structure can
/// be emitted eagerly.
PlaceholderPosition addPlaceholder() {
assert(!Finished && "cannot add more values after finishing builder");
assert(!Frozen && "cannot add values while subbuilder is active");
Builder.Buffer.push_back(nullptr);
return Builder.Buffer.size() - 1;
}
/// Fill a previously-added placeholder.
void fillPlaceholderWithInt(PlaceholderPosition position,
llvm::IntegerType *type, uint64_t value,
bool isSigned = false) {
fillPlaceholder(position, llvm::ConstantInt::get(type, value, isSigned));
}
/// Fill a previously-added placeholder.
void fillPlaceholder(PlaceholderPosition position, llvm::Constant *value) {
assert(!Finished && "cannot change values after finishing builder");
assert(!Frozen && "cannot add values while subbuilder is active");
llvm::Constant *&slot = Builder.Buffer[position.Index];
assert(slot == nullptr && "placeholder already filled");
slot = value;
}
/// Produce an address which will eventually point to the the next
/// position to be filled. This is computed with an indexed
/// getelementptr rather than by computing offsets.
///
/// The returned pointer will have type T*, where T is the given
/// position.
llvm::Constant *getAddrOfCurrentPosition(llvm::Type *type) {
// Make a global variable. We will replace this with a GEP to this
// position after installing the initializer.
auto dummy =
new llvm::GlobalVariable(Builder.CGM.getModule(), type, true,
llvm::GlobalVariable::PrivateLinkage,
nullptr, "");
Builder.SelfReferences.emplace_back(dummy);
auto &entry = Builder.SelfReferences.back();
(void) getGEPIndicesToCurrentPosition(entry.Indices);
return dummy;
}
ArrayRef<llvm::Constant*> getGEPIndicesToCurrentPosition(
llvm::SmallVectorImpl<llvm::Constant*> &indices) {
getGEPIndicesTo(indices, Builder.Buffer.size());
return indices;
}
ConstantArrayBuilder beginArray(llvm::Type *eltTy = nullptr);
ConstantStructBuilder beginStruct(llvm::StructType *structTy = nullptr);
private:
void getGEPIndicesTo(llvm::SmallVectorImpl<llvm::Constant*> &indices,
size_t position) const {
// Recurse on the parent builder if present.
if (Parent) {
Parent->getGEPIndicesTo(indices, Begin);
// Otherwise, add an index to drill into the first level of pointer.
} else {
assert(indices.empty());
indices.push_back(llvm::ConstantInt::get(Builder.CGM.Int32Ty, 0));
}
assert(position >= Begin);
// We have to use i32 here because struct GEPs demand i32 indices.
// It's rather unlikely to matter in practice.
indices.push_back(llvm::ConstantInt::get(Builder.CGM.Int32Ty,
position - Begin));
}
};
template <class Impl>
class AggregateBuilder : public AggregateBuilderBase {
protected:
AggregateBuilder(ConstantInitBuilder &builder,
AggregateBuilderBase *parent)
: AggregateBuilderBase(builder, parent) {}
Impl &asImpl() { return *static_cast<Impl*>(this); }
public:
/// Given that this builder was created by beginning an array or struct
/// component on the given parent builder, finish the array/struct
/// component and add it to the parent.
///
/// It is an intentional choice that the parent is passed in explicitly
/// despite it being redundant with information already kept in the
/// builder. This aids in readability by making it easier to find the
/// places that add components to a builder, as well as "bookending"
/// the sub-builder more explicitly.
void finishAndAddTo(AggregateBuilderBase &parent) {
assert(Parent == &parent && "adding to non-parent builder");
parent.add(asImpl().finishImpl());
}
/// Given that this builder was created by beginning an array or struct
/// directly on a ConstantInitBuilder, finish the array/struct and
/// create a global variable with it as the initializer.
template <class... As>
llvm::GlobalVariable *finishAndCreateGlobal(As &&...args) {
assert(!Parent && "finishing non-root builder");
return Builder.createGlobal(asImpl().finishImpl(),
std::forward<As>(args)...);
}
/// Given that this builder was created by beginning an array or struct
/// directly on a ConstantInitBuilder, finish the array/struct and
/// set it as the initializer of the given global variable.
void finishAndSetAsInitializer(llvm::GlobalVariable *global) {
assert(!Parent && "finishing non-root builder");
return Builder.setGlobalInitializer(global, asImpl().finishImpl());
}
};
ConstantArrayBuilder beginArray(llvm::Type *eltTy = nullptr);
ConstantStructBuilder beginStruct(llvm::StructType *structTy = nullptr);
private:
llvm::GlobalVariable *createGlobal(llvm::Constant *initializer,
const llvm::Twine &name,
CharUnits alignment,
bool constant = false,
llvm::GlobalValue::LinkageTypes linkage
= llvm::GlobalValue::InternalLinkage,
unsigned addressSpace = 0) {
auto GV = new llvm::GlobalVariable(CGM.getModule(),
initializer->getType(),
constant,
linkage,
initializer,
name,
/*insert before*/ nullptr,
llvm::GlobalValue::NotThreadLocal,
addressSpace);
GV->setAlignment(alignment.getQuantity());
resolveSelfReferences(GV);
return GV;
}
void setGlobalInitializer(llvm::GlobalVariable *GV,
llvm::Constant *initializer) {
GV->setInitializer(initializer);
resolveSelfReferences(GV);
}
void resolveSelfReferences(llvm::GlobalVariable *GV) {
for (auto &entry : SelfReferences) {
llvm::Constant *resolvedReference =
llvm::ConstantExpr::getInBoundsGetElementPtr(
GV->getValueType(), GV, entry.Indices);
entry.Dummy->replaceAllUsesWith(resolvedReference);
entry.Dummy->eraseFromParent();
}
}
};
/// A helper class of ConstantInitBuilder, used for building constant
/// array initializers.
class ConstantArrayBuilder
: public ConstantInitBuilder::AggregateBuilder<ConstantArrayBuilder> {
llvm::Type *EltTy;
friend class ConstantInitBuilder;
template <class Impl> friend class ConstantInitBuilder::AggregateBuilder;
ConstantArrayBuilder(ConstantInitBuilder &builder,
AggregateBuilderBase *parent, llvm::Type *eltTy)
: AggregateBuilder(builder, parent), EltTy(eltTy) {}
public:
size_t size() const {
assert(!Finished);
assert(!Frozen);
assert(Begin <= getBuffer().size());
return getBuffer().size() - Begin;
}
bool empty() const {
return size() == 0;
}
private:
/// Form an array constant from the values that have been added to this
/// builder.
llvm::Constant *finishImpl() {
markFinished();
auto &buffer = getBuffer();
assert((Begin < buffer.size() ||
(Begin == buffer.size() && EltTy))
&& "didn't add any array elements without element type");
auto elts = llvm::makeArrayRef(buffer).slice(Begin);
auto eltTy = EltTy ? EltTy : elts[0]->getType();
auto type = llvm::ArrayType::get(eltTy, elts.size());
auto constant = llvm::ConstantArray::get(type, elts);
buffer.erase(buffer.begin() + Begin, buffer.end());
return constant;
}
};
inline ConstantArrayBuilder
ConstantInitBuilder::beginArray(llvm::Type *eltTy) {
return ConstantArrayBuilder(*this, nullptr, eltTy);
}
inline ConstantArrayBuilder
ConstantInitBuilder::AggregateBuilderBase::beginArray(llvm::Type *eltTy) {
return ConstantArrayBuilder(Builder, this, eltTy);
}
/// A helper class of ConstantInitBuilder, used for building constant
/// struct initializers.
class ConstantStructBuilder
: public ConstantInitBuilder::AggregateBuilder<ConstantStructBuilder> {
llvm::StructType *Ty;
friend class ConstantInitBuilder;
template <class Impl> friend class ConstantInitBuilder::AggregateBuilder;
ConstantStructBuilder(ConstantInitBuilder &builder,
AggregateBuilderBase *parent, llvm::StructType *ty)
: AggregateBuilder(builder, parent), Ty(ty) {}
/// Finish the struct.
llvm::Constant *finishImpl() {
markFinished();
auto &buffer = getBuffer();
assert(Begin < buffer.size() && "didn't add any struct elements?");
auto elts = llvm::makeArrayRef(buffer).slice(Begin);
llvm::Constant *constant;
if (Ty) {
constant = llvm::ConstantStruct::get(Ty, elts);
} else {
constant = llvm::ConstantStruct::getAnon(elts, /*packed*/ false);
}
buffer.erase(buffer.begin() + Begin, buffer.end());
return constant;
}
};
inline ConstantStructBuilder
ConstantInitBuilder::beginStruct(llvm::StructType *structTy) {
return ConstantStructBuilder(*this, nullptr, structTy);
}
inline ConstantStructBuilder
ConstantInitBuilder::AggregateBuilderBase::beginStruct(
llvm::StructType *structTy) {
return ConstantStructBuilder(Builder, this, structTy);
}
} // end namespace CodeGen
} // end namespace clang
#endif