Skip to content

Commit f51cfbe

Browse files
committed
[flang] Add nsw flag to do-variable increment with a new option
This patch adds nsw flag to the increment of do-variables when a new option is enabled. NOTE 11.10 in the Fortran 2018 standard says they never overflow. See also the discussion in 74709 and discourse post.
1 parent 6eb9e21 commit f51cfbe

File tree

20 files changed

+659
-23
lines changed

20 files changed

+659
-23
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6539,6 +6539,10 @@ def flang_deprecated_no_hlfir : Flag<["-"], "flang-deprecated-no-hlfir">,
65396539
Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>,
65406540
HelpText<"Do not use HLFIR lowering (deprecated)">;
65416541

6542+
def flang_experimental_integer_overflow : Flag<["-"], "flang-experimental-integer-overflow">,
6543+
Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>,
6544+
HelpText<"Add nsw flag to internal operations such as do-variable increment (experimental)">;
6545+
65426546
//===----------------------------------------------------------------------===//
65436547
// FLangOption + CoreOption + NoXarchOption
65446548
//===----------------------------------------------------------------------===//

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ void Flang::addCodegenOptions(const ArgList &Args,
139139

140140
Args.addAllArgs(CmdArgs, {options::OPT_flang_experimental_hlfir,
141141
options::OPT_flang_deprecated_no_hlfir,
142+
options::OPT_flang_experimental_integer_overflow,
142143
options::OPT_fno_ppc_native_vec_elem_order,
143144
options::OPT_fppc_native_vec_elem_order});
144145
}

flang/include/flang/Lower/LoweringOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,9 @@ ENUM_LOWERINGOPT(NoPPCNativeVecElemOrder, unsigned, 1, 0)
3434
/// On by default.
3535
ENUM_LOWERINGOPT(Underscoring, unsigned, 1, 1)
3636

37+
/// If true, add nsw flags to arithmetic operations for integer.
38+
/// Off by default.
39+
ENUM_LOWERINGOPT(NoSignedWrap, unsigned, 1, 0)
40+
3741
#undef LOWERINGOPT
3842
#undef ENUM_LOWERINGOPT

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ namespace fir {
5454
std::unique_ptr<mlir::Pass> createAffineDemotionPass();
5555
std::unique_ptr<mlir::Pass>
5656
createArrayValueCopyPass(fir::ArrayValueCopyOptions options = {});
57+
std::unique_ptr<mlir::Pass> createCFGConversionPassWithNSW();
5758
std::unique_ptr<mlir::Pass> createExternalNameConversionPass();
5859
std::unique_ptr<mlir::Pass>
5960
createExternalNameConversionPass(bool appendUnderscore);
@@ -89,7 +90,8 @@ createFunctionAttrPass(FunctionAttrTypes &functionAttr, bool noInfsFPMath,
8990
bool noSignedZerosFPMath, bool unsafeFPMath);
9091

9192
void populateCfgConversionRewrites(mlir::RewritePatternSet &patterns,
92-
bool forceLoopToExecuteOnce = false);
93+
bool forceLoopToExecuteOnce = false,
94+
bool setNSW = false);
9395

9496
// declarative passes
9597
#define GEN_PASS_REGISTRATION

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ def CFGConversion : Pass<"cfg-conversion"> {
151151
let options = [
152152
Option<"forceLoopToExecuteOnce", "always-execute-loop-body", "bool",
153153
/*default=*/"false",
154-
"force the body of a loop to execute at least once">
154+
"force the body of a loop to execute at least once">,
155+
Option<"setNSW", "set-nsw", "bool",
156+
/*default=*/"false",
157+
"set nsw on loop variable increment">
155158
];
156159
}
157160

flang/include/flang/Tools/CLOptions.inc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,14 @@ static void addCanonicalizerPassWithoutRegionSimplification(
148148
pm.addPass(mlir::createCanonicalizerPass(config));
149149
}
150150

