https://github.com/mmha created https://github.com/llvm/llvm-project/pull/157116
This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument. >From 6080e710fcf5c2b8527a1c6ea1057891e5820bbf Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Fri, 5 Sep 2025 16:27:28 +0200 Subject: [PATCH] [CIR] Add support for __builtin_alloca This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 16 ++++++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 21 +++++++- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 51 +++++++++++++++++++ .../CIR/Dialect/Transforms/HoistAllocas.cpp | 3 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 9 ++-- clang/test/CIR/CodeGen/builtin_call.cpp | 10 ++++ clang/test/CIR/IR/alloca.cir | 32 ++++++++++++ 8 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 clang/test/CIR/IR/alloca.cir 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 4592078af966b..63aea2c404b54 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..ddd95466dc1d0 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. + auto 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 ee9f58c829ca9..8e8cd2a63dcd5 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1053,9 +1053,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()), + rewriter.getIntegerAttr(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..d416841c5d6fe 100644 --- a/clang/test/CIR/CodeGen/builtin_call.cpp +++ b/clang/test/CIR/CodeGen/builtin_call.cpp @@ -258,3 +258,13 @@ void trap2() { // LLVM: {{.+}}: // LLVM-NEXT: call void @_Z2f1v() // LLVM: } + +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 %{{.+}} 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: } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits