[flang-commits] [flang] 4c263ed - [flang] Add TargetRewrite pass

Diana Picus via flang-commits flang-commits at lists.llvm.org
Tue Nov 9 00:05:12 PST 2021


Author: Diana Picus
Date: 2021-11-09T07:57:31Z
New Revision: 4c263ede5471a7fe3b06e8208b3f1aba1b9dcef2

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

LOG: [flang] Add TargetRewrite pass

This patch adds the basic infrastructure for the TargetRewrite pass,
which rewrites certain FIR dialect operations into target specific
forms. In particular, it converts boxchar function parameters, call
arguments and return values. Other convertions will be included in
future patches.

This patch is part of the effort for upstreaming the fir-dev branch.

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

Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Co-authored-by: Kiran Chandramohan <kiran.chandramohan at arm.com>
Co-authored-by: Tim Keith <tkeith at nvidia.com>

Added: 
    flang/lib/Optimizer/CodeGen/Target.cpp
    flang/lib/Optimizer/CodeGen/Target.h
    flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
    flang/test/Fir/target-rewrite-triple.fir
    flang/test/Fir/target.fir

Modified: 
    flang/include/flang/Optimizer/CodeGen/CGPasses.td
    flang/include/flang/Optimizer/CodeGen/CodeGen.h
    flang/lib/Optimizer/CodeGen/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
index dd8921319d43b..b2ac98ccc6267 100644
--- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td
+++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
@@ -41,4 +41,21 @@ def CodeGenRewrite : Pass<"cg-rewrite"> {
   ];
 }
 
+def TargetRewrite : Pass<"target-rewrite", "mlir::ModuleOp"> {
+  let summary = "Rewrite some FIR dialect into target specific forms.";
+  let description = [{
+      Certain abstractions in the FIR dialect need to be rewritten to reflect
+      representations that may 
diff er based on the target machine.
+  }];
+  let constructor = "::fir::createFirTargetRewritePass()";
+  let dependentDialects = [ "fir::FIROpsDialect" ];
+  let options = [
+    Option<"forcedTargetTriple", "target", "std::string", /*default=*/"",
+           "Override module's target triple.">,
+    Option<"noCharacterConversion", "no-character-conversion",
+           "bool", /*default=*/"false",
+           "Disable target-specific conversion of CHARACTER.">
+  ];
+}
+
 #endif // FORTRAN_OPTIMIZER_CODEGEN_FIR_PASSES

diff  --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index d863545882838..e693bf062edb5 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -22,6 +22,16 @@ struct NameUniquer;
 /// the code gen (to LLVM-IR dialect) conversion.
 std::unique_ptr<mlir::Pass> createFirCodeGenRewritePass();
 
+// FirTargetRewritePass options.
+struct TargetRewriteOptions {
+  bool noCharacterConversion{};
+};
+
+/// Prerequiste pass for code gen. Perform intermediate rewrites to tailor the
+/// FIR for the chosen target.
+std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>> createFirTargetRewritePass(
+    const TargetRewriteOptions &options = TargetRewriteOptions());
+
 /// Convert FIR to the LLVM IR dialect
 std::unique_ptr<mlir::Pass> createFIRToLLVMPass();
 

