Skip to content

Commit 5fc1b74

Browse files
authored
[CIR] Recognize constant aggregate initialization of auto vars (#166850)
This adds code that was previously missing from emitAutoVarAlloca to identify when an aggregate auto var is being emitted with a constant initializer, and the associated code that is called from emitAutoVarInit to store the constant. This allows significantly more efficient initialization.
1 parent 7568a99 commit 5fc1b74

26 files changed

+517
-4256
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ struct MissingFeatures {
189189

190190
// Misc
191191
static bool abiArgInfo() { return false; }
192+
static bool addAutoInitAnnotation() { return false; }
192193
static bool addHeapAllocSiteMetadata() { return false; }
193194
static bool aggEmitFinalDestCopyRValue() { return false; }
194195
static bool aggValueSlot() { return false; }
@@ -198,6 +199,7 @@ struct MissingFeatures {
198199
static bool aggValueSlotMayOverlap() { return false; }
199200
static bool aggValueSlotVolatile() { return false; }
200201
static bool alignCXXRecordDecl() { return false; }
202+
static bool appleKext() { return false; }
201203
static bool armComputeVolatileBitfields() { return false; }
202204
static bool asmGoto() { return false; }
203205
static bool asmInputOperands() { return false; }
@@ -244,6 +246,7 @@ struct MissingFeatures {
244246
static bool deleteArray() { return false; }
245247
static bool devirtualizeDestructor() { return false; }
246248
static bool devirtualizeMemberFunction() { return false; }
249+
static bool dtorCleanups() { return false; }
247250
static bool ehCleanupFlags() { return false; }
248251
static bool ehCleanupHasPrebranchedFallthrough() { return false; }
249252
static bool ehCleanupScope() { return false; }
@@ -289,6 +292,7 @@ struct MissingFeatures {
289292
static bool objCGC() { return false; }
290293
static bool objCLifetime() { return false; }
291294
static bool hlsl() { return false; }
295+
static bool msvcBuiltins() { return false; }
292296
static bool openCL() { return false; }
293297
static bool openMP() { return false; }
294298
static bool opTBAA() { return false; }
@@ -303,6 +307,10 @@ struct MissingFeatures {
303307
static bool setNonGC() { return false; }
304308
static bool setObjCGCLValueClass() { return false; }
305309
static bool setTargetAttributes() { return false; }
310+
static bool shouldCreateMemCpyFromGlobal() { return false; }
311+
static bool shouldSplitConstantStore() { return false; }
312+
static bool shouldUseBZeroPlusStoresToInitialize() { return false; }
313+
static bool shouldUseMemSetToInitialize() { return false; }
306314
static bool simplifyCleanupEntry() { return false; }
307315
static bool sourceLanguageCases() { return false; }
308316
static bool stackBase() { return false; }
@@ -314,16 +322,14 @@ struct MissingFeatures {
314322
static bool thunks() { return false; }
315323
static bool tryEmitAsConstant() { return false; }
316324
static bool typeChecks() { return false; }
317-
static bool weakRefReference() { return false; }
318-
static bool writebacks() { return false; }
319-
static bool appleKext() { return false; }
320-
static bool dtorCleanups() { return false; }
325+
static bool vaArgABILowering() { return false; }
326+
static bool vectorConstants() { return false; }
327+
static bool vlas() { return false; }
321328
static bool vtableInitialization() { return false; }
322329
static bool vtableEmitMetadata() { return false; }
323330
static bool vtableRelativeLayout() { return false; }
324-
static bool msvcBuiltins() { return false; }
325-
static bool vaArgABILowering() { return false; }
326-
static bool vlas() { return false; }
331+
static bool weakRefReference() { return false; }
332+
static bool writebacks() { return false; }
327333

328334
// Missing types
329335
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,41 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,
5050

5151
Address address = Address::invalid();
5252
if (ty->isConstantSizeType()) {
53+
// If this value is an array, struct, or vector with a statically
54+
// determinable constant initializer, there are optimizations we can do.
55+
//
56+
// TODO: We should constant-evaluate the initializer of any variable,
57+
// as long as it is initialized by a constant expression. Currently,
58+
// isConstantInitializer produces wrong answers for structs with
59+
// reference or bitfield members, and a few other cases, and checking
60+
// for POD-ness protects us from some of these.
61+
if (d.getInit() &&
62+
(ty->isArrayType() || ty->isRecordType() || ty->isVectorType()) &&
63+
(d.isConstexpr() ||
64+
((ty.isPODType(getContext()) ||
65+
getContext().getBaseElementType(ty)->isObjCObjectPointerType()) &&
66+
d.getInit()->isConstantInitializer(getContext(), false)))) {
67+
68+
// If the variable's a const type, and it's neither an NRVO
69+
// candidate nor a __block variable and has no mutable members,
70+
// emit it as a global instead.
71+
// Exception is if a variable is located in non-constant address space
72+
// in OpenCL.
73+
// TODO(cir): perhaps we don't need this at all at CIR since this can
74+
// be done as part of lowering down to LLVM.
75+
bool needsDtor =
76+
d.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
77+
if ((!getContext().getLangOpts().OpenCL ||
78+
ty.getAddressSpace() == LangAS::opencl_constant) &&
79+
(cgm.getCodeGenOpts().MergeAllConstants && !nrvo &&
80+
!d.isEscapingByref() &&
81+
ty.isConstantStorage(getContext(), true, !needsDtor))) {
82+
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: type constant");
83+
}
84+
// Otherwise, tell the initialization code that we're in this case.
85+
emission.isConstantAggregate = true;
86+
}
87+
5388
// A normal fixed sized variable becomes an alloca in the entry block,
5489
// unless:
5590
// - it's an NRVO variable.
@@ -131,6 +166,47 @@ bool CIRGenFunction::isTrivialInitializer(const Expr *init) {
131166
return false;
132167
}
133168

169+
static void emitStoresForConstant(CIRGenModule &cgm, const VarDecl &d,
170+
Address addr, bool isVolatile,
171+
CIRGenBuilderTy &builder,
172+
mlir::TypedAttr constant) {
173+
mlir::Type ty = constant.getType();
174+
cir::CIRDataLayout layout{cgm.getModule()};
175+
uint64_t constantSize = layout.getTypeAllocSize(ty);
176+
if (!constantSize)
177+
return;
178+
assert(!cir::MissingFeatures::addAutoInitAnnotation());
179+
assert(!cir::MissingFeatures::vectorConstants());
180+
assert(!cir::MissingFeatures::shouldUseBZeroPlusStoresToInitialize());
181+
assert(!cir::MissingFeatures::shouldUseMemSetToInitialize());
182+
assert(!cir::MissingFeatures::shouldSplitConstantStore());
183+
assert(!cir::MissingFeatures::shouldCreateMemCpyFromGlobal());
184+
// In CIR we want to emit a store for the whole thing, later lowering
185+
// prepare to LLVM should unwrap this into the best policy (see asserts
186+
// above).
187+
//
188+
// FIXME(cir): This is closer to memcpy behavior but less optimal, instead of
189+
// copy from a global, we just create a cir.const out of it.
190+
191+
if (addr.getElementType() != ty)
192+
addr = addr.withElementType(builder, ty);
193+
194+
// If the address is an alloca, set the init attribute.
195+
// The address is usually and alloca, but there is at least one case where
196+
// emitAutoVarInit is called from the OpenACC codegen with an address that
197+
// is not an alloca.
198+
auto allocaOp = addr.getDefiningOp<cir::AllocaOp>();
199+
if (allocaOp)
200+
allocaOp.setInitAttr(mlir::UnitAttr::get(&cgm.getMLIRContext()));
201+
202+
// There are cases where OpenACC codegen calls emitAutoVarInit with a
203+
// temporary decl that doesn't have a source range set.
204+
mlir::Location loc = builder.getUnknownLoc();
205+
if (d.getSourceRange().isValid())
206+
loc = cgm.getLoc(d.getSourceRange());
207+
builder.createStore(loc, builder.getConstant(loc, constant), addr);
208+
}
209+
134210
void CIRGenFunction::emitAutoVarInit(
135211
const CIRGenFunction::AutoVarEmission &emission) {
136212
assert(emission.variable && "emission was not valid!");
@@ -237,6 +313,9 @@ void CIRGenFunction::emitAutoVarInit(
237313
return emitStoreThroughLValue(
238314
RValue::get(builder.getConstant(initLoc, typedConstant)), lv);
239315
}
316+
317+
emitStoresForConstant(cgm, d, addr, type.isVolatileQualified(), builder,
318+
typedConstant);
240319
}
241320

242321
void CIRGenFunction::emitAutoVarCleanups(

clang/test/CIR/CodeGen/agg-expr-lvalue.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,13 @@ void test_string_array_in_array(void) {
9595
}
9696

9797
// CIR-LABEL: cir.func{{.*}} @test_string_array_in_array
98-
// CIR: cir.alloca !cir.array<!cir.array<!s8i x 6> x 2>, {{.*}}, ["matrix", init]
99-
// CIR: cir.get_global
100-
// CIR: cir.copy
101-
// CIR: cir.get_global
102-
// CIR: cir.copy
98+
// CIR: %[[MATRIX:.*]] = cir.alloca !cir.array<!cir.array<!s8i x 6> x 2>, {{.*}}, ["matrix", init]
99+
// CIR: %[[CONST:.*]] = cir.const #cir.const_array<[#cir.const_array<[#cir.int<104> : !s8i, #cir.int<101> : !s8i, #cir.int<108> : !s8i, #cir.int<108> : !s8i, #cir.int<111> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>, #cir.const_array<[#cir.int<119> : !s8i, #cir.int<111> : !s8i, #cir.int<114> : !s8i, #cir.int<108> : !s8i, #cir.int<100> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>]>
100+
// CIR: cir.store{{.*}} %[[CONST]], %[[MATRIX]]
103101

104102
// LLVM-LABEL: define{{.*}} @test_string_array_in_array
105-
// LLVM: alloca [2 x [6 x i8]]
106-
// LLVM: call void @llvm.memcpy
107-
// LLVM: call void @llvm.memcpy
103+
// LLVM: %[[MATRIX:.*]] = alloca [2 x [6 x i8]]
104+
// LLVM: store [2 x [6 x i8]] {{\[}}[6 x i8] c"hello\00", [6 x i8] c"world\00"], ptr %[[MATRIX]]
108105

109106
// OGCG-LABEL: define{{.*}} @test_string_array_in_array
110107
// OGCG: alloca [2 x [6 x i8]]

0 commit comments

Comments
 (0)