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.GetAddrOfConstantStringFromLiteral(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 | |
56 | 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: |
69 | explicit ConstantInitBuilderBase(CodeGenModule &CGM) : CGM(CGM) {} |
70 | |
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 | |
108 | llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() { |
109 | return Builder.Buffer; |
110 | } |
111 | |
112 | const llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() const { |
113 | return Builder.Buffer; |
114 | } |
115 | |
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 | |
128 | ~ConstantAggregateBuilderBase() { |
129 | assert(Finished && "didn't finish aggregate builder" ); |
130 | } |
131 | |
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. |
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. |
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. |
175 | bool empty() const { |
176 | return size() == 0; |
177 | } |
178 | |
179 | /// Abandon this builder completely. |
180 | void abandon() { |
181 | markFinished(); |
182 | Builder.abandon(newEnd: Begin); |
183 | } |
184 | |
185 | /// Add a new value to this initializer. |
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(Elt: 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(value: llvm::ConstantInt::get(Ty: intTy, V: value, IsSigned: isSigned)); |
200 | } |
201 | |
202 | /// Add a null pointer of a specific type. |
203 | void addNullPointer(llvm::PointerType *ptrTy) { |
204 | add(value: llvm::ConstantPointerNull::get(T: ptrTy)); |
205 | } |
206 | |
207 | /// Add a bunch of new values to this initializer. |
208 | void addAll(llvm::ArrayRef<llvm::Constant *> values) { |
209 | assert(!Finished && "cannot add more values after finishing builder" ); |
210 | assert(!Frozen && "cannot add values while subbuilder is active" ); |
211 | Builder.Buffer.append(in_start: values.begin(), in_end: values.end()); |
212 | } |
213 | |
214 | /// Add a relative offset to the given target address, i.e. the |
215 | /// static difference between the target address and the address |
216 | /// of the relative offset. The target must be known to be defined |
217 | /// in the current linkage unit. The offset will have the given |
218 | /// integer type, which must be no wider than intptr_t. Some |
219 | /// targets may not fully support this operation. |
220 | void addRelativeOffset(llvm::IntegerType *type, llvm::Constant *target) { |
221 | add(value: getRelativeOffset(offsetType: type, target)); |
222 | } |
223 | |
224 | /// Same as addRelativeOffset(), but instead relative to an element in this |
225 | /// aggregate, identified by its index. |
226 | void addRelativeOffsetToPosition(llvm::IntegerType *type, |
227 | llvm::Constant *target, size_t position) { |
228 | add(value: getRelativeOffsetToPosition(offsetType: type, target, position)); |
229 | } |
230 | |
231 | /// Add a relative offset to the target address, plus a small |
232 | /// constant offset. This is primarily useful when the relative |
233 | /// offset is known to be a multiple of (say) four and therefore |
234 | /// the tag can be used to express an extra two bits of information. |
235 | void addTaggedRelativeOffset(llvm::IntegerType *type, |
236 | llvm::Constant *address, |
237 | unsigned tag) { |
238 | llvm::Constant *offset = getRelativeOffset(offsetType: type, target: address); |
239 | if (tag) { |
240 | offset = llvm::ConstantExpr::getAdd(C1: offset, |
241 | C2: llvm::ConstantInt::get(Ty: type, V: tag)); |
242 | } |
243 | add(value: offset); |
244 | } |
245 | |
246 | /// Return the offset from the start of the initializer to the |
247 | /// next position, assuming no padding is required prior to it. |
248 | /// |
249 | /// This operation will not succeed if any unsized placeholders are |
250 | /// currently in place in the initializer. |
251 | CharUnits getNextOffsetFromGlobal() const { |
252 | assert(!Finished && "cannot add more values after finishing builder" ); |
253 | assert(!Frozen && "cannot add values while subbuilder is active" ); |
254 | return getOffsetFromGlobalTo(index: Builder.Buffer.size()); |
255 | } |
256 | |
257 | /// An opaque class to hold the abstract position of a placeholder. |
258 | class PlaceholderPosition { |
259 | size_t Index; |
260 | friend class ConstantAggregateBuilderBase; |
261 | PlaceholderPosition(size_t index) : Index(index) {} |
262 | }; |
263 | |
264 | /// Add a placeholder value to the structure. The returned position |
265 | /// can be used to set the value later; it will not be invalidated by |
266 | /// any intermediate operations except (1) filling the same position or |
267 | /// (2) finishing the entire builder. |
268 | /// |
269 | /// This is useful for emitting certain kinds of structure which |
270 | /// contain some sort of summary field, generally a count, before any |
271 | /// of the data. By emitting a placeholder first, the structure can |
272 | /// be emitted eagerly. |
273 | PlaceholderPosition addPlaceholder() { |
274 | assert(!Finished && "cannot add more values after finishing builder" ); |
275 | assert(!Frozen && "cannot add values while subbuilder is active" ); |
276 | Builder.Buffer.push_back(Elt: nullptr); |
277 | return Builder.Buffer.size() - 1; |
278 | } |
279 | |
280 | /// Add a placeholder, giving the expected type that will be filled in. |
281 | PlaceholderPosition addPlaceholderWithSize(llvm::Type *expectedType); |
282 | |
283 | /// Fill a previously-added placeholder. |
284 | void fillPlaceholderWithInt(PlaceholderPosition position, |
285 | llvm::IntegerType *type, uint64_t value, |
286 | bool isSigned = false) { |
287 | fillPlaceholder(position, value: llvm::ConstantInt::get(Ty: type, V: value, IsSigned: isSigned)); |
288 | } |
289 | |
290 | /// Fill a previously-added placeholder. |
291 | void fillPlaceholder(PlaceholderPosition position, llvm::Constant *value) { |
292 | assert(!Finished && "cannot change values after finishing builder" ); |
293 | assert(!Frozen && "cannot add values while subbuilder is active" ); |
294 | llvm::Constant *&slot = Builder.Buffer[position.Index]; |
295 | assert(slot == nullptr && "placeholder already filled" ); |
296 | slot = value; |
297 | } |
298 | |
299 | /// Produce an address which will eventually point to the next |
300 | /// position to be filled. This is computed with an indexed |
301 | /// getelementptr rather than by computing offsets. |
302 | /// |
303 | /// The returned pointer will have type T*, where T is the given type. This |
304 | /// type can differ from the type of the actual element. |
305 | llvm::Constant *getAddrOfCurrentPosition(llvm::Type *type); |
306 | |
307 | /// Produce an address which points to a position in the aggregate being |
308 | /// constructed. This is computed with an indexed getelementptr rather than by |
309 | /// 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 *getAddrOfPosition(llvm::Type *type, size_t position); |
314 | |
315 | llvm::ArrayRef<llvm::Constant*> getGEPIndicesToCurrentPosition( |
316 | llvm::SmallVectorImpl<llvm::Constant*> &indices) { |
317 | getGEPIndicesTo(indices, position: Builder.Buffer.size()); |
318 | return indices; |
319 | } |
320 | |
321 | protected: |
322 | llvm::Constant *finishArray(llvm::Type *eltTy); |
323 | llvm::Constant *finishStruct(llvm::StructType *structTy); |
324 | |
325 | private: |
326 | void getGEPIndicesTo(llvm::SmallVectorImpl<llvm::Constant*> &indices, |
327 | size_t position) const; |
328 | |
329 | llvm::Constant *getRelativeOffset(llvm::IntegerType *offsetType, |
330 | llvm::Constant *target); |
331 | |
332 | llvm::Constant *getRelativeOffsetToPosition(llvm::IntegerType *offsetType, |
333 | llvm::Constant *target, |
334 | size_t position); |
335 | |
336 | CharUnits getOffsetFromGlobalTo(size_t index) const; |
337 | }; |
338 | |
339 | template <class Impl, class Traits> |
340 | class ConstantAggregateBuilderTemplateBase |
341 | : public Traits::AggregateBuilderBase { |
342 | using super = typename Traits::AggregateBuilderBase; |
343 | public: |
344 | using InitBuilder = typename Traits::InitBuilder; |
345 | using ArrayBuilder = typename Traits::ArrayBuilder; |
346 | using StructBuilder = typename Traits::StructBuilder; |
347 | using AggregateBuilderBase = typename Traits::AggregateBuilderBase; |
348 | |
349 | protected: |
350 | ConstantAggregateBuilderTemplateBase(InitBuilder &builder, |
351 | AggregateBuilderBase *parent) |
352 | : super(builder, parent) {} |
353 | |
354 | Impl &asImpl() { return *static_cast<Impl*>(this); } |
355 | |
356 | public: |
357 | ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) { |
358 | return ArrayBuilder(static_cast<InitBuilder&>(this->Builder), this, eltTy); |
359 | } |
360 | |
361 | StructBuilder beginStruct(llvm::StructType *ty = nullptr) { |
362 | return StructBuilder(static_cast<InitBuilder&>(this->Builder), this, ty); |
363 | } |
364 | |
365 | /// Given that this builder was created by beginning an array or struct |
366 | /// component on the given parent builder, finish the array/struct |
367 | /// component and add it to the parent. |
368 | /// |
369 | /// It is an intentional choice that the parent is passed in explicitly |
370 | /// despite it being redundant with information already kept in the |
371 | /// builder. This aids in readability by making it easier to find the |
372 | /// places that add components to a builder, as well as "bookending" |
373 | /// the sub-builder more explicitly. |
374 | void finishAndAddTo(AggregateBuilderBase &parent) { |
375 | assert(this->Parent == &parent && "adding to non-parent builder" ); |
376 | parent.add(asImpl().finishImpl()); |
377 | } |
378 | |
379 | /// Given that this builder was created by beginning an array or struct |
380 | /// directly on a ConstantInitBuilder, finish the array/struct and |
381 | /// create a global variable with it as the initializer. |
382 | template <class... As> |
383 | llvm::GlobalVariable *finishAndCreateGlobal(As &&...args) { |
384 | assert(!this->Parent && "finishing non-root builder" ); |
385 | return this->Builder.createGlobal(asImpl().finishImpl(), |
386 | std::forward<As>(args)...); |
387 | } |
388 | |
389 | /// Given that this builder was created by beginning an array or struct |
390 | /// directly on a ConstantInitBuilder, finish the array/struct and |
391 | /// set it as the initializer of the given global variable. |
392 | void finishAndSetAsInitializer(llvm::GlobalVariable *global) { |
393 | assert(!this->Parent && "finishing non-root builder" ); |
394 | return this->Builder.setGlobalInitializer(global, asImpl().finishImpl()); |
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 | /// return a future which can be used to install the initializer in |
400 | /// a global later. |
401 | /// |
402 | /// This is useful for allowing a finished initializer to passed to |
403 | /// an API which will build the global. However, the "future" preserves |
404 | /// a dependency on the original builder; it is an error to pass it aside. |
405 | ConstantInitFuture finishAndCreateFuture() { |
406 | assert(!this->Parent && "finishing non-root builder" ); |
407 | return this->Builder.createFuture(asImpl().finishImpl()); |
408 | } |
409 | }; |
410 | |
411 | template <class Traits> |
412 | class ConstantArrayBuilderTemplateBase |
413 | : public ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder, |
414 | Traits> { |
415 | using super = |
416 | ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder, Traits>; |
417 | |
418 | public: |
419 | using InitBuilder = typename Traits::InitBuilder; |
420 | using AggregateBuilderBase = typename Traits::AggregateBuilderBase; |
421 | |
422 | private: |
423 | llvm::Type *EltTy; |
424 | |
425 | template <class, class> |
426 | friend class ConstantAggregateBuilderTemplateBase; |
427 | |
428 | protected: |
429 | ConstantArrayBuilderTemplateBase(InitBuilder &builder, |
430 | AggregateBuilderBase *parent, |
431 | llvm::Type *eltTy) |
432 | : super(builder, parent), EltTy(eltTy) {} |
433 | |
434 | private: |
435 | /// Form an array constant from the values that have been added to this |
436 | /// builder. |
437 | llvm::Constant *finishImpl() { |
438 | return AggregateBuilderBase::finishArray(EltTy); |
439 | } |
440 | }; |
441 | |
442 | /// A template class designed to allow other frontends to |
443 | /// easily customize the builder classes used by ConstantInitBuilder, |
444 | /// and thus to extend the API to work with the abstractions they |
445 | /// prefer. This would probably not be necessary if C++ just |
446 | /// supported extension methods. |
447 | template <class Traits> |
448 | class ConstantStructBuilderTemplateBase |
449 | : public ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder, |
450 | Traits> { |
451 | using super = |
452 | ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder,Traits>; |
453 | |
454 | public: |
455 | using InitBuilder = typename Traits::InitBuilder; |
456 | using AggregateBuilderBase = typename Traits::AggregateBuilderBase; |
457 | |
458 | private: |
459 | llvm::StructType *StructTy; |
460 | |
461 | template <class, class> |
462 | friend class ConstantAggregateBuilderTemplateBase; |
463 | |
464 | protected: |
465 | ConstantStructBuilderTemplateBase(InitBuilder &builder, |
466 | AggregateBuilderBase *parent, |
467 | llvm::StructType *structTy) |
468 | : super(builder, parent), StructTy(structTy) { |
469 | if (structTy) this->Packed = structTy->isPacked(); |
470 | } |
471 | |
472 | public: |
473 | void setPacked(bool packed) { |
474 | this->Packed = packed; |
475 | } |
476 | |
477 | /// Use the given type for the struct if its element count is correct. |
478 | /// Don't add more elements after calling this. |
479 | void suggestType(llvm::StructType *structTy) { |
480 | if (this->size() == structTy->getNumElements()) { |
481 | StructTy = structTy; |
482 | } |
483 | } |
484 | |
485 | private: |
486 | /// Form an array constant from the values that have been added to this |
487 | /// builder. |
488 | llvm::Constant *finishImpl() { |
489 | return AggregateBuilderBase::finishStruct(StructTy); |
490 | } |
491 | }; |
492 | |
493 | /// A template class designed to allow other frontends to |
494 | /// easily customize the builder classes used by ConstantInitBuilder, |
495 | /// and thus to extend the API to work with the abstractions they |
496 | /// prefer. This would probably not be necessary if C++ just |
497 | /// supported extension methods. |
498 | template <class Traits> |
499 | class ConstantInitBuilderTemplateBase : public ConstantInitBuilderBase { |
500 | protected: |
501 | ConstantInitBuilderTemplateBase(CodeGenModule &CGM) |
502 | : ConstantInitBuilderBase(CGM) {} |
503 | |
504 | public: |
505 | using InitBuilder = typename Traits::InitBuilder; |
506 | using ArrayBuilder = typename Traits::ArrayBuilder; |
507 | using StructBuilder = typename Traits::StructBuilder; |
508 | |
509 | ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) { |
510 | return ArrayBuilder(static_cast<InitBuilder&>(*this), nullptr, eltTy); |
511 | } |
512 | |
513 | StructBuilder beginStruct(llvm::StructType *structTy = nullptr) { |
514 | return StructBuilder(static_cast<InitBuilder&>(*this), nullptr, structTy); |
515 | } |
516 | }; |
517 | |
518 | class ConstantInitBuilder; |
519 | class ConstantStructBuilder; |
520 | class ConstantArrayBuilder; |
521 | |
522 | struct ConstantInitBuilderTraits { |
523 | using InitBuilder = ConstantInitBuilder; |
524 | using AggregateBuilderBase = ConstantAggregateBuilderBase; |
525 | using ArrayBuilder = ConstantArrayBuilder; |
526 | using StructBuilder = ConstantStructBuilder; |
527 | }; |
528 | |
529 | /// The standard implementation of ConstantInitBuilder used in Clang. |
530 | class ConstantInitBuilder |
531 | : public ConstantInitBuilderTemplateBase<ConstantInitBuilderTraits> { |
532 | public: |
533 | explicit ConstantInitBuilder(CodeGenModule &CGM) : |
534 | ConstantInitBuilderTemplateBase(CGM) {} |
535 | }; |
536 | |
537 | /// A helper class of ConstantInitBuilder, used for building constant |
538 | /// array initializers. |
539 | class ConstantArrayBuilder |
540 | : public ConstantArrayBuilderTemplateBase<ConstantInitBuilderTraits> { |
541 | template <class Traits> |
542 | friend class ConstantInitBuilderTemplateBase; |
543 | |
544 | // The use of explicit qualification is a GCC workaround. |
545 | template <class Impl, class Traits> |
546 | friend class CodeGen::ConstantAggregateBuilderTemplateBase; |
547 | |
548 | ConstantArrayBuilder(ConstantInitBuilder &builder, |
549 | ConstantAggregateBuilderBase *parent, |
550 | llvm::Type *eltTy) |
551 | : ConstantArrayBuilderTemplateBase(builder, parent, eltTy) {} |
552 | }; |
553 | |
554 | /// A helper class of ConstantInitBuilder, used for building constant |
555 | /// struct initializers. |
556 | class ConstantStructBuilder |
557 | : public ConstantStructBuilderTemplateBase<ConstantInitBuilderTraits> { |
558 | template <class Traits> |
559 | friend class ConstantInitBuilderTemplateBase; |
560 | |
561 | // The use of explicit qualification is a GCC workaround. |
562 | template <class Impl, class Traits> |
563 | friend class CodeGen::ConstantAggregateBuilderTemplateBase; |
564 | |
565 | ConstantStructBuilder(ConstantInitBuilder &builder, |
566 | ConstantAggregateBuilderBase *parent, |
567 | llvm::StructType *structTy) |
568 | : ConstantStructBuilderTemplateBase(builder, parent, structTy) {} |
569 | }; |
570 | |
571 | } // end namespace CodeGen |
572 | } // end namespace clang |
573 | |
574 | #endif |
575 | |