diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h --- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h +++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h @@ -9,9 +9,16 @@ #ifndef FORTRAN_LOWER_INTRINSICCALL_H #define FORTRAN_LOWER_INTRINSICCALL_H +#include "flang/Optimizer/Builder/BoxValue.h" #include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/Runtime/Character.h" +#include "flang/Optimizer/Builder/Runtime/Numeric.h" #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" +#include "flang/Runtime/entry-names.h" #include "flang/Runtime/iostat.h" +#include "mlir/Dialect/Complex/IR/Complex.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/Math/IR/Math.h" #include namespace fir { @@ -88,10 +95,6 @@ Inquired }; -/// Enums used to templatize vector intrinsic function generators. Enum does -/// not contain every vector intrinsic, only intrinsics that share generators. -enum class VecOp { Add, And, Mul, Sub, Xor }; - /// Define how a given intrinsic argument must be lowered. struct ArgLoweringRule { LowerIntrinsicArgAs lowerAs; @@ -108,6 +111,11 @@ bool handleDynamicOptional; }; +constexpr auto asValue = fir::LowerIntrinsicArgAs::Value; +constexpr auto asAddr = fir::LowerIntrinsicArgAs::Addr; +constexpr auto asBox = fir::LowerIntrinsicArgAs::Box; +constexpr auto asInquired = fir::LowerIntrinsicArgAs::Inquired; + /// Opaque class defining the argument lowering rules for all the argument of /// an intrinsic. struct IntrinsicArgumentLoweringRules; @@ -314,10 +322,6 @@ /// 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. @@ -339,11 +343,6 @@ mlir::Type resultType, llvm::ArrayRef args); - template - fir::ExtendedValue - genVecAddAndMulSubXor(mlir::Type resultType, - llvm::ArrayRef args); - /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); @@ -559,6 +558,58 @@ return mlir::FunctionType::get(context, argTypes, {resType}); } +//===----------------------------------------------------------------------===// +// Helper functions for argument handling. +//===----------------------------------------------------------------------===// +static inline mlir::Type getConvertedElementType(mlir::MLIRContext *context, + mlir::Type eleTy) { + if (eleTy.isa() && !eleTy.isSignlessInteger()) { + const auto intTy{eleTy.dyn_cast()}; + auto newEleTy{mlir::IntegerType::get(context, intTy.getWidth())}; + return newEleTy; + } + return eleTy; +} + +static inline llvm::SmallVector +getBasesForArgs(llvm::ArrayRef args) { + llvm::SmallVector baseVec; + for (auto arg : args) + baseVec.push_back(getBase(arg)); + return baseVec; +} + +static inline llvm::SmallVector +getTypesForArgs(llvm::ArrayRef args) { + llvm::SmallVector typeVec; + for (auto arg : args) + typeVec.push_back(arg.getType()); + return typeVec; +} + +mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef libFuncName, + mlir::FunctionType libFuncType, + llvm::ArrayRef args); + +template +mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef mathLibFuncName, + mlir::FunctionType mathLibFuncType, + llvm::ArrayRef args); + +template +mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef mathLibFuncName, + mlir::FunctionType mathLibFuncType, + llvm::ArrayRef args); + +mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder, + mlir::Location loc, + llvm::StringRef libFuncName, + mlir::FunctionType libFuncType, + llvm::ArrayRef args); + /// Return argument lowering rules for an intrinsic. /// Returns a nullptr if all the intrinsic arguments should be lowered by value. const IntrinsicArgumentLoweringRules * diff --git a/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h b/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h @@ -0,0 +1,100 @@ +//==-- Builder/PPCIntrinsicCall.h - lowering of PowerPC intrinsics -*-C++-*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_LOWER_PPCINTRINSICCALL_H +#define FORTRAN_LOWER_PPCINTRINSICCALL_H + +#include "flang/Common/static-multimap-view.h" +#include "flang/Optimizer/Builder/IntrinsicCall.h" +#include "mlir/Dialect/Math/IR/Math.h" + +namespace fir { + +/// Enums used to templatize vector intrinsic function generators. Enum does +/// not contain every vector intrinsic, only intrinsics that share generators. +enum class VecOp { Add, And, Mul, Sub, Xor }; + +// Wrapper struct to encapsulate information for a vector type. Preserves +// sign of eleTy if eleTy is signed/unsigned integer. Helps with vector type +// conversions. +struct VecTypeInfo { + mlir::Type eleTy; + uint64_t len; + + mlir::Type toFirVectorType() { return fir::VectorType::get(len, eleTy); } + + // We need a builder to do the signless element conversion. + mlir::Type toMlirVectorType(mlir::MLIRContext *context) { + // Will convert to eleTy to signless int if eleTy is signed/unsigned int. + auto convEleTy{getConvertedElementType(context, eleTy)}; + return mlir::VectorType::get(len, convEleTy); + } + + bool isFloat32() { return mlir::isa(eleTy); } + + bool isFloat64() { return mlir::isa(eleTy); } + + bool isFloat() { return isFloat32() || isFloat64(); } +}; + +//===----------------------------------------------------------------------===// +// Helper functions for argument handling in vector intrinsics. +//===----------------------------------------------------------------------===// + +// Returns a VecTypeInfo with element type and length of given fir vector type. +// Preserves signness of fir vector type if element type of integer. +static inline VecTypeInfo getVecTypeFromFirType(mlir::Type firTy) { + assert(firTy.isa()); + VecTypeInfo vecTyInfo; + vecTyInfo.eleTy = firTy.dyn_cast().getEleTy(); + vecTyInfo.len = firTy.dyn_cast().getLen(); + return vecTyInfo; +} + +static inline VecTypeInfo getVecTypeFromFir(mlir::Value firVec) { + return getVecTypeFromFirType(firVec.getType()); +} + +// Converts array of fir vectors to mlir vectors. +static inline llvm::SmallVector +convertVecArgs(fir::FirOpBuilder &builder, mlir::Location loc, + VecTypeInfo vecTyInfo, llvm::SmallVector args) { + llvm::SmallVector newArgs; + auto ty{vecTyInfo.toMlirVectorType(builder.getContext())}; + assert(ty && "unknown mlir vector type"); + for (size_t i = 0; i < args.size(); i++) + newArgs.push_back(builder.createConvert(loc, ty, args[i])); + return newArgs; +} + +struct PPCIntrinsicLibrary : IntrinsicLibrary { + + // Constructors. + explicit PPCIntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc) + : IntrinsicLibrary(builder, loc) {} + PPCIntrinsicLibrary() = delete; + PPCIntrinsicLibrary(const PPCIntrinsicLibrary &) = delete; + + // PPC intrinsic handlers. + template + void genMtfsf(llvm::ArrayRef); + + template + fir::ExtendedValue + genVecAddAndMulSubXor(mlir::Type resultType, + llvm::ArrayRef args); +}; + +const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name); + +std::pair +checkPPCMathOperationsRange(llvm::StringRef name); + +} // namespace fir + +#endif // FORTRAN_LOWER_PPCINTRINSICCALL_H diff --git a/flang/lib/Optimizer/Builder/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt --- a/flang/lib/Optimizer/Builder/CMakeLists.txt +++ b/flang/lib/Optimizer/Builder/CMakeLists.txt @@ -11,6 +11,7 @@ IntrinsicCall.cpp LowLevelIntrinsics.cpp MutableBox.cpp + PPCIntrinsicCall.cpp Runtime/Allocatable.cpp Runtime/ArrayConstructor.cpp Runtime/Assign.cpp 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 @@ -20,6 +20,7 @@ #include "flang/Optimizer/Builder/Complex.h" #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/MutableBox.h" +#include "flang/Optimizer/Builder/PPCIntrinsicCall.h" #include "flang/Optimizer/Builder/Runtime/Allocatable.h" #include "flang/Optimizer/Builder/Runtime/Character.h" #include "flang/Optimizer/Builder/Runtime/Command.h" @@ -89,88 +90,6 @@ return !isStaticallyAbsent(exv); } -//===----------------------------------------------------------------------===// -// Helper functions for argument handling in vector intrinsics. -//===----------------------------------------------------------------------===// -static mlir::Type getConvertedElementType(mlir::MLIRContext *context, - mlir::Type eleTy) { - if (eleTy.isa() && !eleTy.isSignlessInteger()) { - const auto intTy{eleTy.dyn_cast()}; - auto newEleTy{mlir::IntegerType::get(context, intTy.getWidth())}; - return newEleTy; - } - return eleTy; -} - -// Wrapper struct to encapsulate information for a vector type. Preserves -// sign of eleTy if eleTy is signed/unsigned integer. Helps with vector type -// conversions. -struct VecTypeInfo { - mlir::Type eleTy; - uint64_t len; - - mlir::Type toFirVectorType() { return fir::VectorType::get(len, eleTy); } - - // We need a builder to do the signless element conversion. - mlir::Type toMlirVectorType(mlir::MLIRContext *context) { - // Will convert to eleTy to signless int if eleTy is signed/unsigned int. - auto convEleTy{getConvertedElementType(context, eleTy)}; - return mlir::VectorType::get(len, convEleTy); - } - - bool isFloat32() { return mlir::isa(eleTy); } - - bool isFloat64() { return mlir::isa(eleTy); } - - bool isFloat() { return isFloat32() || isFloat64(); } -}; - -static llvm::SmallVector -getBasesForArgs(llvm::ArrayRef args) { - llvm::SmallVector baseVec; - for (auto arg : args) - baseVec.push_back(getBase(arg)); - return baseVec; -} - -static llvm::SmallVector -getTypesForArgs(llvm::ArrayRef args) { - llvm::SmallVector typeVec; - for (auto arg : args) - typeVec.push_back(arg.getType()); - return typeVec; -} - -// Returns a VecTypeInfo with element type and length of given fir vector type. -// Preserves signness of fir vector type if element type of integer. -static VecTypeInfo getVecTypeFromFirType(mlir::Type firTy) { - assert(firTy.isa()); - VecTypeInfo vecTyInfo; - vecTyInfo.eleTy = firTy.dyn_cast().getEleTy(); - vecTyInfo.len = firTy.dyn_cast().getLen(); - return vecTyInfo; -} - -static VecTypeInfo getVecTypeFromFir(mlir::Value firVec) { - return getVecTypeFromFirType(firVec.getType()); -} - -// Converts array of fir vectors to mlir vectors. -static llvm::SmallVector -convertVecArgs(fir::FirOpBuilder &builder, mlir::Location loc, - VecTypeInfo vecTyInfo, llvm::SmallVector args) { - llvm::SmallVector newArgs; - auto ty{vecTyInfo.toMlirVectorType(builder.getContext())}; - assert(ty && "unknown mlir vector type"); - for (size_t i = 0; i < args.size(); i++) - newArgs.push_back(builder.createConvert(loc, ty, args[i])); - return newArgs; -} - -constexpr auto asValue = fir::LowerIntrinsicArgAs::Value; -constexpr auto asAddr = fir::LowerIntrinsicArgAs::Addr; -constexpr auto asBox = fir::LowerIntrinsicArgAs::Box; -constexpr auto asInquired = fir::LowerIntrinsicArgAs::Inquired; using I = IntrinsicLibrary; /// Flag to indicate that an intrinsic argument has to be handled as @@ -600,38 +519,6 @@ /*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}, - {"__ppc_vec_add", - &I::genVecAddAndMulSubXor, - {{{"arg1", asValue}, {"arg2", asValue}}}, - /*isElemental=*/true}, - {"__ppc_vec_and", - &I::genVecAddAndMulSubXor, - {{{"arg1", asValue}, {"arg2", asValue}}}, - /*isElemental=*/true}, - {"__ppc_vec_mul", - &I::genVecAddAndMulSubXor, - {{{"arg1", asValue}, {"arg2", asValue}}}, - /*isElemental=*/true}, - {"__ppc_vec_sub", - &I::genVecAddAndMulSubXor, - {{{"arg1", asValue}, {"arg2", asValue}}}, - /*isElemental=*/true}, - {"__ppc_vec_xor", - &I::genVecAddAndMulSubXor, - {{{"arg1", asValue}, {"arg2", asValue}}}, - /*isElemental=*/true}, -}; - static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { return name.compare(handler.name) > 0; @@ -641,15 +528,6 @@ : 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( @@ -679,10 +557,10 @@ "dialect to lower complex operations"), llvm::cl::init(false)); -static mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc, - llvm::StringRef libFuncName, - mlir::FunctionType libFuncType, - llvm::ArrayRef args) { +mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef libFuncName, + mlir::FunctionType libFuncType, + llvm::ArrayRef args) { LLVM_DEBUG(llvm::dbgs() << "Generating '" << libFuncName << "' call with type "; libFuncType.dump(); llvm::dbgs() << "\n"); @@ -738,9 +616,11 @@ return libCall.getResult(0); } -static mlir::Value genLibSplitComplexArgsCall( - fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef libFuncName, - mlir::FunctionType libFuncType, llvm::ArrayRef args) { +mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder, + mlir::Location loc, + llvm::StringRef libFuncName, + mlir::FunctionType libFuncType, + llvm::ArrayRef args) { assert(args.size() == 2 && "Incorrect #args to genLibSplitComplexArgsCall"); auto getSplitComplexArgsType = [&builder, &args]() -> mlir::FunctionType { @@ -787,10 +667,10 @@ } template -static mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc, - llvm::StringRef mathLibFuncName, - mlir::FunctionType mathLibFuncType, - llvm::ArrayRef args) { +mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef mathLibFuncName, + mlir::FunctionType mathLibFuncType, + llvm::ArrayRef args) { // TODO: we have to annotate the math operations with flags // that will allow to define FP accuracy/exception // behavior per operation, so that after early multi-module @@ -829,11 +709,10 @@ } template -static mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, - mlir::Location loc, - llvm::StringRef mathLibFuncName, - mlir::FunctionType mathLibFuncType, - llvm::ArrayRef args) { +mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef mathLibFuncName, + mlir::FunctionType mathLibFuncType, + llvm::ArrayRef args) { mlir::Value result; if (disableMlirComplex || (mathRuntimeVersion == preciseVersion && !mathLibFuncName.empty())) { @@ -1136,64 +1015,6 @@ genComplexMathOp}, }; -static constexpr MathOperation ppcMathOperations[] = { - // fcfi is just another name for fcfid, there is no llvm.ppc.fcfi. - {"__ppc_fcfi", "llvm.ppc.fcfid", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fcfid", "llvm.ppc.fcfid", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fcfud", "llvm.ppc.fcfud", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctid", "llvm.ppc.fctid", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctidz", "llvm.ppc.fctidz", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctiw", "llvm.ppc.fctiw", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctiwz", "llvm.ppc.fctiwz", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctudz", "llvm.ppc.fctudz", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fctuwz", "llvm.ppc.fctuwz", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fmadd", "llvm.fma.f32", - genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, - genMathOp}, - {"__ppc_fmadd", "llvm.fma.f64", - genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, - genMathOp}, - {"__ppc_fmsub", "llvm.ppc.fmsubs", - genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, - genLibCall}, - {"__ppc_fmsub", "llvm.ppc.fmsub", - genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, - genLibCall}, - {"__ppc_fnabs", "llvm.ppc.fnabss", genFuncType, Ty::Real<4>>, - genLibCall}, - {"__ppc_fnabs", "llvm.ppc.fnabs", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fnmadd", "llvm.ppc.fnmadds", - genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, - genLibCall}, - {"__ppc_fnmadd", "llvm.ppc.fnmadd", - genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, - genLibCall}, - {"__ppc_fnmsub", "llvm.ppc.fnmsub.f32", - genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, - genLibCall}, - {"__ppc_fnmsub", "llvm.ppc.fnmsub.f64", - genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, - genLibCall}, - {"__ppc_fre", "llvm.ppc.fre", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_fres", "llvm.ppc.fres", genFuncType, Ty::Real<4>>, - genLibCall}, - {"__ppc_frsqrte", "llvm.ppc.frsqrte", genFuncType, Ty::Real<8>>, - genLibCall}, - {"__ppc_frsqrtes", "llvm.ppc.frsqrtes", - genFuncType, Ty::Real<4>>, genLibCall}, -}; - // This helper class computes a "distance" between two function types. // The distance measures how many narrowing conversions of actual arguments // and result of "from" must be made in order to use "to" instead of "from". @@ -1338,10 +1159,6 @@ static constexpr RtMap mathOps(mathOperations); static_assert(mathOps.Verify() && "map must be sorted"); -// PPC -static constexpr RtMap ppcMathOps(ppcMathOperations); -static_assert(ppcMathOps.Verify() && "map must be sorted"); - /// Look for a MathOperation entry specifying how to lower a mathematical /// operation defined by \p name with its result' and operands' types /// specified in the form of a FunctionType \p funcType. @@ -1363,7 +1180,7 @@ // Search ppcMathOps only if targetting PowerPC arch if (fir::getTargetTriple(mod).isPPC() && range.first == range.second) { - range = ppcMathOps.equal_range(name); + range = checkPPCMathOperationsRange(name); } for (auto iter = range.first; iter != range.second && iter; ++iter) { const auto &impl = *iter; @@ -4604,73 +4421,6 @@ return builder.createConvert(loc, resultType, result); } -// VEC_ADD, VEC_AND, VEC_SUB, VEC_MUL, VEC_XOR -template -fir::ExtendedValue IntrinsicLibrary::genVecAddAndMulSubXor( - mlir::Type resultType, llvm::ArrayRef args) { - assert(args.size() == 2); - auto argBases{getBasesForArgs(args)}; - auto argsTy{getTypesForArgs(argBases)}; - assert(argsTy[0].isa() && argsTy[1].isa()); - - auto vecTyInfo{getVecTypeFromFir(argBases[0])}; - - const auto isInteger{vecTyInfo.eleTy.isa()}; - const auto isFloat{vecTyInfo.eleTy.isa()}; - assert((isInteger || isFloat) && "unknown vector type"); - - auto vargs{convertVecArgs(builder, loc, vecTyInfo, argBases)}; - - mlir::Value r{nullptr}; - switch (vop) { - case VecOp::Add: - if (isInteger) - r = builder.create(loc, vargs[0], vargs[1]); - else if (isFloat) - r = builder.create(loc, vargs[0], vargs[1]); - break; - case VecOp::Mul: - if (isInteger) - r = builder.create(loc, vargs[0], vargs[1]); - else if (isFloat) - r = builder.create(loc, vargs[0], vargs[1]); - break; - case VecOp::Sub: - if (isInteger) - r = builder.create(loc, vargs[0], vargs[1]); - else if (isFloat) - r = builder.create(loc, vargs[0], vargs[1]); - break; - case VecOp::And: - case VecOp::Xor: { - mlir::Value arg1{nullptr}; - mlir::Value arg2{nullptr}; - if (isInteger) { - arg1 = vargs[0]; - arg2 = vargs[1]; - } else if (isFloat) { - // bitcast the arguments to integer - auto wd{vecTyInfo.eleTy.dyn_cast().getWidth()}; - auto ftype{builder.getIntegerType(wd)}; - auto bcVecTy{mlir::VectorType::get(vecTyInfo.len, ftype)}; - arg1 = builder.create(loc, bcVecTy, vargs[0]); - arg2 = builder.create(loc, bcVecTy, vargs[1]); - } - if (vop == VecOp::And) - r = builder.create(loc, arg1, arg2); - else if (vop == VecOp::Xor) - r = builder.create(loc, arg1, arg2); - - if (isFloat) - r = builder.create(loc, vargs[0].getType(), r); - - break; - } - } - - return builder.createConvert(loc, argsTy[0], r); -} - static bool hasDefaultLowerBound(const fir::ExtendedValue &exv) { return exv.match( [](const fir::ArrayBoxValue &arr) { return arr.getLBounds().empty(); }, @@ -5376,33 +5126,6 @@ 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) { - libFuncType = genFuncType, Ty::Integer<4>>( - builder.getContext(), builder); - funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsfi", libFuncType); - } else { - libFuncType = genFuncType, Ty::Real<8>>( - builder.getContext(), builder); - 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/lib/Optimizer/Builder/PPCIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp @@ -0,0 +1,235 @@ +//===-- PPCIntrinsicCall.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helper routines for constructing the FIR dialect of MLIR for PowerPC +// intrinsics. Extensive use of MLIR interfaces and MLIR's coding style +// (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this +// module. +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Builder/PPCIntrinsicCall.h" +#include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/IntrinsicCall.h" +#include "flang/Optimizer/Builder/MutableBox.h" +#include "mlir/Dialect/Vector/IR/VectorOps.h" + +namespace fir { + +using PI = PPCIntrinsicLibrary; + +// PPC specific intrinsic handlers. +static constexpr IntrinsicHandler ppcHandlers[]{ + {"__ppc_mtfsf", + static_cast(&PI::genMtfsf), + {{{"mask", asValue}, {"r", asValue}}}, + /*isElemental=*/false}, + {"__ppc_mtfsfi", + static_cast(&PI::genMtfsf), + {{{"bf", asValue}, {"i", asValue}}}, + /*isElemental=*/false}, + {"__ppc_vec_add", + static_cast( + &PI::genVecAddAndMulSubXor), + {{{"arg1", asValue}, {"arg2", asValue}}}, + /*isElemental=*/true}, + {"__ppc_vec_and", + static_cast( + &PI::genVecAddAndMulSubXor), + {{{"arg1", asValue}, {"arg2", asValue}}}, + /*isElemental=*/true}, + {"__ppc_vec_mul", + static_cast( + &PI::genVecAddAndMulSubXor), + {{{"arg1", asValue}, {"arg2", asValue}}}, + /*isElemental=*/true}, + {"__ppc_vec_sub", + static_cast( + &PI::genVecAddAndMulSubXor), + {{{"arg1", asValue}, {"arg2", asValue}}}, + /*isElemental=*/true}, + {"__ppc_vec_xor", + static_cast( + &PI::genVecAddAndMulSubXor), + {{{"arg1", asValue}, {"arg2", asValue}}}, + /*isElemental=*/true}, +}; + +static constexpr MathOperation ppcMathOperations[] = { + // fcfi is just another name for fcfid, there is no llvm.ppc.fcfi. + {"__ppc_fcfi", "llvm.ppc.fcfid", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fcfid", "llvm.ppc.fcfid", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fcfud", "llvm.ppc.fcfud", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctid", "llvm.ppc.fctid", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctidz", "llvm.ppc.fctidz", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctiw", "llvm.ppc.fctiw", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctiwz", "llvm.ppc.fctiwz", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctudz", "llvm.ppc.fctudz", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fctuwz", "llvm.ppc.fctuwz", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fmadd", "llvm.fma.f32", + genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, + genMathOp}, + {"__ppc_fmadd", "llvm.fma.f64", + genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, + genMathOp}, + {"__ppc_fmsub", "llvm.ppc.fmsubs", + genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, + genLibCall}, + {"__ppc_fmsub", "llvm.ppc.fmsub", + genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, + genLibCall}, + {"__ppc_fnabs", "llvm.ppc.fnabss", genFuncType, Ty::Real<4>>, + genLibCall}, + {"__ppc_fnabs", "llvm.ppc.fnabs", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fnmadd", "llvm.ppc.fnmadds", + genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, + genLibCall}, + {"__ppc_fnmadd", "llvm.ppc.fnmadd", + genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, + genLibCall}, + {"__ppc_fnmsub", "llvm.ppc.fnmsub.f32", + genFuncType, Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, + genLibCall}, + {"__ppc_fnmsub", "llvm.ppc.fnmsub.f64", + genFuncType, Ty::Real<8>, Ty::Real<8>, Ty::Real<8>>, + genLibCall}, + {"__ppc_fre", "llvm.ppc.fre", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_fres", "llvm.ppc.fres", genFuncType, Ty::Real<4>>, + genLibCall}, + {"__ppc_frsqrte", "llvm.ppc.frsqrte", genFuncType, Ty::Real<8>>, + genLibCall}, + {"__ppc_frsqrtes", "llvm.ppc.frsqrtes", + genFuncType, Ty::Real<4>>, genLibCall}, +}; + +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; +} + +using RtMap = Fortran::common::StaticMultimapView; +static constexpr RtMap ppcMathOps(ppcMathOperations); +static_assert(ppcMathOps.Verify() && "map must be sorted"); + +std::pair +checkPPCMathOperationsRange(llvm::StringRef name) { + return ppcMathOps.equal_range(name); +} + +//===----------------------------------------------------------------------===// +// PowerPC specific intrinsic handlers. +//===----------------------------------------------------------------------===// + +// MTFSF, MTFSFI +template +void PPCIntrinsicLibrary::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) { + libFuncType = genFuncType, Ty::Integer<4>>( + builder.getContext(), builder); + funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsfi", libFuncType); + } else { + libFuncType = genFuncType, Ty::Real<8>>( + builder.getContext(), builder); + funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsf", libFuncType); + } + builder.create(loc, funcOp, scalarArgs); +} + +// VEC_ADD, VEC_AND, VEC_SUB, VEC_MUL, VEC_XOR +template +fir::ExtendedValue PPCIntrinsicLibrary::genVecAddAndMulSubXor( + mlir::Type resultType, llvm::ArrayRef args) { + assert(args.size() == 2); + auto argBases{getBasesForArgs(args)}; + auto argsTy{getTypesForArgs(argBases)}; + assert(argsTy[0].isa() && argsTy[1].isa()); + + auto vecTyInfo{getVecTypeFromFir(argBases[0])}; + + const auto isInteger{vecTyInfo.eleTy.isa()}; + const auto isFloat{vecTyInfo.eleTy.isa()}; + assert((isInteger || isFloat) && "unknown vector type"); + + auto vargs{convertVecArgs(builder, loc, vecTyInfo, argBases)}; + + mlir::Value r{nullptr}; + switch (vop) { + case VecOp::Add: + if (isInteger) + r = builder.create(loc, vargs[0], vargs[1]); + else if (isFloat) + r = builder.create(loc, vargs[0], vargs[1]); + break; + case VecOp::Mul: + if (isInteger) + r = builder.create(loc, vargs[0], vargs[1]); + else if (isFloat) + r = builder.create(loc, vargs[0], vargs[1]); + break; + case VecOp::Sub: + if (isInteger) + r = builder.create(loc, vargs[0], vargs[1]); + else if (isFloat) + r = builder.create(loc, vargs[0], vargs[1]); + break; + case VecOp::And: + case VecOp::Xor: { + mlir::Value arg1{nullptr}; + mlir::Value arg2{nullptr}; + if (isInteger) { + arg1 = vargs[0]; + arg2 = vargs[1]; + } else if (isFloat) { + // bitcast the arguments to integer + auto wd{vecTyInfo.eleTy.dyn_cast().getWidth()}; + auto ftype{builder.getIntegerType(wd)}; + auto bcVecTy{mlir::VectorType::get(vecTyInfo.len, ftype)}; + arg1 = builder.create(loc, bcVecTy, vargs[0]); + arg2 = builder.create(loc, bcVecTy, vargs[1]); + } + if (vop == VecOp::And) + r = builder.create(loc, arg1, arg2); + else if (vop == VecOp::Xor) + r = builder.create(loc, arg1, arg2); + + if (isFloat) + r = builder.create(loc, vargs[0].getType(), r); + + break; + } + } + + return builder.createConvert(loc, argsTy[0], r); +} + +} // namespace fir