From 4c263ede5471a7fe3b06e8208b3f1aba1b9dcef2 Mon Sep 17 00:00:00 2001 From: Diana Picus Date: Fri, 29 Oct 2021 07:11:12 +0000 Subject: [PATCH] [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 Co-authored-by: Jean Perier Co-authored-by: Kiran Chandramohan Co-authored-by: Tim Keith --- flang/include/flang/Optimizer/CodeGen/CGPasses.td | 17 + flang/include/flang/Optimizer/CodeGen/CodeGen.h | 10 + flang/lib/Optimizer/CodeGen/CMakeLists.txt | 2 + flang/lib/Optimizer/CodeGen/Target.cpp | 143 +++++++++ flang/lib/Optimizer/CodeGen/Target.h | 79 +++++ flang/lib/Optimizer/CodeGen/TargetRewrite.cpp | 367 ++++++++++++++++++++++ flang/test/Fir/target-rewrite-triple.fir | 12 + flang/test/Fir/target.fir | 95 ++++++ 8 files changed, 725 insertions(+) create mode 100644 flang/lib/Optimizer/CodeGen/Target.cpp create mode 100644 flang/lib/Optimizer/CodeGen/Target.h create mode 100644 flang/lib/Optimizer/CodeGen/TargetRewrite.cpp create mode 100644 flang/test/Fir/target-rewrite-triple.fir create mode 100644 flang/test/Fir/target.fir diff --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td index dd89213..b2ac98c 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 differ 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 d863545..e693bf0 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 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> createFirTargetRewritePass( + const TargetRewriteOptions &options = TargetRewriteOptions()); + /// Convert FIR to the LLVM IR dialect std::unique_ptr createFIRToLLVMPass(); diff --git a/flang/lib/Optimizer/CodeGen/CMakeLists.txt b/flang/lib/Optimizer/CodeGen/CMakeLists.txt index d42b38c..b6a63c8 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 0000000..a03f282 --- /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 +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 { + using GenericTarget::GenericTarget; + + static constexpr int defaultWidth = 32; +}; +} // namespace + +//===----------------------------------------------------------------------===// +// x86_64 (x86 64 bit) linux target specifics. +//===----------------------------------------------------------------------===// + +namespace { +struct TargetX86_64 : public GenericTarget { + using GenericTarget::GenericTarget; + + static constexpr int defaultWidth = 64; +}; +} // namespace + +//===----------------------------------------------------------------------===// +// AArch64 linux target specifics. +//===----------------------------------------------------------------------===// + +namespace { +struct TargetAArch64 : public GenericTarget { + using GenericTarget::GenericTarget; + + static constexpr int defaultWidth = 64; +}; +} // namespace + +//===----------------------------------------------------------------------===// +// PPC64le linux target specifics. +//===----------------------------------------------------------------------===// + +namespace { +struct TargetPPC64le : public GenericTarget { + 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::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(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(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(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(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 0000000..b8dea1f --- /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 +#include +#include + +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>; + + static std::unique_ptr + 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` 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` 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 0000000..b0f3e4b --- /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 different 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 &&finalizer) + : code{code}, index{index}, finalizer{finalizer} {} + FixupTy(Codes code, std::size_t index, std::size_t second, + std::function &&finalizer) + : code{code}, index{index}, second{second}, finalizer{finalizer} {} + + Codes code; + std::size_t index; + std::size_t second{}; + llvm::Optional> 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 { +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(op)) { + if (!hasPortableSignature(call.getFunctionType())) + convertCallOp(call); + } else if (auto dispatch = dyn_cast(op)) { + if (!hasPortableSignature(dispatch.getFunctionType())) + convertCallOp(dispatch); + } + }); + + clearMembers(); + } + + mlir::ModuleOp getModule() { return getOperation(); } + + // Convert fir.call and fir.dispatch Ops. + template + void convertCallOp(A callOp) { + auto fnTy = callOp.getFunctionType(); + auto loc = callOp.getLoc(); + rewriter->setInsertionPoint(callOp); + llvm::SmallVector newResTys; + llvm::SmallVector newInTys; + llvm::SmallVector 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, 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> 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 trailingInTys; + llvm::SmallVector 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(ty) + .template Case([&](BoxCharType boxTy) { + bool sret; + if constexpr (std::is_same_v, fir::CallOp>) { + sret = callOp.callee() && + functionArgIsSRet(index, + getModule().lookupSymbol( + *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(loc, std::get(m[0]), + std::get(m[1]), oper); + // unboxed CHARACTER arguments + for (auto e : llvm::enumerate(m)) { + unsigned idx = e.index(); + auto attr = std::get(e.value()); + auto argTy = std::get(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, fir::CallOp>) { + fir::CallOp newCall; + if (callOp.callee().hasValue()) { + newCall = rewriter->create(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(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()) + 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()); + auto func = signature.dyn_cast(); + for (auto ty : func.getResults()) + if ((ty.isa() && !noCharacterConversion)) { + LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n"); + return false; + } + for (auto ty : func.getInputs()) + if ((ty.isa() && !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(); + if (hasPortableSignature(funcTy)) + return; + llvm::SmallVector newResTys; + llvm::SmallVector newInTys; + llvm::SmallVector fixups; + + // Convert return value(s) + for (auto ty : funcTy.getResults()) + newResTys.push_back(ty); + + // Convert arguments + llvm::SmallVector trailingTys; + for (auto e : llvm::enumerate(funcTy.getInputs())) { + auto ty = e.value(); + unsigned index = e.index(); + llvm::TypeSwitch(ty) + .Case([&](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(tup); + auto argTy = std::get(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 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( + 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(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(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> +fir::createFirTargetRewritePass(const TargetRewriteOptions &options) { + return std::make_unique(options); +} diff --git a/flang/test/Fir/target-rewrite-triple.fir b/flang/test/Fir/target-rewrite-triple.fir new file mode 100644 index 0000000..ccaf4fa --- /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 0000000..e2fb31f --- /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>, [[ARG1:%[0-9A-Za-z]+]]: !fir.ref>, [[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>, [[ARG1:%[0-9A-Za-z]+]]: !fir.ref>, [[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>, i32) -> !fir.boxchar<1> + // INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG2]] : (!fir.ref>, i32) -> !fir.boxchar<1> + // INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref>, i64) + // INT32-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref>, i64) + // INT64-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG1]], [[ARG3]] : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT64-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG2]] : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT64-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref>, i64) + // INT64-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref>, i64) + %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>, i64) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>, 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> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i32, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref>, [[ARG3:%[0-9A-Za-z]+]]: i32) +// INT64-LABEL: @boxcharsret +// INT64-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref> {llvm.sret}, [[ARG1:%[0-9A-Za-z]+]]: i64, [[ARG2:%[0-9A-Za-z]+]]: !fir.ref>, [[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>, i32) -> !fir.boxchar<1> + // INT32-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref>, i32) -> !fir.boxchar<1> + // INT32-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + // INT32-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + // INT64-DAG: [[B0:%[0-9]+]] = fir.emboxchar [[ARG0]], [[ARG1]] : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT64-DAG: [[B1:%[0-9]+]] = fir.emboxchar [[ARG2]], [[ARG3]] : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT64-DAG: fir.unboxchar [[B0]] : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + // INT64-DAG: fir.unboxchar [[B1]] : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>>, 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>>, index) -> !fir.ref> + %out_pos = fir.coordinate_of %1#0, %i : (!fir.ref>>, index) -> !fir.ref> + %ch = fir.load %in_pos : !fir.ref> + fir.store %ch to %out_pos : !fir.ref> + } + 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> {llvm.sret}, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: !fir.ref>, {{%[0-9A-Za-z]+}}: !fir.ref>, {{%[0-9A-Za-z]+}}: i32, {{%[0-9A-Za-z]+}}: i32) +// INT64-LABEL: @boxcharmultiple +// INT64-SAME: ({{%[0-9A-Za-z]+}}: !fir.ref> {llvm.sret}, {{%[0-9A-Za-z]+}}: i64, {{%[0-9A-Za-z]+}}: !fir.ref>, {{%[0-9A-Za-z]+}}: !fir.ref>, {{%[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>, i32) +// INT64-LABEL: @boxcharcallee(!fir.ref>, i64) +func private @boxcharcallee(%x : !fir.boxchar<1>) + +// INT32: @boxchararg +// INT64: @boxchararg +func @boxchararg() { + %1 = fir.address_of (@name) : !fir.ref> + %2 = arith.constant 9 : i64 + %3 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + // INT32: [[B:%[0-9A-Za-z]+]] = fir.emboxchar {{.*}} : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT32: [[U:%[0-9A-Za-z]+]]:2 = fir.unboxchar [[B]] : (!fir.boxchar<1>) -> + // (!fir.ref>, i32) + // INT64: [[B:%[0-9A-Za-z]+]] = fir.emboxchar {{.*}} : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT64: [[U:%[0-9A-Za-z]+]]:2 = fir.unboxchar [[B]] : (!fir.boxchar<1>) -> + // (!fir.ref>, i64) + %4 = fir.emboxchar %3, %2 : (!fir.ref>, i64) -> !fir.boxchar<1> + // INT32: fir.call @boxcharcallee([[U]]#0, [[U]]#1) : (!fir.ref>, i32) -> () + // INT64: fir.call @boxcharcallee([[U]]#0, [[U]]#1) : (!fir.ref>, 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> +} -- 2.7.4