/// is ignored because this is already reflected in the result type.
mlir::Value genConversion(mlir::Type, llvm::ArrayRef<mlir::Value>);
+ // PPC intrinsic handlers.
+ template <bool isImm>
+ void genMtfsf(llvm::ArrayRef<fir::ExtendedValue>);
+
/// 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.
/*isElemental=*/true},
};
+// PPC specific intrinsic handlers.
+static constexpr IntrinsicHandler ppcHandlers[]{
+ {"__ppc_mtfsf",
+ &I::genMtfsf<false>,
+ {{{"mask", asValue}, {"r", asValue}}},
+ /*isElemental=*/false},
+ {"__ppc_mtfsfi",
+ &I::genMtfsf<true>,
+ {{{"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;
: 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<bool> outlineAllIntrinsics(
}
template <int Bits>
+static mlir::FunctionType genVoidIntF64FuncType(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 <int BitsA, int BitsB>
+static mlir::FunctionType genVoidIntIntFuncType(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 <int Bits>
static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) {
auto t = mlir::FloatType::getF64(context);
auto r = mlir::IntegerType::get(context, Bits);
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<mlir::Value> mlirArgs;
for (const fir::ExtendedValue &extendedVal : args) {
/// arguments. The mangling pattern is:
/// fir.<generic name>.<result type>.<arg type>...
/// 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.<generic name>.void.<arg type>...
+/// e.g. FREE(INTEGER(4)) is mangled as fir.free.void.i4
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)));
}
//===----------------------------------------------------------------------===//
+// PowerPC specific intrinsic handlers.
+//===----------------------------------------------------------------------===//
+template <bool isImm>
+void IntrinsicLibrary::genMtfsf(llvm::ArrayRef<fir::ExtendedValue> args) {
+ assert(args.size() == 2);
+ llvm::SmallVector<mlir::Value> 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 = genVoidIntIntFuncType<32, 32>(builder.getContext());
+ funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsfi", libFuncType);
+ } else {
+ libFuncType = genVoidIntF64FuncType<32>(builder.getContext());
+ funcOp = builder.addNamedFunction(loc, "llvm.ppc.mtfsf", libFuncType);
+ }
+ builder.create<fir::CallOp>(loc, funcOp, scalarArgs);
+}
+
+//===----------------------------------------------------------------------===//
// Argument lowering rules interface for intrinsic or intrinsic module
// procedure.
//===----------------------------------------------------------------------===//
.AnyFatalError();
}
+bool CheckArgumentIsConstantExprInRange(
+ const evaluate::ActualArguments &actuals, int index, int lowerBound,
+ int upperBound, parser::ContextualMessages &messages) {
+ CHECK(index >= 0 && index < actuals.size());
+
+ const std::optional<evaluate::ActualArgument> &argOptional{actuals[index]};
+ if (!argOptional) {
+ DIE("Actual argument should have value");
+ return false;
+ }
+
+ const evaluate::ActualArgument &arg{argOptional.value()};
+ const evaluate::Expr<evaluate::SomeType> *argExpr{arg.UnwrapExpr()};
+ CHECK(argExpr != nullptr);
+
+ if (!IsConstantExpr(*argExpr)) {
+ messages.Say("Actual argument #%d must be a constant expression"_err_en_US,
+ index + 1);
+ return false;
+ }
+
+ // This does not imply that the kind of the argument is 8. The kind
+ // for the intrinsic's argument should have been check prior. This is just
+ // a conversion so that we can read the constant value.
+ auto scalarValue{evaluate::ToInt64(argExpr)};
+ CHECK(scalarValue.has_value());
+
+ if (*scalarValue < lowerBound || *scalarValue > upperBound) {
+ messages.Say(
+ "Argument #%d must be a constant expression in range %d-%d"_err_en_US,
+ index + 1, lowerBound, upperBound);
+ return false;
+ }
+ return true;
+}
+
+bool CheckPPCIntrinsic(const Symbol &generic, const Symbol &specific,
+ const evaluate::ActualArguments &actuals,
+ evaluate::FoldingContext &context) {
+ parser::ContextualMessages &messages{context.messages()};
+
+ if (specific.name() == "__ppc_mtfsf") {
+ return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages);
+ }
+ if (specific.name() == "__ppc_mtfsfi") {
+ return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages) &&
+ CheckArgumentIsConstantExprInRange(actuals, 1, 0, 15, messages);
+ }
+ return false;
+}
+
bool CheckArguments(const characteristics::Procedure &proc,
evaluate::ActualArguments &actuals, evaluate::FoldingContext &context,
const Scope &scope, bool treatingExternalAsImplicit,