
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.
85 lines
2.8 KiB
C++
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>();
|
|
}
|