Andy Kaylor bff94d774c
[CIR] Emit allocas into the proper lexical scope (#132468)
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.
2025-03-25 16:13:57 -07:00

85 lines
2.8 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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();
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());
// 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<Operation *, 16> ops;
Operation *op = getOperation();
auto mod = mlir::dyn_cast<mlir::ModuleOp>(op);
if (!mod)
mod = op->getParentOfType<mlir::ModuleOp>();
// 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
std::unique_ptr<Pass> mlir::createHoistAllocasPass() {
return std::make_unique<HoistAllocasPass>();
}