Skip to content

Commit adc56f3

Browse files
committed
[Flang][PowerPC] Implement PPC mtfsf/mtfsfi intrinsics
Implements the PowerPC mtfsf and mtfsfi intrinsics as well as introduces semantic error checking code for PowerPC intrinsics Reviewed By: klausler Differential Revision: https://reviews.llvm.org/D144876
1 parent 06ae522 commit adc56f3

File tree

7 files changed

+188
-5
lines changed

7 files changed

+188
-5
lines changed

flang/include/flang/Semantics/expression.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ class ExpressionAnalyzer {
352352
const parser::ProcComponentRef &, ActualArguments &&, bool isSubroutine);
353353
std::optional<characteristics::Procedure> CheckCall(
354354
parser::CharBlock, const ProcedureDesignator &, ActualArguments &);
355+
bool CheckPPCIntrinsic(const ProcedureDesignator &, ActualArguments &);
355356
using AdjustActuals =
356357
std::optional<std::function<bool(const Symbol &, ActualArguments &)>>;
357358
bool ResolveForward(const Symbol &);

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ struct IntrinsicLibrary {
334334
/// is ignored because this is already reflected in the result type.
335335
mlir::Value genConversion(mlir::Type, llvm::ArrayRef<mlir::Value>);
336336

337+
// PPC intrinsic handlers.
338+
template <bool isImm>
339+
void genMtfsf(llvm::ArrayRef<fir::ExtendedValue>);
340+
337341
/// In the template helper below:
338342
/// - "FN func" is a callback to generate the related intrinsic runtime call.
339343
/// - "FD funcDim" is a callback to generate the "dim" runtime call.
@@ -880,6 +884,18 @@ static constexpr IntrinsicHandler handlers[]{
880884
/*isElemental=*/true},
881885
};
882886

887+
// PPC specific intrinsic handlers.
888+
static constexpr IntrinsicHandler ppcHandlers[]{
889+
{"__ppc_mtfsf",
890+
&I::genMtfsf<false>,
891+
{{{"mask", asValue}, {"r", asValue}}},
892+
/*isElemental=*/false},
893+
{"__ppc_mtfsfi",
894+
&I::genMtfsf<true>,
895+
{{{"bf", asValue}, {"i", asValue}}},
896+
/*isElemental=*/false},
897+
};
898+
883899
static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
884900
auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
885901
return name.compare(handler.name) > 0;
@@ -889,6 +905,15 @@ static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
889905
: nullptr;
890906
}
891907

908+
static const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name) {
909+
auto compare = [](const IntrinsicHandler &ppcHandler, llvm::StringRef name) {
910+
return name.compare(ppcHandler.name) > 0;
911+
};
912+
auto result = llvm::lower_bound(ppcHandlers, name, compare);
913+
return result != std::end(ppcHandlers) && result->name == name ? result
914+
: nullptr;
915+
}
916+
892917
/// To make fir output more readable for debug, one can outline all intrinsic
893918
/// implementation in wrappers (overrides the IntrinsicHandler::outline flag).
894919
static llvm::cl::opt<bool> outlineAllIntrinsics(
@@ -980,6 +1005,20 @@ static mlir::FunctionType genF64F64F64F64FuncType(mlir::MLIRContext *context) {
9801005
return mlir::FunctionType::get(context, {t, t, t}, {t});
9811006
}
9821007

1008+
template <int Bits>
1009+
static mlir::FunctionType genVoidIntF64FuncType(mlir::MLIRContext *context) {
1010+
auto t = mlir::IntegerType::get(context, Bits);
1011+
auto u = mlir::FloatType::getF64(context);
1012+
return mlir::FunctionType::get(context, {t, u}, std::nullopt);
1013+
}
1014+
1015+
template <int BitsA, int BitsB>
1016+
static mlir::FunctionType genVoidIntIntFuncType(mlir::MLIRContext *context) {
1017+
auto t = mlir::IntegerType::get(context, BitsA);
1018+
auto u = mlir::IntegerType::get(context, BitsB);
1019+
return mlir::FunctionType::get(context, {t, u}, std::nullopt);
1020+
}
1021+
9831022
template <int Bits>
9841023
static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) {
9851024
auto t = mlir::FloatType::getF64(context);
@@ -1865,15 +1904,30 @@ IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName,
18651904
this->resultMustBeFreed};
18661905
}
18671906

1868-
if (!resultType)
1869-
// Subroutine should have a handler, they are likely missing for now.
1870-
crashOnMissingIntrinsic(loc, name);
1907+
// If targeting PowerPC, check PPC intrinsic handlers.
1908+
auto mod = builder.getModule();
1909+
if (fir::getTargetTriple(mod).isPPC()) {
1910+
if (const IntrinsicHandler *ppcHandler = findPPCIntrinsicHandler(name)) {
1911+
bool outline = ppcHandler->outline || outlineAllIntrinsics;
1912+
return {std::visit(
1913+
[&](auto &generator) -> fir::ExtendedValue {
1914+
return invokeHandler(generator, *ppcHandler, resultType,
1915+
args, outline, *this);
1916+
},
1917+
ppcHandler->generator),
1918+
this->resultMustBeFreed};
1919+
}
1920+
}
18711921

