1 //===- ConstantInitBuilder.h - Builder for LLVM IR constants ----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This class provides a convenient interface for building complex 10 // global initializers of the sort that are frequently required for 11 // language ABIs. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_CODEGEN_CONSTANTINITBUILDER_H 16 #define LLVM_CLANG_CODEGEN_CONSTANTINITBUILDER_H 17 18 #include "llvm/ADT/ArrayRef.h" 19 #include "llvm/ADT/SmallVector.h" 20 #include "llvm/IR/Constants.h" 21 #include "llvm/IR/GlobalValue.h" 22 #include "clang/AST/CharUnits.h" 23 #include "clang/CodeGen/ConstantInitFuture.h" 24 25 #include <vector> 26 27 namespace clang { 28 namespace CodeGen { 29 30 class CodeGenModule; 31 32 /// A convenience builder class for complex constant initializers, 33 /// especially for anonymous global structures used by various language 34 /// runtimes. 35 /// 36 /// The basic usage pattern is expected to be something like: 37 /// ConstantInitBuilder builder(CGM); 38 /// auto toplevel = builder.beginStruct(); 39 /// toplevel.addInt(CGM.SizeTy, widgets.size()); 40 /// auto widgetArray = builder.beginArray(); 41 /// for (auto &widget : widgets) { 42 /// auto widgetDesc = widgetArray.beginStruct(); 43 /// widgetDesc.addInt(CGM.SizeTy, widget.getPower()); 44 /// widgetDesc.add(CGM.GetAddrOfConstantString(widget.getName())); 45 /// widgetDesc.add(CGM.GetAddrOfGlobal(widget.getInitializerDecl())); 46 /// widgetDesc.finishAndAddTo(widgetArray); 47 /// } 48 /// widgetArray.finishAndAddTo(toplevel); 49 /// auto global = toplevel.finishAndCreateGlobal("WIDGET_LIST", Align, 50 /// /*constant*/ true); 51 class ConstantInitBuilderBase { 52 struct SelfReference { 53 llvm::GlobalVariable *Dummy; 54 llvm::SmallVector<llvm::Constant*, 4> Indices; 55 SelfReferenceSelfReference56 SelfReference(llvm::GlobalVariable *dummy) : Dummy(dummy) {} 57 }; 58 CodeGenModule &CGM; 59 llvm::SmallVector<llvm::Constant*, 16> Buffer; 60 std::vector<SelfReference> SelfReferences; 61 bool Frozen = false; 62 63 friend class ConstantInitFuture; 64 friend class ConstantAggregateBuilderBase; 65 template <class, class> 66 friend class ConstantAggregateBuilderTemplateBase; 67 68 protected: ConstantInitBuilderBase(CodeGenModule & CGM)69 explicit ConstantInitBuilderBase(CodeGenModule &CGM) : CGM(CGM) {} 70 ~ConstantInitBuilderBase()71 ~ConstantInitBuilderBase() { 72 assert(Buffer.empty() && "didn't claim all values out of buffer"); 73 assert(SelfReferences.empty() && "didn't apply all self-references"); 74 } 75 76 private: 77 llvm::GlobalVariable *createGlobal(llvm::Constant *initializer, 78 const llvm::Twine &name, 79 CharUnits alignment, 80 bool constant = false, 81 llvm::GlobalValue::LinkageTypes linkage 82 = llvm::GlobalValue::InternalLinkage, 83 unsigned addressSpace = 0); 84 85 ConstantInitFuture createFuture(llvm::Constant *initializer); 86 87 void setGlobalInitializer(llvm::GlobalVariable *GV, 88 llvm::Constant *initializer); 89 90 void resolveSelfReferences(llvm::GlobalVariable *GV); 91 92 void abandon(size_t newEnd); 93 }; 94 95 /// A concrete base class for struct and array aggregate 96 /// initializer builders. 97 class ConstantAggregateBuilderBase { 98 protected: 99 ConstantInitBuilderBase &Builder; 100 ConstantAggregateBuilderBase *Parent; 101 size_t Begin; 102 mutable size_t CachedOffsetEnd = 0; 103 bool Finished = false; 104 bool Frozen = false; 105 bool Packed = false; 106 mutable CharUnits CachedOffsetFromGlobal; 107 getBuffer()108 llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() { 109 return Builder.Buffer; 110 } 111 getBuffer()112 const llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() const { 113 return Builder.Buffer; 114 } 115 ConstantAggregateBuilderBase(ConstantInitBuilderBase & builder,ConstantAggregateBuilderBase * parent)116 ConstantAggregateBuilderBase(ConstantInitBuilderBase &builder, 117 ConstantAggregateBuilderBase *parent) 118 : Builder(builder), Parent(parent), Begin(builder.Buffer.size()) { 119 if (parent) { 120 assert(!parent->Frozen && "parent already has child builder active"); 121 parent->Frozen = true; 122 } else { 123 assert(!builder.Frozen && "builder already has child builder active"); 124 builder.Frozen = true; 125 } 126 } 127 ~ConstantAggregateBuilderBase()128 ~ConstantAggregateBuilderBase() { 129 assert(Finished && "didn't finish aggregate builder"); 130 } 131 markFinished()132 void markFinished() { 133 assert(!Frozen && "child builder still active"); 134 assert(!Finished && "builder already finished"); 135 Finished = true; 136 if (Parent) { 137 assert(Parent->Frozen && 138 "parent not frozen while child builder active"); 139 Parent->Frozen = false; 140 } else { 141 assert(Builder.Frozen && 142 "builder not frozen while child builder active"); 143 Builder.Frozen = false; 144 } 145 } 146 147 public: 148 // Not copyable. 149 ConstantAggregateBuilderBase(const ConstantAggregateBuilderBase &) = delete; 150 ConstantAggregateBuilderBase &operator=(const ConstantAggregateBuilderBase &) 151 = delete; 152 153 // Movable, mostly to allow returning. But we have to write this out 154 // properly to satisfy the assert in the destructor. ConstantAggregateBuilderBase(ConstantAggregateBuilderBase && other)155 ConstantAggregateBuilderBase(ConstantAggregateBuilderBase &&other) 156 : Builder(other.Builder), Parent(other.Parent), Begin(other.Begin), 157 CachedOffsetEnd(other.CachedOffsetEnd), 158 Finished(other.Finished), Frozen(other.Frozen), Packed(other.Packed), 159 CachedOffsetFromGlobal(other.CachedOffsetFromGlobal) { 160 other.Finished = true; 161 } 162 ConstantAggregateBuilderBase &operator=(ConstantAggregateBuilderBase &&other) 163 = delete; 164 165 /// Return the number of elements that have been added to 166 /// this struct or array. size()167 size_t size() const { 168 assert(!this->Finished && "cannot query after finishing builder"); 169 assert(!this->Frozen && "cannot query while sub-builder is active"); 170 assert(this->Begin <= this->getBuffer().size()); 171 return this->getBuffer().size() - this->Begin; 172 } 173 174 /// Return true if no elements have yet been added to this struct or array. empty()175 bool empty() const { 176 return size() == 0; 177 } 178 179 /// Abandon this builder completely. abandon()180 void abandon() { 181 markFinished(); 182 Builder.abandon(Begin); 183 } 184 185 /// Add a new value to this initializer. add(llvm::Constant * value)186 void add(llvm::Constant *value) { 187 assert(value && "adding null value to constant initializer"); 188 assert(!Finished && "cannot add more values after finishing builder"); 189 assert(!Frozen && "cannot add values while subbuilder is active"); 190 Builder.Buffer.push_back(value); 191 } 192 193 /// Add an integer value of type size_t. 194 void addSize(CharUnits size); 195 196 /// Add an integer value of a specific type. 197 void addInt(llvm::IntegerType *intTy, uint64_t value, 198 bool isSigned = false) { 199 add(llvm::ConstantInt::get(intTy, value, isSigned)); 200 } 201 202 /// Add a null pointer of a specific type. addNullPointer(llvm::PointerType * ptrTy)203 void addNullPointer(llvm::PointerType *ptrTy) { 204 add(llvm::ConstantPointerNull::get(ptrTy)); 205 } 206 207 /// Add a bitcast of a value to a specific type. addBitCast(llvm::Constant * value,llvm::Type * type)208 void addBitCast(llvm::Constant *value, llvm::Type *type) { 209 add(llvm::ConstantExpr::getBitCast(value, type)); 210 } 211 212 /// Add a bunch of new values to this initializer. addAll(llvm::ArrayRef<llvm::Constant * > values)213 void addAll(llvm::ArrayRef<llvm::Constant *> values) { 214 assert(!Finished && "cannot add more values after finishing builder"); 215 assert(!Frozen && "cannot add values while subbuilder is active"); 216 Builder.Buffer.append(values.begin(), values.end()); 217 } 218 219 /// Add a relative offset to the given target address, i.e. the 220 /// static difference between the target address and the address 221 /// of the relative offset. The target must be known to be defined 222 /// in the current linkage unit. The offset will have the given 223 /// integer type, which must be no wider than intptr_t. Some 224 /// targets may not fully support this operation. addRelativeOffset(llvm::IntegerType * type,llvm::Constant * target)225 void addRelativeOffset(llvm::IntegerType *type, llvm::Constant *target) { 226 add(getRelativeOffset(type, target)); 227 } 228 229 /// Same as addRelativeOffset(), but instead relative to an element in this 230 /// aggregate, identified by its index. addRelativeOffsetToPosition(llvm::IntegerType * type,llvm::Constant * target,size_t position)231 void addRelativeOffsetToPosition(llvm::IntegerType *type, 232 llvm::Constant *target, size_t position) { 233 add(getRelativeOffsetToPosition(type, target, position)); 234 } 235 236 /// Add a relative offset to the target address, plus a small 237 /// constant offset. This is primarily useful when the relative 238 /// offset is known to be a multiple of (say) four and therefore 239 /// the tag can be used to express an extra two bits of information. addTaggedRelativeOffset(llvm::IntegerType * type,llvm::Constant * address,unsigned tag)240 void addTaggedRelativeOffset(llvm::IntegerType *type, 241 llvm::Constant *address, 242 unsigned tag) { 243 llvm::Constant *offset = getRelativeOffset(type, address); 244 if (tag) { 245 offset = llvm::ConstantExpr::getAdd(offset, 246 llvm::ConstantInt::get(type, tag)); 247 } 248 add(offset); 249 } 250 251 /// Return the offset from the start of the initializer to the 252 /// next position, assuming no padding is required prior to it. 253 /// 254 /// This operation will not succeed if any unsized placeholders are 255 /// currently in place in the initializer. getNextOffsetFromGlobal()256 CharUnits getNextOffsetFromGlobal() const { 257 assert(!Finished && "cannot add more values after finishing builder"); 258 assert(!Frozen && "cannot add values while subbuilder is active"); 259 return getOffsetFromGlobalTo(Builder.Buffer.size()); 260 } 261 262 /// An opaque class to hold the abstract position of a placeholder. 263 class PlaceholderPosition { 264 size_t Index; 265 friend class ConstantAggregateBuilderBase; PlaceholderPosition(size_t index)266 PlaceholderPosition(size_t index) : Index(index) {} 267 }; 268 269 /// Add a placeholder value to the structure. The returned position 270 /// can be used to set the value later; it will not be invalidated by 271 /// any intermediate operations except (1) filling the same position or 272 /// (2) finishing the entire builder. 273 /// 274 /// This is useful for emitting certain kinds of structure which 275 /// contain some sort of summary field, generally a count, before any 276 /// of the data. By emitting a placeholder first, the structure can 277 /// be emitted eagerly. addPlaceholder()278 PlaceholderPosition addPlaceholder() { 279 assert(!Finished && "cannot add more values after finishing builder"); 280 assert(!Frozen && "cannot add values while subbuilder is active"); 281 Builder.Buffer.push_back(nullptr); 282 return Builder.Buffer.size() - 1; 283 } 284 285 /// Add a placeholder, giving the expected type that will be filled in. 286 PlaceholderPosition addPlaceholderWithSize(llvm::Type *expectedType); 287 288 /// Fill a previously-added placeholder. 289 void fillPlaceholderWithInt(PlaceholderPosition position, 290 llvm::IntegerType *type, uint64_t value, 291 bool isSigned = false) { 292 fillPlaceholder(position, llvm::ConstantInt::get(type, value, isSigned)); 293 } 294 295 /// Fill a previously-added placeholder. fillPlaceholder(PlaceholderPosition position,llvm::Constant * value)296 void fillPlaceholder(PlaceholderPosition position, llvm::Constant *value) { 297 assert(!Finished && "cannot change values after finishing builder"); 298 assert(!Frozen && "cannot add values while subbuilder is active"); 299 llvm::Constant *&slot = Builder.Buffer[position.Index]; 300 assert(slot == nullptr && "placeholder already filled"); 301 slot = value; 302 } 303 304 /// Produce an address which will eventually point to the next 305 /// position to be filled. This is computed with an indexed 306 /// getelementptr rather than by computing offsets. 307 /// 308 /// The returned pointer will have type T*, where T is the given type. This 309 /// type can differ from the type of the actual element. 310 llvm::Constant *getAddrOfCurrentPosition(llvm::Type *type); 311 312 /// Produce an address which points to a position in the aggregate being 313 /// constructed. This is computed with an indexed getelementptr rather than by 314 /// computing offsets. 315 /// 316 /// The returned pointer will have type T*, where T is the given type. This 317 /// type can differ from the type of the actual element. 318 llvm::Constant *getAddrOfPosition(llvm::Type *type, size_t position); 319 getGEPIndicesToCurrentPosition(llvm::SmallVectorImpl<llvm::Constant * > & indices)320 llvm::ArrayRef<llvm::Constant*> getGEPIndicesToCurrentPosition( 321 llvm::SmallVectorImpl<llvm::Constant*> &indices) { 322 getGEPIndicesTo(indices, Builder.Buffer.size()); 323 return indices; 324 } 325 326 protected: 327 llvm::Constant *finishArray(llvm::Type *eltTy); 328 llvm::Constant *finishStruct(llvm::StructType *structTy); 329 330 private: 331 void getGEPIndicesTo(llvm::SmallVectorImpl<llvm::Constant*> &indices, 332 size_t position) const; 333 334 llvm::Constant *getRelativeOffset(llvm::IntegerType *offsetType, 335 llvm::Constant *target); 336 337 llvm::Constant *getRelativeOffsetToPosition(llvm::IntegerType *offsetType, 338 llvm::Constant *target, 339 size_t position); 340 341 CharUnits getOffsetFromGlobalTo(size_t index) const; 342 }; 343 344 template <class Impl, class Traits> 345 class ConstantAggregateBuilderTemplateBase 346 : public Traits::AggregateBuilderBase { 347 using super = typename Traits::AggregateBuilderBase; 348 public: 349 using InitBuilder = typename Traits::InitBuilder; 350 using ArrayBuilder = typename Traits::ArrayBuilder; 351 using StructBuilder = typename Traits::StructBuilder; 352 using AggregateBuilderBase = typename Traits::AggregateBuilderBase; 353 354 protected: ConstantAggregateBuilderTemplateBase(InitBuilder & builder,AggregateBuilderBase * parent)355 ConstantAggregateBuilderTemplateBase(InitBuilder &builder, 356 AggregateBuilderBase *parent) 357 : super(builder, parent) {} 358 asImpl()359 Impl &asImpl() { return *static_cast<Impl*>(this); } 360 361 public: 362 ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) { 363 return ArrayBuilder(static_cast<InitBuilder&>(this->Builder), this, eltTy); 364 } 365 366 StructBuilder beginStruct(llvm::StructType *ty = nullptr) { 367 return StructBuilder(static_cast<InitBuilder&>(this->Builder), this, ty); 368 } 369 370 /// Given that this builder was created by beginning an array or struct 371 /// component on the given parent builder, finish the array/struct 372 /// component and add it to the parent. 373 /// 374 /// It is an intentional choice that the parent is passed in explicitly 375 /// despite it being redundant with information already kept in the 376 /// builder. This aids in readability by making it easier to find the 377 /// places that add components to a builder, as well as "bookending" 378 /// the sub-builder more explicitly. finishAndAddTo(AggregateBuilderBase & parent)379 void finishAndAddTo(AggregateBuilderBase &parent) { 380 assert(this->Parent == &parent && "adding to non-parent builder"); 381 parent.add(asImpl().finishImpl()); 382 } 383 384 /// Given that this builder was created by beginning an array or struct 385 /// directly on a ConstantInitBuilder, finish the array/struct and 386 /// create a global variable with it as the initializer. 387 template <class... As> finishAndCreateGlobal(As &&...args)388 llvm::GlobalVariable *finishAndCreateGlobal(As &&...args) { 389 assert(!this->Parent && "finishing non-root builder"); 390 return this->Builder.createGlobal(asImpl().finishImpl(), 391 std::forward<As>(args)...); 392 } 393 394 /// Given that this builder was created by beginning an array or struct 395 /// directly on a ConstantInitBuilder, finish the array/struct and 396 /// set it as the initializer of the given global variable. finishAndSetAsInitializer(llvm::GlobalVariable * global)397 void finishAndSetAsInitializer(llvm::GlobalVariable *global) { 398 assert(!this->Parent && "finishing non-root builder"); 399 return this->Builder.setGlobalInitializer(global, asImpl().finishImpl()); 400 } 401 402 /// Given that this builder was created by beginning an array or struct 403 /// directly on a ConstantInitBuilder, finish the array/struct and 404 /// return a future which can be used to install the initializer in 405 /// a global later. 406 /// 407 /// This is useful for allowing a finished initializer to passed to 408 /// an API which will build the global. However, the "future" preserves 409 /// a dependency on the original builder; it is an error to pass it aside. finishAndCreateFuture()410 ConstantInitFuture finishAndCreateFuture() { 411 assert(!this->Parent && "finishing non-root builder"); 412 return this->Builder.createFuture(asImpl().finishImpl()); 413 } 414 }; 415 416 template <class Traits> 417 class ConstantArrayBuilderTemplateBase 418 : public ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder, 419 Traits> { 420 using super = 421 ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder, Traits>; 422 423 public: 424 using InitBuilder = typename Traits::InitBuilder; 425 using AggregateBuilderBase = typename Traits::AggregateBuilderBase; 426 427 private: 428 llvm::Type *EltTy; 429 430 template <class, class> 431 friend class ConstantAggregateBuilderTemplateBase; 432 433 protected: ConstantArrayBuilderTemplateBase(InitBuilder & builder,AggregateBuilderBase * parent,llvm::Type * eltTy)434 ConstantArrayBuilderTemplateBase(InitBuilder &builder, 435 AggregateBuilderBase *parent, 436 llvm::Type *eltTy) 437 : super(builder, parent), EltTy(eltTy) {} 438 439 private: 440 /// Form an array constant from the values that have been added to this 441 /// builder. finishImpl()442 llvm::Constant *finishImpl() { 443 return AggregateBuilderBase::finishArray(EltTy); 444 } 445 }; 446 447 /// A template class designed to allow other frontends to 448 /// easily customize the builder classes used by ConstantInitBuilder, 449 /// and thus to extend the API to work with the abstractions they 450 /// prefer. This would probably not be necessary if C++ just 451 /// supported extension methods. 452 template <class Traits> 453 class ConstantStructBuilderTemplateBase 454 : public ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder, 455 Traits> { 456 using super = 457 ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder,Traits>; 458 459 public: 460 using InitBuilder = typename Traits::InitBuilder; 461 using AggregateBuilderBase = typename Traits::AggregateBuilderBase; 462 463 private: 464 llvm::StructType *StructTy; 465 466 template <class, class> 467 friend class ConstantAggregateBuilderTemplateBase; 468 469 protected: ConstantStructBuilderTemplateBase(InitBuilder & builder,AggregateBuilderBase * parent,llvm::StructType * structTy)470 ConstantStructBuilderTemplateBase(InitBuilder &builder, 471 AggregateBuilderBase *parent, 472 llvm::StructType *structTy) 473 : super(builder, parent), StructTy(structTy) { 474 if (structTy) this->Packed = structTy->isPacked(); 475 } 476 477 public: setPacked(bool packed)478 void setPacked(bool packed) { 479 this->Packed = packed; 480 } 481 482 /// Use the given type for the struct if its element count is correct. 483 /// Don't add more elements after calling this. suggestType(llvm::StructType * structTy)484 void suggestType(llvm::StructType *structTy) { 485 if (this->size() == structTy->getNumElements()) { 486 StructTy = structTy; 487 } 488 } 489 490 private: 491 /// Form an array constant from the values that have been added to this 492 /// builder. finishImpl()493 llvm::Constant *finishImpl() { 494 return AggregateBuilderBase::finishStruct(StructTy); 495 } 496 }; 497 498 /// A template class designed to allow other frontends to 499 /// easily customize the builder classes used by ConstantInitBuilder, 500 /// and thus to extend the API to work with the abstractions they 501 /// prefer. This would probably not be necessary if C++ just 502 /// supported extension methods. 503 template <class Traits> 504 class ConstantInitBuilderTemplateBase : public ConstantInitBuilderBase { 505 protected: ConstantInitBuilderTemplateBase(CodeGenModule & CGM)506 ConstantInitBuilderTemplateBase(CodeGenModule &CGM) 507 : ConstantInitBuilderBase(CGM) {} 508 509 public: 510 using InitBuilder = typename Traits::InitBuilder; 511 using ArrayBuilder = typename Traits::ArrayBuilder; 512 using StructBuilder = typename Traits::StructBuilder; 513 514 ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) { 515 return ArrayBuilder(static_cast<InitBuilder&>(*this), nullptr, eltTy); 516 } 517 518 StructBuilder beginStruct(llvm::StructType *structTy = nullptr) { 519 return StructBuilder(static_cast<InitBuilder&>(*this), nullptr, structTy); 520 } 521 }; 522 523 class ConstantInitBuilder; 524 class ConstantStructBuilder; 525 class ConstantArrayBuilder; 526 527 struct ConstantInitBuilderTraits { 528 using InitBuilder = ConstantInitBuilder; 529 using AggregateBuilderBase = ConstantAggregateBuilderBase; 530 using ArrayBuilder = ConstantArrayBuilder; 531 using StructBuilder = ConstantStructBuilder; 532 }; 533 534 /// The standard implementation of ConstantInitBuilder used in Clang. 535 class ConstantInitBuilder 536 : public ConstantInitBuilderTemplateBase<ConstantInitBuilderTraits> { 537 public: ConstantInitBuilder(CodeGenModule & CGM)538 explicit ConstantInitBuilder(CodeGenModule &CGM) : 539 ConstantInitBuilderTemplateBase(CGM) {} 540 }; 541 542 /// A helper class of ConstantInitBuilder, used for building constant 543 /// array initializers. 544 class ConstantArrayBuilder 545 : public ConstantArrayBuilderTemplateBase<ConstantInitBuilderTraits> { 546 template <class Traits> 547 friend class ConstantInitBuilderTemplateBase; 548 549 // The use of explicit qualification is a GCC workaround. 550 template <class Impl, class Traits> 551 friend class CodeGen::ConstantAggregateBuilderTemplateBase; 552 ConstantArrayBuilder(ConstantInitBuilder & builder,ConstantAggregateBuilderBase * parent,llvm::Type * eltTy)553 ConstantArrayBuilder(ConstantInitBuilder &builder, 554 ConstantAggregateBuilderBase *parent, 555 llvm::Type *eltTy) 556 : ConstantArrayBuilderTemplateBase(builder, parent, eltTy) {} 557 }; 558 559 /// A helper class of ConstantInitBuilder, used for building constant 560 /// struct initializers. 561 class ConstantStructBuilder 562 : public ConstantStructBuilderTemplateBase<ConstantInitBuilderTraits> { 563 template <class Traits> 564 friend class ConstantInitBuilderTemplateBase; 565 566 // The use of explicit qualification is a GCC workaround. 567 template <class Impl, class Traits> 568 friend class CodeGen::ConstantAggregateBuilderTemplateBase; 569 ConstantStructBuilder(ConstantInitBuilder & builder,ConstantAggregateBuilderBase * parent,llvm::StructType * structTy)570 ConstantStructBuilder(ConstantInitBuilder &builder, 571 ConstantAggregateBuilderBase *parent, 572 llvm::StructType *structTy) 573 : ConstantStructBuilderTemplateBase(builder, parent, structTy) {} 574 }; 575 576 } // end namespace CodeGen 577 } // end namespace clang 578 579 #endif 580