diff options
Diffstat (limited to 'clang/lib/CodeGen/ConstantInitBuilder.cpp')
| -rw-r--r-- | clang/lib/CodeGen/ConstantInitBuilder.cpp | 279 | 
1 files changed, 279 insertions, 0 deletions
| diff --git a/clang/lib/CodeGen/ConstantInitBuilder.cpp b/clang/lib/CodeGen/ConstantInitBuilder.cpp new file mode 100644 index 000000000000..40b1607b5626 --- /dev/null +++ b/clang/lib/CodeGen/ConstantInitBuilder.cpp @@ -0,0 +1,279 @@ +//===--- 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.getQuantity()); +  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) { +  // Compute the address of the relative-address slot. +  auto base = getAddrOfCurrentPosition(offsetType); + +  // 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::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; +} | 