151-
inline void addCfgConversionPass(mlir::PassManager &pm) {
152-
addNestedPassToAllTopLevelOperationsConditionally(
153-
pm, disableCfgConversion, fir::createCFGConversion);
151+
inline void addCfgConversionPass(
152+
mlir::PassManager &pm, const MLIRToLLVMPassPipelineConfig &config) {
153+
if (config.NoSignedWrap)
154+
addNestedPassToAllTopLevelOperationsConditionally(
155+
pm, disableCfgConversion, fir::createCFGConversionPassWithNSW);
156+
else
157+
addNestedPassToAllTopLevelOperationsConditionally(
158+
pm, disableCfgConversion, fir::createCFGConversion);
154159
}
155160

156161
inline void addAVC(
@@ -290,7 +295,7 @@ inline void createDefaultFIROptimizerPassPipeline(
290295
pm.addPass(fir::createAliasTagsPass());
291296

292297
// convert control flow to CFG form
293-
fir::addCfgConversionPass(pm);
298+
fir::addCfgConversionPass(pm, pc);
294299
pm.addPass(mlir::createConvertSCFToCFPass());
295300

296301
pm.addPass(mlir::createCanonicalizerPass(config));

flang/include/flang/Tools/CrossToolHelpers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ struct MLIRToLLVMPassPipelineConfig : public FlangEPCallBacks {
122122
bool NoSignedZerosFPMath =
123123
false; ///< Set no-signed-zeros-fp-math attribute for functions.
124124
bool UnsafeFPMath = false; ///< Set unsafe-fp-math attribute for functions.
125+
bool NoSignedWrap = false; ///< Add nsw flag to numeric operations.
125126
};
126127

127128
struct OffloadModuleOpts {

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,12 @@ bool CompilerInvocation::createFromArgs(
12031203
invoc.loweringOpts.setNoPPCNativeVecElemOrder(true);
12041204
}
12051205

1206+
// -flang-experimental-integer-overflow
1207+
if (args.hasArg(
1208+
clang::driver::options::OPT_flang_experimental_integer_overflow)) {
1209+
invoc.loweringOpts.setNoSignedWrap(true);
1210+
}
1211+
12061212
// Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
12071213
// -Rpass-analysis. This will be used later when processing and outputting the
12081214
// remarks generated by LLVM in ExecuteCompilerInvocation.cpp.

flang/lib/Frontend/FrontendActions.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,9 @@ void CodeGenAction::generateLLVMIR() {
809809
config.VScaleMax = vsr->second;
810810
}
811811

812+
if (ci.getInvocation().getLoweringOpts().getNoSignedWrap())
813+
config.NoSignedWrap = true;
814+
812815
// Create the pass pipeline
813816
fir::createMLIRToLLVMPassPipeline(pm, config, getCurrentFile());
814817
(void)mlir::applyPassManagerCLOptions(pm);

flang/lib/Lower/Bridge.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
20072007
void genFIRIncrementLoopEnd(IncrementLoopNestInfo &incrementLoopNestInfo) {
20082008
assert(!incrementLoopNestInfo.empty() && "empty loop nest");
20092009
mlir::Location loc = toLocation();
2010+
mlir::arith::IntegerOverflowFlags flags{};
2011+
if (getLoweringOptions().getNoSignedWrap())
2012+
flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw);
2013+
auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get(
2014+
builder->getContext(), flags);
20102015
for (auto it = incrementLoopNestInfo.rbegin(),
20112016
rend = incrementLoopNestInfo.rend();
20122017
it != rend; ++it) {
@@ -2021,15 +2026,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
20212026
builder->setInsertionPointToEnd(info.doLoop.getBody());
20222027
llvm::SmallVector<mlir::Value, 2> results;
20232028
results.push_back(builder->create<mlir::arith::AddIOp>(
2024-
loc, info.doLoop.getInductionVar(), info.doLoop.getStep()));
2029+
loc, info.doLoop.getInductionVar(), info.doLoop.getStep(),
2030+
iofAttr));
20252031
// Step loopVariable to help optimizations such as vectorization.
20262032
// Induction variable elimination will clean up as necessary.
20272033
mlir::Value step = builder->createConvert(
20282034
loc, info.getLoopVariableType(), info.doLoop.getStep());
20292035
mlir::Value loopVar =
20302036
builder->create<fir::LoadOp>(loc, info.loopVariable);
20312037
results.push_back(
2032-
builder->create<mlir::arith::AddIOp>(loc, loopVar, step));
2038+
builder->create<mlir::arith::AddIOp>(loc, loopVar, step, iofAttr));
20332039
builder->create<fir::ResultOp>(loc, results);
20342040
builder->setInsertionPointAfter(info.doLoop);
20352041
// The loop control variable may be used after the loop.
@@ -2054,7 +2060,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
20542060
if (info.hasRealControl)
20552061
value = builder->create<mlir::arith::AddFOp>(loc, value, step);
20562062
else
2057-
value = builder->create<mlir::arith::AddIOp>(loc, value, step);
2063+
value = builder->create<mlir::arith::AddIOp>(loc, value, step, iofAttr);
20582064
builder->create<fir::StoreOp>(loc, value, info.loopVariable);
20592065

20602066
genBranch(info.headerBlock);

flang/lib/Lower/IO.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,11 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter,
928928
Fortran::lower::StatementContext stmtCtx;
929929
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
930930
mlir::Location loc = converter.getCurrentLocation();
931+
mlir::arith::IntegerOverflowFlags flags{};
932+
if (converter.getLoweringOptions().getNoSignedWrap())
933+
flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw);
934+
auto iofAttr =
935+
mlir::arith::IntegerOverflowFlagsAttr::get(builder.getContext(), flags);
931936
makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
932937
const auto &itemList = std::get<0>(ioImpliedDo.t);
933938
const auto &control = std::get<1>(ioImpliedDo.t);
@@ -965,7 +970,7 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter,
965970
genItemList(ioImpliedDo);
966971
builder.setInsertionPointToEnd(doLoopOp.getBody());
967972
mlir::Value result = builder.create<mlir::arith::AddIOp>(
968-
loc, doLoopOp.getInductionVar(), doLoopOp.getStep());
973+
loc, doLoopOp.getInductionVar(), doLoopOp.getStep(), iofAttr);
969974
builder.create<fir::ResultOp>(loc, result);
970975
builder.setInsertionPointAfter(doLoopOp);
971976
// The loop control variable may be used after the loop.
@@ -1007,7 +1012,7 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter,
10071012
mlir::OpResult iterateResult = builder.getBlock()->back().getResult(0);
10081013
mlir::Value inductionResult0 = iterWhileOp.getInductionVar();
10091014
auto inductionResult1 = builder.create<mlir::arith::AddIOp>(
1010-
loc, inductionResult0, iterWhileOp.getStep());
1015+
loc, inductionResult0, iterWhileOp.getStep(), iofAttr);
10111016
auto inductionResult = builder.create<mlir::arith::SelectOp>(
10121017
loc, iterateResult, inductionResult1, inductionResult0);
10131018
llvm::SmallVector<mlir::Value> results = {inductionResult, iterateResult};

flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,19 @@ class CfgLoopConv : public mlir::OpRewritePattern<fir::DoLoopOp> {
4343
public:
4444
using OpRewritePattern::OpRewritePattern;
4545

46-
CfgLoopConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
46+
CfgLoopConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce, bool setNSW)
4747
: mlir::OpRewritePattern<fir::DoLoopOp>(ctx),
48-
forceLoopToExecuteOnce(forceLoopToExecuteOnce) {}
48+
forceLoopToExecuteOnce(forceLoopToExecuteOnce), setNSW(setNSW) {}
4949

5050
mlir::LogicalResult
5151
matchAndRewrite(DoLoopOp loop,
5252
mlir::PatternRewriter &rewriter) const override {
5353
auto loc = loop.getLoc();
54+
mlir::arith::IntegerOverflowFlags flags{};
55+
if (setNSW)
56+
flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw);
57+
auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get(
58+
rewriter.getContext(), flags);
5459

5560
// Create the start and end blocks that will wrap the DoLoopOp with an
5661
// initalizer and an end point
@@ -104,7 +109,7 @@ class CfgLoopConv : public mlir::OpRewritePattern<fir::DoLoopOp> {
104109
rewriter.setInsertionPointToEnd(lastBlock);
105110
auto iv = conditionalBlock->getArgument(0);
106111
mlir::Value steppedIndex =
107-
rewriter.create<mlir::arith::AddIOp>(loc, iv, step);
112+
rewriter.create<mlir::arith::AddIOp>(loc, iv, step, iofAttr);
108113
assert(steppedIndex && "must be a Value");
109114
auto lastArg = conditionalBlock->getNumArguments() - 1;
110115
auto itersLeft = conditionalBlock->getArgument(lastArg);
@@ -142,14 +147,15 @@ class CfgLoopConv : public mlir::OpRewritePattern<fir::DoLoopOp> {
142147

143148
private:
144149
bool forceLoopToExecuteOnce;
150+
bool setNSW;
145151
};
146152

147153
/// Convert `fir.if` to control-flow
148154
class CfgIfConv : public mlir::OpRewritePattern<fir::IfOp> {
149155
public:
150156
using OpRewritePattern::OpRewritePattern;
151157

152-
CfgIfConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
158+
CfgIfConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce, bool setNSW)
153159
: mlir::OpRewritePattern<fir::IfOp>(ctx) {}
154160

155161
mlir::LogicalResult
@@ -214,13 +220,19 @@ class CfgIterWhileConv : public mlir::OpRewritePattern<fir::IterWhileOp> {
214220
public:
215221
using OpRewritePattern::OpRewritePattern;
216222

217-
CfgIterWhileConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
218-
: mlir::OpRewritePattern<fir::IterWhileOp>(ctx) {}
223+
CfgIterWhileConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce,
224+
bool setNSW)
225+
: mlir::OpRewritePattern<fir::IterWhileOp>(ctx), setNSW(setNSW) {}
219226

220227
mlir::LogicalResult
221228
matchAndRewrite(fir::IterWhileOp whileOp,
222229
mlir::PatternRewriter &rewriter) const override {
223230
auto loc = whileOp.getLoc();
231+
mlir::arith::IntegerOverflowFlags flags{};
232+
if (setNSW)
233+
flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw);
234+
auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get(
235+
rewriter.getContext(), flags);
224236

225237
// Start by splitting the block containing the 'fir.do_loop' into two parts.
226238
// The part before will get the init code, the part after will be the end
@@ -248,7 +260,8 @@ class CfgIterWhileConv : public mlir::OpRewritePattern<fir::IterWhileOp> {
248260
auto *terminator = lastBodyBlock->getTerminator();
249261
rewriter.setInsertionPointToEnd(lastBodyBlock);
250262
auto step = whileOp.getStep();
251-
mlir::Value stepped = rewriter.create<mlir::arith::AddIOp>(loc, iv, step);
263+
mlir::Value stepped =
264+
rewriter.create<mlir::arith::AddIOp>(loc, iv, step, iofAttr);
252265
assert(stepped && "must be a Value");
253266

254267
llvm::SmallVector<mlir::Value> loopCarried;
@@ -305,17 +318,23 @@ class CfgIterWhileConv : public mlir::OpRewritePattern<fir::IterWhileOp> {
305318
rewriter.replaceOp(whileOp, args);
306319
return success();
307320
}
321+
322+
private:
323+
bool setNSW;
308324
};
309325

310326
/// Convert FIR structured control flow ops to CFG ops.
311327
class CfgConversion : public fir::impl::CFGConversionBase<CfgConversion> {
312328
public:
313329
using CFGConversionBase<CfgConversion>::CFGConversionBase;
314330

331+
CfgConversion(bool setNSW) { this->setNSW = setNSW; }
332+
315333
void runOnOperation() override {
316334
auto *context = &this->getContext();
317335
mlir::RewritePatternSet patterns(context);
318-
fir::populateCfgConversionRewrites(patterns, this->forceLoopToExecuteOnce);
336+
fir::populateCfgConversionRewrites(patterns, this->forceLoopToExecuteOnce,
337+
this->setNSW);
319338
mlir::ConversionTarget target(*context);
320339
target.addLegalDialect<mlir::affine::AffineDialect,
321340
mlir::cf::ControlFlowDialect, FIROpsDialect,
@@ -337,7 +356,12 @@ class CfgConversion : public fir::impl::CFGConversionBase<CfgConversion> {
337356

338357
/// Expose conversion rewriters to other passes
339358
void fir::populateCfgConversionRewrites(mlir::RewritePatternSet &patterns,
340-
bool forceLoopToExecuteOnce) {
359+
bool forceLoopToExecuteOnce,
360+
bool setNSW) {
341361
patterns.insert<CfgLoopConv, CfgIfConv, CfgIterWhileConv>(
342-
patterns.getContext(), forceLoopToExecuteOnce);
362+
patterns.getContext(), forceLoopToExecuteOnce, setNSW);
363+
}
364+
365+
std::unique_ptr<mlir::Pass> fir::createCFGConversionPassWithNSW() {
366+
return std::make_unique<CfgConversion>(true);
343367
}

flang/test/Driver/frontend-forwarding.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
! RUN: -fversion-loops-for-stride \
2020
! RUN: -flang-experimental-hlfir \
2121
! RUN: -flang-deprecated-no-hlfir \
22+
! RUN: -flang-experimental-integer-overflow \
2223
! RUN: -fno-ppc-native-vector-element-order \
2324
! RUN: -fppc-native-vector-element-order \
2425
! RUN: -mllvm -print-before-all \
@@ -50,6 +51,7 @@
5051
! CHECK: "-fversion-loops-for-stride"
5152
! CHECK: "-flang-experimental-hlfir"
5253
! CHECK: "-flang-deprecated-no-hlfir"
54+
! CHECK: "-flang-experimental-integer-overflow"
5355
! CHECK: "-fno-ppc-native-vector-element-order"
5456
! CHECK: "-fppc-native-vector-element-order"
5557
! CHECK: "-Rpass"

0 commit comments

Comments
 (0)