You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
10 KiB
300 lines
10 KiB
//===--- ConstantInitBuilder.cpp - Global initializer builder -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines out-of-line routines for building initializers for
|
|
// global variables, in particular the kind of globals that are implicitly
|
|
// introduced by various language ABIs.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/CodeGen/ConstantInitBuilder.h"
|
|
#include "CodeGenModule.h"
|
|
|
|
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,
|
|
CharUnits alignment,
|
|
bool constant,
|
|
llvm::GlobalValue::LinkageTypes linkage,
|
|
unsigned addressSpace) {
|
|
auto GV = new llvm::GlobalVariable(CGM.getModule(),
|
|
initializer->getType(),
|
|
constant,
|
|
linkage,
|
|
initializer,
|
|
name,
|
|
/*insert before*/ nullptr,
|
|
llvm::GlobalValue::NotThreadLocal,
|
|
addressSpace);
|
|
GV->setAlignment(alignment.getAsAlign());
|
|
resolveSelfReferences(GV);
|
|
return GV;
|
|
}
|
|
|
|
void ConstantInitBuilderBase::setGlobalInitializer(llvm::GlobalVariable *GV,
|
|
llvm::Constant *initializer){
|
|
GV->setInitializer(initializer);
|
|
|
|
if (!SelfReferences.empty())
|
|
resolveSelfReferences(GV);
|
|
}
|
|
|
|
void ConstantInitBuilderBase::resolveSelfReferences(llvm::GlobalVariable *GV) {
|
|
for (auto &entry : SelfReferences) {
|
|
llvm::Constant *resolvedReference =
|
|
llvm::ConstantExpr::getInBoundsGetElementPtr(
|
|
GV->getValueType(), GV, entry.Indices);
|
|
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();
|
|
}
|
|
}
|
|
|
|
void ConstantAggregateBuilderBase::addSize(CharUnits size) {
|
|
add(Builder.CGM.getSize(size));
|
|
}
|
|
|
|
llvm::Constant *
|
|
ConstantAggregateBuilderBase::getRelativeOffset(llvm::IntegerType *offsetType,
|
|
llvm::Constant *target) {
|
|
return getRelativeOffsetToPosition(offsetType, target,
|
|
Builder.Buffer.size() - Begin);
|
|
}
|
|
|
|
llvm::Constant *ConstantAggregateBuilderBase::getRelativeOffsetToPosition(
|
|
llvm::IntegerType *offsetType, llvm::Constant *target, size_t position) {
|
|
// Compute the address of the relative-address slot.
|
|
auto base = getAddrOfPosition(offsetType, position);
|
|
|
|
// Subtract.
|
|
base = llvm::ConstantExpr::getPtrToInt(base, Builder.CGM.IntPtrTy);
|
|
target = llvm::ConstantExpr::getPtrToInt(target, Builder.CGM.IntPtrTy);
|
|
llvm::Constant *offset = llvm::ConstantExpr::getSub(target, base);
|
|
|
|
// Truncate to the relative-address type if necessary.
|
|
if (Builder.CGM.IntPtrTy != offsetType) {
|
|
offset = llvm::ConstantExpr::getTrunc(offset, offsetType);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
llvm::Constant *
|
|
ConstantAggregateBuilderBase::getAddrOfPosition(llvm::Type *type,
|
|
size_t position) {
|
|
// 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)getGEPIndicesTo(entry.Indices, position + Begin);
|
|
return dummy;
|
|
}
|
|
|
|
llvm::Constant *
|
|
ConstantAggregateBuilderBase::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;
|
|
}
|
|
|
|
void ConstantAggregateBuilderBase::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));
|
|
}
|
|
|
|
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);
|
|
|
|
// Fast path: if the cache is valid, just use it.
|
|
if (cacheEnd == end) {
|
|
return CachedOffsetFromGlobal;
|
|
}
|
|
|
|
// If the cached range ends before the index at which the current
|
|
// aggregate starts, recurse for the parent.
|
|
CharUnits offset;
|
|
if (cacheEnd < Begin) {
|
|
assert(cacheEnd == 0);
|
|
assert(Parent && "Begin != 0 for root builder");
|
|
cacheEnd = Begin;
|
|
offset = Parent->getOffsetFromGlobalTo(Begin);
|
|
} else {
|
|
offset = CachedOffsetFromGlobal;
|
|
}
|
|
|
|
// Perform simple layout on the elements in cacheEnd..<end.
|
|
if (cacheEnd != end) {
|
|
auto &layout = Builder.CGM.getDataLayout();
|
|
do {
|
|
llvm::Constant *element = Builder.Buffer[cacheEnd];
|
|
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));
|
|
} while (++cacheEnd != end);
|
|
}
|
|
|
|
// Cache and return.
|
|
CachedOffsetEnd = cacheEnd;
|
|
CachedOffsetFromGlobal = offset;
|
|
return offset;
|
|
}
|
|
|
|
llvm::Constant *ConstantAggregateBuilderBase::finishArray(llvm::Type *eltTy) {
|
|
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);
|
|
if (!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;
|
|
}
|
|
|
|
llvm::Constant *
|
|
ConstantAggregateBuilderBase::finishStruct(llvm::StructType *ty) {
|
|
markFinished();
|
|
|
|
auto &buffer = getBuffer();
|
|
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);
|
|
}
|
|
|
|
buffer.erase(buffer.begin() + Begin, buffer.end());
|
|
return constant;
|
|
}
|