1//===- CIRGenExprAggregrate.cpp - Emit CIR Code from Aggregate Expressions ===//
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 contains code to emit Aggregate Expr nodes as CIR code.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenBuilder.h"
14#include "CIRGenFunction.h"
15#include "CIRGenValue.h"
16#include "clang/CIR/Dialect/IR/CIRAttrs.h"
17
18#include "clang/AST/Expr.h"
19#include "clang/AST/StmtVisitor.h"
20#include <cstdint>
21
22using namespace clang;
23using namespace clang::CIRGen;
24
25namespace {
26class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
27
28 CIRGenFunction &cgf;
29 AggValueSlot dest;
30
31 AggValueSlot ensureSlot(mlir::Location loc, QualType t) {
32 if (!dest.isIgnored())
33 return dest;
34
35 cgf.cgm.errorNYI(loc, "Slot for ignored address");
36 return dest;
37 }
38
39public:
40 AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest)
41 : cgf(cgf), dest(dest) {}
42
43 void emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy,
44 Expr *exprToVisit, ArrayRef<Expr *> args,
45 Expr *arrayFiller);
46
47 void emitInitializationToLValue(Expr *e, LValue lv);
48
49 void emitNullInitializationToLValue(mlir::Location loc, LValue lv);
50
51 void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
52
53 void VisitInitListExpr(InitListExpr *e);
54 void VisitCXXConstructExpr(const CXXConstructExpr *e);
55
56 void visitCXXParenListOrInitListExpr(Expr *e, ArrayRef<Expr *> args,
57 FieldDecl *initializedFieldInUnion,
58 Expr *arrayFiller);
59};
60
61} // namespace
62
63static bool isTrivialFiller(Expr *e) {
64 if (!e)
65 return true;
66
67 if (isa<ImplicitValueInitExpr>(Val: e))
68 return true;
69
70 if (auto *ile = dyn_cast<InitListExpr>(Val: e)) {
71 if (ile->getNumInits())
72 return false;
73 return isTrivialFiller(e: ile->getArrayFiller());
74 }
75
76 if (const auto *cons = dyn_cast_or_null<CXXConstructExpr>(Val: e))
77 return cons->getConstructor()->isDefaultConstructor() &&
78 cons->getConstructor()->isTrivial();
79
80 return false;
81}
82
83void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
84 QualType arrayQTy, Expr *e,
85 ArrayRef<Expr *> args, Expr *arrayFiller) {
86 CIRGenBuilderTy &builder = cgf.getBuilder();
87 const mlir::Location loc = cgf.getLoc(e->getSourceRange());
88
89 const uint64_t numInitElements = args.size();
90
91 const QualType elementType =
92 cgf.getContext().getAsArrayType(T: arrayQTy)->getElementType();
93
94 if (elementType.isDestructedType()) {
95 cgf.cgm.errorNYI(loc, "dtorKind NYI");
96 return;
97 }
98
99 const QualType elementPtrType = cgf.getContext().getPointerType(T: elementType);
100
101 const mlir::Type cirElementType = cgf.convertType(elementType);
102 const cir::PointerType cirElementPtrType =
103 builder.getPointerTo(cirElementType);
104
105 auto begin = builder.create<cir::CastOp>(loc, cirElementPtrType,
106 cir::CastKind::array_to_ptrdecay,
107 destPtr.getPointer());
108
109 const CharUnits elementSize =
110 cgf.getContext().getTypeSizeInChars(T: elementType);
111 const CharUnits elementAlign =
112 destPtr.getAlignment().alignmentOfArrayElement(elementSize);
113
114 // The 'current element to initialize'. The invariants on this
115 // variable are complicated. Essentially, after each iteration of
116 // the loop, it points to the last initialized element, except
117 // that it points to the beginning of the array before any
118 // elements have been initialized.
119 mlir::Value element = begin;
120
121 // Don't build the 'one' before the cycle to avoid
122 // emmiting the redundant `cir.const 1` instrs.
123 mlir::Value one;
124
125 // Emit the explicit initializers.
126 for (uint64_t i = 0; i != numInitElements; ++i) {
127 // Advance to the next element.
128 if (i > 0) {
129 one = builder.getConstantInt(loc, cgf.PtrDiffTy, i);
130 element = builder.createPtrStride(loc, begin, one);
131 }
132
133 const Address address = Address(element, cirElementType, elementAlign);
134 const LValue elementLV = cgf.makeAddrLValue(addr: address, ty: elementType);
135 emitInitializationToLValue(e: args[i], lv: elementLV);
136 }
137
138 const uint64_t numArrayElements = arrayTy.getSize();
139
140 // Check whether there's a non-trivial array-fill expression.
141 const bool hasTrivialFiller = isTrivialFiller(e: arrayFiller);
142
143 // Any remaining elements need to be zero-initialized, possibly
144 // using the filler expression. We can skip this if the we're
145 // emitting to zeroed memory.
146 if (numInitElements != numArrayElements &&
147 !(dest.isZeroed() && hasTrivialFiller &&
148 cgf.getTypes().isZeroInitializable(elementType))) {
149 // Advance to the start of the rest of the array.
150 if (numInitElements) {
151 one = builder.getConstantInt(loc, cgf.PtrDiffTy, 1);
152 element = builder.create<cir::PtrStrideOp>(loc, cirElementPtrType,
153 element, one);
154 }
155
156 // Allocate the temporary variable
157 // to store the pointer to first unitialized element
158 const Address tmpAddr = cgf.createTempAlloca(
159 cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp");
160 LValue tmpLV = cgf.makeAddrLValue(addr: tmpAddr, ty: elementPtrType);
161 cgf.emitStoreThroughLValue(RValue::src: get(element), dst: tmpLV);
162
163 // TODO(CIR): Replace this part later with cir::DoWhileOp
164 for (unsigned i = numInitElements; i != numArrayElements; ++i) {
165 cir::LoadOp currentElement = builder.createLoad(loc, tmpAddr);
166
167 // Emit the actual filler expression.
168 const LValue elementLV = cgf.makeAddrLValue(
169 addr: Address(currentElement, cirElementType, elementAlign), ty: elementType);
170
171 if (arrayFiller)
172 emitInitializationToLValue(e: arrayFiller, lv: elementLV);
173 else
174 emitNullInitializationToLValue(loc, elementLV);
175
176 // Advance pointer and store them to temporary variable
177 one = builder.getConstantInt(loc, cgf.PtrDiffTy, 1);
178 cir::PtrStrideOp nextElement =
179 builder.createPtrStride(loc, currentElement, one);
180 cgf.emitStoreThroughLValue(RValue::src: get(nextElement), dst: tmpLV);
181 }
182 }
183}
184
185void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) {
186 const QualType type = lv.getType();
187
188 if (isa<ImplicitValueInitExpr, CXXScalarValueInitExpr>(Val: e)) {
189 const mlir::Location loc = e->getSourceRange().isValid()
190 ? cgf.getLoc(e->getSourceRange())
191 : *cgf.currSrcLoc;
192 return emitNullInitializationToLValue(loc, lv);
193 }
194
195 if (isa<NoInitExpr>(Val: e))
196 return;
197
198 if (type->isReferenceType())
199 cgf.cgm.errorNYI(feature: "emitInitializationToLValue ReferenceType");
200
201 switch (cgf.getEvaluationKind(type)) {
202 case cir::TEK_Complex:
203 cgf.cgm.errorNYI(feature: "emitInitializationToLValue TEK_Complex");
204 break;
205 case cir::TEK_Aggregate:
206 cgf.emitAggExpr(e, slot: AggValueSlot::forLValue(lv));
207 return;
208 case cir::TEK_Scalar:
209 if (lv.isSimple())
210 cgf.emitScalarInit(e, cgf.getLoc(e->getSourceRange()), lv);
211 else
212 cgf.emitStoreThroughLValue(RValue::src: get(cgf.emitScalarExpr(e)), dst: lv);
213 return;
214 }
215}
216
217void AggExprEmitter::VisitCXXConstructExpr(const CXXConstructExpr *e) {
218 AggValueSlot slot = ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
219 cgf.emitCXXConstructExpr(e, dest: slot);
220}
221
222void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc,
223 LValue lv) {
224 const QualType type = lv.getType();
225
226 // If the destination slot is already zeroed out before the aggregate is
227 // copied into it, we don't have to emit any zeros here.
228 if (dest.isZeroed() && cgf.getTypes().isZeroInitializable(type))
229 return;
230
231 if (cgf.hasScalarEvaluationKind(type)) {
232 // For non-aggregates, we can store the appropriate null constant.
233 mlir::Value null = cgf.cgm.emitNullConstant(type, loc);
234 if (lv.isSimple()) {
235 cgf.emitStoreOfScalar(null, lv, /* isInitialization */ true);
236 return;
237 }
238
239 cgf.cgm.errorNYI(feature: "emitStoreThroughBitfieldLValue");
240 return;
241 }
242
243 // There's a potential optimization opportunity in combining
244 // memsets; that would be easy for arrays, but relatively
245 // difficult for structures with the current code.
246 cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType());
247}
248
249void AggExprEmitter::VisitInitListExpr(InitListExpr *e) {
250 if (e->hadArrayRangeDesignator())
251 llvm_unreachable("GNU array range designator extension");
252
253 if (e->isTransparent())
254 return Visit(e: e->getInit(Init: 0));
255
256 visitCXXParenListOrInitListExpr(
257 e, e->inits(), e->getInitializedFieldInUnion(), e->getArrayFiller());
258}
259
260void AggExprEmitter::visitCXXParenListOrInitListExpr(
261 Expr *e, ArrayRef<Expr *> args, FieldDecl *initializedFieldInUnion,
262 Expr *arrayFiller) {
263
264 const AggValueSlot dest =
265 ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
266
267 if (e->getType()->isConstantArrayType()) {
268 cir::ArrayType arrayTy =
269 cast<cir::ArrayType>(dest.getAddress().getElementType());
270 emitArrayInit(destPtr: dest.getAddress(), arrayTy: arrayTy, arrayQTy: e->getType(), e, args,
271 arrayFiller);
272 return;
273 }
274
275 cgf.cgm.errorNYI(
276 feature: "visitCXXParenListOrInitListExpr Record or VariableSizeArray type");
277}
278
279void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) {
280 AggExprEmitter(*this, slot).Visit(e: const_cast<Expr *>(e));
281}
282
283LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) {
284 assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!");
285 Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange()));
286 LValue lv = makeAddrLValue(addr: temp, ty: e->getType());
287 emitAggExpr(e, slot: AggValueSlot::forLValue(lv));
288 return lv;
289}
290

source code of clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp