[clang] 6b4803f - [CIR] Add support for __builtin_alloca (#157116)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 9 06:47:22 PDT 2025
Author: Morris Hafner
Date: 2025-09-09T15:47:17+02:00
New Revision: 6b4803fd484e2562281a6d742495656637e3df3a
URL: https://github.com/llvm/llvm-project/commit/6b4803fd484e2562281a6d742495656637e3df3a
DIFF: https://github.com/llvm/llvm-project/commit/6b4803fd484e2562281a6d742495656637e3df3a.diff
LOG: [CIR] Add support for __builtin_alloca (#157116)
This patch adds support for the alloca builtin and extends AllocaOp with
a dynamic size argument.
Added:
clang/test/CIR/IR/alloca.cir
Modified:
clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/test/CIR/CodeGen/builtin_call.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 8dc4ca2f9c4e1..a3f167e3cde2c 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -221,6 +221,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::ConstPtrAttr::get(type, getI64IntegerAttr(value));
}
+ mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
+ mlir::Type type, llvm::StringRef name,
+ mlir::IntegerAttr alignment,
+ mlir::Value dynAllocSize) {
+ return cir::AllocaOp::create(*this, loc, addrType, type, name, alignment,
+ dynAllocSize);
+ }
+
+ mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
+ mlir::Type type, llvm::StringRef name,
+ clang::CharUnits alignment,
+ mlir::Value dynAllocSize) {
+ mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
+ return createAlloca(loc, addrType, type, name, alignmentAttr, dynAllocSize);
+ }
+
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
mlir::Type type, llvm::StringRef name,
mlir::IntegerAttr alignment) {
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bbbd10bfd8ad6..34ee0b8b29a0c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -341,6 +341,11 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
The presence of the `const` attribute indicates that the local variable is
declared with C/C++ `const` keyword.
+ The `dynAllocSize` specifies the size to dynamically allocate on the stack
+ and ignores the allocation size based on the original type. This is useful
+ when handling VLAs or the `alloca` builtin and is omitted when declaring
+ regular local variables.
+
The result type is a pointer to the input's type.
Example:
@@ -356,6 +361,7 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
}];
let arguments = (ins
+ Optional<CIR_AnyFundamentalIntType>:$dynAllocSize,
TypeAttr:$allocaType,
StrAttr:$name,
UnitAttr:$init,
@@ -372,16 +378,29 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
OpBuilder<(ins "mlir::Type":$addr,
"mlir::Type":$allocaType,
"llvm::StringRef":$name,
- "mlir::IntegerAttr":$alignment)>
+ "mlir::IntegerAttr":$alignment)>,
+
+ OpBuilder<(ins "mlir::Type":$addr,
+ "mlir::Type":$allocaType,
+ "llvm::StringRef":$name,
+ "mlir::IntegerAttr":$alignment,
+ "mlir::Value":$dynAllocSize),
+ [{
+ if (dynAllocSize)
+ $_state.addOperands(dynAllocSize);
+ build($_builder, $_state, addr, allocaType, name, alignment);
+ }]>
];
let extraClassDeclaration = [{
// Whether the alloca input type is a pointer.
bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
+ bool isDynamic() { return (bool)getDynAllocSize(); }
}];
let assemblyFormat = [{
$allocaType `,` qualified(type($addr)) `,`
+ ($dynAllocSize^ `:` type($dynAllocSize) `,`)?
`[` $name
(`,` `init` $init^)?
(`,` `const` $constant^)?
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 948e3feca2bb5..1e64278d118b5 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -62,7 +62,6 @@ struct MissingFeatures {
static bool opAllocaEscapeByReference() { return false; }
static bool opAllocaReference() { return false; }
static bool opAllocaAnnotations() { return false; }
- static bool opAllocaDynAllocSize() { return false; }
static bool opAllocaCaptureByInit() { return false; }
// FuncOp handling
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index b68e91f64dc84..7d410dbf07a01 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -149,6 +149,57 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
emitVAEnd(emitVAListRef(e->getArg(0)).getPointer());
return {};
+ case Builtin::BIalloca:
+ case Builtin::BI_alloca:
+ case Builtin::BI__builtin_alloca_uninitialized:
+ case Builtin::BI__builtin_alloca: {
+ // Get alloca size input
+ mlir::Value size = emitScalarExpr(e->getArg(0));
+
+ // The alignment of the alloca should correspond to __BIGGEST_ALIGNMENT__.
+ const TargetInfo &ti = getContext().getTargetInfo();
+ const CharUnits suitableAlignmentInBytes =
+ getContext().toCharUnitsFromBits(ti.getSuitableAlign());
+
+ // Emit the alloca op with type `u8 *` to match the semantics of
+ // `llvm.alloca`. We later bitcast the type to `void *` to match the
+ // semantics of C/C++
+ // FIXME(cir): It may make sense to allow AllocaOp of type `u8` to return a
+ // pointer of type `void *`. This will require a change to the allocaOp
+ // verifier.
+ mlir::Value allocaAddr = builder.createAlloca(
+ getLoc(e->getSourceRange()), builder.getUInt8PtrTy(),
+ builder.getUInt8Ty(), "bi_alloca", suitableAlignmentInBytes, size);
+
+ // Initialize the allocated buffer if required.
+ if (builtinID != Builtin::BI__builtin_alloca_uninitialized) {
+ // Initialize the alloca with the given size and alignment according to
+ // the lang opts. Only the trivial non-initialization is supported for
+ // now.
+
+ switch (getLangOpts().getTrivialAutoVarInit()) {
+ case LangOptions::TrivialAutoVarInitKind::Uninitialized:
+ // Nothing to initialize.
+ break;
+ case LangOptions::TrivialAutoVarInitKind::Zero:
+ case LangOptions::TrivialAutoVarInitKind::Pattern:
+ cgm.errorNYI("trivial auto var init");
+ break;
+ }
+ }
+
+ // An alloca will always return a pointer to the alloca (stack) address
+ // space. This address space need not be the same as the AST / Language
+ // default (e.g. in C / C++ auto vars are in the generic address space). At
+ // the AST level this is handled within CreateTempAlloca et al., but for the
+ // builtin / dynamic alloca we have to handle it here.
+ assert(!cir::MissingFeatures::addressSpace());
+
+ // Bitcast the alloca to the expected type.
+ return RValue::get(
+ builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
+ }
+
case Builtin::BIfabs:
case Builtin::BIfabsf:
case Builtin::BIfabsl:
diff --git a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
index 4e0a041d26ce1..72bbf08c79b16 100644
--- a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
@@ -42,7 +42,8 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
if (alloca->getBlock() == &entryBlock)
return;
// Don't hoist allocas with dynamic alloca size.
- assert(!cir::MissingFeatures::opAllocaDynAllocSize());
+ if (alloca.getDynAllocSize())
+ return;
// Hoist allocas into the entry block.
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index dc2bb5feeaf9d..4b865d120fabd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1098,9 +1098,12 @@ mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
cir::AllocaOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- assert(!cir::MissingFeatures::opAllocaDynAllocSize());
- mlir::Value size = rewriter.create<mlir::LLVM::ConstantOp>(
- op.getLoc(), typeConverter->convertType(rewriter.getIndexType()), 1);
+ mlir::Value size =
+ op.isDynamic()
+ ? adaptor.getDynAllocSize()
+ : rewriter.create<mlir::LLVM::ConstantOp>(
+ op.getLoc(),
+ typeConverter->convertType(rewriter.getIndexType()), 1);
mlir::Type elementTy =
convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType());
mlir::Type resultTy =
diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp b/clang/test/CIR/CodeGen/builtin_call.cpp
index 09be7937a4330..853d894a3311b 100644
--- a/clang/test/CIR/CodeGen/builtin_call.cpp
+++ b/clang/test/CIR/CodeGen/builtin_call.cpp
@@ -211,6 +211,10 @@ void unreachable() {
// LLVM: unreachable
// LLVM: }
+// OGCG-LABEL: @_Z11unreachablev
+// OGCG: unreachable
+// OGCG: }
+
void f1();
void unreachable2() {
__builtin_unreachable();
@@ -229,6 +233,9 @@ void unreachable2() {
// LLVM-NEXT: call void @_Z2f1v()
// LLVM: }
+// OGCG-LABEL: @_Z12unreachable2v
+// OGCG: unreachable
+
void trap() {
__builtin_trap();
}
@@ -241,6 +248,10 @@ void trap() {
// LLVM: call void @llvm.trap()
// LLVM: }
+// OGCG-LABEL: @_Z4trapv
+// OGCG: call void @llvm.trap()
+// OGCG: }
+
void trap2() {
__builtin_trap();
f1();
@@ -258,3 +269,40 @@ void trap2() {
// LLVM: {{.+}}:
// LLVM-NEXT: call void @_Z2f1v()
// LLVM: }
+
+// OGCG-LABEL: define{{.*}} void @_Z5trap2v
+// OGCG: call void @llvm.trap()
+// OGCG-NEXT: call void @_Z2f1v()
+// OGCG: ret void
+// OGCG: }
+
+void *test_alloca(unsigned long n) {
+ return __builtin_alloca(n);
+}
+
+// CIR-LABEL: @_Z11test_allocam(
+// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
+
+// LLVM-LABEL: @_Z11test_allocam(
+// LLVM: alloca i8, i64 %{{.+}}
+
+// OGCG-LABEL: @_Z11test_allocam(
+// OGCG: alloca i8, i64 %{{.+}}
+
+bool test_multiple_allocas(unsigned long n) {
+ void *a = __builtin_alloca(n);
+ void *b = __builtin_alloca(n);
+ return a != b;
+}
+
+// CIR-LABEL: @_Z21test_multiple_allocasm(
+// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
+// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
+
+// LLVM-LABEL: @_Z21test_multiple_allocasm(
+// LLVM: alloca i8, i64 %{{.+}}
+// LLVM: alloca i8, i64 %{{.+}}
+
+// OGCG-LABEL: @_Z21test_multiple_allocasm(
+// OGCG: alloca i8, i64 %{{.+}}
+// OGCG: alloca i8, i64 %{{.+}}
diff --git a/clang/test/CIR/IR/alloca.cir b/clang/test/CIR/IR/alloca.cir
new file mode 100644
index 0000000000000..12f7e6ac6a914
--- /dev/null
+++ b/clang/test/CIR/IR/alloca.cir
@@ -0,0 +1,32 @@
+
+// RUN: cir-opt %s | FileCheck %s
+
+!u64i = !cir.int<u, 64>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+module {
+ cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
+ %0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
+ %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
+ cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
+ %2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
+ // Dynamically sized alloca
+ %3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
+ %4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
+ cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ %5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ cir.return %5 : !cir.ptr<!void>
+ }
+
+ // CHECK: cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
+ // CHECK: %0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
+ // CHECK: %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
+ // CHECK: cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
+ // CHECK: %2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
+ // CHECK: %3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
+ // CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
+ // CHECK: cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ // CHECK: %5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CHECK: cir.return %5 : !cir.ptr<!void>
+ // CHECK: }
+}
More information about the cfe-commits
mailing list