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 createCIRCanonicalizePass(); std::unique_ptr createCIRFlattenCFGPass(); +std::unique_ptr 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..47fc90836fca6 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(paramVar)->getName(), - convertType(paramVar->getType()), paramLoc, alignment); + convertType(paramVar->getType()), paramLoc, alignment, + /*insertIntoFnEntryBlock=*/true); 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..4e0a041d26ce1 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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() = 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(); + 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([&](cir::AllocaOp alloca) { + if (alloca->getBlock() == &entryBlock) + return; + // Don't hoist allocas with dynamic alloca size. + assert(!cir::MissingFeatures::opAllocaDynAllocSize()); + + // Hoist allocas into the entry block. + + // 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 ops; + + Operation *op = getOperation(); + auto mod = mlir::dyn_cast(op); + if (!mod) + mod = op->getParentOfType(); + + // 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([&](cir::FuncOp op) { + process(mod, op); + return mlir::WalkResult::skip(); + }); +} + +} // namespace + +std::unique_ptr mlir::createHoistAllocasPass() { + return std::make_unique(); +} 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..f0b570a92964d 100644 --- a/clang/test/CIR/CodeGen/loop.cpp +++ b/clang/test/CIR/CodeGen/loop.cpp @@ -44,3 +44,148 @@ 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, ["i", init] {alignment = 4 : i64} +// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr +// 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, ["i", init] {alignment = 4 : i64} +// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: cir.store %[[ZERO]], %[[I]] : !s32i, !cir.ptr +// 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]] + +// 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, ["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 +// 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]] 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 +#true = #cir.bool : !cir.bool + +module { + cir.func @l1() { + cir.scope { + %0 = cir.alloca !s32i, !cir.ptr, ["i", init] {alignment = 4 : i64} + %1 = cir.const #cir.int<0> : !s32i + cir.store %1, %0 : !s32i, !cir.ptr + 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, ["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 + // 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, ["i", init] {alignment = 4 : i64} + %2 = cir.const #cir.int<0> : !s32i + cir.store %2, %1 : !s32i, !cir.ptr + } + cir.yield + } step { + cir.yield + } + } + cir.return + } + // CHECK: cir.func @l2 + // CHECK-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr, ["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 + // 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, ["i", init, const] {alignment = 4 : i64} + %2 = cir.const #cir.int<0> : !s32i + cir.store %2, %1 : !s32i, !cir.ptr + } + cir.yield + } step { + cir.yield + } + } + cir.return + } + // CHECK: cir.func @l3 + // CHECK-NEXT: %[[I:.*]] = cir.alloca !s32i, !cir.ptr, ["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 + // 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(