diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h index e72ef22ea6afc..b3fe52f4b7146 100644 --- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h +++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h @@ -20,6 +20,7 @@ #include "flang/Common/Fortran.h" #include "flang/Common/uint128.h" #include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/MLIRContext.h" @@ -418,7 +419,7 @@ static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc, return func; auto funTy = RuntimeEntry::getTypeModel()(builder.getContext()); func = builder.createFunction(loc, name, funTy); - func->setAttr("fir.runtime", builder.getUnitAttr()); + func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr()); return func; } diff --git a/flang/include/flang/Optimizer/Dialect/FIRDialect.h b/flang/include/flang/Optimizer/Dialect/FIRDialect.h index 29f8d437fc5a3..baa447c1aa31b 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRDialect.h +++ b/flang/include/flang/Optimizer/Dialect/FIRDialect.h @@ -37,6 +37,11 @@ class FIROpsDialect final : public mlir::Dialect { void printAttribute(mlir::Attribute attr, mlir::DialectAsmPrinter &p) const override; + /// Return string name of fir.runtime attribute. + static constexpr llvm::StringRef getFirRuntimeAttrName() { + return "fir.runtime"; + } + private: // Register the Attributes of this dialect. void registerAttributes(); diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp index 0b075cd51e0ed..9f38f03310aab 100644 --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -28,6 +28,7 @@ #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" #include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Support/FIRContext.h" #include "flang/Parser/parse-tree.h" #include "flang/Runtime/io-api.h" @@ -169,7 +170,8 @@ static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc, return func; auto funTy = getTypeModel()(builder.getContext()); func = builder.createFunction(loc, name, funTy); - func->setAttr("fir.runtime", builder.getUnitAttr()); + func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(), + builder.getUnitAttr()); func->setAttr("fir.io", builder.getUnitAttr()); return func; } diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp index 28191a372b405..572cfef09ad65 100644 --- a/flang/lib/Optimizer/CodeGen/Target.cpp +++ b/flang/lib/Optimizer/CodeGen/Target.cpp @@ -22,6 +22,19 @@ using namespace fir; +namespace fir::details { +llvm::StringRef Attributes::getIntExtensionAttrName() const { + // The attribute names are available via LLVM dialect interfaces + // like getZExtAttrName(), getByValAttrName(), etc., so we'd better + // use them than literals. + if (isZeroExt()) + return "llvm.zeroext"; + else if (isSignExt()) + return "llvm.signext"; + return {}; +} +} // namespace fir::details + // Reduce a REAL/float type to the floating point semantics. static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, mlir::Type type) { @@ -41,8 +54,8 @@ struct GenericTarget : public CodeGenSpecifics { assert(fir::isa_real(eleTy)); // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy - mlir::TypeRange range = {eleTy, eleTy}; - return mlir::TupleType::get(eleTy.getContext(), range); + return mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{eleTy, eleTy}); } mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { @@ -50,8 +63,8 @@ struct GenericTarget : public CodeGenSpecifics { auto ptrTy = fir::ReferenceType::get(eleTy); // Use a type that will be translated into LLVM as: // { t*, index } - mlir::TypeRange range = {ptrTy, idxTy}; - return mlir::TupleType::get(eleTy.getContext(), range); + return mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{ptrTy, idxTy}); } Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { @@ -67,6 +80,46 @@ struct GenericTarget : public CodeGenSpecifics { /*sret=*/sret, /*append=*/!sret}); return marshal; } + + CodeGenSpecifics::Marshalling + integerArgumentType(mlir::Location loc, + mlir::IntegerType argTy) const override { + CodeGenSpecifics::Marshalling marshal; + AT::IntegerExtension intExt = AT::IntegerExtension::None; + if (argTy.getWidth() < getCIntTypeWidth()) { + // isSigned() and isUnsigned() branches below are dead code currently. + // If needed, we can generate calls with signed/unsigned argument types + // to more precisely match C side (e.g. for Fortran runtime functions + // with 'unsigned short' arguments). + if (argTy.isSigned()) + intExt = AT::IntegerExtension::Sign; + else if (argTy.isUnsigned()) + intExt = AT::IntegerExtension::Zero; + else if (argTy.isSignless()) { + // Zero extend for 'i1' and sign extend for other types. + if (argTy.getWidth() == 1) + intExt = AT::IntegerExtension::Zero; + else + intExt = AT::IntegerExtension::Sign; + } + } + + marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, + /*sret=*/false, /*append=*/false, + /*intExt=*/intExt}); + return marshal; + } + + CodeGenSpecifics::Marshalling + integerReturnType(mlir::Location loc, + mlir::IntegerType argTy) const override { + return integerArgumentType(loc, argTy); + } + + // Width of 'int' type is 32-bits for almost all targets, except + // for AVR and MSP430 (see TargetInfo initializations + // in clang/lib/Basic/Targets). + unsigned char getCIntTypeWidth() const override { return 32; } }; } // namespace @@ -86,8 +139,8 @@ struct TargetI386 : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy, byval, align 4 - mlir::TypeRange range = {eleTy, eleTy}; - auto structTy = mlir::TupleType::get(eleTy.getContext(), range); + auto structTy = + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); marshal.emplace_back(fir::ReferenceType::get(structTy), AT{/*alignment=*/4, /*byval=*/true}); return marshal; @@ -105,8 +158,8 @@ struct TargetI386 : public GenericTarget { } else if (sem == &llvm::APFloat::IEEEdouble()) { // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy, sret, align 4 - mlir::TypeRange range = {eleTy, eleTy}; - auto structTy = mlir::TupleType::get(eleTy.getContext(), range); + auto structTy = mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{eleTy, eleTy}); marshal.emplace_back(fir::ReferenceType::get(structTy), AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); } else { @@ -141,10 +194,10 @@ struct TargetX86_64 : public GenericTarget { } else if (sem == &llvm::APFloat::IEEEquad()) { // Use a type that will be translated into LLVM as: // { fp128, fp128 } struct of 2 fp128, byval, align 16 - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(fir::ReferenceType::get( - mlir::TupleType::get(eleTy.getContext(), range)), - AT{/*align=*/16, /*byval=*/true}); + marshal.emplace_back( + fir::ReferenceType::get(mlir::TupleType::get( + eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), + AT{/*align=*/16, /*byval=*/true}); } else { TODO(loc, "complex for this precision"); } @@ -161,16 +214,16 @@ struct TargetX86_64 : public GenericTarget { } else if (sem == &llvm::APFloat::IEEEdouble()) { // Use a type that will be translated into LLVM as: // { double, double } struct of 2 double - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), + marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{eleTy, eleTy}), AT{}); } else if (sem == &llvm::APFloat::IEEEquad()) { // Use a type that will be translated into LLVM as: // { fp128, fp128 } struct of 2 fp128, sret, align 16 - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(fir::ReferenceType::get( - mlir::TupleType::get(eleTy.getContext(), range)), - AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); + marshal.emplace_back( + fir::ReferenceType::get(mlir::TupleType::get( + eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), + AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); } else { TODO(loc, "complex for this precision"); } @@ -211,8 +264,8 @@ struct TargetAArch64 : public GenericTarget { sem == &llvm::APFloat::IEEEdouble()) { // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), + marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{eleTy, eleTy}), AT{}); } else { TODO(loc, "complex for this precision"); @@ -246,8 +299,9 @@ struct TargetPPC64 : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { t, t } struct of 2 element type - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); + marshal.emplace_back( + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), + AT{}); return marshal; } }; @@ -277,8 +331,9 @@ struct TargetPPC64le : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { t, t } struct of 2 element type - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); + marshal.emplace_back( + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), + AT{}); return marshal; } }; @@ -300,8 +355,8 @@ struct TargetSparc : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy - mlir::TypeRange range = {eleTy, eleTy}; - auto structTy = mlir::TupleType::get(eleTy.getContext(), range); + auto structTy = + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); return marshal; } @@ -312,8 +367,8 @@ struct TargetSparc : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy, byval - mlir::TypeRange range = {eleTy, eleTy}; - auto structTy = mlir::TupleType::get(eleTy.getContext(), range); + auto structTy = + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); marshal.emplace_back(fir::ReferenceType::get(structTy), AT{/*alignment=*/0, /*byval=*/true}); return marshal; @@ -343,10 +398,10 @@ struct TargetSparcV9 : public GenericTarget { } else if (sem == &llvm::APFloat::IEEEquad()) { // Use a type that will be translated into LLVM as: // { fp128, fp128 } struct of 2 fp128, byval, align 16 - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(fir::ReferenceType::get( - mlir::TupleType::get(eleTy.getContext(), range)), - AT{/*align=*/16, /*byval=*/true}); + marshal.emplace_back( + fir::ReferenceType::get(mlir::TupleType::get( + eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), + AT{/*align=*/16, /*byval=*/true}); } else { TODO(loc, "complex for this precision"); } @@ -358,8 +413,9 @@ struct TargetSparcV9 : public GenericTarget { CodeGenSpecifics::Marshalling marshal; // Use a type that will be translated into LLVM as: // { eleTy, eleTy } struct of 2 eleTy - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); + marshal.emplace_back( + mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), + AT{}); return marshal; } }; @@ -398,8 +454,8 @@ struct TargetRISCV64 : public GenericTarget { sem == &llvm::APFloat::IEEEdouble()) { // Use a type that will be translated into LLVM as: // { t, t } struct of 2 eleTy, byVal - mlir::TypeRange range = {eleTy, eleTy}; - marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), + marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), + mlir::TypeRange{eleTy, eleTy}), AT{/*alignment=*/0, /*byval=*/true}); } else { TODO(loc, "complex for this precision"); diff --git a/flang/lib/Optimizer/CodeGen/Target.h b/flang/lib/Optimizer/CodeGen/Target.h index 7f6d8d96d23bc..be6ae6cd75f83 100644 --- a/flang/lib/Optimizer/CodeGen/Target.h +++ b/flang/lib/Optimizer/CodeGen/Target.h @@ -29,21 +29,29 @@ namespace details { /// LLVMContext. class Attributes { public: + enum class IntegerExtension { None, Zero, Sign }; + Attributes(unsigned short alignment = 0, bool byval = false, - bool sret = false, bool append = false) - : alignment{alignment}, byval{byval}, sret{sret}, append{append} {} + bool sret = false, bool append = false, + IntegerExtension intExt = IntegerExtension::None) + : alignment{alignment}, byval{byval}, sret{sret}, append{append}, + intExt{intExt} {} 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; } + bool isZeroExt() const { return intExt == IntegerExtension::Zero; } + bool isSignExt() const { return intExt == IntegerExtension::Sign; } + llvm::StringRef getIntExtensionAttrName() const; private: unsigned short alignment{}; bool byval : 1; bool sret : 1; bool append : 1; + IntegerExtension intExt; }; } // namespace details @@ -94,6 +102,47 @@ class CodeGenSpecifics { virtual Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret = false) const = 0; + // Compute ABI rules for an integer argument of the given mlir::IntegerType + // \p argTy. Note that this methods is supposed to be called for + // arguments passed by value not via reference, e.g. the 'i1' argument here: + // declare i1 @_FortranAioOutputLogical(ptr, i1) + // + // \p loc is the location of the operation using/specifying the argument. + // + // Currently, the only supported marshalling is whether the argument + // should be zero or sign extended. + // + // The zero/sign extension is especially important to comply with the ABI + // used by C/C++ compiler that builds Fortran runtime. As in the above + // example the callee will expect the caller to zero extend the second + // argument up to the size of the C/C++'s 'int' type. + // The corresponding handling in clang is done in + // DefaultABIInfo::classifyArgumentType(), and the logic may brielfy + // be explained as some sort of extension is required if the integer + // type is shorter than the size of 'int' for the target. + // The related code is located in ASTContext::isPromotableIntegerType() + // and ABIInfo::isPromotableIntegerTypeForABI(). + // In particular, the latter returns 'true' for 'bool', several kinds + // of 'char', 'short', 'wchar' and enumerated types. + // The type of the extensions (zero or sign) depends on the signedness + // of the original language type. + // + // It is not clear how to handle signless integer types. + // From the point of Fortran-C interface all supported integer types + // seem to be signed except for CFI_type_Bool/bool that is supported + // via signless 'i1', but that is treated as unsigned type by clang + // (e.g. 'bool' arguments are using 'zeroext' ABI). + virtual Marshalling integerArgumentType(mlir::Location loc, + mlir::IntegerType argTy) const = 0; + + // By default, integer argument and return values use the same + // zero/sign extension rules. + virtual Marshalling integerReturnType(mlir::Location loc, + mlir::IntegerType argTy) const = 0; + + // Returns width in bits of C/C++ 'int' type size. + virtual unsigned char getCIntTypeWidth() const = 0; + protected: mlir::MLIRContext &context; llvm::Triple triple; diff --git a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp index 223a8bc11aa9b..bddeffb985192 100644 --- a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp +++ b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp @@ -101,14 +101,14 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { // Convert ops in target-specific patterns. mod.walk([&](mlir::Operation *op) { if (auto call = mlir::dyn_cast(op)) { - if (!hasPortableSignature(call.getFunctionType())) + if (!hasPortableSignature(call.getFunctionType(), op)) convertCallOp(call); } else if (auto dispatch = mlir::dyn_cast(op)) { - if (!hasPortableSignature(dispatch.getFunctionType())) + if (!hasPortableSignature(dispatch.getFunctionType(), op)) convertCallOp(dispatch); } else if (auto addr = mlir::dyn_cast(op)) { if (addr.getType().isa() && - !hasPortableSignature(addr.getType())) + !hasPortableSignature(addr.getType(), op)) convertAddrOp(addr); } }); @@ -457,19 +457,23 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { /// then it is considered portable for any target, and this function will /// return `true`. Otherwise, the signature is not portable and `false` is /// returned. - bool hasPortableSignature(mlir::Type signature) { + bool hasPortableSignature(mlir::Type signature, mlir::Operation *op) { assert(signature.isa()); auto func = signature.dyn_cast(); + bool hasFirRuntime = op->hasAttrOfType( + fir::FIROpsDialect::getFirRuntimeAttrName()); for (auto ty : func.getResults()) if ((ty.isa() && !noCharacterConversion) || - (fir::isa_complex(ty) && !noComplexConversion)) { + (fir::isa_complex(ty) && !noComplexConversion) || + (ty.isa() && hasFirRuntime)) { LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n"); return false; } for (auto ty : func.getInputs()) if (((ty.isa() || fir::isCharacterProcedureTuple(ty)) && !noCharacterConversion) || - (fir::isa_complex(ty) && !noComplexConversion)) { + (fir::isa_complex(ty) && !noComplexConversion) || + (ty.isa() && hasFirRuntime)) { LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n"); return false; } @@ -490,13 +494,14 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { /// the immediately subsequent target code gen. void convertSignature(mlir::func::FuncOp func) { auto funcTy = func.getFunctionType().cast(); - if (hasPortableSignature(funcTy) && !hasHostAssociations(func)) + if (hasPortableSignature(funcTy, func) && !hasHostAssociations(func)) return; llvm::SmallVector newResTys; llvm::SmallVector newInTys; llvm::SmallVector> savedAttrs; llvm::SmallVector> extraAttrs; llvm::SmallVector fixups; + llvm::SmallVector, 1> resultAttrs; // Save argument attributes in case there is a shift so we can replace them // correctly. @@ -524,6 +529,22 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { else doComplexReturn(func, cmplx, newResTys, newInTys, fixups); }) + .Case([&](mlir::IntegerType intTy) { + auto m = specifics->integerArgumentType(func.getLoc(), intTy); + assert(m.size() == 1); + auto attr = std::get(m[0]); + auto retTy = std::get(m[0]); + std::size_t resId = newResTys.size(); + llvm::StringRef extensionAttrName = attr.getIntExtensionAttrName(); + if (!extensionAttrName.empty() && + // TODO: we have to do the same for BIND(C) routines. + func->hasAttrOfType( + fir::FIROpsDialect::getFirRuntimeAttrName())) + resultAttrs.emplace_back( + resId, rewriter->getNamedAttr(extensionAttrName, + rewriter->getUnitAttr())); + newResTys.push_back(retTy); + }) .Default([&](mlir::Type ty) { newResTys.push_back(ty); }); // Saved potential shift in argument. Handling of result can add arguments @@ -587,6 +608,26 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { newInTys.push_back(ty); } }) + .Case([&](mlir::IntegerType intTy) { + auto m = specifics->integerArgumentType(func.getLoc(), intTy); + assert(m.size() == 1); + auto attr = std::get(m[0]); + auto argTy = std::get(m[0]); + auto argNo = newInTys.size(); + llvm::StringRef extensionAttrName = attr.getIntExtensionAttrName(); + if (!extensionAttrName.empty() && + // TODO: we have to do the same for BIND(C) routines. + func->hasAttrOfType( + fir::FIROpsDialect::getFirRuntimeAttrName())) { + fixups.emplace_back(FixupTy::Codes::ArgumentType, argNo, + [=](mlir::func::FuncOp func) { + func.setArgAttr( + argNo, extensionAttrName, + mlir::UnitAttr::get(func.getContext())); + }); + } + newInTys.push_back(argTy); + }) .Default([&](mlir::Type ty) { newInTys.push_back(ty); }); if (func.getArgAttrOfType(index, @@ -623,14 +664,18 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { case FixupTy::Codes::ArgumentType: { // Argument is pass-by-value, but its type has likely been modified to // suit the target ABI convention. + auto oldArgTy = + fir::ReferenceType::get(oldArgTys[fixup.index - offset]); + // If type did not change, keep the original argument. + if (newInTys[fixup.index] == oldArgTy) + break; + auto newArg = func.front().insertArgument(fixup.index, newInTys[fixup.index], loc); rewriter->setInsertionPointToStart(&func.front()); auto mem = rewriter->create(loc, newInTys[fixup.index]); rewriter->create(loc, newArg, mem); - auto oldArgTy = - fir::ReferenceType::get(oldArgTys[fixup.index - offset]); auto cast = rewriter->create(loc, oldArgTy, mem); mlir::Value load = rewriter->create(loc, cast); func.getArgument(fixup.index + 1).replaceAllUsesWith(load); @@ -759,6 +804,10 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase { func.setArgAttr(extraAttr.first, extraAttr.second.getName(), extraAttr.second.getValue()); + for (auto [resId, resAttrList] : resultAttrs) + for (mlir::NamedAttribute resAttr : resAttrList) + func.setResultAttr(resId, resAttr.getName(), resAttr.getValue()); + // Replace attributes to the correct argument if there was an argument shift // to the right. if (argumentShift > 0) { diff --git a/flang/test/Fir/target-rewrite-integer.fir b/flang/test/Fir/target-rewrite-integer.fir new file mode 100644 index 0000000000000..b7426acf06238 --- /dev/null +++ b/flang/test/Fir/target-rewrite-integer.fir @@ -0,0 +1,77 @@ +// RUN: fir-opt --split-input-file --target-rewrite="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=I32,ALL +// RUN: fir-opt --split-input-file --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=X64,ALL +// RUN: fir-opt --split-input-file --target-rewrite="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=AARCH64,ALL +// RUN: fir-opt --split-input-file --target-rewrite="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=PPC,ALL +// RUN: fir-opt --split-input-file --target-rewrite="target=sparc64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=SPARCV9,ALL +// RUN: fir-opt --split-input-file --target-rewrite="target=sparcv9-sun-solaris2.11" %s | FileCheck %s --check-prefixes=SPARCV9,ALL + +// ----- + +// subroutine test_i1(x) +// logical x +// print *, x +// end subroutine test_i1 + +// ALL-LABEL: @_QPtest_i1 +// I32: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext}) +// X64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext}) +// AARCH64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext}) +// PPC: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext}) +// SPARCV9: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext}) +func.func @_QPtest_i1(%arg0: !fir.ref> {fir.bindc_name = "x"}) { + %c3_i32 = arith.constant 3 : i32 + %c-1_i32 = arith.constant -1 : i32 + %0 = fir.address_of(@_QQcl.2E2F746573742E66393000) : !fir.ref> + %1 = fir.convert %0 : (!fir.ref>) -> !fir.ref + %2 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %1, %c3_i32) : (i32, !fir.ref, i32) -> !fir.ref + %3 = fir.load %arg0 : !fir.ref> + %4 = fir.convert %3 : (!fir.logical<4>) -> i1 + %5 = fir.call @_FortranAioOutputLogical(%2, %4) : (!fir.ref, i1) -> i1 + %6 = fir.call @_FortranAioEndIoStatement(%2) : (!fir.ref) -> i32 + return +} +func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref, i32) -> !fir.ref attributes {fir.io, fir.runtime} +fir.global linkonce @_QQcl.2E2F746573742E66393000 constant : !fir.char<1,11> { + %0 = fir.string_lit "./test.f90\00"(11) : !fir.char<1,11> + fir.has_value %0 : !fir.char<1,11> +} +func.func private @_FortranAioOutputLogical(!fir.ref, i1) -> i1 attributes {fir.io, fir.runtime} +func.func private @_FortranAioEndIoStatement(!fir.ref) -> i32 attributes {fir.io, fir.runtime} + +// ----- + +// Manually created test with 'si1' argument/return type. +// Flang does not use 'si1' type currently. + +// ALL-LABEL: @_QPtest_si1 +// I32: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext}) +// X64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext}) +// AARCH64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext}) +// PPC: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext}) +// SPARCV9: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext}) +func.func @_QPtest_si1(%arg0: !fir.ref> {fir.bindc_name = "x"}) { + %0 = fir.load %arg0 : !fir.ref> + %1 = fir.convert %0 : (!fir.logical<4>) -> si1 + %2 = fir.call @_SomeFunc_si1(%1) : (si1) -> si1 + return +} +func.func private @_SomeFunc_si1(si1) -> si1 attributes {fir.runtime} + +// ----- + +// Manually created test with 'ui1' argument/return type. +// Flang does not use 'ui1' type currently. + +// ALL-LABEL: @_QPtest_ui1 +// I32: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext}) +// X64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext}) +// AARCH64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext}) +// PPC: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext}) +// SPARCV9: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext}) +func.func @_QPtest_ui1(%arg0: !fir.ref> {fir.bindc_name = "x"}) { + %0 = fir.load %arg0 : !fir.ref> + %1 = fir.convert %0 : (!fir.logical<4>) -> ui1 + %2 = fir.call @_SomeFunc_ui1(%1) : (ui1) -> ui1 + return +} +func.func private @_SomeFunc_ui1(ui1) -> ui1 attributes {fir.runtime}