clang 24.0.0git
HoistAllocas.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "PassDetail.h"
10#include "mlir/Dialect/Func/IR/FuncOps.h"
11#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
12#include "mlir/IR/PatternMatch.h"
13#include "mlir/Support/LogicalResult.h"
14#include "mlir/Transforms/DialectConversion.h"
15#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
19#include "llvm/Support/TimeProfiler.h"
20
21using namespace mlir;
22using namespace cir;
23
24namespace mlir {
25#define GEN_PASS_DEF_HOISTALLOCAS
26#include "clang/CIR/Dialect/Passes.h.inc"
27} // namespace mlir
28
29namespace {
30
31struct HoistAllocasPass : public impl::HoistAllocasBase<HoistAllocasPass> {
32
33 HoistAllocasPass() = default;
34 void runOnOperation() override;
35};
36
37// Find the block that an alloca should be hoisted into. Allocas are normally
38// hoisted to the entry block of the enclosing function. However, an alloca may
39// be nested inside an OpenMP region such as omp.parallel, omp.teams
40// etc. Hoisting it out of these ops breaks the isolated from above requirement
41// for omp.teams and it changes privatization semantics.
42static mlir::Block *getHoistDestBlock(cir::AllocaOp alloca) {
43 mlir::Region *region = alloca->getParentRegion();
44 while (true) {
45 mlir::Operation *parentOp = region->getParentOp();
46
47 // Note: We may want some kind of interface in the future for blocking
48 // alloca hoisting since other dialects may have similar restrictions.
49 if (parentOp->hasTrait<mlir::OpTrait::IsIsolatedFromAbove>() ||
50 mlir::isa<mlir::omp::OutlineableOpenMPOpInterface>(parentOp))
51 return &region->front();
52 region = parentOp->getParentRegion();
53 }
54}
55
56static void process(mlir::ModuleOp mod, cir::FuncOp func) {
57 if (func.getRegion().empty())
58 return;
59
60 // Keep track of destination so that the order of allocas is preserved.
61 llvm::DenseMap<mlir::Block *, mlir::Operation *> insertPoints;
62
63 // Post-order is the default, but the code below requires it, so
64 // let's not depend on the default staying that way.
65 func.getBody().walk<mlir::WalkOrder::PostOrder>([&](cir::AllocaOp alloca) {
66 mlir::Block *destBlock = getHoistDestBlock(alloca);
67 // Don't hoist allocas with dynamic alloca size.
68 if (alloca.getDynAllocSize())
69 return;
70 if (alloca->getBlock() == destBlock) {
71 // Already in the right block, but not necessarily ahead of the control
72 // flow in it. This pass runs before FlattenCFG, which splits the body at
73 // each structured control-flow op, so an alloca that sits after one ends
74 // up in a non-entry block and SROA only ever collects allocas from the
75 // entry block, so it would never be promoted at all.
76 // Only a region-carrying op ahead of the alloca can strand it that way;
77 // straight-line code cannot.
78 if (std::all_of(destBlock->begin(), alloca->getIterator(),
79 [](mlir::Operation &blockOp) {
80 return blockOp.getNumRegions() == 0;
81 }))
82 return;
83 }
84
85 // Hoist allocas into the entry block.
86
87 // Preserving the `const` attribute on hoisted allocas can cause LLVM to
88 // incorrectly introduce invariant group metadata in some circumstances.
89 // The incubator performs some analysis to determine whether the attribute
90 // can be preserved, but it only runs this analysis when optimizations are
91 // enabled. Until we start tracking the optimization level, we can just
92 // always remove the `const` attribute.
94 if (alloca.getConstant())
95 alloca.setConstant(false);
96
97 mlir::Operation *&insertPoint =
98 insertPoints.try_emplace(destBlock, &*destBlock->begin()).first->second;
99 alloca->moveBefore(insertPoint);
100 });
101}
102
103void HoistAllocasPass::runOnOperation() {
104 llvm::TimeTraceScope scope("Hoist Allocas");
105 llvm::SmallVector<Operation *, 16> ops;
106
107 Operation *op = getOperation();
108 auto mod = mlir::dyn_cast<mlir::ModuleOp>(op);
109 if (!mod)
110 mod = op->getParentOfType<mlir::ModuleOp>();
111
112 // If we ever introduce nested cir.function ops, we'll need to make this
113 // walk in post-order and recurse into nested functions.
114 getOperation()->walk<mlir::WalkOrder::PreOrder>([&](cir::FuncOp op) {
115 process(mod, op);
116 return mlir::WalkResult::skip();
117 });
118}
119
120} // namespace
121
122std::unique_ptr<Pass> mlir::createHoistAllocasPass() {
123 return std::make_unique<HoistAllocasPass>();
124}
std::unique_ptr< Pass > createHoistAllocasPass()
static bool optInfoAttr()