blob: 684aa4462915e514dfb0e7eed0bd6572e519d9a3 [file] [log] [blame]
//===- AddAliasTags.cpp ---------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
/// \file
/// Adds TBAA alias tags to fir loads and stores, based on information from
/// fir::AliasAnalysis. More are added later in CodeGen - see fir::TBAABuilder
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Analysis/AliasAnalysis.h"
#include "flang/Optimizer/Analysis/TBAAForest.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
#include "flang/Optimizer/Transforms/Passes.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
namespace fir {
#define GEN_PASS_DEF_ADDALIASTAGS
#include "flang/Optimizer/Transforms/Passes.h.inc"
} // namespace fir
#define DEBUG_TYPE "fir-add-alias-tags"
static llvm::cl::opt<bool>
enableDummyArgs("dummy-arg-tbaa", llvm::cl::init(true), llvm::cl::Hidden,
llvm::cl::desc("Add TBAA tags to dummy arguments"));
static llvm::cl::opt<bool>
enableGlobals("globals-tbaa", llvm::cl::init(true), llvm::cl::Hidden,
llvm::cl::desc("Add TBAA tags to global variables"));
static llvm::cl::opt<bool>
enableDirect("direct-tbaa", llvm::cl::init(true), llvm::cl::Hidden,
llvm::cl::desc("Add TBAA tags to direct variables"));
// This is **known unsafe** (misscompare in spec2017/wrf_r). It should
// not be enabled by default.
// The code is kept so that these may be tried with new benchmarks to see if
// this is worth fixing in the future.
static llvm::cl::opt<bool> enableLocalAllocs(
"local-alloc-tbaa", llvm::cl::init(false), llvm::cl::Hidden,
llvm::cl::desc("Add TBAA tags to local allocations. UNSAFE."));
namespace {
/// Shared state per-module
class PassState {
public:
/// memoised call to fir::AliasAnalysis::getSource
inline const fir::AliasAnalysis::Source &getSource(mlir::Value value) {
if (!analysisCache.contains(value))
analysisCache.insert({value, analysis.getSource(value)});
return analysisCache[value];
}
/// get the per-function TBAATree for this function
inline const fir::TBAATree &getFuncTree(mlir::func::FuncOp func) {
return forrest[func];
}
private:
fir::AliasAnalysis analysis;
llvm::DenseMap<mlir::Value, fir::AliasAnalysis::Source> analysisCache;
fir::TBAAForrest forrest;
};
class AddAliasTagsPass : public fir::impl::AddAliasTagsBase<AddAliasTagsPass> {
public:
void runOnOperation() override;
private:
/// The real workhorse of the pass. This is a runOnOperation() which
/// operates on fir::FirAliasTagOpInterface, using some extra state
void runOnAliasInterface(fir::FirAliasTagOpInterface op, PassState &state);
};
} // namespace
static fir::DeclareOp getDeclareOp(mlir::Value arg) {
for (mlir::Operation *use : arg.getUsers())
if (fir::DeclareOp declare = mlir::dyn_cast<fir::DeclareOp>(use))
return declare;
return nullptr;
}
/// Get the name of a function argument using the "fir.bindc_name" attribute,
/// or ""
static std::string getFuncArgName(mlir::Value arg) {
// first try getting the name from the hlfir.declare
if (fir::DeclareOp declare = getDeclareOp(arg))
return declare.getUniqName().str();
// get from attribute on function argument
// always succeeds because arg is a function argument
mlir::BlockArgument blockArg = mlir::cast<mlir::BlockArgument>(arg);
assert(blockArg.getOwner() && blockArg.getOwner()->isEntryBlock() &&
"arg is a function argument");
mlir::FunctionOpInterface func = mlir::dyn_cast<mlir::FunctionOpInterface>(
blockArg.getOwner()->getParentOp());
mlir::StringAttr attr = func.getArgAttrOfType<mlir::StringAttr>(
blockArg.getArgNumber(), "fir.bindc_name");
if (!attr)
return "";
return attr.str();
}
void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
PassState &state) {
mlir::func::FuncOp func = op->getParentOfType<mlir::func::FuncOp>();
if (!func)
return;
llvm::SmallVector<mlir::Value> accessedOperands = op.getAccessedOperands();
assert(accessedOperands.size() == 1 &&
"load and store only access one address");
mlir::Value memref = accessedOperands.front();
// skip boxes. These get an "any descriptor access" tag in TBAABuilder
// (CodeGen). I didn't see any speedup from giving each box a separate TBAA
// type.
if (mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(memref.getType())))
return;
LLVM_DEBUG(llvm::dbgs() << "Analysing " << op << "\n");
const fir::AliasAnalysis::Source &source = state.getSource(memref);
if (source.isTargetOrPointer()) {
LLVM_DEBUG(llvm::dbgs().indent(2) << "Skipping TARGET/POINTER\n");
// These will get an "any data access" tag in TBAABuilder (CodeGen): causing
// them to "MayAlias" with all non-box accesses
return;
}
mlir::LLVM::TBAATagAttr tag;
// TBAA for dummy arguments
if (enableDummyArgs &&
source.kind == fir::AliasAnalysis::SourceKind::Argument) {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to dummy argument at " << *op << "\n");
std::string name = getFuncArgName(source.u.get<mlir::Value>());
if (!name.empty())
tag = state.getFuncTree(func).dummyArgDataTree.getTag(name);
else
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for dummy argument " << *op
<< "\n");
// TBAA for global variables
} else if (enableGlobals &&
source.kind == fir::AliasAnalysis::SourceKind::Global) {
mlir::SymbolRefAttr glbl = source.u.get<mlir::SymbolRefAttr>();
const char *name = glbl.getRootReference().data();
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name
<< " at " << *op << "\n");
tag = state.getFuncTree(func).globalDataTree.getTag(name);
// TBAA for SourceKind::Direct
} else if (enableDirect &&
source.kind == fir::AliasAnalysis::SourceKind::Direct) {
if (source.u.is<mlir::SymbolRefAttr>()) {
mlir::SymbolRefAttr glbl = source.u.get<mlir::SymbolRefAttr>();
const char *name = glbl.getRootReference().data();
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name
<< " at " << *op << "\n");
tag = state.getFuncTree(func).directDataTree.getTag(name);
} else {
// SourceKind::Direct is likely to be extended to cases which are not a
// SymbolRefAttr in the future
LLVM_DEBUG(llvm::dbgs().indent(2) << "Can't get name for direct "
<< source << " at " << *op << "\n");
}
// TBAA for local allocations
} else if (enableLocalAllocs &&
source.kind == fir::AliasAnalysis::SourceKind::Allocate) {
std::optional<llvm::StringRef> name;
mlir::Operation *sourceOp = source.u.get<mlir::Value>().getDefiningOp();
if (auto alloc = mlir::dyn_cast_or_null<fir::AllocaOp>(sourceOp))
name = alloc.getUniqName();
else if (auto alloc = mlir::dyn_cast_or_null<fir::AllocMemOp>(sourceOp))
name = alloc.getUniqName();
if (name) {
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to allocation "
<< name << " at " << *op << "\n");
tag = state.getFuncTree(func).allocatedDataTree.getTag(*name);
} else {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for allocation " << *op
<< "\n");
}
} else {
if (source.kind != fir::AliasAnalysis::SourceKind::Argument &&
source.kind != fir::AliasAnalysis::SourceKind::Allocate &&
source.kind != fir::AliasAnalysis::SourceKind::Global)
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: unsupported value: " << source << "\n");
}
if (tag)
op.setTBAATags(mlir::ArrayAttr::get(&getContext(), tag));
}
void AddAliasTagsPass::runOnOperation() {
LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n");
// MLIR forbids storing state in a pass because different instances might be
// used in different threads.
// Instead this pass stores state per mlir::ModuleOp (which is what MLIR
// thinks the pass operates on), then the real work of the pass is done in
// runOnAliasInterface
PassState state;
mlir::ModuleOp mod = getOperation();
mod.walk(
[&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); });
LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n");
}
std::unique_ptr<mlir::Pass> fir::createAliasTagsPass() {
return std::make_unique<AddAliasTagsPass>();
}