Skip to content

Commit 59b18b5

Browse files
committed
[flang] Avoid unnecessary temporaries in ArrayValueCopy.
Assume no conflict between pointer arrays and arrays without the target attribute, if the fact of an array not having the target attribute can be reliably computed. This change speeds up SPEC CPU2017/527.cam from 2.5k seconds to 880 seconds on Icelake, and makes further performance investigation easier. Differential Revision: https://reviews.llvm.org/D142273
1 parent c3737a6 commit 59b18b5

File tree

12 files changed

+438
-84
lines changed

12 files changed

+438
-84
lines changed

flang/include/flang/Optimizer/Dialect/FIROpsSupport.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,27 @@ inline bool isInternalPorcedure(mlir::func::FuncOp func) {
106106

107107
/// Tell if \p value is:
108108
/// - a function argument that has attribute \p attributeName
109-
/// - or, the result of fir.alloca/fir.allocamem op that has attribute \p
109+
/// - or, the result of fir.alloca/fir.allocmem op that has attribute \p
110110
/// attributeName
111111
/// - or, the result of a fir.address_of of a fir.global that has attribute \p
112112
/// attributeName
113113
/// - or, a fir.box loaded from a fir.ref<fir.box> that matches one of the
114114
/// previous cases.
115115
bool valueHasFirAttribute(mlir::Value value, llvm::StringRef attributeName);
116116

117+
/// A more conservative version of valueHasFirAttribute().
118+
/// If `value` is one of the operation/function-argument cases listed
119+
/// for valueHasFirAttribute():
120+
/// * if any of the `attributeNames` attributes is set, then the function
121+
/// will return true.
122+
/// * otherwise, it will return false.
123+
///
124+
/// Otherwise, the function will return true indicating that the attributes
125+
/// may actually be set but we were not able to reach the point of definition
126+
/// to confirm that.
127+
bool valueMayHaveFirAttributes(mlir::Value value,
128+
llvm::ArrayRef<llvm::StringRef> attributeNames);
129+
117130
/// Scan the arguments of a FuncOp to determine if any arguments have the
118131
/// attribute `attr` placed on them. This can be used to determine if the
119132
/// function has any host associations, for example.

flang/include/flang/Optimizer/Transforms/Passes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ namespace fir {
4747
std::unique_ptr<mlir::Pass> createAbstractResultOnFuncOptPass();
4848
std::unique_ptr<mlir::Pass> createAbstractResultOnGlobalOptPass();
4949
std::unique_ptr<mlir::Pass> createAffineDemotionPass();
50-
std::unique_ptr<mlir::Pass> createArrayValueCopyPass();
50+
std::unique_ptr<mlir::Pass>
51+
createArrayValueCopyPass(fir::ArrayValueCopyOptions options = {});
5152
std::unique_ptr<mlir::Pass> createFirToCfgPass();
5253
std::unique_ptr<mlir::Pass> createCharacterConversionPass();
5354
std::unique_ptr<mlir::Pass> createExternalNameConversionPass();

flang/include/flang/Optimizer/Transforms/Passes.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ def ArrayValueCopy : Pass<"array-value-copy", "::mlir::func::FuncOp"> {
117117
}];
118118
let constructor = "::fir::createArrayValueCopyPass()";
119119
let dependentDialects = [ "fir::FIROpsDialect" ];
120+
let options = [
121+
Option<"optimizeConflicts", "optimize-conflicts", "bool",
122+
/*default=*/"false",
123+
"do more detailed conflict analysis to reduce the number "
124+
"of temporaries">
125+
];
120126
}
121127

122128
def CharacterConversion : Pass<"character-conversion"> {

flang/include/flang/Tools/CLOptions.inc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,12 @@ inline void addCfgConversionPass(mlir::PassManager &pm) {
104104
pm, disableCfgConversion, fir::createFirToCfgPass);
105105
}
106106

107-
inline void addAVC(mlir::PassManager &pm) {
107+
inline void addAVC(
108+
mlir::PassManager &pm, const llvm::OptimizationLevel &optLevel) {
109+
ArrayValueCopyOptions options;
110+
options.optimizeConflicts = optLevel.isOptimizingForSpeed();
108111
addNestedPassConditionally<mlir::func::FuncOp>(
109-
pm, disableFirAvc, fir::createArrayValueCopyPass);
112+
pm, disableFirAvc, [&]() { return createArrayValueCopyPass(options); });
110113
}
111114

