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