diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -129,6 +129,20 @@ return !isStaticallyAbsent(exv); } +// Helper function to check if a constant int mlir::Value is in a given range. +// The range check is inclusive. +bool isConstantIntInRange(mlir::Value value, int lower, int upper) { + if (auto constInt = dyn_cast(value.getDefiningOp()) + .getValue() + .dyn_cast_or_null()) { + return (constInt.getInt() < lower || constInt.getInt() > upper) ? false + : true; + } else { + assert( + "mlir::Value passed to helper function should be of type constant int"); + } +} + // TODO error handling -> return a code or directly emit messages ? struct IntrinsicLibrary { @@ -323,6 +337,10 @@ /// is ignored because this is already reflected in the result type. mlir::Value genConversion(mlir::Type, llvm::ArrayRef); + // PPC intrinsic handlers. + template + void genMtfsf(llvm::ArrayRef); + /// In the template helper below: /// - "FN func" is a callback to generate the related intrinsic runtime call. /// - "FD funcDim" is a callback to generate the "dim" runtime call. @@ -859,6 +877,18 @@ /*isElemental=*/true}, }; +// PPC specific intrinsic handlers. +static constexpr IntrinsicHandler ppcHandlers[]{ + {"__ppc_mtfsf", + &I::genMtfsf, + {{{"mask", asValue}, {"r", asValue}}}, + /*isElemental=*/false}, + {"__ppc_mtfsfi", + &I::genMtfsf, + {{{"bf", asValue}, {"i", asValue}}}, + /*isElemental=*/false}, +}; + static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { return name.compare(handler.name) > 0; @@ -868,6 +898,15 @@ : nullptr; } +static const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name) { + auto compare = [](const IntrinsicHandler &ppcHandler, llvm::StringRef name) { + return name.compare(ppcHandler.name) > 0; + }; + auto result = llvm::lower_bound(ppcHandlers, name, compare); + return result != std::end(ppcHandlers) && result->name == name ? result + : nullptr; +} + /// To make fir output more readable for debug, one can outline all intrinsic /// implementation in wrappers (overrides the IntrinsicHandler::outline flag). static llvm::cl::opt outlineAllIntrinsics( @@ -959,6 +998,20 @@ return mlir::FunctionType::get(context, {t, t, t}, {t}); } +template +static mlir::FunctionType genIntF64SubroutineType(mlir::MLIRContext *context) { + auto t = mlir::IntegerType::get(context, Bits); + auto u = mlir::FloatType::getF64(context); + return mlir::FunctionType::get(context, {t, u}, std::nullopt); +} + +template +static mlir::FunctionType genIntIntSubroutineType(mlir::MLIRContext *context) { + auto t = mlir::IntegerType::get(context, BitsA); + auto u = mlir::IntegerType::get(context, BitsB); + return mlir::FunctionType::get(context, {t, u}, std::nullopt); +} + template static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) { auto t = mlir::FloatType::getF64(context); @@ -1787,15 +1840,30 @@ this->resultMustBeFreed}; } - if (!resultType) - // Subroutine should have a handler, they are likely missing for now. - crashOnMissingIntrinsic(loc, name); + // If targeting PowerPC, check PPC intrinsic handlers. + auto mod = builder.getModule(); + if (fir::getTargetTriple(mod).isPPC()) { + if (const IntrinsicHandler *ppcHandler = findPPCIntrinsicHandler(name)) { + bool outline = ppcHandler->outline || outlineAllIntrinsics; + return {std::visit( + [&](auto &generator) -> fir::ExtendedValue { + return invokeHandler(generator, *ppcHandler, resultType, + args, outline, *this); + }, + ppcHandler->generator), + this->resultMustBeFreed}; + } + } // Try the runtime if no special handler was defined for the // intrinsic being called. Maths runtime only has numerical elemental. // No optional arguments are expected at this point, the code will // crash if it gets absent optional. + if (!resultType) + // Subroutine should have a handler, they are likely missing for now. + crashOnMissingIntrinsic(loc, name); + // FIXME: using toValue to get the type won't work with array arguments. llvm::SmallVector mlirArgs; for (const fir::ExtendedValue &extendedVal : args) { @@ -1893,12 +1961,20 @@ /// arguments. The mangling pattern is: /// fir...... /// e.g ACOS(COMPLEX(4)) is mangled as fir.acos.z4.z4 +/// For subroutines no result type is return but in order to still provide +/// a unique mangled name, we use "void" as the return type. As in: +/// fir..void.... +/// TODO: add an example if this mangling pattern is approved static std::string mangleIntrinsicProcedure(llvm::StringRef intrinsic, mlir::FunctionType funTy) { std::string name = "fir."; name.append(intrinsic.str()).append("."); - assert(funTy.getNumResults() == 1 && "only function mangling supported"); - name.append(typeToString(funTy.getResult(0))); + if (funTy.getNumResults() == 1) + name.append(typeToString(funTy.getResult(0))); + else if (funTy.getNumResults() == 0) + name.append("void"); + else + llvm_unreachable("more than one result value for function"); unsigned e = funTy.getNumInputs(); for (decltype(e) i = 0; i < e; ++i) name.append(".").append(typeToString(funTy.getInput(i))); @@ -5373,6 +5449,65 @@ return result; } +//===----------------------------------------------------------------------===// +// PowerPC specific intrinsic handlers. +//===----------------------------------------------------------------------===// +template +void IntrinsicLibrary::genMtfsf(llvm::ArrayRef args) { + assert(args.size() == 2); + llvm::SmallVector scalarArgs; + for (const fir::ExtendedValue &arg : args) + if (arg.getUnboxed()) + scalarArgs.emplace_back(fir::getBase(arg)); + else + mlir::emitError(loc, "nonscalar intrinsic argument"); + + mlir::FunctionType libFuncType; + mlir::func::FuncOp funcOp; + if (isImm) { + // Check arguments and create FuncOp for mtfsfi. + for (mlir::Value &scalarArg : scalarArgs) { + mlir::Type type = scalarArg.getType(); + if (scalarArg.getDefiningOp() != nullptr && + !isa(scalarArg.getDefiningOp())) + mlir::emitError(loc, "expected constant integer argument"); + if (!type.isa()) + llvm_unreachable( + "expected an integer argument, this should have already been " + "caught when matching the function declaration"); + } + if (scalarArgs[0].getDefiningOp() != nullptr && + !isConstantIntInRange(scalarArgs[0], 0, 7)) + mlir::emitError( + loc, + "expected first argument to be constant integer in range 0 to 7"); + if (scalarArgs[1].getDefiningOp() != nullptr && + !isConstantIntInRange(scalarArgs[1], 0, 15)) + mlir::emitError( + loc, + "expected second argument to be constant integer in range 0 to 15"); + libFuncType = genIntIntSubroutineType<32, 32>(builder.getContext()); + funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsfi", libFuncType); + } else { + // Check arguments and create FuncOp for mtfsf. + mlir::Type type = scalarArgs[0].getType(); + if (scalarArgs[0].getDefiningOp() != nullptr && + !isa(scalarArgs[0].getDefiningOp())) + mlir::emitError(loc, "expected constant integer argument"); + if (!type.isa()) + llvm_unreachable("expected an integer argument, this should have already " + "been caught when matching the function declaration"); + if (scalarArgs[0].getDefiningOp() != nullptr && + !isConstantIntInRange(scalarArgs[0], 0, 7)) + mlir::emitError( + loc, + "expected first argument to be constant integer in range 0 to 7"); + libFuncType = genIntF64SubroutineType<32>(builder.getContext()); + funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsf", libFuncType); + } + builder.create(loc, funcOp, scalarArgs); +} + //===----------------------------------------------------------------------===// // Argument lowering rules interface for intrinsic or intrinsic module // procedure. diff --git a/flang/module/__fortran_ppc_intrinsics.f90 b/flang/module/__fortran_ppc_intrinsics.f90 --- a/flang/module/__fortran_ppc_intrinsics.f90 +++ b/flang/module/__fortran_ppc_intrinsics.f90 @@ -52,4 +52,21 @@ end interface fnmsub public :: fnmsub +! mtfsf, mtfsfi + interface mtfsf + subroutine __ppc_mtfsf(mask, r) + integer(4), intent(in) :: mask + real(8), intent(in) :: r + end subroutine __ppc_mtfsf + end interface mtfsf + public :: mtfsf + + interface mtfsfi + subroutine __ppc_mtfsfi(bf, i) + integer(4), intent(in) :: bf + integer(4), intent(in) :: i + end subroutine __ppc_mtfsfi + end interface mtfsfi + public :: mtfsfi + end module __Fortran_PPC_intrinsics diff --git a/flang/test/Lower/ppc-intrinsics.f90 b/flang/test/Lower/ppc-intrinsics.f90 --- a/flang/test/Lower/ppc-intrinsics.f90 +++ b/flang/test/Lower/ppc-intrinsics.f90 @@ -65,3 +65,18 @@ ! CHECK-FIR: fir.call @fir.__ppc_fnmsub.f64.f64.f64.f64 ! CHECK-LLVMIR: call contract double @llvm.ppc.fnmsub.f64(double %{{[0-9]}}, double %{{[0-9]}}, double %{{[0-9]}}) end + +! CHECK-LABEL: mtfsf_test +subroutine mtfsf_test(r) + real(8) :: r + call mtfsf(1, r) +! CHECK-FIR: fir.call @fir.__ppc_mtfsf.void.i32.f64 +! CHECK-LLVMIR: call void @llvm.ppc.mtfsf(i32 {{[0-9]}}, double %{{[0-9]}}) +end + +! CHECK-LABEL: mtfsfi_test +subroutine mtfsfi_test() + call mtfsfi(1, 2) +! CHECK-FIR: fir.call @fir.__ppc_mtfsfi.void.i32.i32 +! CHECK-LLVMIR: call void @llvm.ppc.mtfsfi(i32 {{[0-9]}}, i32 {{[0-9]}}) +end