[flang-commits] [flang] ff79411 - [flang] Use proper attributes for runtime calls with 'i1' arguments/returns.

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Mon Jan 30 15:40:20 PST 2023


Author: Slava Zakharin
Date: 2023-01-30T15:39:59-08:00
New Revision: ff794116f9815979f9a3a1195aff9be49334da7a

URL: https://github.com/llvm/llvm-project/commit/ff794116f9815979f9a3a1195aff9be49334da7a
DIFF: https://github.com/llvm/llvm-project/commit/ff794116f9815979f9a3a1195aff9be49334da7a.diff

LOG: [flang] Use proper attributes for runtime calls with 'i1' arguments/returns.

Clang uses signext/zeroext attributes for integer arguments shorter than
the default 'int' type on a target. So Flang has to match this for functions
from Fortran runtime and also for BIND(C) routines. This patch implements
ABI adjustments only for Fortran runtime calls. BIND(C) part will be done
separately.

This resolves https://github.com/llvm/llvm-project/issues/58579

Differential Revision: https://reviews.llvm.org/D142677

Added: 
    flang/test/Fir/target-rewrite-integer.fir

Modified: 
    flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
    flang/include/flang/Optimizer/Dialect/FIRDialect.h
    flang/lib/Lower/IO.cpp
    flang/lib/Optimizer/CodeGen/Target.cpp
    flang/lib/Optimizer/CodeGen/Target.h
    flang/lib/Optimizer/CodeGen/TargetRewrite.cpp

Removed: 
    


################################################################################
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<E>()(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<TargetI386> {
     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<TargetI386> {
     } 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<TargetX86_64> {
     } 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<TargetX86_64> {
     } 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<TargetAArch64> {
         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<TargetPPC64> {
     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<TargetPPC64le> {
     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<TargetSparc> {
     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<TargetSparc> {
     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<TargetSparcV9> {
     } 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<TargetSparcV9> {
     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<TargetRISCV64> {
         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<TargetRewrite> {
     // Convert ops in target-specific patterns.
     mod.walk([&](mlir::Operation *op) {
       if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
-        if (!hasPortableSignature(call.getFunctionType()))
+        if (!hasPortableSignature(call.getFunctionType(), op))
           convertCallOp(call);
       } else if (auto dispatch = mlir::dyn_cast<fir::DispatchOp>(op)) {
-        if (!hasPortableSignature(dispatch.getFunctionType()))
+        if (!hasPortableSignature(dispatch.getFunctionType(), op))
           convertCallOp(dispatch);
       } else if (auto addr = mlir::dyn_cast<fir::AddrOfOp>(op)) {
         if (addr.getType().isa<mlir::FunctionType>() &&
-            !hasPortableSignature(addr.getType()))
+            !hasPortableSignature(addr.getType(), op))
           convertAddrOp(addr);
       }
     });
@@ -457,19 +457,23 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase<TargetRewrite> {
   /// 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<mlir::FunctionType>());
     auto func = signature.dyn_cast<mlir::FunctionType>();
+    bool hasFirRuntime = op->hasAttrOfType<mlir::UnitAttr>(
+        fir::FIROpsDialect::getFirRuntimeAttrName());
     for (auto ty : func.getResults())
       if ((ty.isa<fir::BoxCharType>() && !noCharacterConversion) ||
-          (fir::isa_complex(ty) && !noComplexConversion)) {
+          (fir::isa_complex(ty) && !noComplexConversion) ||
+          (ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
         LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
         return false;
       }
     for (auto ty : func.getInputs())
       if (((ty.isa<fir::BoxCharType>() || fir::isCharacterProcedureTuple(ty)) &&
            !noCharacterConversion) ||
-          (fir::isa_complex(ty) && !noComplexConversion)) {
+          (fir::isa_complex(ty) && !noComplexConversion) ||
+          (ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
         LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
         return false;
       }
@@ -490,13 +494,14 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase<TargetRewrite> {
   /// the immediately subsequent target code gen.
   void convertSignature(mlir::func::FuncOp func) {
     auto funcTy = func.getFunctionType().cast<mlir::FunctionType>();
-    if (hasPortableSignature(funcTy) && !hasHostAssociations(func))
+    if (hasPortableSignature(funcTy, func) && !hasHostAssociations(func))
       return;
     llvm::SmallVector<mlir::Type> newResTys;
     llvm::SmallVector<mlir::Type> newInTys;
     llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> savedAttrs;
     llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> extraAttrs;
     llvm::SmallVector<FixupTy> fixups;
+    llvm::SmallVector<std::pair<unsigned, mlir::NamedAttrList>, 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<TargetRewrite> {
             else
               doComplexReturn(func, cmplx, newResTys, newInTys, fixups);
           })
+          .Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
+            auto m = specifics->integerArgumentType(func.getLoc(), intTy);
+            assert(m.size() == 1);
+            auto attr = std::get<fir::CodeGenSpecifics::Attributes>(m[0]);
+            auto retTy = std::get<mlir::Type>(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<mlir::UnitAttr>(
+                    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<TargetRewrite> {
               newInTys.push_back(ty);
             }
           })
+          .Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
+            auto m = specifics->integerArgumentType(func.getLoc(), intTy);
+            assert(m.size() == 1);
+            auto attr = std::get<fir::CodeGenSpecifics::Attributes>(m[0]);
+            auto argTy = std::get<mlir::Type>(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<mlir::UnitAttr>(
+                    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<mlir::UnitAttr>(index,
@@ -623,14 +664,18 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase<TargetRewrite> {
         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<fir::AllocaOp>(loc, newInTys[fixup.index]);
           rewriter->create<fir::StoreOp>(loc, newArg, mem);
-          auto oldArgTy =
-              fir::ReferenceType::get(oldArgTys[fixup.index - offset]);
           auto cast = rewriter->create<fir::ConvertOp>(loc, oldArgTy, mem);
           mlir::Value load = rewriter->create<fir::LoadOp>(loc, cast);
           func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
@@ -759,6 +804,10 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase<TargetRewrite> {
       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.logical<4>> {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<!fir.char<1,11>>
+  %1 = fir.convert %0 : (!fir.ref<!fir.char<1,11>>) -> !fir.ref<i8>
+  %2 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %1, %c3_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
+  %3 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
+  %4 = fir.convert %3 : (!fir.logical<4>) -> i1
+  %5 = fir.call @_FortranAioOutputLogical(%2, %4) : (!fir.ref<i8>, i1) -> i1
+  %6 = fir.call @_FortranAioEndIoStatement(%2) : (!fir.ref<i8>) -> i32
+  return
+}
+func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref<i8>, i32) -> !fir.ref<i8> 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<i8>, i1) -> i1 attributes {fir.io, fir.runtime}
+func.func private @_FortranAioEndIoStatement(!fir.ref<i8>) -> 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.logical<4>> {fir.bindc_name = "x"}) {
+  %0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
+  %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.logical<4>> {fir.bindc_name = "x"}) {
+  %0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
+  %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}


        


More information about the flang-commits mailing list