Skip to content

Commit 7172836

Browse files
committed
[flang] Fold real-valued MODULO() and MOD()
Evaluate real-valued references to the intrinsic functions MODULO and MOD at compilation time without recourse to an external math library. Differential Revision: https://reviews.llvm.org/D125151
1 parent 671afac commit 7172836

File tree

6 files changed

+92
-9
lines changed

6 files changed

+92
-9
lines changed

flang/include/flang/Evaluate/real.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,21 @@ class Real : public common::RealDetails<PREC> {
119119
const Real &, Rounding rounding = defaultRounding) const;
120120

121121
ValueWithRealFlags<Real> SQRT(Rounding rounding = defaultRounding) const;
122-
123122
// NEAREST(), IEEE_NEXT_AFTER(), IEEE_NEXT_UP(), and IEEE_NEXT_DOWN()
124123
ValueWithRealFlags<Real> NEAREST(bool upward) const;
125-
126124
// HYPOT(x,y)=SQRT(x**2 + y**2) computed so as to avoid spurious
127125
// intermediate overflows.
128126
ValueWithRealFlags<Real> HYPOT(
129127
const Real &, Rounding rounding = defaultRounding) const;
128+
// DIM(X,Y) = MAX(X-Y, 0)
129+
ValueWithRealFlags<Real> DIM(
130+
const Real &, Rounding rounding = defaultRounding) const;
131+
// MOD(x,y) = x - AINT(x/y)*y
132+
// MODULO(x,y) = x - FLOOR(x/y)*y
133+
ValueWithRealFlags<Real> MOD(
134+
const Real &, Rounding rounding = defaultRounding) const;
135+
ValueWithRealFlags<Real> MODULO(
136+
const Real &, Rounding rounding = defaultRounding) const;
130137

131138
// DIM(X,Y) = MAX(X-Y, 0)
132139
ValueWithRealFlags<Real> DIM(

flang/lib/Evaluate/fold-real.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
7272
} else if (name == "amax0" || name == "amin0" || name == "amin1" ||
7373
name == "amax1" || name == "dmin1" || name == "dmax1") {
7474
return RewriteSpecificMINorMAX(context, std::move(funcRef));
75-
} else if (name == "atan" || name == "atan2" || name == "mod") {
75+
} else if (name == "atan" || name == "atan2") {
7676
std::string localName{name == "atan" ? "atan2" : name};
7777
CHECK(args.size() == 2);
7878
if (auto callable{GetHostRuntimeWrapper<T, T, T>(localName)}) {
@@ -159,6 +159,35 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
159159
RelationalOperator::GT, T::Scalar::HUGE().Negate());
160160
} else if (name == "merge") {
161161
return FoldMerge<T>(context, std::move(funcRef));
162+
} else if (name == "min") {
163+
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
164+
} else if (name == "minval") {
165+
return FoldMaxvalMinval<T>(
166+
context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE());
167+
} else if (name == "mod") {
168+
CHECK(args.size() == 2);
169+
return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
170+
ScalarFunc<T, T, T>(
171+
[&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> {
172+
auto result{x.MOD(y)};
173+
if (result.flags.test(RealFlag::DivideByZero)) {
174+
context.messages().Say(
175+
"second argument to MOD must not be zero"_warn_en_US);
176+
}
177+
return result.value;
178+
}));
179+
} else if (name == "modulo") {
180+
CHECK(args.size() == 2);
181+
return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
182+
ScalarFunc<T, T, T>(
183+
[&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> {
184+
auto result{x.MODULO(y)};
185+
if (result.flags.test(RealFlag::DivideByZero)) {
186+
context.messages().Say(
187+
"second argument to MODULO must not be zero"_warn_en_US);
188+
}
189+
return result.value;
190+
}));
162191
} else if (name == "nearest") {
163192
if (const auto *sExpr{UnwrapExpr<Expr<SomeReal>>(args[1])}) {
164193
return common::visit(
@@ -184,11 +213,6 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
184213
},
185214
sExpr->u);
186215
}
187-
} else if (name == "min") {
188-
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
189-
} else if (name == "minval") {
190-
return FoldMaxvalMinval<T>(
191-
context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE());
192216
} else if (name == "product") {
193217
auto one{Scalar<T>::FromInteger(value::Integer<8>{1}).value};
194218
return FoldProduct<T>(context, std::move(funcRef), one);

flang/lib/Evaluate/intrinsics-library.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ struct HostRuntimeLibrary<HostT, LibraryVersion::Libm> {
230230
FolderFactory<F, F{std::log}>::Create("log"),
231231
FolderFactory<F, F{std::log10}>::Create("log10"),
232232
FolderFactory<F, F{std::lgamma}>::Create("log_gamma"),
233-
FolderFactory<F2, F2{std::fmod}>::Create("mod"),
234233
FolderFactory<F2, F2{std::pow}>::Create("pow"),
235234
FolderFactory<F, F{std::sin}>::Create("sin"),
236235
FolderFactory<F, F{std::sinh}>::Create("sinh"),

flang/lib/Evaluate/real.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,32 @@ ValueWithRealFlags<Real<W, P>> Real<W, P>::HYPOT(
422422
return result;
423423
}
424424

425+
// MOD(x,y) = x - AINT(x/y)*y
426+
template <typename W, int P>
427+
ValueWithRealFlags<Real<W, P>> Real<W, P>::MOD(
428+
const Real &y, Rounding rounding) const {
429+
ValueWithRealFlags<Real> result;
430+
Real quotient{Divide(y, rounding).AccumulateFlags(result.flags)};
431+
Real toInt{quotient.ToWholeNumber(common::RoundingMode::ToZero)
432+
.AccumulateFlags(result.flags)};
433+
Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
434+
result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
435+
return result;
436+
}
437+
438+
// MODULO(x,y) = x - FLOOR(x/y)*y
439+
template <typename W, int P>
440+
ValueWithRealFlags<Real<W, P>> Real<W, P>::MODULO(
441+
const Real &y, Rounding rounding) const {
442+
ValueWithRealFlags<Real> result;
443+
Real quotient{Divide(y, rounding).AccumulateFlags(result.flags)};
444+
Real toInt{quotient.ToWholeNumber(common::RoundingMode::Down)
445+
.AccumulateFlags(result.flags)};
446+
Real product{toInt.Multiply(y, rounding).AccumulateFlags(result.flags)};
447+
result.value = Subtract(product, rounding).AccumulateFlags(result.flags);
448+
return result;
449+
}
450+
425451
template <typename W, int P>
426452
ValueWithRealFlags<Real<W, P>> Real<W, P>::DIM(
427453
const Real &y, Rounding rounding) const {

flang/test/Evaluate/fold-mod.f90

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
! RUN: %python %S/test_folding.py %s %flang_fc1
2+
! Tests folding of integer and real MOD and MODULO
3+
module m1
4+
logical, parameter :: test_mod_i1 = mod(8, 5) == 3
5+
logical, parameter :: test_mod_i2 = mod(-8, 5) == -3
6+
logical, parameter :: test_mod_i3 = mod(8, -5) == 3
7+
logical, parameter :: test_mod_i4 = mod(-8, -5) == -3
8+
9+
logical, parameter :: test_mod_r1 = mod(3., 2.) == 1.
10+
logical, parameter :: test_mod_r2 = mod(8., 5.) == 3.
11+
logical, parameter :: test_mod_r3 = mod(-8., 5.) == -3.
12+
logical, parameter :: test_mod_r4 = mod(8., -5.) == 3.
13+
logical, parameter :: test_mod_r5 = mod(-8., -5.) == -3.
14+
15+
logical, parameter :: test_modulo_i1 = modulo(8, 5) == 3
16+
logical, parameter :: test_modulo_i2 = modulo(-8, 5) == 2
17+
logical, parameter :: test_modulo_i3 = modulo(8, -5) == -2
18+
logical, parameter :: test_modulo_i4 = modulo(-8, -5) == -3
19+
20+
logical, parameter :: test_modulo_r1 = modulo(8., 5.) == 3.
21+
logical, parameter :: test_modulo_r2 = modulo(-8., 5.) == 2.
22+
logical, parameter :: test_modulo_r3 = modulo(8., -5.) == -2.
23+
logical, parameter :: test_modulo_r4 = modulo(-8., -5.) == -3.
24+
end module

flang/test/Evaluate/folding04.f90

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ module real_tests
3232
!WARN: warning: invalid argument on intrinsic function
3333
real(4), parameter :: nan_r4_acos5 = acos(r4_pinf)
3434
TEST_ISNAN(nan_r4_acos5)
35+
!WARN: warning: second argument to MOD must not be zero
36+
real(4), parameter :: nan_r4_mod = mod(3.5, 0.)
37+
TEST_ISNAN(nan_r4_mod)
3538

3639
!WARN: warning: overflow on intrinsic function
3740
logical, parameter :: test_exp_overflow = exp(256._4).EQ.r4_pinf

0 commit comments

Comments
 (0)