Skip to content

Commit 6b4803f

Browse files
authored
[CIR] Add support for __builtin_alloca (#157116)
This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument.
1 parent 78fb8b0 commit 6b4803f

File tree

8 files changed

+175
-6
lines changed

8 files changed

+175
-6
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
221221
return cir::ConstPtrAttr::get(type, getI64IntegerAttr(value));
222222
}
223223

224+
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
225+
mlir::Type type, llvm::StringRef name,
226+
mlir::IntegerAttr alignment,
227+
mlir::Value dynAllocSize) {
228+
return cir::AllocaOp::create(*this, loc, addrType, type, name, alignment,
229+
dynAllocSize);
230+
}
231+
232+
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
233+
mlir::Type type, llvm::StringRef name,
234+
clang::CharUnits alignment,
235+
mlir::Value dynAllocSize) {
236+
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
237+
return createAlloca(loc, addrType, type, name, alignmentAttr, dynAllocSize);
238+
}
239+
224240
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
225241
mlir::Type type, llvm::StringRef name,
226242
mlir::IntegerAttr alignment) {

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,11 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
341341
The presence of the `const` attribute indicates that the local variable is
342342
declared with C/C++ `const` keyword.
343343

344+
The `dynAllocSize` specifies the size to dynamically allocate on the stack
345+
and ignores the allocation size based on the original type. This is useful
346+
when handling VLAs or the `alloca` builtin and is omitted when declaring
347+
regular local variables.
348+
344349
The result type is a pointer to the input's type.
345350

346351
Example:
@@ -356,6 +361,7 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
356361
}];
357362

358363
let arguments = (ins
364+
Optional<CIR_AnyFundamentalIntType>:$dynAllocSize,
359365
TypeAttr:$allocaType,
360366
StrAttr:$name,
361367
UnitAttr:$init,
@@ -372,16 +378,29 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
372378
OpBuilder<(ins "mlir::Type":$addr,
373379
"mlir::Type":$allocaType,
374380
"llvm::StringRef":$name,
375-
"mlir::IntegerAttr":$alignment)>
381+
"mlir::IntegerAttr":$alignment)>,
382+
383+
OpBuilder<(ins "mlir::Type":$addr,
384+
"mlir::Type":$allocaType,
385+
"llvm::StringRef":$name,
386+
"mlir::IntegerAttr":$alignment,
387+
"mlir::Value":$dynAllocSize),
388+
[{
389+
if (dynAllocSize)
390+
$_state.addOperands(dynAllocSize);
391+
build($_builder, $_state, addr, allocaType, name, alignment);
392+
}]>
376393
];
377394

378395
let extraClassDeclaration = [{
379396
// Whether the alloca input type is a pointer.
380397
bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
398+
bool isDynamic() { return (bool)getDynAllocSize(); }
381399
}];
382400

383401
let assemblyFormat = [{
384402
$allocaType `,` qualified(type($addr)) `,`
403+
($dynAllocSize^ `:` type($dynAllocSize) `,`)?
385404
`[` $name
386405
(`,` `init` $init^)?
387406
(`,` `const` $constant^)?

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ struct MissingFeatures {
6262
static bool opAllocaEscapeByReference() { return false; }
6363
static bool opAllocaReference() { return false; }
6464
static bool opAllocaAnnotations() { return false; }
65-
static bool opAllocaDynAllocSize() { return false; }
6665
static bool opAllocaCaptureByInit() { return false; }
6766

6867
// FuncOp handling

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,57 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
149149
emitVAEnd(emitVAListRef(e->getArg(0)).getPointer());
150150
return {};
151151

152+
case Builtin::BIalloca:
153+
case Builtin::BI_alloca:
154+
case Builtin::BI__builtin_alloca_uninitialized:
155+
case Builtin::BI__builtin_alloca: {
156+
// Get alloca size input
157+
mlir::Value size = emitScalarExpr(e->getArg(0));
158+
159+
// The alignment of the alloca should correspond to __BIGGEST_ALIGNMENT__.
160+
const TargetInfo &ti = getContext().getTargetInfo();
161+
const CharUnits suitableAlignmentInBytes =
162+
getContext().toCharUnitsFromBits(ti.getSuitableAlign());
163+
164+
// Emit the alloca op with type `u8 *` to match the semantics of
165+
// `llvm.alloca`. We later bitcast the type to `void *` to match the
166+
// semantics of C/C++
167+
// FIXME(cir): It may make sense to allow AllocaOp of type `u8` to return a
168+
// pointer of type `void *`. This will require a change to the allocaOp
169+
// verifier.
170+
mlir::Value allocaAddr = builder.createAlloca(
171+
getLoc(e->getSourceRange()), builder.getUInt8PtrTy(),
172+
builder.getUInt8Ty(), "bi_alloca", suitableAlignmentInBytes, size);
173+
174+
// Initialize the allocated buffer if required.
175+
if (builtinID != Builtin::BI__builtin_alloca_uninitialized) {
176+
// Initialize the alloca with the given size and alignment according to
177+
// the lang opts. Only the trivial non-initialization is supported for
178+
// now.
179+
180+
switch (getLangOpts().getTrivialAutoVarInit()) {
181+
case LangOptions::TrivialAutoVarInitKind::Uninitialized:
182+
// Nothing to initialize.
183+
break;
184+
case LangOptions::TrivialAutoVarInitKind::Zero:
185+
case LangOptions::TrivialAutoVarInitKind::Pattern:
186+
cgm.errorNYI("trivial auto var init");
187+
break;
188+
}
189+
}
190+
191+
// An alloca will always return a pointer to the alloca (stack) address
192+
// space. This address space need not be the same as the AST / Language
193+
// default (e.g. in C / C++ auto vars are in the generic address space). At
194+
// the AST level this is handled within CreateTempAlloca et al., but for the
195+
// builtin / dynamic alloca we have to handle it here.
196+
assert(!cir::MissingFeatures::addressSpace());
197+
198+
// Bitcast the alloca to the expected type.
199+
return RValue::get(
200+
builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
201+
}
202+
152203
case Builtin::BIfabs:
153204
case Builtin::BIfabsf:
154205
case Builtin::BIfabsl:

clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
4242
if (alloca->getBlock() == &entryBlock)
4343
return;
4444
// Don't hoist allocas with dynamic alloca size.
45-
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
45+
if (alloca.getDynAllocSize())
46+
return;
4647

4748
// Hoist allocas into the entry block.
4849

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,9 +1098,12 @@ mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
10981098
mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
10991099
cir::AllocaOp op, OpAdaptor adaptor,
11001100
mlir::ConversionPatternRewriter &rewriter) const {
1101-
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
1102-
mlir::Value size = rewriter.create<mlir::LLVM::ConstantOp>(
1103-
op.getLoc(), typeConverter->convertType(rewriter.getIndexType()), 1);
1101+
mlir::Value size =
1102+
op.isDynamic()
1103+
? adaptor.getDynAllocSize()
1104+
: rewriter.create<mlir::LLVM::ConstantOp>(
1105+
op.getLoc(),
1106+
typeConverter->convertType(rewriter.getIndexType()), 1);
11041107
mlir::Type elementTy =
11051108
convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType());
11061109
mlir::Type resultTy =

clang/test/CIR/CodeGen/builtin_call.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ void unreachable() {
211211
// LLVM: unreachable
212212
// LLVM: }
213213

214+
// OGCG-LABEL: @_Z11unreachablev
215+
// OGCG: unreachable
216+
// OGCG: }
217+
214218
void f1();
215219
void unreachable2() {
216220
__builtin_unreachable();
@@ -229,6 +233,9 @@ void unreachable2() {
229233
// LLVM-NEXT: call void @_Z2f1v()
230234
// LLVM: }
231235

236+
// OGCG-LABEL: @_Z12unreachable2v
237+
// OGCG: unreachable
238+
232239
void trap() {
233240
__builtin_trap();
234241
}
@@ -241,6 +248,10 @@ void trap() {
241248
// LLVM: call void @llvm.trap()
242249
// LLVM: }
243250

251+
// OGCG-LABEL: @_Z4trapv
252+
// OGCG: call void @llvm.trap()
253+
// OGCG: }
254+
244255
void trap2() {
245256
__builtin_trap();
246257
f1();
@@ -258,3 +269,40 @@ void trap2() {
258269
// LLVM: {{.+}}:
259270
// LLVM-NEXT: call void @_Z2f1v()
260271
// LLVM: }
272+
273+
// OGCG-LABEL: define{{.*}} void @_Z5trap2v
274+
// OGCG: call void @llvm.trap()
275+
// OGCG-NEXT: call void @_Z2f1v()
276+
// OGCG: ret void
277+
// OGCG: }
278+
279+
void *test_alloca(unsigned long n) {
280+
return __builtin_alloca(n);
281+
}
282+
283+
// CIR-LABEL: @_Z11test_allocam(
284+
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
285+
286+
// LLVM-LABEL: @_Z11test_allocam(
287+
// LLVM: alloca i8, i64 %{{.+}}
288+
289+
// OGCG-LABEL: @_Z11test_allocam(
290+
// OGCG: alloca i8, i64 %{{.+}}
291+
292+
bool test_multiple_allocas(unsigned long n) {
293+
void *a = __builtin_alloca(n);
294+
void *b = __builtin_alloca(n);
295+
return a != b;
296+
}
297+
298+
// CIR-LABEL: @_Z21test_multiple_allocasm(
299+
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
300+
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
301+
302+
// LLVM-LABEL: @_Z21test_multiple_allocasm(
303+
// LLVM: alloca i8, i64 %{{.+}}
304+
// LLVM: alloca i8, i64 %{{.+}}
305+
306+
// OGCG-LABEL: @_Z21test_multiple_allocasm(
307+
// OGCG: alloca i8, i64 %{{.+}}
308+
// OGCG: alloca i8, i64 %{{.+}}

clang/test/CIR/IR/alloca.cir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
// RUN: cir-opt %s | FileCheck %s
3+
4+
!u64i = !cir.int<u, 64>
5+
!u8i = !cir.int<u, 8>
6+
!void = !cir.void
7+
module {
8+
cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
9+
%0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
10+
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
11+
cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
12+
%2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
13+
// Dynamically sized alloca
14+
%3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
15+
%4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
16+
cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
17+
%5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
18+
cir.return %5 : !cir.ptr<!void>
19+
}
20+
21+
// CHECK: cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
22+
// CHECK: %0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
23+
// CHECK: %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
24+
// CHECK: cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
25+
// CHECK: %2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
26+
// CHECK: %3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
27+
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
28+
// CHECK: cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
29+
// CHECK: %5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
30+
// CHECK: cir.return %5 : !cir.ptr<!void>
31+
// CHECK: }
32+
}

0 commit comments

Comments
 (0)