[flang-commits] [flang] 65431d3 - [fir] TargetRewrite: Rewrite COMPLEX values
Diana Picus via flang-commits
flang-commits at lists.llvm.org
Tue Nov 9 01:27:34 PST 2021
Author: Diana Picus
Date: 2021-11-09T09:26:10Z
New Revision: 65431d3aeb9067c5df92827652fbb480859e8078
URL: https://github.com/llvm/llvm-project/commit/65431d3aeb9067c5df92827652fbb480859e8078
DIFF: https://github.com/llvm/llvm-project/commit/65431d3aeb9067c5df92827652fbb480859e8078.diff
LOG: [fir] TargetRewrite: Rewrite COMPLEX values
Rewrite function signatures and calls to functions that accept or return
COMPLEX values.
Also teach insert_value and extract_value about the MLIR ComplexType, by
adding AnyComplex to AnyCompositeLike.
This patch is part of the effort for upstreaming the fir-dev branch.
Differential Revision: https://reviews.llvm.org/D113273
Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Kiran Chandramohan <kiran.chandramohan at arm.com>
Co-authored-by: Tim Keith <tkeith at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Added:
flang/test/Fir/target-rewrite-boxchar.fir
flang/test/Fir/target-rewrite-complex.fir
Modified:
flang/include/flang/Optimizer/CodeGen/CGPasses.td
flang/include/flang/Optimizer/CodeGen/CodeGen.h
flang/include/flang/Optimizer/Dialect/FIRTypes.td
flang/lib/Optimizer/CodeGen/Target.cpp
flang/lib/Optimizer/CodeGen/Target.h
flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
Removed:
flang/test/Fir/target.fir
################################################################################
diff --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
index b2ac98ccc6267..51b12180156b3 100644
--- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td
+++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
@@ -54,7 +54,10 @@ def TargetRewrite : Pass<"target-rewrite", "mlir::ModuleOp"> {
"Override module's target triple.">,
Option<"noCharacterConversion", "no-character-conversion",
"bool", /*default=*/"false",
- "Disable target-specific conversion of CHARACTER.">
+ "Disable target-specific conversion of CHARACTER.">,
+ Option<"noComplexConversion", "no-complex-conversion",
+ "bool", /*default=*/"false",
+ "Disable target-specific conversion of COMPLEX.">
];
}
diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index e693bf062edb5..3f98e061a7a27 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -25,6 +25,7 @@ std::unique_ptr<mlir::Pass> createFirCodeGenRewritePass();
// FirTargetRewritePass options.
struct TargetRewriteOptions {
bool noCharacterConversion{};
+ bool noComplexConversion{};
};
/// Prerequiste pass for code gen. Perform intermediate rewrites to tailor the
diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
index 28533f372b9c0..28ad6df503d5d 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
@@ -506,8 +506,11 @@ def AnyRealLike : TypeConstraint<Or<[FloatLike.predicate,
def AnyIntegerType : Type<AnyIntegerLike.predicate, "any integer">;
// Composable types
+// Note that we include both fir_ComplexType and AnyComplex, so we can use both
+// the FIR ComplexType and the MLIR ComplexType (the former is used to represent
+// Fortran complex and the latter for C++ std::complex).
def AnyCompositeLike : TypeConstraint<Or<[fir_RecordType.predicate,
- fir_SequenceType.predicate, fir_ComplexType.predicate,
+ fir_SequenceType.predicate, fir_ComplexType.predicate, AnyComplex.predicate,
fir_VectorType.predicate, IsTupleTypePred, fir_CharacterType.predicate]>,
"any composite">;
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index a03f2829bb286..78e52b9110382 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -20,6 +20,15 @@
using namespace fir;
+// Reduce a REAL/float type to the floating point semantics.
+static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
+ mlir::Type type) {
+ assert(isa_real(type));
+ if (auto ty = type.dyn_cast<fir::RealType>())
+ return kindMap.getFloatSemantics(ty.getFKind());
+ return type.cast<mlir::FloatType>().getFloatSemantics();
+}
+
namespace {
template <typename S>
struct GenericTarget : public CodeGenSpecifics {
@@ -35,7 +44,8 @@ struct GenericTarget : public CodeGenSpecifics {
// split format with all pointers first (in the declared position) and all
// LEN arguments appended after all of the dummy arguments.
// NB: Other conventions/ABIs can/should be supported via options.
- marshal.emplace_back(idxTy, AT{/*append=*/!sret});
+ marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
+ /*sret=*/sret, /*append=*/!sret});
return marshal;
}
};
@@ -50,6 +60,39 @@ struct TargetI386 : public GenericTarget<TargetI386> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 32;
+
+ CodeGenSpecifics::Marshalling
+ complexArgumentType(mlir::Type eleTy) const override {
+ assert(fir::isa_real(eleTy));
+ CodeGenSpecifics::Marshalling marshal;
+ // { t, t } struct of 2 eleTy, byval, align 4
+ mlir::TypeRange range = {eleTy, eleTy};
+ auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
+ marshal.emplace_back(fir::ReferenceType::get(structTy),
+ AT{/*alignment=*/4, /*byval=*/true});
+ return marshal;
+ }
+
+ CodeGenSpecifics::Marshalling
+ complexReturnType(mlir::Type eleTy) const override {
+ assert(fir::isa_real(eleTy));
+ CodeGenSpecifics::Marshalling marshal;
+ const auto *sem = &floatToSemantics(kindMap, eleTy);
+ if (sem == &llvm::APFloat::IEEEsingle()) {
+ // i64 pack both floats in a 64-bit GPR
+ marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
+ AT{});
+ } else if (sem == &llvm::APFloat::IEEEdouble()) {
+ // { t, t } struct of 2 eleTy, sret, align 4
+ mlir::TypeRange range = {eleTy, eleTy};
+ auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
+ marshal.emplace_back(fir::ReferenceType::get(structTy),
+ AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
+ } else {
+ llvm::report_fatal_error("complex for this precision not implemented");
+ }
+ return marshal;
+ }
};
} // namespace
@@ -62,6 +105,41 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 64;
+
+ CodeGenSpecifics::Marshalling
+ complexArgumentType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ const auto *sem = &floatToSemantics(kindMap, eleTy);
+ if (sem == &llvm::APFloat::IEEEsingle()) {
+ // <2 x t> vector of 2 eleTy
+ marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
+ } else if (sem == &llvm::APFloat::IEEEdouble()) {
+ // two distinct double arguments
+ marshal.emplace_back(eleTy, AT{});
+ marshal.emplace_back(eleTy, AT{});
+ } else {
+ llvm::report_fatal_error("complex for this precision not implemented");
+ }
+ return marshal;
+ }
+
+ CodeGenSpecifics::Marshalling
+ complexReturnType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ const auto *sem = &floatToSemantics(kindMap, eleTy);
+ if (sem == &llvm::APFloat::IEEEsingle()) {
+ // <2 x t> vector of 2 eleTy
+ marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
+ } else if (sem == &llvm::APFloat::IEEEdouble()) {
+ // ( t, t ) tuple of 2 eleTy
+ mlir::TypeRange range = {eleTy, eleTy};
+ marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
+ AT{});
+ } else {
+ llvm::report_fatal_error("complex for this precision not implemented");
+ }
+ return marshal;
+ }
};
} // namespace
@@ -74,6 +152,36 @@ struct TargetAArch64 : public GenericTarget<TargetAArch64> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 64;
+
+ CodeGenSpecifics::Marshalling
+ complexArgumentType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ const auto *sem = &floatToSemantics(kindMap, eleTy);
+ if (sem == &llvm::APFloat::IEEEsingle() ||
+ sem == &llvm::APFloat::IEEEdouble()) {
+ // [2 x t] array of 2 eleTy
+ marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
+ } else {
+ llvm::report_fatal_error("complex for this precision not implemented");
+ }
+ return marshal;
+ }
+
+ CodeGenSpecifics::Marshalling
+ complexReturnType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ const auto *sem = &floatToSemantics(kindMap, eleTy);
+ if (sem == &llvm::APFloat::IEEEsingle() ||
+ sem == &llvm::APFloat::IEEEdouble()) {
+ // ( t, t ) tuple of 2 eleTy
+ mlir::TypeRange range = {eleTy, eleTy};
+ marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
+ AT{});
+ } else {
+ llvm::report_fatal_error("complex for this precision not implemented");
+ }
+ return marshal;
+ }
};
} // namespace
@@ -86,6 +194,24 @@ struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 64;
+
+ CodeGenSpecifics::Marshalling
+ complexArgumentType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ // two distinct element type arguments (re, im)
+ marshal.emplace_back(eleTy, AT{});
+ marshal.emplace_back(eleTy, AT{});
+ return marshal;
+ }
+
+ CodeGenSpecifics::Marshalling
+ complexReturnType(mlir::Type eleTy) const override {
+ CodeGenSpecifics::Marshalling marshal;
+ // ( t, t ) tuple of 2 element type
+ mlir::TypeRange range = {eleTy, eleTy};
+ marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
+ return marshal;
+ }
};
} // namespace
diff --git a/flang/lib/Optimizer/CodeGen/Target.h b/flang/lib/Optimizer/CodeGen/Target.h
index b8dea1f837a92..eb9c93dff97f0 100644
--- a/flang/lib/Optimizer/CodeGen/Target.h
+++ b/flang/lib/Optimizer/CodeGen/Target.h
@@ -29,11 +29,20 @@ namespace details {
/// LLVMContext.
class Attributes {
public:
- Attributes(bool append = false) : append{append} {}
+ Attributes(unsigned short alignment = 0, bool byval = false,
+ bool sret = false, bool append = false)
+ : alignment{alignment}, byval{byval}, sret{sret}, append{append} {}
+ unsigned getAlignment() const { return alignment; }
+ bool hasAlignment() const { return alignment != 0; }
+ bool isByVal() const { return byval; }
+ bool isSRet() const { return sret; }
bool isAppend() const { return append; }
private:
+ unsigned short alignment{};
+ bool byval : 1;
+ bool sret : 1;
bool append : 1;
};
@@ -56,6 +65,15 @@ class CodeGenSpecifics {
CodeGenSpecifics() = delete;
virtual ~CodeGenSpecifics() {}
+ /// Type representation of a `complex<eleTy>` type argument when passed by
+ /// value. An argument value may need to be passed as a (safe) reference
+ /// argument.
+ virtual Marshalling complexArgumentType(mlir::Type eleTy) const = 0;
+
+ /// Type representation of a `complex<eleTy>` type return value. Such a return
+ /// value may need to be converted to a hidden reference argument.
+ virtual Marshalling complexReturnType(mlir::Type eleTy) const = 0;
+
/// Type representation of a `boxchar<n>` type argument when passed by value.
/// An argument value may need to be passed as a (safe) reference argument.
///
diff --git a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
index b0f3e4bae67a8..25e1e44671d10 100644
--- a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
+++ b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
@@ -35,7 +35,15 @@ namespace {
/// Fixups for updating a FuncOp's arguments and return values.
struct FixupTy {
- enum class Codes { CharPair, Trailing };
+ enum class Codes {
+ ArgumentAsLoad,
+ ArgumentType,
+ CharPair,
+ ReturnAsStore,
+ ReturnType,
+ Split,
+ Trailing
+ };
FixupTy(Codes code, std::size_t index, std::size_t second = 0)
: code{code}, index{index}, second{second} {}
@@ -60,6 +68,7 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
public:
TargetRewrite(const TargetRewriteOptions &options) {
noCharacterConversion = options.noCharacterConversion;
+ noComplexConversion = options.noComplexConversion;
}
void runOnOperation() override final {
@@ -99,6 +108,76 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
mlir::ModuleOp getModule() { return getOperation(); }
+ template <typename A, typename B, typename C>
+ std::function<mlir::Value(mlir::Operation *)>
+ rewriteCallComplexResultType(A ty, B &newResTys, B &newInTys, C &newOpers) {
+ auto m = specifics->complexReturnType(ty.getElementType());
+ // Currently targets mandate COMPLEX is a single aggregate or packed
+ // scalar, including the sret case.
+ assert(m.size() == 1 && "target lowering of complex return not supported");
+ auto resTy = std::get<mlir::Type>(m[0]);
+ auto attr = std::get<CodeGenSpecifics::Attributes>(m[0]);
+ auto loc = mlir::UnknownLoc::get(resTy.getContext());
+ if (attr.isSRet()) {
+ assert(isa_ref_type(resTy));
+ mlir::Value stack =
+ rewriter->create<fir::AllocaOp>(loc, dyn_cast_ptrEleTy(resTy));
+ newInTys.push_back(resTy);
+ newOpers.push_back(stack);
+ return [=](mlir::Operation *) -> mlir::Value {
+ auto memTy = ReferenceType::get(ty);
+ auto cast = rewriter->create<ConvertOp>(loc, memTy, stack);
+ return rewriter->create<fir::LoadOp>(loc, cast);
+ };
+ }
+ newResTys.push_back(resTy);
+ return [=](mlir::Operation *call) -> mlir::Value {
+ auto mem = rewriter->create<fir::AllocaOp>(loc, resTy);
+ rewriter->create<fir::StoreOp>(loc, call->getResult(0), mem);
+ auto memTy = ReferenceType::get(ty);
+ auto cast = rewriter->create<ConvertOp>(loc, memTy, mem);
+ return rewriter->create<fir::LoadOp>(loc, cast);
+ };
+ }
+
+ template <typename A, typename B, typename C>
+ void rewriteCallComplexInputType(A ty, mlir::Value oper, B &newInTys,
+ C &newOpers) {
+ auto m = specifics->complexArgumentType(ty.getElementType());
+ auto *ctx = ty.getContext();
+ auto loc = mlir::UnknownLoc::get(ctx);
+ if (m.size() == 1) {
+ // COMPLEX is a single aggregate
+ auto resTy = std::get<mlir::Type>(m[0]);
+ auto attr = std::get<CodeGenSpecifics::Attributes>(m[0]);
+ auto oldRefTy = ReferenceType::get(ty);
+ if (attr.isByVal()) {
+ auto mem = rewriter->create<fir::AllocaOp>(loc, ty);
+ rewriter->create<fir::StoreOp>(loc, oper, mem);
+ newOpers.push_back(rewriter->create<ConvertOp>(loc, resTy, mem));
+ } else {
+ auto mem = rewriter->create<fir::AllocaOp>(loc, resTy);
+ auto cast = rewriter->create<ConvertOp>(loc, oldRefTy, mem);
+ rewriter->create<fir::StoreOp>(loc, oper, cast);
+ newOpers.push_back(rewriter->create<fir::LoadOp>(loc, mem));
+ }
+ newInTys.push_back(resTy);
+ } else {
+ assert(m.size() == 2);
+ // COMPLEX is split into 2 separate arguments
+ for (auto e : llvm::enumerate(m)) {
+ auto &tup = e.value();
+ auto ty = std::get<mlir::Type>(tup);
+ auto index = e.index();
+ auto idx = rewriter->getIntegerAttr(rewriter->getIndexType(), index);
+ auto val = rewriter->create<ExtractValueOp>(
+ loc, ty, oper, rewriter->getArrayAttr(idx));
+ newInTys.push_back(ty);
+ newOpers.push_back(val);
+ }
+ }
+ }
+
// Convert fir.call and fir.dispatch Ops.
template <typename A>
void convertCallOp(A callOp) {
@@ -124,7 +203,16 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
llvm::Optional<std::function<mlir::Value(mlir::Operation *)>> wrap;
if (fnTy.getResults().size() == 1) {
mlir::Type ty = fnTy.getResult(0);
- newResTys.push_back(ty);
+ llvm::TypeSwitch<mlir::Type>(ty)
+ .template Case<fir::ComplexType>([&](fir::ComplexType cmplx) {
+ wrap = rewriteCallComplexResultType(cmplx, newResTys, newInTys,
+ newOpers);
+ })
+ .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
+ wrap = rewriteCallComplexResultType(cmplx, newResTys, newInTys,
+ newOpers);
+ })
+ .Default([&](mlir::Type ty) { newResTys.push_back(ty); });
} else if (fnTy.getResults().size() > 1) {
TODO(loc, "multiple results not supported yet");
}
@@ -169,6 +257,12 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
}
}
})
+ .template Case<fir::ComplexType>([&](fir::ComplexType cmplx) {
+ rewriteCallComplexInputType(cmplx, oper, newInTys, newOpers);
+ })
+ .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
+ rewriteCallComplexInputType(cmplx, oper, newInTys, newOpers);
+ })
.Default([&](mlir::Type ty) {
newInTys.push_back(ty);
newOpers.push_back(oper);
@@ -198,6 +292,33 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
TODO(loc, "dispatch not implemented");
}
}
+
+ // Result type fixup for fir::ComplexType and mlir::ComplexType
+ template <typename A, typename B>
+ void lowerComplexSignatureRes(A cmplx, B &newResTys, B &newInTys) {
+ if (noComplexConversion) {
+ newResTys.push_back(cmplx);
+ } else {
+ for (auto &tup : specifics->complexReturnType(cmplx.getElementType())) {
+ auto argTy = std::get<mlir::Type>(tup);
+ if (std::get<CodeGenSpecifics::Attributes>(tup).isSRet())
+ newInTys.push_back(argTy);
+ else
+ newResTys.push_back(argTy);
+ }
+ }
+ }
+
+ // Argument type fixup for fir::ComplexType and mlir::ComplexType
+ template <typename A, typename B>
+ void lowerComplexSignatureArg(A cmplx, B &newInTys) {
+ if (noComplexConversion)
+ newInTys.push_back(cmplx);
+ else
+ for (auto &tup : specifics->complexArgumentType(cmplx.getElementType()))
+ newInTys.push_back(std::get<mlir::Type>(tup));
+ }
+
/// Convert the type signatures on all the functions present in the module.
/// As the type signature is being changed, this must also update the
/// function itself to use any new arguments, etc.
@@ -215,12 +336,14 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
assert(signature.isa<mlir::FunctionType>());
auto func = signature.dyn_cast<mlir::FunctionType>();
for (auto ty : func.getResults())
- if ((ty.isa<BoxCharType>() && !noCharacterConversion)) {
+ if ((ty.isa<BoxCharType>() && !noCharacterConversion) ||
+ (isa_complex(ty) && !noComplexConversion)) {
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
return false;
}
for (auto ty : func.getInputs())
- if ((ty.isa<BoxCharType>() && !noCharacterConversion)) {
+ if ((ty.isa<BoxCharType>() && !noCharacterConversion) ||
+ (isa_complex(ty) && !noComplexConversion)) {
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
return false;
}
@@ -239,7 +362,20 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
// Convert return value(s)
for (auto ty : funcTy.getResults())
- newResTys.push_back(ty);
+ llvm::TypeSwitch<mlir::Type>(ty)
+ .Case<fir::ComplexType>([&](fir::ComplexType cmplx) {
+ if (noComplexConversion)
+ newResTys.push_back(cmplx);
+ else
+ doComplexReturn(func, cmplx, newResTys, newInTys, fixups);
+ })
+ .Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
+ if (noComplexConversion)
+ newResTys.push_back(cmplx);
+ else
+ doComplexReturn(func, cmplx, newResTys, newInTys, fixups);
+ })
+ .Default([&](mlir::Type ty) { newResTys.push_back(ty); });
// Convert arguments
llvm::SmallVector<mlir::Type> trailingTys;
@@ -276,6 +412,18 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
}
}
})
+ .Case<fir::ComplexType>([&](fir::ComplexType cmplx) {
+ if (noComplexConversion)
+ newInTys.push_back(cmplx);
+ else
+ doComplexArg(func, cmplx, newInTys, fixups);
+ })
+ .Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
+ if (noComplexConversion)
+ newInTys.push_back(cmplx);
+ else
+ doComplexArg(func, cmplx, newInTys, fixups);
+ })
.Default([&](mlir::Type ty) { newInTys.push_back(ty); });
}
@@ -289,6 +437,37 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
for (std::remove_const_t<decltype(fixupSize)> i = 0; i < fixupSize; ++i) {
const auto &fixup = fixups[i];
switch (fixup.code) {
+ case FixupTy::Codes::ArgumentAsLoad: {
+ // Argument was pass-by-value, but is now pass-by-reference and
+ // possibly with a
diff erent element type.
+ auto newArg =
+ func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+ rewriter->setInsertionPointToStart(&func.front());
+ auto oldArgTy = ReferenceType::get(oldArgTys[fixup.index - offset]);
+ auto cast = rewriter->create<ConvertOp>(loc, oldArgTy, newArg);
+ auto load = rewriter->create<fir::LoadOp>(loc, cast);
+ func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
+ func.front().eraseArgument(fixup.index + 1);
+ } break;
+ case FixupTy::Codes::ArgumentType: {
+ // Argument is pass-by-value, but its type has likely been modified to
+ // suit the target ABI convention.
+ auto newArg =
+ func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+ rewriter->setInsertionPointToStart(&func.front());
+ auto mem =
+ rewriter->create<fir::AllocaOp>(loc, newInTys[fixup.index]);
+ rewriter->create<fir::StoreOp>(loc, newArg, mem);
+ auto oldArgTy = ReferenceType::get(oldArgTys[fixup.index - offset]);
+ auto cast = rewriter->create<ConvertOp>(loc, oldArgTy, mem);
+ mlir::Value load = rewriter->create<fir::LoadOp>(loc, cast);
+ func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
+ func.front().eraseArgument(fixup.index + 1);
+ LLVM_DEBUG(llvm::dbgs()
+ << "old argument: " << oldArgTy.getEleTy()
+ << ", repl: " << load << ", new argument: "
+ << func.getArgument(fixup.index).getType() << '\n');
+ } break;
case FixupTy::Codes::CharPair: {
// The FIR boxchar argument has been split into a pair of distinct
// arguments that are in juxtaposition to each other.
@@ -304,6 +483,59 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
offset++;
}
} break;
+ case FixupTy::Codes::ReturnAsStore: {
+ // The value being returned is now being returned in memory (callee
+ // stack space) through a hidden reference argument.
+ auto newArg =
+ func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+ offset++;
+ func.walk([&](mlir::ReturnOp ret) {
+ rewriter->setInsertionPoint(ret);
+ auto oldOper = ret.getOperand(0);
+ auto oldOperTy = ReferenceType::get(oldOper.getType());
+ auto cast = rewriter->create<ConvertOp>(loc, oldOperTy, newArg);
+ rewriter->create<fir::StoreOp>(loc, oldOper, cast);
+ rewriter->create<mlir::ReturnOp>(loc);
+ ret.erase();
+ });
+ } break;
+ case FixupTy::Codes::ReturnType: {
+ // The function is still returning a value, but its type has likely
+ // changed to suit the target ABI convention.
+ func.walk([&](mlir::ReturnOp ret) {
+ rewriter->setInsertionPoint(ret);
+ auto oldOper = ret.getOperand(0);
+ auto oldOperTy = ReferenceType::get(oldOper.getType());
+ auto mem =
+ rewriter->create<fir::AllocaOp>(loc, newResTys[fixup.index]);
+ auto cast = rewriter->create<ConvertOp>(loc, oldOperTy, mem);
+ rewriter->create<fir::StoreOp>(loc, oldOper, cast);
+ mlir::Value load = rewriter->create<fir::LoadOp>(loc, mem);
+ rewriter->create<mlir::ReturnOp>(loc, load);
+ ret.erase();
+ });
+ } break;
+ case FixupTy::Codes::Split: {
+ // The FIR argument has been split into a pair of distinct arguments
+ // that are in juxtaposition to each other. (For COMPLEX value.)
+ auto newArg =
+ func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+ if (fixup.second == 1) {
+ rewriter->setInsertionPointToStart(&func.front());
+ auto cplxTy = oldArgTys[fixup.index - offset - fixup.second];
+ auto undef = rewriter->create<UndefOp>(loc, cplxTy);
+ auto zero = rewriter->getIntegerAttr(rewriter->getIndexType(), 0);
+ auto one = rewriter->getIntegerAttr(rewriter->getIndexType(), 1);
+ auto cplx1 = rewriter->create<InsertValueOp>(
+ loc, cplxTy, undef, func.front().getArgument(fixup.index - 1),
+ rewriter->getArrayAttr(zero));
+ auto cplx = rewriter->create<InsertValueOp>(
+ loc, cplxTy, cplx1, newArg, rewriter->getArrayAttr(one));
+ func.getArgument(fixup.index + 1).replaceAllUsesWith(cplx);
+ func.front().eraseArgument(fixup.index + 1);
+ offset++;
+ }
+ } break;
case FixupTy::Codes::Trailing: {
// The FIR argument has been split into a pair of distinct arguments.
// The first part of the pair appears in the original argument
@@ -341,6 +573,81 @@ class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
return false;
}
+ /// Convert a complex return value. This can involve converting the return
+ /// value to a "hidden" first argument or packing the complex into a wide
+ /// GPR.
+ template <typename A, typename B, typename C>
+ void doComplexReturn(mlir::FuncOp func, A cmplx, B &newResTys, B &newInTys,
+ C &fixups) {
+ if (noComplexConversion) {
+ newResTys.push_back(cmplx);
+ return;
+ }
+ auto m = specifics->complexReturnType(cmplx.getElementType());
+ assert(m.size() == 1);
+ auto &tup = m[0];
+ auto attr = std::get<CodeGenSpecifics::Attributes>(tup);
+ auto argTy = std::get<mlir::Type>(tup);
+ if (attr.isSRet()) {
+ unsigned argNo = newInTys.size();
+ fixups.emplace_back(
+ FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::FuncOp func) {
+ func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr());
+ });
+ newInTys.push_back(argTy);
+ return;
+ }
+ fixups.emplace_back(FixupTy::Codes::ReturnType, newResTys.size());
+ newResTys.push_back(argTy);
+ }
+
+ /// Convert a complex argument value. This can involve storing the value to
+ /// a temporary memory location or factoring the value into two distinct
+ /// arguments.
+ template <typename A, typename B, typename C>
+ void doComplexArg(mlir::FuncOp func, A cmplx, B &newInTys, C &fixups) {
+ if (noComplexConversion) {
+ newInTys.push_back(cmplx);
+ return;
+ }
+ auto m = specifics->complexArgumentType(cmplx.getElementType());
+ const auto fixupCode =
+ m.size() > 1 ? FixupTy::Codes::Split : FixupTy::Codes::ArgumentType;
+ for (auto e : llvm::enumerate(m)) {
+ auto &tup = e.value();
+ auto index = e.index();
+ auto attr = std::get<CodeGenSpecifics::Attributes>(tup);
+ auto argTy = std::get<mlir::Type>(tup);
+ auto argNo = newInTys.size();
+ if (attr.isByVal()) {
+ if (auto align = attr.getAlignment())
+ fixups.emplace_back(
+ FixupTy::Codes::ArgumentAsLoad, argNo, [=](mlir::FuncOp func) {
+ func.setArgAttr(argNo, "llvm.byval", rewriter->getUnitAttr());
+ func.setArgAttr(argNo, "llvm.align",
+ rewriter->getIntegerAttr(
+ rewriter->getIntegerType(32), align));
+ });
+ else
+ fixups.emplace_back(FixupTy::Codes::ArgumentAsLoad, newInTys.size(),
+ [=](mlir::FuncOp func) {
+ func.setArgAttr(argNo, "llvm.byval",
+ rewriter->getUnitAttr());
+ });
+ } else {
+ if (auto align = attr.getAlignment())
+ fixups.emplace_back(fixupCode, argNo, index, [=](mlir::FuncOp func) {
+ func.setArgAttr(
+ argNo, "llvm.align",
+ rewriter->getIntegerAttr(rewriter->getIntegerType(32), align));
+ });
+ else
+ fixups.emplace_back(fixupCode, argNo, index);
+ }
+ newInTys.push_back(argTy);
+ }
+ }
+
private:
// Replace `op` and remove it.
void replaceOp(mlir::Operation *op, mlir::ValueRange newValues) {
diff --git a/flang/test/Fir/target.fir b/flang/test/Fir/target-rewrite-boxchar.fir
similarity index 100%
rename from flang/test/Fir/target.fir
rename to flang/test/Fir/target-rewrite-boxchar.fir
diff --git a/flang/test/Fir/target-rewrite-complex.fir b/flang/test/Fir/target-rewrite-complex.fir
new file mode 100644
index 0000000000000..54fd2f2adf53d
--- /dev/null
+++ b/flang/test/Fir/target-rewrite-complex.fir
@@ -0,0 +1,454 @@
+// RUN: fir-opt --target-rewrite="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefix=I32
+// RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=X64
+// RUN: fir-opt --target-rewrite="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=AARCH64
+// RUN: fir-opt --target-rewrite="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefix=PPC
+
+// Test that we rewrite the signature and body of a function that returns a
+// complex<4>.
+// I32-LABEL: func @returncomplex4() -> i64
+// X64-LABEL: func @returncomplex4() -> !fir.vector<2:!fir.real<4>>
+// AARCH64-LABEL: func @returncomplex4() -> tuple<!fir.real<4>, !fir.real<4>>
+// PPC-LABEL: func @returncomplex4() -> tuple<!fir.real<4>, !fir.real<4>>
+func @returncomplex4() -> !fir.complex<4> {
+ // I32: fir.insert_value
+ // I32: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
+ // X64: fir.insert_value
+ // X64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
+ // AARCH64: fir.insert_value
+ // AARCH64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
+ // PPC: fir.insert_value
+ // PPC: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
+ %1 = fir.undefined !fir.complex<4>
+ %2 = arith.constant 2.0 : f32
+ %3 = fir.convert %2 : (f32) -> !fir.real<4>
+ %c0 = arith.constant 0 : i32
+ %4 = fir.insert_value %1, %3, [0 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ %c1 = arith.constant 1 : i32
+ %5 = arith.constant -42.0 : f32
+ %6 = fir.insert_value %4, %5, [1 : index] : (!fir.complex<4>, f32) -> !fir.complex<4>
+
+ // I32: [[ADDRI64:%[0-9A-Za-z]+]] = fir.alloca i64
+ // I32: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRI64]] : (!fir.ref<i64>) -> !fir.ref<!fir.complex<4>>
+ // I32: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // I32: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRI64]] : !fir.ref<i64>
+ // I32: return [[RES]] : i64
+ // X64: [[ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRV]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // X64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRV]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64: return [[RES]] : !fir.vector<2:!fir.real<4>>
+ // AARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<4>, !fir.real<4>>
+ // AARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // AARCH64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // AARCH64: return [[RES]] : tuple<!fir.real<4>, !fir.real<4>>
+ // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<4>, !fir.real<4>>
+ // PPC: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // PPC: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // PPC: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // PPC: return [[RES]] : tuple<!fir.real<4>, !fir.real<4>>
+ return %6 : !fir.complex<4>
+}
+
+// Test that we rewrite the signature and body of a function that returns a
+// complex<8>.
+// I32-LABEL:func @returncomplex8
+// I32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.sret})
+// X64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
+// AARCH64-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
+// PPC-LABEL: func @returncomplex8() -> tuple<!fir.real<8>, !fir.real<8>>
+func @returncomplex8() -> !fir.complex<8> {
+ // I32: fir.insert_value
+ // I32: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
+ // X64: fir.insert_value
+ // X64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
+ // AARCH64: fir.insert_value
+ // AARCH64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
+ // PPC: fir.insert_value
+ // PPC: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
+ %1 = fir.undefined !fir.complex<8>
+ %2 = arith.constant 1.0 : f64
+ %3 = arith.constant -4.0 : f64
+ %c0 = arith.constant 0 : i32
+ %4 = fir.insert_value %1, %3, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8>
+ %c1 = arith.constant 1 : i32
+ %5 = fir.insert_value %4, %2, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8>
+
+ // I32: [[ADDR:%[0-9A-Za-z]+]] = fir.convert [[ARG0]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // I32: fir.store [[VAL]] to [[ADDR]] : !fir.ref<!fir.complex<8>>
+ // I32: return{{ *$}}
+ // X64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // X64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // X64: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // X64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // X64: return [[RES]] : tuple<!fir.real<8>, !fir.real<8>>
+ // AARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // AARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // AARCH64: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // AARCH64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // AARCH64: return [[RES]] : tuple<!fir.real<8>, !fir.real<8>>
+ // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // PPC: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // PPC: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // PPC: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // PPC: return [[RES]] : tuple<!fir.real<8>, !fir.real<8>>
+ return %5 : !fir.complex<8>
+}
+
+// Test that we rewrite the signature of a function that accepts a complex<4>.
+// I32-LABEL: func private @paramcomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval})
+// X64-LABEL: func private @paramcomplex4(!fir.vector<2:!fir.real<4>>)
+// AARCH64-LABEL: func private @paramcomplex4(!fir.array<2x!fir.real<4>>)
+// PPC-LABEL: func private @paramcomplex4(!fir.real<4>, !fir.real<4>)
+func private @paramcomplex4(!fir.complex<4>) -> ()
+
+// Test that we rewrite calls to functions that return or accept complex<4>.
+// I32-LABEL: func @callcomplex4()
+// X64-LABEL: func @callcomplex4()
+// AARCH64-LABEL: func @callcomplex4()
+func @callcomplex4() {
+
+ // I32: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> i64
+ // X64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> !fir.vector<2:!fir.real<4>>
+ // AARCH64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> tuple<!fir.real<4>, !fir.real<4>>
+ // PPC: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> tuple<!fir.real<4>, !fir.real<4>>
+ %1 = fir.call @returncomplex4() : () -> !fir.complex<4>
+
+ // I32: [[ADDRI64:%[0-9A-Za-z]+]] = fir.alloca i64
+ // I32: fir.store [[RES]] to [[ADDRI64]] : !fir.ref<i64>
+ // I32: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRI64]] : (!fir.ref<i64>) -> !fir.ref<!fir.complex<4>>
+ // I32: [[C:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // I32: [[ADDRC2:%[0-9A-Za-z]+]] = fir.alloca !fir.complex<4>
+ // I32: fir.store [[C]] to [[ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // I32: [[T:%[0-9A-Za-z]+]] = fir.convert [[ADDRC2]] : (!fir.ref<!fir.complex<4>>) -> !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // I32: fir.call @paramcomplex4([[T]]) : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> ()
+
+ // X64: [[ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64: fir.store [[RES]] to [[ADDRV]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRV]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // X64: [[ADDRV2:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64: [[ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[ADDRV2]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64: fir.store [[V]] to [[ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // X64: [[VRELOADED:%[0-9A-Za-z]+]] = fir.load [[ADDRV2]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64: fir.call @paramcomplex4([[VRELOADED]]) : (!fir.vector<2:!fir.real<4>>) -> ()
+
+ // AARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<4>, !fir.real<4>>
+ // AARCH64: fir.store [[RES]] to [[ADDRT]] : !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // AARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // AARCH64: [[ADDRARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64: [[ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[ADDRARR]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64: fir.store [[V]] to [[ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // AARCH64: [[ARR:%[0-9A-Za-z]+]] = fir.load [[ADDRARR]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64: fir.call @paramcomplex4([[ARR]]) : (!fir.array<2x!fir.real<4>>) -> ()
+
+ // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<4>, !fir.real<4>>
+ // PPC: fir.store [[RES]] to [[ADDRT]] : !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // PPC: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // PPC: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<4>>
+ // PPC: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC: fir.call @paramcomplex4([[A]], [[B]]) : (!fir.real<4>, !fir.real<4>) -> ()
+ fir.call @paramcomplex4(%1) : (!fir.complex<4>) -> ()
+ return
+}
+
+// Test that we rewrite the signature of a function that accepts a complex<8>.
+// I32-LABEL: func private @paramcomplex8(!fir.ref<tuple<!fir.real<8>, !fir.real<8>>> {llvm.align = 4 : i32, llvm.byval})
+// X64-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>)
+// AARCH64-LABEL: func private @paramcomplex8(!fir.array<2x!fir.real<8>>)
+// PPC-LABEL: func private @paramcomplex8(!fir.real<8>, !fir.real<8>)
+func private @paramcomplex8(!fir.complex<8>) -> ()
+
+// Test that we rewrite calls to functions that return or accept complex<8>.
+// I32-LABEL: func @callcomplex8()
+// X64-LABEL: func @callcomplex8()
+// AARCH64-LABEL: func @callcomplex8()
+// PPC-LABEL: func @callcomplex8()
+func @callcomplex8() {
+ // I32: [[RES:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // I32: fir.call @returncomplex8([[RES]]) : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> ()
+ // X64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<!fir.real<8>, !fir.real<8>>
+ // AARCH64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<!fir.real<8>, !fir.real<8>>
+ // PPC: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<!fir.real<8>, !fir.real<8>>
+ %1 = fir.call @returncomplex8() : () -> !fir.complex<8>
+
+ // I32: [[RESC:%[0-9A-Za-z]+]] = fir.convert [[RES]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // I32: [[V:%[0-9A-Za-z]+]] = fir.load [[RESC]] : !fir.ref<!fir.complex<8>>
+ // I32: [[ADDRC:%[0-9A-Za-z]+]] = fir.alloca !fir.complex<8>
+ // I32: fir.store [[V]] to [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // I32: [[ADDRT:%[0-9A-Za-z]+]] = fir.convert [[ADDRC]] : (!fir.ref<!fir.complex<8>>) -> !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // I32: fir.call @paramcomplex8([[ADDRT]]) : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> ()
+
+ // X64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // X64: fir.store [[RES]] to [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // X64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // X64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // X64: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : index] : (!fir.complex<8>) -> !fir.real<8>
+ // X64: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : index] : (!fir.complex<8>) -> !fir.real<8>
+ // X64: fir.call @paramcomplex8([[A]], [[B]]) : (!fir.real<8>, !fir.real<8>) -> ()
+
+ // AARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // AARCH64: fir.store [[RES]] to [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // AARCH64: [[ADDRV:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // AARCH64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRV]] : !fir.ref<!fir.complex<8>>
+ // AARCH64: [[ADDRARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<8>>
+ // AARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRARR]] : (!fir.ref<!fir.array<2x!fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // AARCH64: fir.store [[V]] to [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // AARCH64: [[ARR:%[0-9A-Za-z]+]] = fir.load [[ADDRARR]] : !fir.ref<!fir.array<2x!fir.real<8>>>
+ // AARCH64: fir.call @paramcomplex8([[ARR]]) : (!fir.array<2x!fir.real<8>>) -> ()
+
+ // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<!fir.real<8>, !fir.real<8>>
+ // PPC: fir.store [[RES]] to [[ADDRT]] : !fir.ref<tuple<!fir.real<8>, !fir.real<8>>>
+ // PPC: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<!fir.real<8>, !fir.real<8>>>) -> !fir.ref<!fir.complex<8>>
+ // PPC: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<!fir.complex<8>>
+ // PPC: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : index] : (!fir.complex<8>) -> !fir.real<8>
+ // PPC: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : index] : (!fir.complex<8>) -> !fir.real<8>
+ // PPC: fir.call @paramcomplex8([[A]], [[B]]) : (!fir.real<8>, !fir.real<8>) -> ()
+ fir.call @paramcomplex8(%1) : (!fir.complex<8>) -> ()
+ return
+}
+
+// Test multiple complex<4> parameters and arguments
+// I32-LABEL: func private @calleemultipleparamscomplex4(!fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval})
+// X64-LABEL: func private @calleemultipleparamscomplex4(!fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>)
+// AARCH64-LABEL: func private @calleemultipleparamscomplex4(!fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>)
+// PPC-LABEL: func private @calleemultipleparamscomplex4(!fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>)
+func private @calleemultipleparamscomplex4(!fir.complex<4>, !fir.complex<4>, !fir.complex<4>) -> ()
+
+// I32-LABEL: func @multipleparamscomplex4
+// I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval}, [[Z3:%[0-9A-Za-z]+]]: !fir.ref<tuple<!fir.real<4>, !fir.real<4>>> {llvm.align = 4 : i32, llvm.byval})
+// X64-LABEL: func @multipleparamscomplex4
+// X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>, [[Z3:%[0-9A-Za-z]+]]: !fir.vector<2:!fir.real<4>>)
+// AARCH64-LABEL: func @multipleparamscomplex4
+// AARCH64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.array<2x!fir.real<4>>, [[Z2:%[0-9A-Za-z]+]]: !fir.array<2x!fir.real<4>>, [[Z3:%[0-9A-Za-z]+]]: !fir.array<2x!fir.real<4>>)
+// PPC-LABEL: func @multipleparamscomplex4
+// PPC-SAME: ([[A1:%[0-9A-Za-z]+]]: !fir.real<4>, [[B1:%[0-9A-Za-z]+]]: !fir.real<4>, [[A2:%[0-9A-Za-z]+]]: !fir.real<4>, [[B2:%[0-9A-Za-z]+]]: !fir.real<4>, [[A3:%[0-9A-Za-z]+]]: !fir.real<4>, [[B3:%[0-9A-Za-z]+]]: !fir.real<4>)
+func @multipleparamscomplex4(%z1 : !fir.complex<4>, %z2 : !fir.complex<4>, %z3 : !fir.complex<4>) {
+ // I32-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z1]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDR]] : !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z2_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z2]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDR]] : !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z3_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z3]] : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z3_VAL:%[0-9A-Za-z]+]] = fir.load [[Z3_ADDR]] : !fir.ref<!fir.complex<4>>
+
+ // I32-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.alloca !fir.complex<4>
+ // I32-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z1_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDRC]] : (!fir.ref<!fir.complex<4>>) -> !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // I32-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.alloca !fir.complex<4>
+ // I32-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z2_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDRC]] : (!fir.ref<!fir.complex<4>>) -> !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+ // I32-DAG: [[Z3_ADDRC:%[0-9A-Za-z]+]] = fir.alloca !fir.complex<4>
+ // I32-DAG: fir.store [[Z3_VAL]] to [[Z3_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // I32-DAG: [[Z3_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z3_ADDRC]] : (!fir.ref<!fir.complex<4>>) -> !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>
+
+ // I32: fir.call @calleemultipleparamscomplex4([[Z1_ADDRT]], [[Z2_ADDRT]], [[Z3_ADDRT]]) : (!fir.ref<tuple<!fir.real<4>, !fir.real<4>>>, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>, !fir.ref<tuple<!fir.real<4>, !fir.real<4>>>) -> ()
+
+ // X64-DAG: [[Z3_ADDR:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: fir.store [[Z3]] to [[Z3_ADDR]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64-DAG: [[Z3_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z3_ADDR]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z3_VAL:%[0-9A-Za-z]+]] = fir.load [[Z3_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z2_ADDR:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: fir.store [[Z2]] to [[Z2_ADDR]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDR]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: fir.store [[Z1]] to [[Z1_ADDR]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDR]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRC]] : !fir.ref<!fir.complex<4>>
+
+ // X64-DAG: [[Z1_ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: [[Z1_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDRV]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z1_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRV]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64-DAG: [[Z2_ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: [[Z2_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDRV]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z2_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRV]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+ // X64-DAG: [[Z3_ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:!fir.real<4>>
+ // X64-DAG: [[Z3_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z3_ADDRV]] : (!fir.ref<!fir.vector<2:!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // X64-DAG: fir.store [[Z3_VAL]] to [[Z3_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // X64-DAG: [[Z3_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z3_ADDRV]] : !fir.ref<!fir.vector<2:!fir.real<4>>>
+
+ // X64: fir.call @calleemultipleparamscomplex4([[Z1_RELOADED]], [[Z2_RELOADED]], [[Z3_RELOADED]]) : (!fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>, !fir.vector<2:!fir.real<4>>) -> ()
+
+ // AARCH64-DAG: [[Z3_ARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: fir.store [[Z3]] to [[Z3_ARR]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64-DAG: [[Z3_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z3_ARR]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z3_VAL:%[0-9A-Za-z]+]] = fir.load [[Z3_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z2_ARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: fir.store [[Z2]] to [[Z2_ARR]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z2_ARR]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRC]] : !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z1_ARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: fir.store [[Z1]] to [[Z1_ARR]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z1_ARR]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRC]] : !fir.ref<!fir.complex<4>>
+
+ // AARCH64-DAG: [[Z1_ARR2:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: [[Z1_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z1_ARR2]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z1_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z1_ARR2]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64-DAG: [[Z2_ARR2:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: [[Z2_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z2_ARR2]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z2_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z2_ARR2]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+ // AARCH64-DAG: [[Z3_ARR2:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2x!fir.real<4>>
+ // AARCH64-DAG: [[Z3_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z3_ARR2]] : (!fir.ref<!fir.array<2x!fir.real<4>>>) -> !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: fir.store [[Z3_VAL]] to [[Z3_ADDRC2]] : !fir.ref<!fir.complex<4>>
+ // AARCH64-DAG: [[Z3_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z3_ARR2]] : !fir.ref<!fir.array<2x!fir.real<4>>>
+
+ // AARCH64: fir.call @calleemultipleparamscomplex4([[Z1_RELOADED]], [[Z2_RELOADED]], [[Z3_RELOADED]]) : (!fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>, !fir.array<2x!fir.real<4>>) -> ()
+
+ // PPC-DAG: [[Z3_EMPTY:%[0-9A-Za-z]+]] = fir.undefined !fir.complex<4>
+ // PPC-DAG: [[Z3_PARTIAL:%[0-9A-Za-z]+]] = fir.insert_value [[Z3_EMPTY]], [[A3]], [0 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ // PPC-DAG: [[Z3:%[0-9A-Za-z]+]] = fir.insert_value [[Z3_PARTIAL]], [[B3]], [1 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ // PPC-DAG: [[Z2_EMPTY:%[0-9A-Za-z]+]] = fir.undefined !fir.complex<4>
+ // PPC-DAG: [[Z2_PARTIAL:%[0-9A-Za-z]+]] = fir.insert_value [[Z2_EMPTY]], [[A2]], [0 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ // PPC-DAG: [[Z2:%[0-9A-Za-z]+]] = fir.insert_value [[Z2_PARTIAL]], [[B2]], [1 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ // PPC-DAG: [[Z1_EMPTY:%[0-9A-Za-z]+]] = fir.undefined !fir.complex<4>
+ // PPC-DAG: [[Z1_PARTIAL:%[0-9A-Za-z]+]] = fir.insert_value [[Z1_EMPTY]], [[A1]], [0 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+ // PPC-DAG: [[Z1:%[0-9A-Za-z]+]] = fir.insert_value [[Z1_PARTIAL]], [[B1]], [1 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4>
+
+ // PPC-DAG: [[A1_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z1]], [0 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC-DAG: [[B1_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z1]], [1 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC-DAG: [[A2_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z2]], [0 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC-DAG: [[B2_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z2]], [1 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC-DAG: [[A3_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z3]], [0 : index] : (!fir.complex<4>) -> !fir.real<4>
+ // PPC-DAG: [[B3_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z3]], [1 : index] : (!fir.complex<4>) -> !fir.real<4>
+
+ // PPC: fir.call @calleemultipleparamscomplex4([[A1_EXTR]], [[B1_EXTR]], [[A2_EXTR]], [[B2_EXTR]], [[A3_EXTR]], [[B3_EXTR]]) : (!fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>, !fir.real<4>) -> ()
+
+ fir.call @calleemultipleparamscomplex4(%z1, %z2, %z3) : (!fir.complex<4>, !fir.complex<4>, !fir.complex<4>) -> ()
+ return
+}
+
+// Test that we rewrite the signature of and calls to a function that accepts
+// and returns MLIR complex<f32>.
+
+// I32-LABEL: func private @mlircomplexf32
+// I32-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 4 : i32, llvm.byval})
+// I32-SAME: -> i64
+// X64-LABEL: func private @mlircomplexf32
+// X64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.vector<2:f32>, [[Z2:%[0-9A-Za-z]+]]: !fir.vector<2:f32>)
+// X64-SAME: -> !fir.vector<2:f32>
+// AARCH64-LABEL: func private @mlircomplexf32
+// AARCH64-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.array<2xf32>, [[Z2:%[0-9A-Za-z]+]]: !fir.array<2xf32>)
+// AARCH64-SAME: -> tuple<f32, f32>
+// PPC-LABEL: func private @mlircomplexf32
+// PPC-SAME: ([[A1:%[0-9A-Za-z]+]]: f32, [[B1:%[0-9A-Za-z]+]]: f32, [[A2:%[0-9A-Za-z]+]]: f32, [[B2:%[0-9A-Za-z]+]]: f32)
+// PPC-SAME: -> tuple<f32, f32>
+func private @mlircomplexf32(%z1: complex<f32>, %z2: complex<f32>) -> complex<f32> {
+
+ // I32-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z1]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // I32-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDR]] : !fir.ref<complex<f32>>
+ // I32-DAG: [[Z2_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z2]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // I32-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDR]] : !fir.ref<complex<f32>>
+
+ // I32-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+ // I32-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC]] : !fir.ref<complex<f32>>
+ // I32-DAG: [[Z1_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDRC]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+ // I32-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+ // I32-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC]] : !fir.ref<complex<f32>>
+ // I32-DAG: [[Z2_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDRC]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+
+ // I32: [[VAL:%[0-9A-Za-z]+]] = fir.call @mlircomplexf32([[Z1_ADDRT]], [[Z2_ADDRT]]) : (!fir.ref<tuple<f32, f32>>, !fir.ref<tuple<f32, f32>>) -> i64
+
+ // X64-DAG: [[Z2_ADDR:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64-DAG: fir.store [[Z2]] to [[Z2_ADDR]] : !fir.ref<!fir.vector<2:f32>>
+ // X64-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDR]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRC]] : !fir.ref<complex<f32>>
+ // X64-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64-DAG: fir.store [[Z1]] to [[Z1_ADDR]] : !fir.ref<!fir.vector<2:f32>>
+ // X64-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDR]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRC]] : !fir.ref<complex<f32>>
+
+ // X64-DAG: [[Z1_ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64-DAG: [[Z1_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDRV]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC2]] : !fir.ref<complex<f32>>
+ // X64-DAG: [[Z1_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRV]] : !fir.ref<!fir.vector<2:f32>>
+ // X64-DAG: [[Z2_ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64-DAG: [[Z2_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDRV]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC2]] : !fir.ref<complex<f32>>
+ // X64-DAG: [[Z2_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRV]] : !fir.ref<!fir.vector<2:f32>>
+
+ // X64: [[VAL:%[0-9A-Za-z]+]] = fir.call @mlircomplexf32([[Z1_RELOADED]], [[Z2_RELOADED]]) : (!fir.vector<2:f32>, !fir.vector<2:f32>) -> !fir.vector<2:f32>
+
+ // AARCH64-DAG: [[Z2_ARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2xf32>
+ // AARCH64-DAG: fir.store [[Z2]] to [[Z2_ARR]] : !fir.ref<!fir.array<2xf32>>
+ // AARCH64-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z2_ARR]] : (!fir.ref<!fir.array<2xf32>>) -> !fir.ref<complex<f32>>
+ // AARCH64-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDRC]] : !fir.ref<complex<f32>>
+ // AARCH64-DAG: [[Z1_ARR:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2xf32>
+ // AARCH64-DAG: fir.store [[Z1]] to [[Z1_ARR]] : !fir.ref<!fir.array<2xf32>>
+ // AARCH64-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.convert [[Z1_ARR]] : (!fir.ref<!fir.array<2xf32>>) -> !fir.ref<complex<f32>>
+ // AARCH64-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDRC]] : !fir.ref<complex<f32>>
+
+ // AARCH64-DAG: [[Z1_ARR2:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2xf32>
+ // AARCH64-DAG: [[Z1_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z1_ARR2]] : (!fir.ref<!fir.array<2xf32>>) -> !fir.ref<complex<f32>>
+ // AARCH64-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC2]] : !fir.ref<complex<f32>>
+ // AARCH64-DAG: [[Z1_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z1_ARR2]] : !fir.ref<!fir.array<2xf32>>
+ // AARCH64-DAG: [[Z2_ARR2:%[0-9A-Za-z]+]] = fir.alloca !fir.array<2xf32>
+ // AARCH64-DAG: [[Z2_ADDRC2:%[0-9A-Za-z]+]] = fir.convert [[Z2_ARR2]] : (!fir.ref<!fir.array<2xf32>>) -> !fir.ref<complex<f32>>
+ // AARCH64-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC2]] : !fir.ref<complex<f32>>
+ // AARCH64-DAG: [[Z2_RELOADED:%[0-9A-Za-z]+]] = fir.load [[Z2_ARR2]] : !fir.ref<!fir.array<2xf32>>
+
+ // AARCH64: [[VAL:%[0-9A-Za-z]+]] = fir.call @mlircomplexf32([[Z1_RELOADED]], [[Z2_RELOADED]]) : (!fir.array<2xf32>, !fir.array<2xf32>) -> tuple<f32, f32>
+
+ // PPC-DAG: [[Z2_EMPTY:%[0-9A-Za-z]+]] = fir.undefined complex<f32>
+ // PPC-DAG: [[Z2_PARTIAL:%[0-9A-Za-z]+]] = fir.insert_value [[Z2_EMPTY]], [[A2]], [0 : index] : (complex<f32>, f32) -> complex<f32>
+ // PPC-DAG: [[Z2:%[0-9A-Za-z]+]] = fir.insert_value [[Z2_PARTIAL]], [[B2]], [1 : index] : (complex<f32>, f32) -> complex<f32>
+ // PPC-DAG: [[Z1_EMPTY:%[0-9A-Za-z]+]] = fir.undefined complex<f32>
+ // PPC-DAG: [[Z1_PARTIAL:%[0-9A-Za-z]+]] = fir.insert_value [[Z1_EMPTY]], [[A1]], [0 : index] : (complex<f32>, f32) -> complex<f32>
+ // PPC-DAG: [[Z1:%[0-9A-Za-z]+]] = fir.insert_value [[Z1_PARTIAL]], [[B1]], [1 : index] : (complex<f32>, f32) -> complex<f32>
+
+ // PPC-DAG: [[A1_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z1]], [0 : index] : (complex<f32>) -> f32
+ // PPC-DAG: [[B1_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z1]], [1 : index] : (complex<f32>) -> f32
+ // PPC-DAG: [[A2_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z2]], [0 : index] : (complex<f32>) -> f32
+ // PPC-DAG: [[B2_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z2]], [1 : index] : (complex<f32>) -> f32
+
+ // PPC: [[VAL:%[0-9A-Za-z]+]] = fir.call @mlircomplexf32([[A1_EXTR]], [[B1_EXTR]], [[A2_EXTR]], [[B2_EXTR]]) : (f32, f32, f32, f32) -> tuple<f32, f32>
+ %0 = fir.call @mlircomplexf32(%z1, %z2) : (complex<f32>, complex<f32>) -> complex<f32>
+
+
+ // I32: [[ADDRI64:%[0-9A-Za-z]+]] = fir.alloca i64
+ // I32: fir.store [[VAL]] to [[ADDRI64]] : !fir.ref<i64>
+ // I32: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRI64]] : (!fir.ref<i64>) -> !fir.ref<complex<f32>>
+ // I32: [[VAL_2:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<complex<f32>>
+ // I32: [[ADDRI64_2:%[0-9A-Za-z]+]] = fir.alloca i64
+ // I32: [[ADDRC_2:%[0-9A-Za-z]+]] = fir.convert [[ADDRI64_2]] : (!fir.ref<i64>) -> !fir.ref<complex<f32>>
+ // I32: fir.store [[VAL_2]] to [[ADDRC_2]] : !fir.ref<complex<f32>>
+ // I32: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRI64_2]] : !fir.ref<i64>
+ // I32: return [[RES]] : i64
+
+ // X64: [[ADDRV:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64: fir.store [[VAL]] to [[ADDRV]] : !fir.ref<!fir.vector<2:f32>>
+ // X64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRV]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<complex<f32>>
+ // X64: [[ADDRV_2:%[0-9A-Za-z]+]] = fir.alloca !fir.vector<2:f32>
+ // X64: [[ADDRC_2:%[0-9A-Za-z]+]] = fir.convert [[ADDRV_2]] : (!fir.ref<!fir.vector<2:f32>>) -> !fir.ref<complex<f32>>
+ // X64: fir.store [[V]] to [[ADDRC_2]] : !fir.ref<complex<f32>>
+ // X64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRV_2]] : !fir.ref<!fir.vector<2:f32>>
+ // X64: return [[RES]] : !fir.vector<2:f32>
+
+ // AARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+ // AARCH64: fir.store [[VAL]] to [[ADDRT]] : !fir.ref<tuple<f32, f32>>
+ // AARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // AARCH64: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<complex<f32>>
+ // AARCH64: [[ADDRT_2:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+ // AARCH64: [[ADDRC_2:%[0-9A-Za-z]+]] = fir.convert [[ADDRT_2]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // AARCH64: fir.store [[V]] to [[ADDRC_2]] : !fir.ref<complex<f32>>
+ // AARCH64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT_2]] : !fir.ref<tuple<f32, f32>>
+ // AARCH64: return [[RES]] : tuple<f32, f32>
+
+ // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+ // PPC: fir.store [[VAL]] to [[ADDRT]] : !fir.ref<tuple<f32, f32>>
+ // PPC: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // PPC: [[V:%[0-9A-Za-z]+]] = fir.load [[ADDRC]] : !fir.ref<complex<f32>>
+ // PPC: [[ADDRT_2:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+ // PPC: [[ADDRC_2:%[0-9A-Za-z]+]] = fir.convert [[ADDRT_2]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+ // PPC: fir.store [[V]] to [[ADDRC_2]] : !fir.ref<complex<f32>>
+ // PPC: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT_2]] : !fir.ref<tuple<f32, f32>>
+ // PPC: return [[RES]] : tuple<f32, f32>
+ return %0 : complex<f32>
+}
More information about the flang-commits
mailing list