diff  --git a/flang/lib/Optimizer/CodeGen/CMakeLists.txt b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
index d42b38c8a61fa..b6a63c8ee8b8b 100644
--- a/flang/lib/Optimizer/CodeGen/CMakeLists.txt
+++ b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
@@ -2,6 +2,8 @@ add_flang_library(FIRCodeGen
   CGOps.cpp
   CodeGen.cpp
   PreCGRewrite.cpp
+  Target.cpp
+  TargetRewrite.cpp
 
   DEPENDS
   FIRDialect

diff  --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
new file mode 100644
index 0000000000000..a03f2829bb286
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -0,0 +1,143 @@
+//===-- Target.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "Target.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Support/KindMapping.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/TypeRange.h"
+
+#define DEBUG_TYPE "flang-codegen-target"
+
+using namespace fir;
+
+namespace {
+template <typename S>
+struct GenericTarget : public CodeGenSpecifics {
+  using CodeGenSpecifics::CodeGenSpecifics;
+  using AT = CodeGenSpecifics::Attributes;
+
+  Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
+    CodeGenSpecifics::Marshalling marshal;
+    auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
+    auto ptrTy = fir::ReferenceType::get(eleTy);
+    marshal.emplace_back(ptrTy, AT{});
+    // Return value arguments are grouped as a pair. Others are passed in a
+    // 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});
+    return marshal;
+  }
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// i386 (x86 32 bit) linux target specifics.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct TargetI386 : public GenericTarget<TargetI386> {
+  using GenericTarget::GenericTarget;
+
+  static constexpr int defaultWidth = 32;
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// x86_64 (x86 64 bit) linux target specifics.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct TargetX86_64 : public GenericTarget<TargetX86_64> {
+  using GenericTarget::GenericTarget;
+
+  static constexpr int defaultWidth = 64;
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// AArch64 linux target specifics.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct TargetAArch64 : public GenericTarget<TargetAArch64> {
+  using GenericTarget::GenericTarget;
+
+  static constexpr int defaultWidth = 64;
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// PPC64le linux target specifics.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
+  using GenericTarget::GenericTarget;
+
+  static constexpr int defaultWidth = 64;
+};
+} // namespace
+
+// Instantiate the overloaded target instance based on the triple value.
+// Currently, the implementation only instantiates `i386-unknown-linux-gnu`,
+// `x86_64-unknown-linux-gnu`, aarch64 and ppc64le like triples. Other targets
+// should be added to this file as needed.
+std::unique_ptr<fir::CodeGenSpecifics>
+fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
+                           KindMapping &&kindMap) {
+  switch (trp.getArch()) {
+  default:
+    break;
+  case llvm::Triple::ArchType::x86:
+    switch (trp.getOS()) {
+    default:
+      break;
+    case llvm::Triple::OSType::Linux:
+    case llvm::Triple::OSType::Darwin:
+      return std::make_unique<TargetI386>(ctx, std::move(trp),
+                                          std::move(kindMap));
+    }
+    break;
+  case llvm::Triple::ArchType::x86_64:
+    switch (trp.getOS()) {
+    default:
+      break;
+    case llvm::Triple::OSType::Linux:
+    case llvm::Triple::OSType::Darwin:
+      return std::make_unique<TargetX86_64>(ctx, std::move(trp),
+                                            std::move(kindMap));
+    }
+    break;
+  case llvm::Triple::ArchType::aarch64:
+    switch (trp.getOS()) {
+    default:
+      break;
+    case llvm::Triple::OSType::Linux:
+    case llvm::Triple::OSType::Darwin:
+      return std::make_unique<TargetAArch64>(ctx, std::move(trp),
+                                             std::move(kindMap));
+    }
+    break;
+  case llvm::Triple::ArchType::ppc64le:
+    switch (trp.getOS()) {
+    default:
+      break;
+    case llvm::Triple::OSType::Linux:
+      return std::make_unique<TargetPPC64le>(ctx, std::move(trp),
+                                             std::move(kindMap));
+    }
+    break;
+  }
+  llvm::report_fatal_error("target not implemented");
+}

diff  --git a/flang/lib/Optimizer/CodeGen/Target.h b/flang/lib/Optimizer/CodeGen/Target.h
new file mode 100644
index 0000000000000..b8dea1f837a92
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/Target.h
@@ -0,0 +1,79 @@
+//===- Target.h - target specific details -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTMIZER_CODEGEN_TARGET_H
+#define FORTRAN_OPTMIZER_CODEGEN_TARGET_H
+
+#include "flang/Optimizer/Support/KindMapping.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "llvm/ADT/Triple.h"
+#include <memory>
+#include <tuple>
+#include <vector>
+
+namespace fir {
+
+namespace details {
+/// Extra information about how to marshal an argument or return value that
+/// modifies a signature per a particular ABI's calling convention.
+/// Note: llvm::Attribute is not used directly, because its use depends on an
+/// LLVMContext.
+class Attributes {
+public:
+  Attributes(bool append = false) : append{append} {}
+
+  bool isAppend() const { return append; }
+
+private:
+  bool append : 1;
+};
+
+} // namespace details
+
+/// Some details of how to represent certain features depend on the target and
+/// ABI that is being used.  These specifics are captured here and guide the
+/// lowering of FIR to LLVM-IR dialect.
+class CodeGenSpecifics {
+public:
+  using Attributes = details::Attributes;
+  using Marshalling = std::vector<std::tuple<mlir::Type, Attributes>>;
+
+  static std::unique_ptr<CodeGenSpecifics>
+  get(mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap);
+
+  CodeGenSpecifics(mlir::MLIRContext *ctx, llvm::Triple &&trp,
+                   KindMapping &&kindMap)
+      : context{*ctx}, triple{std::move(trp)}, kindMap{std::move(kindMap)} {}
+  CodeGenSpecifics() = delete;
+  virtual ~CodeGenSpecifics() {}
+
+  /// 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.
+  ///
+  /// A function that returns a `boxchar<n>` type value must already have
+  /// converted that return value to a parameter decorated with the 'sret'
+  /// Attribute (https://llvm.org/docs/LangRef.html#parameter-attributes).
+  /// This requirement is in keeping with Fortran semantics, which require the
+  /// caller to allocate the space for the return CHARACTER value and pass
+  /// a pointer and the length of that space (a boxchar) to the called function.
+  virtual Marshalling boxcharArgumentType(mlir::Type eleTy,
+                                          bool sret = false) const = 0;
+
+protected:
+  mlir::MLIRContext &context;
+  llvm::Triple triple;
+  KindMapping kindMap;
+};
+
+} // namespace fir
+
+#endif // FORTRAN_OPTMIZER_CODEGEN_TARGET_H

diff  --git a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
new file mode 100644
index 0000000000000..b0f3e4bae67a8
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
@@ -0,0 +1,367 @@
+//===-- TargetRewrite.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Target rewrite: rewriting of ops to make target-specific lowerings manifest.
+// LLVM expects 
diff erent lowering idioms to be used for distinct target
+// triples. These distinctions are handled by this pass.
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassDetail.h"
+#include "Target.h"
+#include "flang/Lower/Todo.h"
+#include "flang/Optimizer/CodeGen/CodeGen.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Support/FIRContext.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Debug.h"
+
+using namespace fir;
+
+#define DEBUG_TYPE "flang-target-rewrite"
+
+namespace {
+
+/// Fixups for updating a FuncOp's arguments and return values.
+struct FixupTy {
+  enum class Codes { CharPair, Trailing };
+
+  FixupTy(Codes code, std::size_t index, std::size_t second = 0)
+      : code{code}, index{index}, second{second} {}
+  FixupTy(Codes code, std::size_t index,
+          std::function<void(mlir::FuncOp)> &&finalizer)
+      : code{code}, index{index}, finalizer{finalizer} {}
+  FixupTy(Codes code, std::size_t index, std::size_t second,
+          std::function<void(mlir::FuncOp)> &&finalizer)
+      : code{code}, index{index}, second{second}, finalizer{finalizer} {}
+
+  Codes code;
+  std::size_t index;
+  std::size_t second{};
+  llvm::Optional<std::function<void(mlir::FuncOp)>> finalizer{};
+}; // namespace
+
+/// Target-specific rewriting of the FIR. This is a prerequisite pass to code
+/// generation that traverses the FIR and modifies types and operations to a
+/// form that is appropriate for the specific target. LLVM IR has specific
+/// idioms that are used for distinct target processor and ABI combinations.
+class TargetRewrite : public TargetRewriteBase<TargetRewrite> {
+public:
+  TargetRewrite(const TargetRewriteOptions &options) {
+    noCharacterConversion = options.noCharacterConversion;
+  }
+
+  void runOnOperation() override final {
+    auto &context = getContext();
+    mlir::OpBuilder rewriter(&context);
+
+    auto mod = getModule();
+    if (!forcedTargetTriple.empty()) {
+      setTargetTriple(mod, forcedTargetTriple);
+    }
+
+    auto specifics = CodeGenSpecifics::get(getOperation().getContext(),
+                                           getTargetTriple(getOperation()),
+                                           getKindMapping(getOperation()));
+    setMembers(specifics.get(), &rewriter);
+
+    // Perform type conversion on signatures and call sites.
+    if (mlir::failed(convertTypes(mod))) {
+      mlir::emitError(mlir::UnknownLoc::get(&context),
+                      "error in converting types to target abi");
+      signalPassFailure();
+    }
+
+    // Convert ops in target-specific patterns.
+    mod.walk([&](mlir::Operation *op) {
+      if (auto call = dyn_cast<fir::CallOp>(op)) {
+        if (!hasPortableSignature(call.getFunctionType()))
+          convertCallOp(call);
+      } else if (auto dispatch = dyn_cast<DispatchOp>(op)) {
+        if (!hasPortableSignature(dispatch.getFunctionType()))
+          convertCallOp(dispatch);
+      }
+    });
+
+    clearMembers();
+  }
+
+  mlir::ModuleOp getModule() { return getOperation(); }
+
+  // Convert fir.call and fir.dispatch Ops.
+  template <typename A>
+  void convertCallOp(A callOp) {
+    auto fnTy = callOp.getFunctionType();
+    auto loc = callOp.getLoc();
+    rewriter->setInsertionPoint(callOp);
+    llvm::SmallVector<mlir::Type> newResTys;
+    llvm::SmallVector<mlir::Type> newInTys;
+    llvm::SmallVector<mlir::Value> newOpers;
+
+    // If the call is indirect, the first argument must still be the function
+    // to call.
+    int dropFront = 0;
+    if constexpr (std::is_same_v<std::decay_t<A>, fir::CallOp>) {
+      if (!callOp.callee().hasValue()) {
+        newInTys.push_back(fnTy.getInput(0));
+        newOpers.push_back(callOp.getOperand(0));
+        dropFront = 1;
+      }
+    }
+
+    // Determine the rewrite function, `wrap`, for the result value.
+    llvm::Optional<std::function<mlir::Value(mlir::Operation *)>> wrap;
+    if (fnTy.getResults().size() == 1) {
+      mlir::Type ty = fnTy.getResult(0);
+      newResTys.push_back(ty);
+    } else if (fnTy.getResults().size() > 1) {
+      TODO(loc, "multiple results not supported yet");
+    }
+
+    llvm::SmallVector<mlir::Type> trailingInTys;
+    llvm::SmallVector<mlir::Value> trailingOpers;
+    for (auto e : llvm::enumerate(
+             llvm::zip(fnTy.getInputs().drop_front(dropFront),
+                       callOp.getOperands().drop_front(dropFront)))) {
+      mlir::Type ty = std::get<0>(e.value());
+      mlir::Value oper = std::get<1>(e.value());
+      unsigned index = e.index();
+      llvm::TypeSwitch<mlir::Type>(ty)
+          .template Case<BoxCharType>([&](BoxCharType boxTy) {
+            bool sret;
+            if constexpr (std::is_same_v<std::decay_t<A>, fir::CallOp>) {
+              sret = callOp.callee() &&
+                     functionArgIsSRet(index,
+                                       getModule().lookupSymbol<mlir::FuncOp>(
+                                           *callOp.callee()));
+            } else {
+              // TODO: dispatch case; how do we put arguments on a call?
+              // We cannot put both an sret and the dispatch object first.
+              sret = false;
+              TODO(loc, "dispatch + sret not supported yet");
+            }
+            auto m = specifics->boxcharArgumentType(boxTy.getEleTy(), sret);
+            auto unbox =
+                rewriter->create<UnboxCharOp>(loc, std::get<mlir::Type>(m[0]),
+                                              std::get<mlir::Type>(m[1]), oper);
+            // unboxed CHARACTER arguments
+            for (auto e : llvm::enumerate(m)) {
+              unsigned idx = e.index();
+              auto attr = std::get<CodeGenSpecifics::Attributes>(e.value());
+              auto argTy = std::get<mlir::Type>(e.value());
+              if (attr.isAppend()) {
+                trailingInTys.push_back(argTy);
+                trailingOpers.push_back(unbox.getResult(idx));
+              } else {
+                newInTys.push_back(argTy);
+                newOpers.push_back(unbox.getResult(idx));
+              }
+            }
+          })
+          .Default([&](mlir::Type ty) {
+            newInTys.push_back(ty);
+            newOpers.push_back(oper);
+          });
+    }
+    newInTys.insert(newInTys.end(), trailingInTys.begin(), trailingInTys.end());
+    newOpers.insert(newOpers.end(), trailingOpers.begin(), trailingOpers.end());
+    if constexpr (std::is_same_v<std::decay_t<A>, fir::CallOp>) {
+      fir::CallOp newCall;
+      if (callOp.callee().hasValue()) {
+        newCall = rewriter->create<A>(loc, callOp.callee().getValue(),
+                                      newResTys, newOpers);
+      } else {
+        // Force new type on the input operand.
+        newOpers[0].setType(mlir::FunctionType::get(
+            callOp.getContext(),
+            mlir::TypeRange{newInTys}.drop_front(dropFront), newResTys));
+        newCall = rewriter->create<A>(loc, newResTys, newOpers);
+      }
+      LLVM_DEBUG(llvm::dbgs() << "replacing call with " << newCall << '\n');
+      if (wrap.hasValue())
+        replaceOp(callOp, (*wrap)(newCall.getOperation()));
+      else
+        replaceOp(callOp, newCall.getResults());
+    } else {
+      // A is fir::DispatchOp
+      TODO(loc, "dispatch not implemented");
+    }
+  }
+  /// 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.
+  mlir::LogicalResult convertTypes(mlir::ModuleOp mod) {
+    for (auto fn : mod.getOps<mlir::FuncOp>())
+      convertSignature(fn);
+    return mlir::success();
+  }
+
+  /// If the signature does not need any special target-specific converions,
+  /// 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) {
+    assert(signature.isa<mlir::FunctionType>());
+    auto func = signature.dyn_cast<mlir::FunctionType>();
+    for (auto ty : func.getResults())
+      if ((ty.isa<BoxCharType>() && !noCharacterConversion)) {
+        LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
+        return false;
+      }
+    for (auto ty : func.getInputs())
+      if ((ty.isa<BoxCharType>() && !noCharacterConversion)) {
+        LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
+        return false;
+      }
+    return true;
+  }
+
+  /// Rewrite the signatures and body of the `FuncOp`s in the module for
+  /// the immediately subsequent target code gen.
+  void convertSignature(mlir::FuncOp func) {
+    auto funcTy = func.getType().cast<mlir::FunctionType>();
+    if (hasPortableSignature(funcTy))
+      return;
+    llvm::SmallVector<mlir::Type> newResTys;
+    llvm::SmallVector<mlir::Type> newInTys;
+    llvm::SmallVector<FixupTy> fixups;
+
+    // Convert return value(s)
+    for (auto ty : funcTy.getResults())
+      newResTys.push_back(ty);
+
+    // Convert arguments
+    llvm::SmallVector<mlir::Type> trailingTys;
+    for (auto e : llvm::enumerate(funcTy.getInputs())) {
+      auto ty = e.value();
+      unsigned index = e.index();
+      llvm::TypeSwitch<mlir::Type>(ty)
+          .Case<BoxCharType>([&](BoxCharType boxTy) {
+            if (noCharacterConversion) {
+              newInTys.push_back(boxTy);
+            } else {
+              // Convert a CHARACTER argument type. This can involve separating
+              // the pointer and the LEN into two arguments and moving the LEN
+              // argument to the end of the arg list.
+              bool sret = functionArgIsSRet(index, func);
+              for (auto e : llvm::enumerate(specifics->boxcharArgumentType(
+                       boxTy.getEleTy(), sret))) {
+                auto &tup = e.value();
+                auto index = e.index();
+                auto attr = std::get<CodeGenSpecifics::Attributes>(tup);
+                auto argTy = std::get<mlir::Type>(tup);
+                if (attr.isAppend()) {
+                  trailingTys.push_back(argTy);
+                } else {
+                  if (sret) {
+                    fixups.emplace_back(FixupTy::Codes::CharPair,
+                                        newInTys.size(), index);
+                  } else {
+                    fixups.emplace_back(FixupTy::Codes::Trailing,
+                                        newInTys.size(), trailingTys.size());
+                  }
+                  newInTys.push_back(argTy);
+                }
+              }
+            }
+          })
+          .Default([&](mlir::Type ty) { newInTys.push_back(ty); });
+    }
+
+    if (!func.empty()) {
+      // If the function has a body, then apply the fixups to the arguments and
+      // return ops as required. These fixups are done in place.
+      auto loc = func.getLoc();
+      const auto fixupSize = fixups.size();
+      const auto oldArgTys = func.getType().getInputs();
+      int offset = 0;
+      for (std::remove_const_t<decltype(fixupSize)> i = 0; i < fixupSize; ++i) {
+        const auto &fixup = fixups[i];
+        switch (fixup.code) {
+        case FixupTy::Codes::CharPair: {
+          // The FIR boxchar argument has been split into a pair of distinct
+          // arguments that are in juxtaposition to each other.
+          auto newArg =
+              func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+          if (fixup.second == 1) {
+            rewriter->setInsertionPointToStart(&func.front());
+            auto boxTy = oldArgTys[fixup.index - offset - fixup.second];
+            auto box = rewriter->create<EmboxCharOp>(
+                loc, boxTy, func.front().getArgument(fixup.index - 1), newArg);
+            func.getArgument(fixup.index + 1).replaceAllUsesWith(box);
+            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
+          // position. The second part of the pair is appended after all the
+          // original arguments. (Boxchar arguments.)
+          auto newBufArg =
+              func.front().insertArgument(fixup.index, newInTys[fixup.index]);
+          auto newLenArg = func.front().addArgument(trailingTys[fixup.second]);
+          auto boxTy = oldArgTys[fixup.index - offset];
+          rewriter->setInsertionPointToStart(&func.front());
+          auto box =
+              rewriter->create<EmboxCharOp>(loc, boxTy, newBufArg, newLenArg);
+          func.getArgument(fixup.index + 1).replaceAllUsesWith(box);
+          func.front().eraseArgument(fixup.index + 1);
+        } break;
+        }
+      }
+    }
+
+    // Set the new type and finalize the arguments, etc.
+    newInTys.insert(newInTys.end(), trailingTys.begin(), trailingTys.end());
+    auto newFuncTy =
+        mlir::FunctionType::get(func.getContext(), newInTys, newResTys);
+    LLVM_DEBUG(llvm::dbgs() << "new func: " << newFuncTy << '\n');
+    func.setType(newFuncTy);
+
+    for (auto &fixup : fixups)
+      if (fixup.finalizer)
+        (*fixup.finalizer)(func);
+  }
+
+  inline bool functionArgIsSRet(unsigned index, mlir::FuncOp func) {
+    if (auto attr = func.getArgAttrOfType<mlir::UnitAttr>(index, "llvm.sret"))
+      return true;
+    return false;
+  }
+
+private:
+  // Replace `op` and remove it.
+  void replaceOp(mlir::Operation *op, mlir::ValueRange newValues) {
+    op->replaceAllUsesWith(newValues);
+    op->dropAllReferences();
+    op->erase();
+  }
+
+  inline void setMembers(CodeGenSpecifics *s, mlir::OpBuilder *r) {
+    specifics = s;
+    rewriter = r;
+  }
+
+  inline void clearMembers() { setMembers(nullptr, nullptr); }
+
+  CodeGenSpecifics *specifics{};
+  mlir::OpBuilder *rewriter;
+}; // namespace
+} // namespace
+
+std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
+fir::createFirTargetRewritePass(const TargetRewriteOptions &options) {
+  return std::make_unique<TargetRewrite>(options);
+}

diff  --git a/flang/test/Fir/target-rewrite-triple.fir b/flang/test/Fir/target-rewrite-triple.fir
new file mode 100644
index 0000000000000..ccaf4fa68b5b5
--- /dev/null
+++ b/flang/test/Fir/target-rewrite-triple.fir
@@ -0,0 +1,12 @@
+// RUN: fir-opt --target-rewrite %s | FileCheck %s --check-prefix=UNCHANGED
+// RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=CHANGED
+
+// UNCHANGED: fir.triple = "aarch64-unknown-linux-gnu"
+// CHANGED: fir.triple = "x86_64-unknown-linux-gnu"
+// CHANGED-NOT: fir.triple = "aarch64-unknown-linux-gnu"
+module attributes {fir.triple = "aarch64-unknown-linux-gnu"}  {
+  func @dummyfunc() -> () {
+    return
+  }
+}
+

diff  --git a/flang/test/Fir/target.fir b/flang/test/Fir/target.fir
new file mode 100644
index 0000000000000..e2fb31ffeceac
--- /dev/null
+++ b/flang/test/Fir/target.fir
@@ -0,0 +1,95 @@
+// RUN: fir-opt --target-rewrite="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT32
+// RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT64
+// RUN: fir-opt --target-rewrite="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT64
+// RUN: fir-opt --target-rewrite="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT64
+
+// Test that we rewrite the signatures and bodies of functions that take boxchar
+// parameters.
+// INT32-LABEL: @boxcharparams
+// INT32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG1:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG2:%[0-9A-Za-z]+]]: i32, [[ARG3:%[0-9A-Za-z]+]]: i32) -> i64
+// INT64-LABEL: @boxcharparams
+// INT64-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG1:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG2:%[0-9A-Za-z]+]]: i64, [[ARG3:%[0-9A-Za-z]+]]: i64) -> i64
+func @boxcharparams(%arg0 : !fir.boxchar<1>, %arg1 : !fir.boxchar<1>) -> i64 {
+  // INT32-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG1]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
+  // INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG2]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
+  // INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  // INT32-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  // INT64-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG1]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT64-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG2]] : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT64-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  // INT64-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
+  %3 = arith.addi %1#1, %2#1 : i64
+  return %3 : i64
+}
+
+// Test that we rewrite the signatures and bodies of functions that return a
+// boxchar.
+// INT32-LABEL: @boxcharsret
+// INT32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i32, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i32)
+// INT64-LABEL: @boxcharsret
+// INT64-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i64, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref<!fir.char<1,?>>, [[ARG3:%[0-9A-Za-z]+]]: i64)
+func @boxcharsret(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>) {
+  // INT32-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG1]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
+  // INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i32) -> !fir.boxchar<1>
+  // INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  // INT32-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  // INT64-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG1]] : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT64-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT64-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  // INT64-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.array<?x!fir.char<1>>>, i64)
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %3 = fir.convert %1#1 : (i64) -> index
+  %last = arith.subi %3, %c1 : index
+  fir.do_loop %i = %c0 to %last step %c1 {
+    %in_pos = fir.coordinate_of %2#0, %i : (!fir.ref<!fir.array<?x!fir.char<1>>>, index) -> !fir.ref<!fir.char<1>>
+    %out_pos = fir.coordinate_of %1#0, %i : (!fir.ref<!fir.array<?x!fir.char<1>>>, index) -> !fir.ref<!fir.char<1>>
+    %ch = fir.load %in_pos : !fir.ref<!fir.char<1>>
+    fir.store %ch to %out_pos : !fir.ref<!fir.char<1>>
+  }
+  return
+}
+
+// Test that we rewrite the signatures of functions with a sret parameter and
+// several other parameters.
+// INT32-LABEL: @boxcharmultiple
+// INT32-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret}, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: i32)
+// INT64-LABEL: @boxcharmultiple
+// INT64-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>> {llvm.sret}, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: !fir.ref<!fir.char<1,?>>, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: i64)
+func @boxcharmultiple(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>, %arg2 : !fir.boxchar<1>) {
+  return
+}
+
+// Test that we rewrite calls to functions that take boxchar arguments.
+// INT32-LABEL: @boxcharcallee(!fir.ref<!fir.char<1,?>>, i32)
+// INT64-LABEL: @boxcharcallee(!fir.ref<!fir.char<1,?>>, i64)
+func private @boxcharcallee(%x : !fir.boxchar<1>)
+
+// INT32: @boxchararg
+// INT64: @boxchararg
+func @boxchararg() {
+  %1 = fir.address_of (@name) : !fir.ref<!fir.char<1,9>>
+  %2 = arith.constant 9 : i64
+  %3 = fir.convert %1 : (!fir.ref<!fir.char<1,9>>) -> !fir.ref<!fir.char<1,?>>
+  // INT32: [[B:%[0-9A-Za-z]+]] = fir.emboxchar {{.*}} : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT32: [[U:%[0-9A-Za-z]+]]:2 = fir.unboxchar [[B]] : (!fir.boxchar<1>) ->
+  // (!fir.ref<!fir.char<1, ?>>, i32)
+  // INT64: [[B:%[0-9A-Za-z]+]] = fir.emboxchar {{.*}} : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT64: [[U:%[0-9A-Za-z]+]]:2 = fir.unboxchar [[B]] : (!fir.boxchar<1>) ->
+  // (!fir.ref<!fir.char<1, ?>>, i64)
+  %4 = fir.emboxchar %3, %2 : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
+  // INT32: fir.call @boxcharcallee([[U]]#0, [[U]]#1) : (!fir.ref<!fir.char<1,?>>, i32) -> ()
+  // INT64: fir.call @boxcharcallee([[U]]#0, [[U]]#1) : (!fir.ref<!fir.char<1,?>>, i64) -> ()
+  fir.call @boxcharcallee(%4) : (!fir.boxchar<1>) -> ()
+  return
+}
+
+fir.global @name constant : !fir.char<1,9> {
+  %str = fir.string_lit "Your name"(9) : !fir.char<1,9>
+  //constant 1
+  fir.has_value %str : !fir.char<1,9>
+}


        


More information about the flang-commits mailing list