18721922
// Try the runtime if no special handler was defined for the
18731923
// intrinsic being called. Maths runtime only has numerical elemental.
18741924
// No optional arguments are expected at this point, the code will
18751925
// crash if it gets absent optional.
18761926

1927+
if (!resultType)
1928+
// Subroutine should have a handler, they are likely missing for now.
1929+
crashOnMissingIntrinsic(loc, name);
1930+
18771931
// FIXME: using toValue to get the type won't work with array arguments.
18781932
llvm::SmallVector<mlir::Value> mlirArgs;
18791933
for (const fir::ExtendedValue &extendedVal : args) {
@@ -1971,12 +2025,20 @@ static std::string typeToString(mlir::Type t) {
19712025
/// arguments. The mangling pattern is:
19722026
/// fir.<generic name>.<result type>.<arg type>...
19732027
/// e.g ACOS(COMPLEX(4)) is mangled as fir.acos.z4.z4
2028+
/// For subroutines no result type is return but in order to still provide
2029+
/// a unique mangled name, we use "void" as the return type. As in:
2030+
/// fir.<generic name>.void.<arg type>...
2031+
/// e.g. FREE(INTEGER(4)) is mangled as fir.free.void.i4
19742032
static std::string mangleIntrinsicProcedure(llvm::StringRef intrinsic,
19752033
mlir::FunctionType funTy) {
19762034
std::string name = "fir.";
19772035
name.append(intrinsic.str()).append(".");
1978-
assert(funTy.getNumResults() == 1 && "only function mangling supported");
1979-
name.append(typeToString(funTy.getResult(0)));
2036+
if (funTy.getNumResults() == 1)
2037+
name.append(typeToString(funTy.getResult(0)));
2038+
else if (funTy.getNumResults() == 0)
2039+
name.append("void");
2040+
else
2041+
llvm_unreachable("more than one result value for function");
19802042
unsigned e = funTy.getNumInputs();
19812043
for (decltype(e) i = 0; i < e; ++i)
19822044
name.append(".").append(typeToString(funTy.getInput(i)));
@@ -5520,6 +5582,31 @@ mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
55205582
return result;
55215583
}
55225584

5585+
//===----------------------------------------------------------------------===//
5586+
// PowerPC specific intrinsic handlers.
5587+
//===----------------------------------------------------------------------===//
5588+
template <bool isImm>
5589+
void IntrinsicLibrary::genMtfsf(llvm::ArrayRef<fir::ExtendedValue> args) {
5590+
assert(args.size() == 2);
5591+
llvm::SmallVector<mlir::Value> scalarArgs;
5592+
for (const fir::ExtendedValue &arg : args)
5593+
if (arg.getUnboxed())
5594+
scalarArgs.emplace_back(fir::getBase(arg));
5595+
else
5596+
mlir::emitError(loc, "nonscalar intrinsic argument");
5597+
5598+
mlir::FunctionType libFuncType;
5599+
mlir::func::FuncOp funcOp;
5600+
if (isImm) {
5601+
libFuncType = genVoidIntIntFuncType<32, 32>(builder.getContext());
5602+
funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsfi", libFuncType);
5603+
} else {
5604+
libFuncType = genVoidIntF64FuncType<32>(builder.getContext());
5605+
funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsf", libFuncType);
5606+
}
5607+
builder.create<fir::CallOp>(loc, funcOp, scalarArgs);
5608+
}
5609+
55235610
//===----------------------------------------------------------------------===//
55245611
// Argument lowering rules interface for intrinsic or intrinsic module
55255612
// procedure.

flang/lib/Semantics/check-call.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,57 @@ bool CheckInterfaceForGeneric(const characteristics::Procedure &proc,
12281228
.AnyFatalError();
12291229
}
12301230

1231+
bool CheckArgumentIsConstantExprInRange(
1232+
const evaluate::ActualArguments &actuals, int index, int lowerBound,
1233+
int upperBound, parser::ContextualMessages &messages) {
1234+
CHECK(index >= 0 && index < actuals.size());
1235+
1236+
const std::optional<evaluate::ActualArgument> &argOptional{actuals[index]};
1237+
if (!argOptional) {
1238+
DIE("Actual argument should have value");
1239+
return false;
1240+
}
1241+
1242+
const evaluate::ActualArgument &arg{argOptional.value()};
1243+
const evaluate::Expr<evaluate::SomeType> *argExpr{arg.UnwrapExpr()};
1244+
CHECK(argExpr != nullptr);
1245+
1246+
if (!IsConstantExpr(*argExpr)) {
1247+
messages.Say("Actual argument #%d must be a constant expression"_err_en_US,
1248+
index + 1);
1249+
return false;
1250+
}
1251+
1252+
// This does not imply that the kind of the argument is 8. The kind
1253+
// for the intrinsic's argument should have been check prior. This is just
1254+
// a conversion so that we can read the constant value.
1255+
auto scalarValue{evaluate::ToInt64(argExpr)};
1256+
CHECK(scalarValue.has_value());
1257+
1258+
if (*scalarValue < lowerBound || *scalarValue > upperBound) {
1259+
messages.Say(
1260+
"Argument #%d must be a constant expression in range %d-%d"_err_en_US,
1261+
index + 1, lowerBound, upperBound);
1262+
return false;
1263+
}
1264+
return true;
1265+
}
1266+
1267+
bool CheckPPCIntrinsic(const Symbol &generic, const Symbol &specific,
1268+
const evaluate::ActualArguments &actuals,
1269+
evaluate::FoldingContext &context) {
1270+
parser::ContextualMessages &messages{context.messages()};
1271+
1272+
if (specific.name() == "__ppc_mtfsf") {
1273+
return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages);
1274+
}
1275+
if (specific.name() == "__ppc_mtfsfi") {
1276+
return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages) &&
1277+
CheckArgumentIsConstantExprInRange(actuals, 1, 0, 15, messages);
1278+
}
1279+
return false;
1280+
}
1281+
12311282
bool CheckArguments(const characteristics::Procedure &proc,
12321283
evaluate::ActualArguments &actuals, evaluate::FoldingContext &context,
12331284
const Scope &scope, bool treatingExternalAsImplicit,

flang/lib/Semantics/check-call.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ bool CheckArguments(const evaluate::characteristics::Procedure &,
3737
bool treatingExternalAsImplicit,
3838
const evaluate::SpecificIntrinsic *intrinsic);
3939

40+
bool CheckPPCIntrinsic(const Symbol &generic, const Symbol &specific,
41+
const evaluate::ActualArguments &actuals,
42+
evaluate::FoldingContext &context);
43+
bool CheckArgumentIsConstantExprInRange(
44+
const evaluate::ActualArguments &actuals, int index, int lowerBound,
45+
int upperBound, parser::ContextualMessages &messages);
46+
4047
// Checks actual arguments for the purpose of resolving a generic interface.
4148
bool CheckInterfaceForGeneric(const evaluate::characteristics::Procedure &,
4249
evaluate::ActualArguments &, const evaluate::FoldingContext &,

flang/lib/Semantics/expression.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2518,6 +2518,11 @@ auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name,
25182518
mightBeStructureConstructor)};
25192519
resolution = pair.first;
25202520
dueToAmbiguity = pair.second;
2521+
if (context_.GetPPCBuiltinsScope() &&
2522+
resolution->name().ToString().rfind("__ppc_", 0) == 0) {
2523+
semantics::CheckPPCIntrinsic(
2524+
*symbol, *resolution, arguments, GetFoldingContext());
2525+
}
25212526
if (resolution) {
25222527
// re-resolve name to the specific procedure
25232528
name.symbol = const_cast<Symbol *>(resolution);

flang/module/__fortran_ppc_intrinsics.f90

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,21 @@ end function func_r8r8i
157157
procedure :: __ppc_frsqrtes
158158
end interface frsqrtes
159159
public :: frsqrtes
160+
161+
! mtfsf, mtfsfi
162+
interface mtfsf
163+
subroutine __ppc_mtfsf(mask, r)
164+
integer(4), intent(in) :: mask
165+
real(8), intent(in) :: r
166+
end subroutine __ppc_mtfsf
167+
end interface mtfsf
168+
public :: mtfsf
169+
170+
interface mtfsfi
171+
subroutine __ppc_mtfsfi(bf, i)
172+
integer(4), intent(in) :: bf
173+
integer(4), intent(in) :: i
174+
end subroutine __ppc_mtfsfi
175+
end interface mtfsfi
176+
public :: mtfsfi
160177
end module __Fortran_PPC_intrinsics

flang/test/Lower/ppc-intrinsics.f90

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,18 @@ subroutine frsqrtes_test(x)
185185
! CHECK-FIR: fir.call @fir.__ppc_frsqrtes.f32.f32
186186
! CHECK-LLVMIR: call contract float @llvm.ppc.frsqrtes(float %{{[0-9]}})
187187
end
188+
189+
! CHECK-LABEL: mtfsf_test
190+
subroutine mtfsf_test(r)
191+
real(8) :: r
192+
call mtfsf(1, r)
193+
! CHECK-FIR: fir.call @fir.__ppc_mtfsf.void.i32.f64
194+
! CHECK-LLVMIR: call void @llvm.ppc.mtfsf(i32 {{[0-9]}}, double %{{[0-9]}})
195+
end
196+
197+
! CHECK-LABEL: mtfsfi_test
198+
subroutine mtfsfi_test()
199+
call mtfsfi(1, 2)
200+
! CHECK-FIR: fir.call @fir.__ppc_mtfsfi.void.i32.i32
201+
! CHECK-LLVMIR: call void @llvm.ppc.mtfsfi(i32 {{[0-9]}}, i32 {{[0-9]}})
202+
end

0 commit comments

Comments
 (0)