[clang] [CIR] Emit allocas into the proper lexical scope (PR #132468)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 25 14:23:03 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/132468
>From 9e84f55c840890befed2720e498c068ff4ca36cd Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 21 Mar 2025 13:11:11 -0700
Subject: [PATCH 1/4] [CIR] Emit allocas into the proper lexical scope
Alloca operations were being emitted into the entry block of the current
function unconditionally, even if the variable they represented was
declared in a different scope. This change upstreams the code for handling
insertion of the alloca into the proper lexcial scope. It also adds a
CIR-to-CIR transformation to hoist allocas to the function entry block,
which is necessary to produce the expected LLVM IR during lowering.
---
clang/include/clang/CIR/Dialect/Passes.h | 1 +
clang/include/clang/CIR/Dialect/Passes.td | 10 ++
clang/include/clang/CIR/MissingFeatures.h | 7 +-
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 3 +-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 37 ++++--
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 6 +-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 10 +-
.../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 +
.../CIR/Dialect/Transforms/HoistAllocas.cpp | 81 +++++++++++++
clang/lib/CIR/Lowering/CIRPasses.cpp | 1 +
clang/test/CIR/CodeGen/loop.cpp | 97 +++++++++++++++
clang/test/CIR/Transforms/hoist-allocas.cir | 113 ++++++++++++++++++
clang/tools/cir-opt/cir-opt.cpp | 4 +
13 files changed, 355 insertions(+), 16 deletions(-)
create mode 100644 clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
create mode 100644 clang/test/CIR/Transforms/hoist-allocas.cir
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
index aa84241bdecf0..133eb462dcf1f 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -22,6 +22,7 @@ namespace mlir {
std::unique_ptr<Pass> createCIRCanonicalizePass();
std::unique_ptr<Pass> createCIRFlattenCFGPass();
+std::unique_ptr<Pass> createHoistAllocasPass();
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index 16133d020a7c8..74c255861c879 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -29,6 +29,16 @@ def CIRCanonicalize : Pass<"cir-canonicalize"> {
let dependentDialects = ["cir::CIRDialect"];
}
+def HoistAllocas : Pass<"cir-hoist-allocas"> {
+ let summary = "Hoist allocas to the entry of the function";
+ let description = [{
+ This pass hoist all non-dynamic allocas to the entry of the function.
+ This is helpful for later code generation.
+ }];
+ let constructor = "mlir::createHoistAllocasPass()";
+ let dependentDialects = ["cir::CIRDialect"];
+}
+
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
let summary = "Produces flatten CFG";
let description = [{
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 795f5e707fbb5..3a102d90aba8f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -130,13 +130,16 @@ struct MissingFeatures {
static bool continueOp() { return false; }
static bool ifOp() { return false; }
static bool labelOp() { return false; }
+ static bool ptrDiffOp() { return false; }
+ static bool ptrStrideOp() { return false; }
static bool selectOp() { return false; }
static bool switchOp() { return false; }
static bool ternaryOp() { return false; }
static bool tryOp() { return false; }
static bool zextOp() { return false; }
- static bool ptrStrideOp() { return false; }
- static bool ptrDiffOp() { return false; }
+
+ // Future CIR attributes
+ static bool optInfoAttr() { return false; }
};
} // namespace cir
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index a93e8dbcb42de..f2153c23ebb43 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -49,7 +49,8 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
// A normal fixed sized variable becomes an alloca in the entry block,
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
- address = createTempAlloca(allocaTy, alignment, loc, d.getName());
+ address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
+ /*insertIntoFnEntryBlock=*/false);
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
emission.Addr = address;
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index db062f95f122e..8fd09b4cfefeb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -317,10 +317,27 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
}
mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
- mlir::Location loc,
- CharUnits alignment) {
- mlir::Block *entryBlock = getCurFunctionEntryBlock();
+ mlir::Location loc, CharUnits alignment,
+ bool insertIntoFnEntryBlock,
+ mlir::Value arraySize) {
+ mlir::Block *entryBlock = insertIntoFnEntryBlock
+ ? getCurFunctionEntryBlock()
+ : curLexScope->getEntryBlock();
+
+ // If this is an alloca in the entry basic block of a cir.try and there's
+ // a surrounding cir.scope, make sure the alloca ends up in the surrounding
+ // scope instead. This is necessary in order to guarantee all SSA values are
+ // reachable during cleanups.
+ assert(!cir::MissingFeatures::tryOp());
+
+ return emitAlloca(name, ty, loc, alignment,
+ builder.getBestAllocaInsertPoint(entryBlock), arraySize);
+}
+mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
+ mlir::Location loc, CharUnits alignment,
+ mlir::OpBuilder::InsertPoint ip,
+ mlir::Value arraySize) {
// CIR uses its own alloca address space rather than follow the target data
// layout like original CodeGen. The data layout awareness should be done in
// the lowering pass instead.
@@ -331,7 +348,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
mlir::Value addr;
{
mlir::OpBuilder::InsertionGuard guard(builder);
- builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock));
+ builder.restoreInsertionPoint(ip);
addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
/*var type*/ ty, name, alignIntAttr);
assert(!cir::MissingFeatures::astVarDeclInterface());
@@ -346,11 +363,13 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
return builder.createDummyValue(loc, t, alignment);
}
-/// This creates an alloca and inserts it at the current insertion point of the
-/// builder.
+/// This creates an alloca and inserts it into the entry block if
+/// \p insertIntoFnEntryBlock is true, otherwise it inserts it at the current
+/// insertion point of the builder.
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
- mlir::Location loc,
- const Twine &name) {
- mlir::Value alloca = emitAlloca(name.str(), ty, loc, align);
+ mlir::Location loc, const Twine &name,
+ bool insertIntoFnEntryBlock) {
+ mlir::Value alloca =
+ emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock);
return Address(alloca, ty, align);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 4ba3d416007f2..d1c5adc2c6c4b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -138,7 +138,8 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
CharUnits alignment) {
if (!type->isVoidType()) {
- fnRetAlloca = emitAlloca("__retval", convertType(type), loc, alignment);
+ fnRetAlloca = emitAlloca("__retval", convertType(type), loc, alignment,
+ /*insertIntoFnEntryBlock=*/false);
}
}
@@ -293,7 +294,8 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
mlir::Value addrVal =
emitAlloca(cast<NamedDecl>(paramVar)->getName(),
- convertType(paramVar->getType()), paramLoc, alignment);
+ convertType(paramVar->getType()), paramLoc, alignment,
+ /*insertIntoFnEntryBlock=*/false);
declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
/*isParam=*/true);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 7d1fa0712c7ac..cc04610f23fcb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -109,7 +109,13 @@ class CIRGenFunction : public CIRGenTypeCache {
public:
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
- mlir::Location loc, clang::CharUnits alignment);
+ mlir::Location loc, clang::CharUnits alignment,
+ bool insertIntoFnEntryBlock,
+ mlir::Value arraySize = nullptr);
+ mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
+ mlir::Location loc, clang::CharUnits alignment,
+ mlir::OpBuilder::InsertPoint ip,
+ mlir::Value arraySize = nullptr);
mlir::Value createDummyValue(mlir::Location loc, clang::QualType qt);
@@ -483,7 +489,7 @@ class CIRGenFunction : public CIRGenTypeCache {
LexicalScope *curLexScope = nullptr;
Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
- const Twine &name = "tmp");
+ const Twine &name, bool insertIntoFnEntryBlock);
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 648666d2461de..4678435b54c79 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -1,6 +1,7 @@
add_clang_library(MLIRCIRTransforms
CIRCanonicalize.cpp
FlattenCFG.cpp
+ HoistAllocas.cpp
DEPENDS
MLIRCIRPassIncGen
diff --git a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
new file mode 100644
index 0000000000000..e41563b89d24e
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassDetail.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/Support/LogicalResult.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/Passes.h"
+#include "clang/CIR/MissingFeatures.h"
+#include "llvm/Support/TimeProfiler.h"
+
+using namespace mlir;
+using namespace cir;
+
+namespace {
+
+struct HoistAllocasPass : public HoistAllocasBase<HoistAllocasPass> {
+
+ HoistAllocasPass() = default;
+ void runOnOperation() override;
+};
+
+static void process(mlir::ModuleOp mod, cir::FuncOp func) {
+ if (func.getRegion().empty())
+ return;
+
+ // Hoist all static allocas to the entry block.
+ mlir::Block &entryBlock = func.getRegion().front();
+ llvm::SmallVector<cir::AllocaOp> allocas;
+ func.getBody().walk([&](cir::AllocaOp alloca) {
+ if (alloca->getBlock() == &entryBlock)
+ return;
+ // Don't hoist allocas with dynamic alloca size.
+ assert(!cir::MissingFeatures::opAllocaDynAllocSize());
+ allocas.push_back(alloca);
+ });
+ if (allocas.empty())
+ return;
+
+ mlir::Operation *insertPoint = &*entryBlock.begin();
+
+ for (auto alloca : allocas) {
+ // Preserving the `const` attribute on hoisted allocas can cause LLVM to
+ // incorrectly introduce invariant group metadata in some circumstances.
+ // The incubator performs some analysis to determine whether the attribute
+ // can be preserved, but it only runs this analysis when optimizations are
+ // enabled. Until we start tracking the optimization level, we can just
+ // always remove the `const` attribute.
+ assert(!cir::MissingFeatures::optInfoAttr());
+ if (alloca.getConstant())
+ alloca.setConstant(false);
+
+ alloca->moveBefore(insertPoint);
+ }
+}
+
+void HoistAllocasPass::runOnOperation() {
+ llvm::TimeTraceScope scope("Hoist Allocas");
+ llvm::SmallVector<Operation *, 16> ops;
+
+ Operation *op = getOperation();
+ auto mod = mlir::dyn_cast<mlir::ModuleOp>(op);
+ if (!mod)
+ mod = op->getParentOfType<mlir::ModuleOp>();
+
+ getOperation()->walk([&](cir::FuncOp op) { process(mod, op); });
+}
+
+} // namespace
+
+std::unique_ptr<Pass> mlir::createHoistAllocasPass() {
+ return std::make_unique<HoistAllocasPass>();
+}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 1616ac6145151..a37a0480a56ac 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -37,6 +37,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
namespace mlir {
void populateCIRPreLoweringPasses(OpPassManager &pm) {
+ pm.addPass(createHoistAllocasPass());
pm.addPass(createCIRFlattenCFGPass());
}
diff --git a/clang/test/CIR/CodeGen/loop.cpp b/clang/test/CIR/CodeGen/loop.cpp
index 449317016e99d..61d18684ed1b2 100644
--- a/clang/test/CIR/CodeGen/loop.cpp
+++ b/clang/test/CIR/CodeGen/loop.cpp
@@ -44,3 +44,100 @@ void l0() {
// OGCG: br label %[[FOR_COND:.*]]
// OGCG: [[FOR_COND]]:
// OGCG: br label %[[FOR_COND]]
+
+void l1() {
+ for (int i = 0; ; ) {
+ }
+}
+
+// CIR: cir.func @l1
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT: cir.for : cond {
+// CIR-NEXT: %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT: cir.condition(%[[TRUE]])
+// CIR-NEXT: } body {
+// CIR-NEXT: cir.yield
+// CIR-NEXT: } step {
+// CIR-NEXT: cir.yield
+// CIR-NEXT: }
+// CIR-NEXT: }
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+
+// LLVM: define void @l1()
+// LLVM: %[[I:.*]] = alloca i32, i64 1, align 4
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: store i32 0, ptr %[[I]], align 4
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL2]]:
+// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL5:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: br label %[[LABEL4:.*]]
+// LLVM: [[LABEL4]]:
+// LLVM: br label %[[LABEL2]]
+// LLVM: [[LABEL5]]:
+// LLVM: br label %[[LABEL6:.*]]
+// LLVM: [[LABEL6]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z2l1v()
+// OGCG: entry:
+// OGCG: %[[I:.*]] = alloca i32, align 4
+// OGCG: store i32 0, ptr %[[I]], align 4
+// OGCG: br label %[[FOR_COND:.*]]
+// OGCG: [[FOR_COND]]:
+// OGCG: br label %[[FOR_COND]]
+
+void l2() {
+ for (;;) {
+ int i = 0;
+ }
+}
+
+// CIR: cir.func @l2
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: cir.for : cond {
+// CIR-NEXT: %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT: cir.condition(%[[TRUE]])
+// CIR-NEXT: } body {
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT: }
+// CIR-NEXT: cir.yield
+// CIR-NEXT: } step {
+// CIR-NEXT: cir.yield
+// CIR-NEXT: }
+// CIR-NEXT: }
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+
+// LLVM: define void @l2()
+// LLVM: %[[I:.*]] = alloca i32, i64 1, align 4
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL2]]:
+// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL5:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: store i32 0, ptr %[[I]], align 4
+// LLVM: br label %[[LABEL4:.*]]
+// LLVM: [[LABEL4]]:
+// LLVM: br label %[[LABEL2]]
+// LLVM: [[LABEL5]]:
+// LLVM: br label %[[LABEL6:.*]]
+// LLVM: [[LABEL6]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z2l2v()
+// OGCG: entry:
+// OGCG: %[[I:.*]] = alloca i32, align 4
+// OGCG: br label %[[FOR_COND:.*]]
+// OGCG: [[FOR_COND]]:
+// OGCG: store i32 0, ptr %[[I]], align 4
+// OGCG: br label %[[FOR_COND]]
diff --git a/clang/test/CIR/Transforms/hoist-allocas.cir b/clang/test/CIR/Transforms/hoist-allocas.cir
new file mode 100644
index 0000000000000..df7b9f48be9dc
--- /dev/null
+++ b/clang/test/CIR/Transforms/hoist-allocas.cir
@@ -0,0 +1,113 @@
+ // RUN: cir-opt %s -cir-hoist-allocas -o - | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+#true = #cir.bool<true> : !cir.bool
+
+module {
+ cir.func @l1() {
+ cir.scope {
+ %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+ %1 = cir.const #cir.int<0> : !s32i
+ cir.store %1, %0 : !s32i, !cir.ptr<!s32i>
+ cir.for : cond {
+ %2 = cir.const #true
+ cir.condition(%2)
+ } body {
+ cir.yield
+ } step {
+ cir.yield
+ }
+ }
+ cir.return
+ }
+ // CHECK: cir.func @l1
+ // CHECK-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+ // CHECK-NEXT: cir.scope {
+ // CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+ // CHECK-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+ // CHECK-NEXT: cir.for : cond {
+ // CHECK-NEXT: %[[TRUE:.*]] = cir.const #true
+ // CHECK-NEXT: cir.condition(%[[TRUE]])
+ // CHECK-NEXT: } body {
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: } step {
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: }
+ // CHECK-NEXT: }
+ // CHECK-NEXT: cir.return
+ // CHECK-NEXT: }
+
+ cir.func @l2() {
+ cir.scope {
+ cir.for : cond {
+ %0 = cir.const #true
+ cir.condition(%0)
+ } body {
+ cir.scope {
+ %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+ %2 = cir.const #cir.int<0> : !s32i
+ cir.store %2, %1 : !s32i, !cir.ptr<!s32i>
+ }
+ cir.yield
+ } step {
+ cir.yield
+ }
+ }
+ cir.return
+ }
+ // CHECK: cir.func @l2
+ // CHECK-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+ // CHECK-NEXT: cir.scope {
+ // CHECK-NEXT: cir.for : cond {
+ // CHECK-NEXT: %[[TRUE:.*]] = cir.const #true
+ // CHECK-NEXT: cir.condition(%[[TRUE]])
+ // CHECK-NEXT: } body {
+ // CHECK-NEXT: cir.scope {
+ // CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+ // CHECK-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+ // CHECK-NEXT: }
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: } step {
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: }
+ // CHECK-NEXT: }
+ // CHECK-NEXT: cir.return
+ // CHECK-NEXT: }
+
+ cir.func @l3() {
+ cir.scope {
+ cir.for : cond {
+ %0 = cir.const #true
+ cir.condition(%0)
+ } body {
+ cir.scope {
+ %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init, const] {alignment = 4 : i64}
+ %2 = cir.const #cir.int<0> : !s32i
+ cir.store %2, %1 : !s32i, !cir.ptr<!s32i>
+ }
+ cir.yield
+ } step {
+ cir.yield
+ }
+ }
+ cir.return
+ }
+ // CHECK: cir.func @l3
+ // CHECK-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+ // CHECK-NEXT: cir.scope {
+ // CHECK-NEXT: cir.for : cond {
+ // CHECK-NEXT: %[[TRUE:.*]] = cir.const #true
+ // CHECK-NEXT: cir.condition(%[[TRUE]])
+ // CHECK-NEXT: } body {
+ // CHECK-NEXT: cir.scope {
+ // CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+ // CHECK-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+ // CHECK-NEXT: }
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: } step {
+ // CHECK-NEXT: cir.yield
+ // CHECK-NEXT: }
+ // CHECK-NEXT: }
+ // CHECK-NEXT: cir.return
+ // CHECK-NEXT: }
+}
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index 79a26c7986f0b..e50fa70582966 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -48,6 +48,10 @@ int main(int argc, char **argv) {
return mlir::createCIRFlattenCFGPass();
});
+ ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
+ return mlir::createHoistAllocasPass();
+ });
+
mlir::registerTransformsPasses();
return mlir::asMainReturnCode(MlirOptMain(
>From b256a408eb237924b9098c2a2625ce8f750ed078 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 21 Mar 2025 13:18:43 -0700
Subject: [PATCH 2/4] Fix auto
---
clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
index e41563b89d24e..3306e825f17f7 100644
--- a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
@@ -47,7 +47,7 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
mlir::Operation *insertPoint = &*entryBlock.begin();
- for (auto alloca : allocas) {
+ for (cir::AllocaOp alloca : allocas) {
// Preserving the `const` attribute on hoisted allocas can cause LLVM to
// incorrectly introduce invariant group metadata in some circumstances.
// The incubator performs some analysis to determine whether the attribute
>From df52d2f0159d58f51ff616a4afd3f46c0f93f909 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Mon, 24 Mar 2025 12:43:39 -0700
Subject: [PATCH 3/4] Address review feedback
---
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 2 +-
clang/test/CIR/CodeGen/loop.cpp | 48 ++++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index d1c5adc2c6c4b..47fc90836fca6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -295,7 +295,7 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
mlir::Value addrVal =
emitAlloca(cast<NamedDecl>(paramVar)->getName(),
convertType(paramVar->getType()), paramLoc, alignment,
- /*insertIntoFnEntryBlock=*/false);
+ /*insertIntoFnEntryBlock=*/true);
declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
/*isParam=*/true);
diff --git a/clang/test/CIR/CodeGen/loop.cpp b/clang/test/CIR/CodeGen/loop.cpp
index 61d18684ed1b2..f0b570a92964d 100644
--- a/clang/test/CIR/CodeGen/loop.cpp
+++ b/clang/test/CIR/CodeGen/loop.cpp
@@ -141,3 +141,51 @@ void l2() {
// OGCG: [[FOR_COND]]:
// OGCG: store i32 0, ptr %[[I]], align 4
// OGCG: br label %[[FOR_COND]]
+
+// This is the same as l2 but without a compound statement for the body.
+void l3() {
+ for (;;)
+ int i = 0;
+}
+
+// CIR: cir.func @l3
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
+// CIR-NEXT: cir.for : cond {
+// CIR-NEXT: %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT: cir.condition(%[[TRUE]])
+// CIR-NEXT: } body {
+// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT: cir.yield
+// CIR-NEXT: } step {
+// CIR-NEXT: cir.yield
+// CIR-NEXT: }
+// CIR-NEXT: }
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+
+// LLVM: define void @l3()
+// LLVM: %[[I:.*]] = alloca i32, i64 1, align 4
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL2]]:
+// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL5:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: store i32 0, ptr %[[I]], align 4
+// LLVM: br label %[[LABEL4:.*]]
+// LLVM: [[LABEL4]]:
+// LLVM: br label %[[LABEL2]]
+// LLVM: [[LABEL5]]:
+// LLVM: br label %[[LABEL6:.*]]
+// LLVM: [[LABEL6]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z2l3v()
+// OGCG: entry:
+// OGCG: %[[I:.*]] = alloca i32, align 4
+// OGCG: br label %[[FOR_COND:.*]]
+// OGCG: [[FOR_COND]]:
+// OGCG: store i32 0, ptr %[[I]], align 4
+// OGCG: br label %[[FOR_COND]]
>From 3ac208be50754fee9552253b786e2d36df70d473 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 25 Mar 2025 12:11:19 -0700
Subject: [PATCH 4/4] Refactor HoistAlloca according to suggestions
---
.../CIR/Dialect/Transforms/HoistAllocas.cpp | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
index 3306e825f17f7..4e0a041d26ce1 100644
--- a/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
@@ -34,20 +34,18 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
// Hoist all static allocas to the entry block.
mlir::Block &entryBlock = func.getRegion().front();
- llvm::SmallVector<cir::AllocaOp> allocas;
- func.getBody().walk([&](cir::AllocaOp alloca) {
+ mlir::Operation *insertPoint = &*entryBlock.begin();
+
+ // Post-order is the default, but the code below requires it, so
+ // let's not depend on the default staying that way.
+ func.getBody().walk<mlir::WalkOrder::PostOrder>([&](cir::AllocaOp alloca) {
if (alloca->getBlock() == &entryBlock)
return;
// Don't hoist allocas with dynamic alloca size.
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
- allocas.push_back(alloca);
- });
- if (allocas.empty())
- return;
- mlir::Operation *insertPoint = &*entryBlock.begin();
+ // Hoist allocas into the entry block.
- for (cir::AllocaOp alloca : allocas) {
// Preserving the `const` attribute on hoisted allocas can cause LLVM to
// incorrectly introduce invariant group metadata in some circumstances.
// The incubator performs some analysis to determine whether the attribute
@@ -59,7 +57,7 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
alloca.setConstant(false);
alloca->moveBefore(insertPoint);
- }
+ });
}
void HoistAllocasPass::runOnOperation() {
@@ -71,7 +69,12 @@ void HoistAllocasPass::runOnOperation() {
if (!mod)
mod = op->getParentOfType<mlir::ModuleOp>();
- getOperation()->walk([&](cir::FuncOp op) { process(mod, op); });
+ // If we ever introduce nested cir.function ops, we'll need to make this
+ // walk in post-order and recurse into nested functions.
+ getOperation()->walk<mlir::WalkOrder::PreOrder>([&](cir::FuncOp op) {
+ process(mod, op);
+ return mlir::WalkResult::skip();
+ });
}
} // namespace
More information about the cfe-commits
mailing list