112115
inline void addMemoryAllocationOpt(mlir::PassManager &pm) {
@@ -169,7 +172,7 @@ inline void createDefaultFIROptimizerPassPipeline(
169172
mlir::GreedyRewriteConfig config;
170173
config.enableRegionSimplification = false;
171174
pm.addPass(mlir::createCSEPass());
172-
fir::addAVC(pm);
175+
fir::addAVC(pm, optLevel);
173176
pm.addNestedPass<mlir::func::FuncOp>(fir::createCharacterConversionPass());
174177
pm.addPass(mlir::createCanonicalizerPass(config));
175178
pm.addPass(fir::createSimplifyRegionLitePass());

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3540,8 +3540,48 @@ bool fir::hasHostAssociationArgument(mlir::func::FuncOp func) {
35403540
return false;
35413541
}
35423542

3543-
bool fir::valueHasFirAttribute(mlir::Value value,
3544-
llvm::StringRef attributeName) {
3543+
// Test if value's definition has the specified set of
3544+
// attributeNames. The value's definition is one of the operations
3545+
// that are able to carry the Fortran variable attributes, e.g.
3546+
// fir.alloca or fir.allocmem. Function arguments may also represent
3547+
// value definitions and carry relevant attributes.
3548+
//
3549+
// If it is not possible to reach the limited set of definition
3550+
// entities from the given value, then the function will return
3551+
// std::nullopt. Otherwise, the definition is known and the return
3552+
// value is computed as:
3553+
// * if checkAny is true, then the function will return true
3554+
// iff any of the attributeNames attributes is set on the definition.
3555+
// * if checkAny is false, then the function will return true
3556+
// iff all of the attributeNames attributes are set on the definition.
3557+
static std::optional<bool>
3558+
valueCheckFirAttributes(mlir::Value value,
3559+
llvm::ArrayRef<llvm::StringRef> attributeNames,
3560+
bool checkAny) {
3561+
auto testAttributeSets = [&](llvm::ArrayRef<mlir::NamedAttribute> setAttrs,
3562+
llvm::ArrayRef<llvm::StringRef> checkAttrs) {
3563+
if (checkAny) {
3564+
// Return true iff any of checkAttrs attributes is present
3565+
// in setAttrs set.
3566+
for (llvm::StringRef checkAttrName : checkAttrs)
3567+
if (llvm::any_of(setAttrs, [&](mlir::NamedAttribute setAttr) {
3568+
return setAttr.getName() == checkAttrName;
3569+
}))
3570+
return true;
3571+
3572+
return false;
3573+
}
3574+
3575+
// Return true iff all attributes from checkAttrs are present
3576+
// in setAttrs set.
3577+
for (mlir::StringRef checkAttrName : checkAttrs)
3578+
if (llvm::none_of(setAttrs, [&](mlir::NamedAttribute setAttr) {
3579+
return setAttr.getName() == checkAttrName;
3580+
}))
3581+
return false;
3582+
3583+
return true;
3584+
};
35453585
// If this is a fir.box that was loaded, the fir attributes will be on the
35463586
// related fir.ref<fir.box> creation.
35473587
if (value.getType().isa<fir::BoxType>())
@@ -3553,32 +3593,50 @@ bool fir::valueHasFirAttribute(mlir::Value value,
35533593
if (blockArg.getOwner() && blockArg.getOwner()->isEntryBlock())
35543594
if (auto funcOp = mlir::dyn_cast<mlir::func::FuncOp>(
35553595
blockArg.getOwner()->getParentOp()))
3556-
if (funcOp.getArgAttr(blockArg.getArgNumber(), attributeName))
3557-
return true;
3558-
return false;
3596+
return testAttributeSets(
3597+
mlir::cast<mlir::FunctionOpInterface>(*funcOp).getArgAttrs(
3598+
blockArg.getArgNumber()),
3599+
attributeNames);
3600+
3601+
// If it is not a function argument, the attributes are unknown.
3602+
return std::nullopt;
35593603
}
35603604

35613605
if (auto definingOp = value.getDefiningOp()) {
35623606
// If this is an allocated value, look at the allocation attributes.
35633607
if (mlir::isa<fir::AllocMemOp>(definingOp) ||
3564-
mlir::isa<AllocaOp>(definingOp))
3565-
return definingOp->hasAttr(attributeName);
3608+
mlir::isa<fir::AllocaOp>(definingOp))
3609+
return testAttributeSets(definingOp->getAttrs(), attributeNames);
35663610
// If this is an imported global, look at AddrOfOp and GlobalOp attributes.
35673611
// Both operations are looked at because use/host associated variable (the
35683612
// AddrOfOp) can have ASYNCHRONOUS/VOLATILE attributes even if the ultimate
35693613
// entity (the globalOp) does not have them.
35703614
if (auto addressOfOp = mlir::dyn_cast<fir::AddrOfOp>(definingOp)) {
3571-
if (addressOfOp->hasAttr(attributeName))
3615+
if (testAttributeSets(addressOfOp->getAttrs(), attributeNames))
35723616
return true;
35733617
if (auto module = definingOp->getParentOfType<mlir::ModuleOp>())
35743618
if (auto globalOp =
35753619
module.lookupSymbol<fir::GlobalOp>(addressOfOp.getSymbol()))
3576-
return globalOp->hasAttr(attributeName);
3620+
return testAttributeSets(globalOp->getAttrs(), attributeNames);
35773621
}
35783622
}
35793623
// TODO: Construct associated entities attributes. Decide where the fir
35803624
// attributes must be placed/looked for in this case.
3581-
return false;
3625+
return std::nullopt;
3626+
}
3627+
3628+
bool fir::valueMayHaveFirAttributes(
3629+
mlir::Value value, llvm::ArrayRef<llvm::StringRef> attributeNames) {
3630+
std::optional<bool> mayHaveAttr =
3631+
valueCheckFirAttributes(value, attributeNames, /*checkAny=*/true);
3632+
return mayHaveAttr.value_or(true);
3633+
}
3634+
3635+
bool fir::valueHasFirAttribute(mlir::Value value,
3636+
llvm::StringRef attributeName) {
3637+
std::optional<bool> mayHaveAttr =
3638+
valueCheckFirAttributes(value, {attributeName}, /*checkAny=*/false);
3639+
return mayHaveAttr.value_or(false);
35823640
}
35833641

35843642
bool fir::anyFuncArgsHaveAttr(mlir::func::FuncOp func, llvm::StringRef attr) {

0 commit comments

Comments
 (0)