mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 10:58:11 +08:00
[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.
This commit is contained in:
@@ -229,6 +229,24 @@ RT_API_ATTRS BTy FPowI(BTy base, ETy exp) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Exponentiation operator for (Unsigned ** Unsigned) cases
|
||||
template <typename Ty> RT_API_ATTRS Ty UPow(Ty base, Ty exp) {
|
||||
if (exp == Ty{0})
|
||||
return Ty{1};
|
||||
Ty result{1};
|
||||
while (true) {
|
||||
if (exp & Ty{1}) {
|
||||
result *= base;
|
||||
}
|
||||
exp >>= 1;
|
||||
if (exp == Ty{0}) {
|
||||
break;
|
||||
}
|
||||
base *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
RT_EXT_API_GROUP_BEGIN
|
||||
|
||||
@@ -933,6 +951,27 @@ CppTypeFor<TypeCategory::Real, 16> RTDEF(FPow16k)(
|
||||
}
|
||||
#endif
|
||||
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> RTDEF(UPow1)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> e) {
|
||||
return UPow(b, e);
|
||||
}
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> RTDEF(UPow2)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> e) {
|
||||
return UPow(b, e);
|
||||
}
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> RTDEF(UPow4)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> e) {
|
||||
return UPow(b, e);
|
||||
}
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> RTDEF(UPow8)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> e) {
|
||||
return UPow(b, e);
|
||||
}
|
||||
|
||||
RT_EXT_API_GROUP_END
|
||||
} // extern "C"
|
||||
} // namespace Fortran::runtime
|
||||
|
||||
@@ -566,9 +566,9 @@ private:
|
||||
using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
|
||||
Convert<Result, TypeCategory::Real>,
|
||||
Convert<Result, TypeCategory::Unsigned>>;
|
||||
using Operations =
|
||||
std::tuple<Parentheses<Result>, Negate<Result>, Add<Result>,
|
||||
Subtract<Result>, Multiply<Result>, Divide<Result>, Extremum<Result>>;
|
||||
using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
|
||||
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
|
||||
Power<Result>, Extremum<Result>>;
|
||||
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
|
||||
Designator<Result>, FunctionRef<Result>>;
|
||||
|
||||
|
||||
@@ -750,11 +750,11 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
|
||||
// one of the operands to the type of the other. Handles special cases with
|
||||
// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
|
||||
// powers.
|
||||
template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
|
||||
template <template <typename> class OPR>
|
||||
std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
|
||||
Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
|
||||
|
||||
extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
|
||||
extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
|
||||
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
||||
int defaultRealKind);
|
||||
extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
||||
|
||||
@@ -453,6 +453,19 @@ CppTypeFor<TypeCategory::Real, 16> RTDECL(FPow16k)(
|
||||
CppTypeFor<TypeCategory::Integer, 8> e);
|
||||
#endif
|
||||
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> RTDEF(UPow1)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 1> e);
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> RTDEF(UPow2)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 2> e);
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> RTDEF(UPow4)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 4> e);
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> RTDEF(UPow8)(
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> b,
|
||||
CppTypeFor<TypeCategory::Unsigned, 8> e);
|
||||
|
||||
} // extern "C"
|
||||
} // namespace Fortran::runtime
|
||||
#endif // FORTRAN_RUNTIME_NUMERIC_H_
|
||||
|
||||
@@ -495,7 +495,7 @@ Expr<SomeComplex> PromoteMixedComplexReal(
|
||||
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
|
||||
// the operands to a dyadic operation where one is permitted, it assumes the
|
||||
// type and kind of the other operand.
|
||||
template <template <typename> class OPR, bool CAN_BE_UNSIGNED>
|
||||
template <template <typename> class OPR>
|
||||
std::optional<Expr<SomeType>> NumericOperation(
|
||||
parser::ContextualMessages &messages, Expr<SomeType> &&x,
|
||||
Expr<SomeType> &&y, int defaultRealKind) {
|
||||
@@ -510,13 +510,8 @@ std::optional<Expr<SomeType>> NumericOperation(
|
||||
std::move(rx), std::move(ry)));
|
||||
},
|
||||
[&](Expr<SomeUnsigned> &&ix, Expr<SomeUnsigned> &&iy) {
|
||||
if constexpr (CAN_BE_UNSIGNED) {
|
||||
return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>(
|
||||
std::move(ix), std::move(iy)));
|
||||
} else {
|
||||
messages.Say("Operands must not be UNSIGNED"_err_en_US);
|
||||
return NoExpr();
|
||||
}
|
||||
return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>(
|
||||
std::move(ix), std::move(iy)));
|
||||
},
|
||||
// Mixed REAL/INTEGER operations
|
||||
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
||||
@@ -575,34 +570,31 @@ std::optional<Expr<SomeType>> NumericOperation(
|
||||
},
|
||||
// Operations with one typeless operand
|
||||
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
return NumericOperation<OPR>(messages,
|
||||
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
|
||||
defaultRealKind);
|
||||
},
|
||||
[&](BOZLiteralConstant &&bx, Expr<SomeUnsigned> &&iy) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
return NumericOperation<OPR>(messages,
|
||||
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
|
||||
defaultRealKind);
|
||||
},
|
||||
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
return NumericOperation<OPR>(messages,
|
||||
AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
|
||||
defaultRealKind);
|
||||
},
|
||||
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
|
||||
defaultRealKind);
|
||||
return NumericOperation<OPR>(messages, std::move(x),
|
||||
AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
|
||||
},
|
||||
[&](Expr<SomeUnsigned> &&ix, BOZLiteralConstant &&by) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
|
||||
defaultRealKind);
|
||||
return NumericOperation<OPR>(messages, std::move(x),
|
||||
AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
|
||||
},
|
||||
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
|
||||
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
||||
std::move(x), AsGenericExpr(ConvertTo(rx, std::move(by))),
|
||||
defaultRealKind);
|
||||
return NumericOperation<OPR>(messages, std::move(x),
|
||||
AsGenericExpr(ConvertTo(rx, std::move(by))), defaultRealKind);
|
||||
},
|
||||
// Error cases
|
||||
[&](Expr<SomeUnsigned> &&, auto &&) {
|
||||
@@ -621,7 +613,7 @@ std::optional<Expr<SomeType>> NumericOperation(
|
||||
std::move(x.u), std::move(y.u));
|
||||
}
|
||||
|
||||
template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
|
||||
template std::optional<Expr<SomeType>> NumericOperation<Power>(
|
||||
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
||||
int defaultRealKind);
|
||||
template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
||||
|
||||
@@ -138,7 +138,7 @@ static const char __ldlu_r8x2[] = "__ldlu_r8x2_";
|
||||
/// Table that drives the fir generation depending on the intrinsic or intrinsic
|
||||
/// module procedure one to one mapping with Fortran arguments. If no mapping is
|
||||
/// defined here for a generic intrinsic, genRuntimeCall will be called
|
||||
/// to look for a match in the runtime a emit a call. Note that the argument
|
||||
/// to look for a match in the runtime and emit a call. Note that the argument
|
||||
/// lowering rules for an intrinsic need to be provided only if at least one
|
||||
/// argument must not be lowered by value. In which case, the lowering rules
|
||||
/// should be provided for all the intrinsic arguments for completeness.
|
||||
@@ -1062,7 +1062,7 @@ prettyPrintIntrinsicName(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
llvm::StringRef suffix, mlir::FunctionType funcType) {
|
||||
std::string output = prefix.str();
|
||||
llvm::raw_string_ostream sstream(output);
|
||||
if (name == "pow") {
|
||||
if (name == "pow" || name == "pow-unsigned") {
|
||||
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
|
||||
std::string displayName{" ** "};
|
||||
sstream << mlirTypeToIntrinsicFortran(builder, funcType.getInput(0), loc,
|
||||
@@ -1675,6 +1675,14 @@ static constexpr MathOperation mathOperations[] = {
|
||||
genComplexPow},
|
||||
{"pow", RTNAME_STRING(cqpowk), FuncTypeComplex16Complex16Integer8,
|
||||
genLibF128Call},
|
||||
{"pow-unsigned", RTNAME_STRING(UPow1),
|
||||
genFuncType<Ty::Integer<1>, Ty::Integer<1>, Ty::Integer<1>>, genLibCall},
|
||||
{"pow-unsigned", RTNAME_STRING(UPow2),
|
||||
genFuncType<Ty::Integer<2>, Ty::Integer<2>, Ty::Integer<2>>, genLibCall},
|
||||
{"pow-unsigned", RTNAME_STRING(UPow4),
|
||||
genFuncType<Ty::Integer<4>, Ty::Integer<4>, Ty::Integer<4>>, genLibCall},
|
||||
{"pow-unsigned", RTNAME_STRING(UPow8),
|
||||
genFuncType<Ty::Integer<8>, Ty::Integer<8>, Ty::Integer<8>>, genLibCall},
|
||||
{"remainder", "remainderf",
|
||||
genFuncType<Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, genLibCall},
|
||||
{"remainder", "remainder",
|
||||
@@ -9441,6 +9449,14 @@ mlir::Value genPow(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
// implementation and mark it 'strictfp'.
|
||||
// Another option is to implement it in Fortran runtime library
|
||||
// (just like matmul).
|
||||
if (type.isUnsignedInteger()) {
|
||||
assert(x.getType().isUnsignedInteger() && y.getType().isUnsignedInteger() &&
|
||||
"unsigned pow requires unsigned arguments");
|
||||
return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow-unsigned", type,
|
||||
{x, y});
|
||||
}
|
||||
assert(!x.getType().isUnsignedInteger() && !y.getType().isUnsignedInteger() &&
|
||||
"non-unsigned pow requires non-unsigned arguments");
|
||||
return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
|
||||
}
|
||||
|
||||
|
||||
@@ -3783,10 +3783,9 @@ MaybeExpr NumericBinaryHelper(
|
||||
analyzer.CheckForNullPointer();
|
||||
analyzer.CheckForAssumedRank();
|
||||
analyzer.CheckConformance();
|
||||
constexpr bool canBeUnsigned{opr != NumericOperator::Power};
|
||||
return NumericOperation<OPR, canBeUnsigned>(
|
||||
context.GetContextualMessages(), analyzer.MoveExpr(0),
|
||||
analyzer.MoveExpr(1), context.GetDefaultKind(TypeCategory::Real));
|
||||
return NumericOperation<OPR>(context.GetContextualMessages(),
|
||||
analyzer.MoveExpr(0), analyzer.MoveExpr(1),
|
||||
context.GetDefaultKind(TypeCategory::Real));
|
||||
} else {
|
||||
return analyzer.TryDefinedOp(AsFortran(opr),
|
||||
"Operands of %s must be numeric; have %s and %s"_err_en_US);
|
||||
|
||||
@@ -24,3 +24,29 @@ end
|
||||
!CHECK: fir.store %[[VAL_13]] to %[[VAL_2]] : !fir.ref<ui32>
|
||||
!CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_2]] : !fir.ref<ui32>
|
||||
!CHECK: return %[[VAL_14]] : ui32
|
||||
|
||||
unsigned function f02(u, v)
|
||||
unsigned, intent(in) :: u, v
|
||||
f02 = u ** v - 1u
|
||||
end
|
||||
|
||||
!CHECK: func.func @_QPf02(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u"}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v"}) -> ui32 {
|
||||
!CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
|
||||
!CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
|
||||
!CHECK: %[[VAL_1:.*]] = fir.alloca ui32 {bindc_name = "f02", uniq_name = "_QFf02Ef02"}
|
||||
!CHECK: %[[VAL_2:.*]] = fir.declare %[[VAL_1]] {uniq_name = "_QFf02Ef02"} : (!fir.ref<ui32>) -> !fir.ref<ui32>
|
||||
!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>
|
||||
!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>
|
||||
!CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_3]] : !fir.ref<ui32>
|
||||
!CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]] : !fir.ref<ui32>
|
||||
!CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_5]] : (ui32) -> i64
|
||||
!CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_6]] : (ui32) -> i64
|
||||
!CHECK: %[[VAL_9:.*]] = fir.call @_FortranAUPow8(%[[VAL_7]], %[[VAL_8]]) fastmath<contract> : (i64, i64) -> i64
|
||||
!CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (i64) -> ui32
|
||||
!CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (ui32) -> i32
|
||||
!CHECK: %[[VAL_12:.*]] = arith.subi %[[VAL_11]], %[[C1_i32]] : i32
|
||||
!CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (i32) -> ui32
|
||||
!CHECK: fir.store %[[VAL_13]] to %[[VAL_2]] : !fir.ref<ui32>
|
||||
!CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_2]] : !fir.ref<ui32>
|
||||
!CHECK: return %[[VAL_14]] : ui32
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ print *, 0u + 1u ! ok
|
||||
print *, 0u - 1u ! ok
|
||||
print *, 0u * 1u ! ok
|
||||
print *, 0u / 1u ! ok
|
||||
!ERROR: Operands must not be UNSIGNED
|
||||
print *, 0u ** 1u
|
||||
print *, 0u ** 1u ! ok
|
||||
|
||||
print *, uint((0.,0.)) ! ok
|
||||
print *, uint(z'123') ! ok
|
||||
|
||||
Reference in New Issue
Block a user