Skip to content

Conversation

@mmha
Copy link
Contributor

@mmha mmha commented Sep 5, 2025

This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Sep 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 5, 2025

@llvm/pr-subscribers-clang

Author: Morris Hafner (mmha)

Changes

This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument.


Full diff: https://github.com/llvm/llvm-project/pull/157116.diff

8 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+16)
  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+20-1)
  • (modified) clang/include/clang/CIR/MissingFeatures.h (-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+51)
  • (modified) clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp (+2-1)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+6-3)
  • (modified) clang/test/CIR/CodeGen/builtin_call.cpp (+10)
  • (added) clang/test/CIR/IR/alloca.cir (+32)
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: }
+}

@llvmbot
Copy link
Member

llvmbot commented Sep 5, 2025

@llvm/pr-subscribers-clangir

Author: Morris Hafner (mmha)

Changes

This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument.


Full diff: https://github.com/llvm/llvm-project/pull/157116.diff

8 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+16)
  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+20-1)
  • (modified) clang/include/clang/CIR/MissingFeatures.h (-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+51)
  • (modified) clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp (+2-1)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+6-3)
  • (modified) clang/test/CIR/CodeGen/builtin_call.cpp (+10)
  • (added) clang/test/CIR/IR/alloca.cir (+32)
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: }
+}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const TargetInfo &TI = getContext().getTargetInfo();
const TargetInfo &ti = getContext().getTargetInfo();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a problem if there are multiple "bi_alloca" values in the same function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name string attribute isn't used for anything (or at least I couldn't find a use) so this shouldn't be an issue.

Also note that we have some precedence with array ctors and __array_idx: https://godbolt.org/z/dT3G4neYT

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the OGCG checks? This test seems to have started drifting away from doing that for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a some missing OGCG checks with the exception of __builtin_expect and __builtin_expect_with_probability because OGCG doesn't lower to the LLVM intrinsic.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once existing comments are addressed

mmha added 2 commits September 8, 2025 17:38
This patch adds support for the alloca builtin and extends AllocaOp with a dynamic size argument.
@mmha mmha merged commit 6b4803f into llvm:main Sep 9, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants