Skip to content

Commit 85b8b69

Browse files
authored
[flang] Support UNSIGNED ** (#154601)
GNU Fortran added support for UNSIGNED ** UNSIGNED power operations; we should do the same for portability. This actually simplifies semantics a bit, since I had to go out of my way to exclude Power as a supported operation for UNSIGNED.
1 parent f65f60e commit 85b8b69

File tree

9 files changed

+118
-34
lines changed

9 files changed

+118
-34
lines changed

flang-rt/lib/runtime/numeric.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,24 @@ RT_API_ATTRS BTy FPowI(BTy base, ETy exp) {
229229
return result;
230230
}
231231

232+
// Exponentiation operator for (Unsigned ** Unsigned) cases
233+
template <typename Ty> RT_API_ATTRS Ty UPow(Ty base, Ty exp) {
234+
if (exp == Ty{0})
235+
return Ty{1};
236+
Ty result{1};
237+
while (true) {
238+
if (exp & Ty{1}) {
239+
result *= base;
240+
}
241+
exp >>= 1;
242+
if (exp == Ty{0}) {
243+
break;
244+
}
245+
base *= base;
246+
}
247+
return result;
248+
}
249+
232250
extern "C" {
233251
RT_EXT_API_GROUP_BEGIN
234252

@@ -933,6 +951,27 @@ CppTypeFor<TypeCategory::Real, 16> RTDEF(FPow16k)(
933951
}
934952
#endif
935953

954+
CppTypeFor<TypeCategory::Unsigned, 1> RTDEF(UPow1)(
955+
CppTypeFor<TypeCategory::Unsigned, 1> b,
956+
CppTypeFor<TypeCategory::Unsigned, 1> e) {
957+
return UPow(b, e);
958+
}
959+
CppTypeFor<TypeCategory::Unsigned, 2> RTDEF(UPow2)(
960+
CppTypeFor<TypeCategory::Unsigned, 2> b,
961+
CppTypeFor<TypeCategory::Unsigned, 2> e) {
962+
return UPow(b, e);
963+
}
964+
CppTypeFor<TypeCategory::Unsigned, 4> RTDEF(UPow4)(
965+
CppTypeFor<TypeCategory::Unsigned, 4> b,
966+
CppTypeFor<TypeCategory::Unsigned, 4> e) {
967+
return UPow(b, e);
968+
}
969+
CppTypeFor<TypeCategory::Unsigned, 8> RTDEF(UPow8)(
970+
CppTypeFor<TypeCategory::Unsigned, 8> b,
971+
CppTypeFor<TypeCategory::Unsigned, 8> e) {
972+
return UPow(b, e);
973+
}
974+
936975
RT_EXT_API_GROUP_END
937976
} // extern "C"
938977
} // namespace Fortran::runtime

flang/include/flang/Evaluate/expression.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,9 @@ class Expr<Type<TypeCategory::Unsigned, KIND>>
566566
using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
567567
Convert<Result, TypeCategory::Real>,
568568
Convert<Result, TypeCategory::Unsigned>>;
569-
using Operations =
570-
std::tuple<Parentheses<Result>, Negate<Result>, Add<Result>,
571-
Subtract<Result>, Multiply<Result>, Divide<Result>, Extremum<Result>>;
569+
using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
570+
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
571+
Power<Result>, Extremum<Result>>;
572572
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
573573
Designator<Result>, FunctionRef<Result>>;
574574

flang/include/flang/Evaluate/tools.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,11 +750,11 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
750750
// one of the operands to the type of the other. Handles special cases with
751751
// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
752752
// powers.
753-
template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
753+
template <template <typename> class OPR>
754754
std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
755755
Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
756756

757-
extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
757+
extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
758758
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
759759
int defaultRealKind);
760760
extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(

flang/include/flang/Runtime/numeric.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,19 @@ CppTypeFor<TypeCategory::Real, 16> RTDECL(FPow16k)(
453453
CppTypeFor<TypeCategory::Integer, 8> e);
454454
#endif
455455

456+
CppTypeFor<TypeCategory::Unsigned, 1> RTDEF(UPow1)(
457+
CppTypeFor<TypeCategory::Unsigned, 1> b,
458+
CppTypeFor<TypeCategory::Unsigned, 1> e);
459+
CppTypeFor<TypeCategory::Unsigned, 2> RTDEF(UPow2)(
460+
CppTypeFor<TypeCategory::Unsigned, 2> b,
461+
CppTypeFor<TypeCategory::Unsigned, 2> e);
462+
CppTypeFor<TypeCategory::Unsigned, 4> RTDEF(UPow4)(
463+
CppTypeFor<TypeCategory::Unsigned, 4> b,
464+
CppTypeFor<TypeCategory::Unsigned, 4> e);
465+
CppTypeFor<TypeCategory::Unsigned, 8> RTDEF(UPow8)(
466+
CppTypeFor<TypeCategory::Unsigned, 8> b,
467+
CppTypeFor<TypeCategory::Unsigned, 8> e);
468+
456469
} // extern "C"
457470
} // namespace Fortran::runtime
458471
#endif // FORTRAN_RUNTIME_NUMERIC_H_

flang/lib/Evaluate/tools.cpp

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ Expr<SomeComplex> PromoteMixedComplexReal(
495495
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
496496
// the operands to a dyadic operation where one is permitted, it assumes the
497497
// type and kind of the other operand.
498-
template <template <typename> class OPR, bool CAN_BE_UNSIGNED>
498+
template <template <typename> class OPR>
499499
std::optional<Expr<SomeType>> NumericOperation(
500500
parser::ContextualMessages &messages, Expr<SomeType> &&x,
501501
Expr<SomeType> &&y, int defaultRealKind) {
@@ -510,13 +510,8 @@ std::optional<Expr<SomeType>> NumericOperation(
510510
std::move(rx), std::move(ry)));
511511
},
512512
[&](Expr<SomeUnsigned> &&ix, Expr<SomeUnsigned> &&iy) {
513-
if constexpr (CAN_BE_UNSIGNED) {
514-
return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>(
515-
std::move(ix), std::move(iy)));
516-
} else {
517-
messages.Say("Operands must not be UNSIGNED"_err_en_US);
518-
return NoExpr();
519-
}
513+
return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>(
514+
std::move(ix), std::move(iy)));
520515
},
521516
// Mixed REAL/INTEGER operations
522517
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
@@ -575,34 +570,31 @@ std::optional<Expr<SomeType>> NumericOperation(
575570
},
576571
// Operations with one typeless operand
577572
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
578-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
573+
return NumericOperation<OPR>(messages,
579574
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
580575
defaultRealKind);
581576
},
582577
[&](BOZLiteralConstant &&bx, Expr<SomeUnsigned> &&iy) {
583-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
578+
return NumericOperation<OPR>(messages,
584579
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
585580
defaultRealKind);
586581
},
587582
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
588-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
583+
return NumericOperation<OPR>(messages,
589584
AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
590585
defaultRealKind);
591586
},
592587
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
593-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
594-
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
595-
defaultRealKind);
588+
return NumericOperation<OPR>(messages, std::move(x),
589+
AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
596590
},
597591
[&](Expr<SomeUnsigned> &&ix, BOZLiteralConstant &&by) {
598-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
599-
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
600-
defaultRealKind);
592+
return NumericOperation<OPR>(messages, std::move(x),
593+
AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
601594
},
602595
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
603-
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
604-
std::move(x), AsGenericExpr(ConvertTo(rx, std::move(by))),
605-
defaultRealKind);
596+
return NumericOperation<OPR>(messages, std::move(x),
597+
AsGenericExpr(ConvertTo(rx, std::move(by))), defaultRealKind);
606598
},
607599
// Error cases
608600
[&](Expr<SomeUnsigned> &&, auto &&) {
@@ -621,7 +613,7 @@ std::optional<Expr<SomeType>> NumericOperation(
621613
std::move(x.u), std::move(y.u));
622614
}
623615

624-
template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
616+
template std::optional<Expr<SomeType>> NumericOperation<Power>(
625617
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
626618
int defaultRealKind);
627619
template std::optional<Expr<SomeType>> NumericOperation<Multiply>(

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ static const char __ldlu_r8x2[] = "__ldlu_r8x2_";
138138
/// Table that drives the fir generation depending on the intrinsic or intrinsic
139139
/// module procedure one to one mapping with Fortran arguments. If no mapping is
140140
/// defined here for a generic intrinsic, genRuntimeCall will be called
141-
/// to look for a match in the runtime a emit a call. Note that the argument
141+
/// to look for a match in the runtime and emit a call. Note that the argument
142142
/// lowering rules for an intrinsic need to be provided only if at least one
143143
/// argument must not be lowered by value. In which case, the lowering rules
144144
/// should be provided for all the intrinsic arguments for completeness.
@@ -1062,7 +1062,7 @@ prettyPrintIntrinsicName(fir::FirOpBuilder &builder, mlir::Location loc,
10621062
llvm::StringRef suffix, mlir::FunctionType funcType) {
10631063
std::string output = prefix.str();
10641064
llvm::raw_string_ostream sstream(output);
1065-
if (name == "pow") {
1065+
if (name == "pow" || name == "pow-unsigned") {
10661066
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
10671067
std::string displayName{" ** "};
10681068
sstream << mlirTypeToIntrinsicFortran(builder, funcType.getInput(0), loc,
@@ -1675,6 +1675,14 @@ static constexpr MathOperation mathOperations[] = {
16751675
genComplexPow},
16761676
{"pow", RTNAME_STRING(cqpowk), FuncTypeComplex16Complex16Integer8,
16771677
genLibF128Call},
1678+
{"pow-unsigned", RTNAME_STRING(UPow1),
1679+
genFuncType<Ty::Integer<1>, Ty::Integer<1>, Ty::Integer<1>>, genLibCall},
1680+
{"pow-unsigned", RTNAME_STRING(UPow2),
1681+
genFuncType<Ty::Integer<2>, Ty::Integer<2>, Ty::Integer<2>>, genLibCall},
1682+
{"pow-unsigned", RTNAME_STRING(UPow4),
1683+
genFuncType<Ty::Integer<4>, Ty::Integer<4>, Ty::Integer<4>>, genLibCall},
1684+
{"pow-unsigned", RTNAME_STRING(UPow8),
1685+
genFuncType<Ty::Integer<8>, Ty::Integer<8>, Ty::Integer<8>>, genLibCall},
16781686
{"remainder", "remainderf",
16791687
genFuncType<Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, genLibCall},
16801688
{"remainder", "remainder",
@@ -9441,6 +9449,14 @@ mlir::Value genPow(fir::FirOpBuilder &builder, mlir::Location loc,
94419449
// implementation and mark it 'strictfp'.
94429450
// Another option is to implement it in Fortran runtime library
94439451
// (just like matmul).
9452+
if (type.isUnsignedInteger()) {
9453+
assert(x.getType().isUnsignedInteger() && y.getType().isUnsignedInteger() &&
9454+
"unsigned pow requires unsigned arguments");
9455+
return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow-unsigned", type,
9456+
{x, y});
9457+
}
9458+
assert(!x.getType().isUnsignedInteger() && !y.getType().isUnsignedInteger() &&
9459+
"non-unsigned pow requires non-unsigned arguments");
94449460
return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
94459461
}
94469462

flang/lib/Semantics/expression.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3783,10 +3783,9 @@ MaybeExpr NumericBinaryHelper(
37833783
analyzer.CheckForNullPointer();
37843784
analyzer.CheckForAssumedRank();
37853785
analyzer.CheckConformance();
3786-
constexpr bool canBeUnsigned{opr != NumericOperator::Power};
3787-
return NumericOperation<OPR, canBeUnsigned>(
3788-
context.GetContextualMessages(), analyzer.MoveExpr(0),
3789-
analyzer.MoveExpr(1), context.GetDefaultKind(TypeCategory::Real));
3786+
return NumericOperation<OPR>(context.GetContextualMessages(),
3787+
analyzer.MoveExpr(0), analyzer.MoveExpr(1),
3788+
context.GetDefaultKind(TypeCategory::Real));
37903789
} else {
37913790
return analyzer.TryDefinedOp(AsFortran(opr),
37923791
"Operands of %s must be numeric; have %s and %s"_err_en_US);

flang/test/Lower/unsigned-ops.f90

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,29 @@ unsigned function f01(u, v)
2424
!CHECK: fir.store %[[VAL_13]] to %[[VAL_2]] : !fir.ref<ui32>
2525
!CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_2]] : !fir.ref<ui32>
2626
!CHECK: return %[[VAL_14]] : ui32
27+
28+
unsigned function f02(u, v)
29+
unsigned, intent(in) :: u, v
30+
f02 = u ** v - 1u
31+
end
32+
33+
!CHECK: func.func @_QPf02(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u"}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v"}) -> ui32 {
34+
!CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
35+
!CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
36+
!CHECK: %[[VAL_1:.*]] = fir.alloca ui32 {bindc_name = "f02", uniq_name = "_QFf02Ef02"}
37+
!CHECK: %[[VAL_2:.*]] = fir.declare %[[VAL_1]] {uniq_name = "_QFf02Ef02"} : (!fir.ref<ui32>) -> !fir.ref<ui32>
38+
!CHECK: %[[VAL_3:.*]] = fir.declare %[[ARG0]] dummy_scope %[[VAL_0]] {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFf02Eu"} : (!fir.ref<ui32>, !fir.dscope) -> !fir.ref<ui32>
39+
!CHECK: %[[VAL_4:.*]] = fir.declare %[[ARG1]] dummy_scope %[[VAL_0]] {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFf02Ev"} : (!fir.ref<ui32>, !fir.dscope) -> !fir.ref<ui32>
40+
!CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_3]] : !fir.ref<ui32>
41+
!CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]] : !fir.ref<ui32>
42+
!CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_5]] : (ui32) -> i64
43+
!CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_6]] : (ui32) -> i64
44+
!CHECK: %[[VAL_9:.*]] = fir.call @_FortranAUPow8(%[[VAL_7]], %[[VAL_8]]) fastmath<contract> : (i64, i64) -> i64
45+
!CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (i64) -> ui32
46+
!CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (ui32) -> i32
47+
!CHECK: %[[VAL_12:.*]] = arith.subi %[[VAL_11]], %[[C1_i32]] : i32
48+
!CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (i32) -> ui32
49+
!CHECK: fir.store %[[VAL_13]] to %[[VAL_2]] : !fir.ref<ui32>
50+
!CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_2]] : !fir.ref<ui32>
51+
!CHECK: return %[[VAL_14]] : ui32
52+

flang/test/Semantics/unsigned-errors.f90

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
print *, 0u - 1u ! ok
2121
print *, 0u * 1u ! ok
2222
print *, 0u / 1u ! ok
23-
!ERROR: Operands must not be UNSIGNED
24-
print *, 0u ** 1u
23+
print *, 0u ** 1u ! ok
2524

2625
print *, uint((0.,0.)) ! ok
2726
print *, uint(z'123') ! ok

0 commit comments

Comments
 (0)