[flang-commits] [flang] [flang] Updating drivers to create data layout before semantics (PR #73301)

via flang-commits flang-commits at lists.llvm.org
Mon Nov 27 00:47:58 PST 2023


https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/73301

>From 70522c592e82cc4ac88a2ef61d3568041a300508 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Thu, 23 Nov 2023 08:34:49 -0800
Subject: [PATCH 1/3] [flang] Updating drivers to create data layout before
 semantics

Preliminary patch to change lowering/code generation to use
llvm::DataLayout information instead of generating "sizeof" GEP (see
https://github.com/llvm/llvm-project/issues/71507).

Fortran Semantic analysis needs to know about the target type size and
alignment to deal with common blocks, and intrinsics like
C_SIZEOF/TRANSFER. This information should be obtained from the
llvm::DataLayout so that it is consistent during the whole compilation
flow.

This change is changing flang-new and bbc drivers to:
1. Create the llvm::TargetMachine so that the data layout of the target
   can be obtained before semantics.
2. Sharing bbc/flang-new set-up of the SemanticConstext.targetCharateristics
   from the llvm::TargetMachine. For now, the actual part that set-up
   the Fortran type size and alignment from the llvm::DataLayout is left
   TODO so that this change is mostly an NFC impacting the drivers.
3. Let the lowering bridge set-up the mlir::Module datalayout attributes
   since it is doing it for the target attribute, and that allows the
   llvm data layout information to be available during lowering.

As a consequence, LLVM targets must be registered when running
semantics, and it is not possible to run semantics for a target that
is not registered with the -triple option (hence the power pc specific
modules can only be built if the PowerPC target is available).
---
 .../flang/Frontend/CompilerInvocation.h       |  17 ++
 .../include/flang/Frontend/FrontendActions.h  |   4 -
 flang/include/flang/Lower/Bridge.h            |  13 +-
 .../flang/Optimizer/Support/DataLayout.h      |  39 ++++
 flang/include/flang/Tools/TargetSetup.h       |  63 ++++++
 flang/lib/Frontend/CompilerInstance.cpp       |   2 +
 flang/lib/Frontend/CompilerInvocation.cpp     | 146 ++++++++++++--
 flang/lib/Frontend/FrontendActions.cpp        | 180 ++----------------
 flang/lib/Lower/Bridge.cpp                    |   6 +-
 flang/lib/Optimizer/Support/CMakeLists.txt    |   2 +
 flang/lib/Optimizer/Support/DataLayout.cpp    |  47 +++++
 flang/test/Fir/tco-default-datalayout.fir     |  12 ++
 flang/test/Fir/tco-explicit-datalayout.fir    |  13 ++
 flang/test/Lower/bbc-host-datalayout.f90      |   9 +
 flang/test/Lower/bbc-target-datalayout.f90    |  10 +
 flang/test/Semantics/realkinds-aarch64-01.f90 |   1 +
 flang/tools/bbc/CMakeLists.txt                |   3 +
 flang/tools/bbc/bbc.cpp                       |  46 +++--
 flang/tools/f18/CMakeLists.txt                |  11 +-
 flang/tools/tco/tco.cpp                       |  14 +-
 .../unittests/Frontend/FrontendActionTest.cpp |  19 +-
 21 files changed, 441 insertions(+), 216 deletions(-)
 create mode 100644 flang/include/flang/Optimizer/Support/DataLayout.h
 create mode 100644 flang/include/flang/Tools/TargetSetup.h
 create mode 100644 flang/lib/Optimizer/Support/DataLayout.cpp
 create mode 100644 flang/test/Fir/tco-default-datalayout.fir
 create mode 100644 flang/test/Fir/tco-explicit-datalayout.fir
 create mode 100644 flang/test/Lower/bbc-host-datalayout.f90
 create mode 100644 flang/test/Lower/bbc-target-datalayout.f90

diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h
index 229aa75748f725d..acbf644af24cde4 100644
--- a/flang/include/flang/Frontend/CompilerInvocation.h
+++ b/flang/include/flang/Frontend/CompilerInvocation.h
@@ -24,6 +24,7 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "llvm/Option/ArgList.h"
+#include "llvm/Target/TargetMachine.h"
 #include <memory>
 
 namespace Fortran::frontend {
@@ -113,6 +114,8 @@ class CompilerInvocation : public CompilerInvocationBase {
   bool enableConformanceChecks = false;
   bool enableUsageChecks = false;
 
+  std::unique_ptr<llvm::TargetMachine> targetMachine;
+
   /// Used in e.g. unparsing to dump the analyzed rather than the original
   /// parse-tree objects.
   Fortran::parser::AnalyzedObjectsAsFortran asFortran{
@@ -206,6 +209,14 @@ class CompilerInvocation : public CompilerInvocationBase {
   const Fortran::common::IntrinsicTypeDefaultKinds &getDefaultKinds() const {
     return defaultKinds;
   }
+  const llvm::TargetMachine &getTargetMachine() const {
+    assert(targetMachine && "target machine was not set");
+    return *targetMachine;
+  }
+  llvm::TargetMachine &getTargetMachine() {
+    assert(targetMachine && "target machine was not set");
+    return *targetMachine;
+  }
 
   /// Create a compiler invocation from a list of input options.
   /// \returns true on success.
@@ -260,6 +271,12 @@ class CompilerInvocation : public CompilerInvocationBase {
   /// Set \p loweringOptions controlling lowering behavior based
   /// on the \p optimizationLevel.
   void setLoweringOptions();
+
+  /// Sets up LLVM's TargetMachine.
+  bool setUpTargetMachine(clang::DiagnosticsEngine &diags);
+
+  /// Produces the string which represents target feature
+  std::string getTargetFeatures(clang::DiagnosticsEngine &diags);
 };
 
 } // end namespace Fortran::frontend
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index 8272e0729ce3fc6..e2e859f3a81bd7a 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -21,7 +21,6 @@
 #include "mlir/IR/BuiltinOps.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/Module.h"
-#include "llvm/Target/TargetMachine.h"
 #include <memory>
 
 namespace Fortran::frontend {
@@ -204,8 +203,6 @@ class CodeGenAction : public FrontendAction {
   void executeAction() override;
   /// Runs prescan, parsing, sema and lowers to MLIR.
   bool beginSourceFileAction() override;
-  /// Sets up LLVM's TargetMachine.
-  bool setUpTargetMachine();
   /// Runs the optimization (aka middle-end) pipeline on the LLVM module
   /// associated with this action.
   void runOptimizationPipeline(llvm::raw_pwrite_stream &os);
@@ -234,7 +231,6 @@ class CodeGenAction : public FrontendAction {
 
   BackendActionTy action;
 
-  std::unique_ptr<llvm::TargetMachine> tm;
   /// }
 public:
   ~CodeGenAction() override;
diff --git a/flang/include/flang/Lower/Bridge.h b/flang/include/flang/Lower/Bridge.h
index d363068acfdbb9d..6c0d14d65edae1e 100644
--- a/flang/include/flang/Lower/Bridge.h
+++ b/flang/include/flang/Lower/Bridge.h
@@ -22,6 +22,10 @@
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
 #include "mlir/IR/BuiltinOps.h"
 
+namespace llvm {
+class DataLayout;
+} // namespace llvm
+
 namespace Fortran {
 namespace common {
 class IntrinsicTypeDefaultKinds;
@@ -59,10 +63,12 @@ class LoweringBridge {
          llvm::StringRef triple, fir::KindMapping &kindMap,
          const Fortran::lower::LoweringOptions &loweringOptions,
          const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
-         const Fortran::common::LanguageFeatureControl &languageFeatures) {
+         const Fortran::common::LanguageFeatureControl &languageFeatures,
+         const llvm::DataLayout *dataLayout = nullptr) {
     return LoweringBridge(ctx, semanticsContext, defaultKinds, intrinsics,
                           targetCharacteristics, allCooked, triple, kindMap,
-                          loweringOptions, envDefaults, languageFeatures);
+                          loweringOptions, envDefaults, languageFeatures,
+                          dataLayout);
   }
 
   //===--------------------------------------------------------------------===//
@@ -140,7 +146,8 @@ class LoweringBridge {
       fir::KindMapping &kindMap,
       const Fortran::lower::LoweringOptions &loweringOptions,
       const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
-      const Fortran::common::LanguageFeatureControl &languageFeatures);
+      const Fortran::common::LanguageFeatureControl &languageFeatures,
+      const llvm::DataLayout *dataLayout);
   LoweringBridge() = delete;
   LoweringBridge(const LoweringBridge &) = delete;
 
diff --git a/flang/include/flang/Optimizer/Support/DataLayout.h b/flang/include/flang/Optimizer/Support/DataLayout.h
new file mode 100644
index 000000000000000..fe683a72927df04
--- /dev/null
+++ b/flang/include/flang/Optimizer/Support/DataLayout.h
@@ -0,0 +1,39 @@
+//===-- Optimizer/Support/DataLayout.h --------------------------*- 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_OPTIMIZER_SUPPORT_DATALAYOUT_H
+#define FORTRAN_OPTIMIZER_SUPPORT_DATALAYOUT_H
+
+namespace mlir {
+class ModuleOp;
+}
+namespace llvm {
+class DataLayout;
+}
+
+namespace fir::support {
+/// Create an mlir::DataLayoutSpecInterface attribute from an llvm::DataLayout
+/// and set it on the provided mlir::ModuleOp.
+/// Also set the llvm.data_layout attribute with the string representation of
+/// the llvm::DataLayout on the module.
+/// These attributes are replaced if they were already set.
+void setMLIRDataLayout(mlir::ModuleOp mlirModule, const llvm::DataLayout &dl);
+
+/// Create an mlir::DataLayoutSpecInterface from the llvm>data_layout attribute
+/// if one is provided. If such attribute is not available, create a default
+/// target independent layout when allowDefaultLayout is true. Otherwise do
+/// nothing.
+void setMLIRDataLayoutFromAttributes(mlir::ModuleOp mlirModule,
+                                     bool allowDefaultLayout);
+} // namespace fir::support
+
+#endif // FORTRAN_OPTIMIZER_SUPPORT_DATALAYOUT_H
diff --git a/flang/include/flang/Tools/TargetSetup.h b/flang/include/flang/Tools/TargetSetup.h
new file mode 100644
index 000000000000000..0b09acefde98a3c
--- /dev/null
+++ b/flang/include/flang/Tools/TargetSetup.h
@@ -0,0 +1,63 @@
+//===-- Tools/TargetSetup.h ------------------------------------- *-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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_TOOLS_TARGET_SETUP_H
+#define FORTRAN_TOOLS_TARGET_SETUP_H
+
+#include "flang/Evaluate/target.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Host.h"
+#include <memory>
+
+namespace Fortran::tools {
+
+[[maybe_unused]] inline static void setUpTargetCharacteristics(
+    Fortran::evaluate::TargetCharacteristics &targetCharacteristics,
+    const llvm::TargetMachine &targetMachine,
+    const std::string &compilerVersion, const std::string &compilerOptions) {
+
+  const llvm::Triple &targetTriple{targetMachine.getTargetTriple()};
+  // FIXME: Handle real(3) ?
+  if (targetTriple.getArch() != llvm::Triple::ArchType::x86_64)
+    targetCharacteristics.DisableType(
+        Fortran::common::TypeCategory::Real, /*kind=*/10);
+
+  targetCharacteristics.set_compilerOptionsString(compilerOptions)
+      .set_compilerVersionString(compilerVersion);
+
+  if (targetTriple.isPPC())
+    targetCharacteristics.set_isPPC(true);
+
+  // TODO: use target machine data layout to set-up the target characteristics
+  // type size and alignment info.
+}
+
+/// Create a target machine that is at least sufficient to get data-layout
+/// information required by flang semantics and lowering. Note that it may not
+/// contain all the CPU feature information to get optimized assembly generation
+/// from LLVM IR. Drivers that needs to generate assembly from LLVM IR should
+/// create a target machine according to their specific options.
+[[maybe_unused]] inline static std::unique_ptr<llvm::TargetMachine>
+createTargetMachine(llvm::StringRef targetTriple, std::string &error) {
+  std::string triple{targetTriple};
+  if (triple.empty())
+    triple = llvm::sys::getDefaultTargetTriple();
+
+  const llvm::Target *theTarget =
+      llvm::TargetRegistry::lookupTarget(triple, error);
+  if (!theTarget)
+    return nullptr;
+  return std::unique_ptr<llvm::TargetMachine>{
+      theTarget->createTargetMachine(triple, /*CPU=*/"",
+          /*Features=*/"", llvm::TargetOptions(),
+          /*Reloc::Model=*/std::nullopt)};
+}
+} // namespace Fortran::tools
+
+#endif // FORTRAN_TOOLS_TARGET_SETUP_H
diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index 21ebf52f76410bd..1e3f5034a438a24 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -156,6 +156,8 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
   invoc.setFortranOpts();
   // Set the encoding to read all input files in based on user input.
   allSources->set_encoding(invoc.getFortranOpts().encoding);
+  if (!invoc.setUpTargetMachine(getDiagnostics()))
+    return false;
   // Create the semantics context and set semantic options.
   invoc.setSemanticsOpts(*this->allCookedSources);
   // Set options controlling lowering to FIR.
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index cb4f2d6a6225205..fe53f0f2116ad66 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -18,6 +18,7 @@
 #include "flang/Frontend/PreprocessorOptions.h"
 #include "flang/Frontend/TargetOptions.h"
 #include "flang/Semantics/semantics.h"
+#include "flang/Tools/TargetSetup.h"
 #include "flang/Version.inc"
 #include "clang/Basic/AllDiagnostics.h"
 #include "clang/Basic/DiagnosticDriver.h"
@@ -25,9 +26,11 @@
 #include "clang/Driver/DriverDiagnostic.h"
 #include "clang/Driver/OptionUtils.h"
 #include "clang/Driver/Options.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Frontend/Debug/Options.h"
+#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
@@ -37,6 +40,7 @@
 #include "llvm/Support/Process.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/TargetParser.h"
 #include "llvm/TargetParser/Triple.h"
 #include <cstdlib>
 #include <memory>
@@ -1347,20 +1351,10 @@ void CompilerInvocation::setSemanticsOpts(
       .set_moduleFileSuffix(getModuleFileSuffix())
       .set_underscoring(getCodeGenOpts().Underscoring);
 
-  llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)};
-  // FIXME: Handle real(3) ?
-  if (targetTriple.getArch() != llvm::Triple::ArchType::x86_64) {
-    semanticsContext->targetCharacteristics().DisableType(
-        Fortran::common::TypeCategory::Real, /*kind=*/10);
-  }
-
-  std::string version = Fortran::common::getFlangFullVersion();
-  semanticsContext->targetCharacteristics()
-      .set_compilerOptionsString(allCompilerInvocOpts)
-      .set_compilerVersionString(version);
-
-  if (targetTriple.isPPC())
-    semanticsContext->targetCharacteristics().set_isPPC(true);
+  std::string compilerVersion = Fortran::common::getFlangFullVersion();
+  Fortran::tools::setUpTargetCharacteristics(
+      semanticsContext->targetCharacteristics(), getTargetMachine(),
+      compilerVersion, allCompilerInvocOpts);
 }
 
 /// Set \p loweringOptions controlling lowering behavior based
@@ -1387,3 +1381,127 @@ void CompilerInvocation::setLoweringOptions() {
       .setAssociativeMath(langOptions.AssociativeMath)
       .setReciprocalMath(langOptions.ReciprocalMath);
 }
+
+// Get feature string which represents combined explicit target features
+// for AMD GPU and the target features specified by the user
+static std::string
+getExplicitAndImplicitAMDGPUTargetFeatures(clang::DiagnosticsEngine &diags,
+                                           const TargetOptions &targetOpts,
+                                           const llvm::Triple triple) {
+  llvm::StringRef cpu = targetOpts.cpu;
+  llvm::StringMap<bool> implicitFeaturesMap;
+  std::string errorMsg;
+  // Get the set of implicit target features
+  llvm::AMDGPU::fillAMDGPUFeatureMap(cpu, triple, implicitFeaturesMap);
+
+  // Add target features specified by the user
+  for (auto &userFeature : targetOpts.featuresAsWritten) {
+    std::string userKeyString = userFeature.substr(1);
+    implicitFeaturesMap[userKeyString] = (userFeature[0] == '+');
+  }
+
+  if (!llvm::AMDGPU::insertWaveSizeFeature(cpu, triple, implicitFeaturesMap,
+                                           errorMsg)) {
+    unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                            "Unsupported feature ID: %0");
+    diags.Report(diagID) << errorMsg.data();
+    return std::string();
+  }
+
+  llvm::SmallVector<std::string> featuresVec;
+  for (auto &implicitFeatureItem : implicitFeaturesMap) {
+    featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
+                           implicitFeatureItem.first().str())
+                              .str());
+  }
+  llvm::sort(featuresVec);
+  return llvm::join(featuresVec, ",");
+}
+
+// Get feature string which represents combined explicit target features
+// for NVPTX and the target features specified by the user/
+// TODO: Have a more robust target conf like `clang/lib/Basic/Targets/NVPTX.cpp`
+static std::string
+getExplicitAndImplicitNVPTXTargetFeatures(clang::DiagnosticsEngine &diags,
+                                          const TargetOptions &targetOpts,
+                                          const llvm::Triple triple) {
+  llvm::StringRef cpu = targetOpts.cpu;
+  llvm::StringMap<bool> implicitFeaturesMap;
+  std::string errorMsg;
+  bool ptxVer = false;
+
+  // Add target features specified by the user
+  for (auto &userFeature : targetOpts.featuresAsWritten) {
+    llvm::StringRef userKeyString(llvm::StringRef(userFeature).drop_front(1));
+    implicitFeaturesMap[userKeyString.str()] = (userFeature[0] == '+');
+    // Check if the user provided a PTX version
+    if (userKeyString.startswith("ptx"))
+      ptxVer = true;
+  }
+
+  // Set the default PTX version to `ptx61` if none was provided.
+  // TODO: set the default PTX version based on the chip.
+  if (!ptxVer)
+    implicitFeaturesMap["ptx61"] = true;
+
+  // Set the compute capability.
+  implicitFeaturesMap[cpu.str()] = true;
+
+  llvm::SmallVector<std::string> featuresVec;
+  for (auto &implicitFeatureItem : implicitFeaturesMap) {
+    featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
+                           implicitFeatureItem.first().str())
+                              .str());
+  }
+  llvm::sort(featuresVec);
+  return llvm::join(featuresVec, ",");
+}
+
+std::string
+CompilerInvocation::getTargetFeatures(clang::DiagnosticsEngine &diags) {
+  const TargetOptions &targetOpts = getTargetOpts();
+  const llvm::Triple triple(targetOpts.triple);
+
+  // Clang does not append all target features to the clang -cc1 invocation.
+  // Some target features are parsed implicitly by clang::TargetInfo child
+  // class. Clang::TargetInfo classes are the basic clang classes and
+  // they cannot be reused by Flang.
+  // That's why we need to extract implicit target features and add
+  // them to the target features specified by the user
+  if (triple.isAMDGPU()) {
+    return getExplicitAndImplicitAMDGPUTargetFeatures(diags, targetOpts,
+                                                      triple);
+  } else if (triple.isNVPTX()) {
+    return getExplicitAndImplicitNVPTXTargetFeatures(diags, targetOpts, triple);
+  }
+  return llvm::join(targetOpts.featuresAsWritten.begin(),
+                    targetOpts.featuresAsWritten.end(), ",");
+}
+
+bool CompilerInvocation::setUpTargetMachine(clang::DiagnosticsEngine &diags) {
+  const std::string &theTriple = getTargetOpts().triple;
+
+  // Create `Target`
+  std::string error;
+  const llvm::Target *theTarget =
+      llvm::TargetRegistry::lookupTarget(theTriple, error);
+  if (!theTarget) {
+    diags.Report(clang::diag::err_fe_unable_to_create_target) << error;
+    return false;
+  }
+
+  // Create `TargetMachine`
+  const auto &CGOpts = getCodeGenOpts();
+  std::optional<llvm::CodeGenOptLevel> OptLevelOrNone =
+      llvm::CodeGenOpt::getLevel(CGOpts.OptimizationLevel);
+  assert(OptLevelOrNone && "Invalid optimization level!");
+  llvm::CodeGenOptLevel OptLevel = *OptLevelOrNone;
+  std::string featuresStr = getTargetFeatures(diags);
+  targetMachine.reset(theTarget->createTargetMachine(
+      theTriple, /*CPU=*/targetOpts.cpu,
+      /*Features=*/featuresStr, llvm::TargetOptions(),
+      /*Reloc::Model=*/CGOpts.getRelocationModel(),
+      /*CodeModel::Model=*/std::nullopt, OptLevel));
+  assert(tm && "Failed to create TargetMachine");
+  return true;
+}
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index f573ac82c91cd8e..1cf064366f9c6e6 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -21,6 +21,7 @@
 #include "flang/Lower/Support/Verifier.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
+#include "flang/Optimizer/Support/DataLayout.h"
 #include "flang/Optimizer/Support/InitFIR.h"
 #include "flang/Optimizer/Support/Utils.h"
 #include "flang/Optimizer/Transforms/Passes.h"
@@ -53,7 +54,6 @@
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Verifier.h"
 #include "llvm/IRReader/IRReader.h"
-#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Object/OffloadBinary.h"
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Passes/PassPlugin.h"
@@ -65,7 +65,6 @@
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Target/TargetMachine.h"
-#include "llvm/TargetParser/TargetParser.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
 #include <memory>
 #include <system_error>
@@ -139,111 +138,6 @@ bool PrescanAndSemaDebugAction::beginSourceFileAction() {
          (generateRtTypeTables() || true);
 }
 
-// Get feature string which represents combined explicit target features
-// for AMD GPU and the target features specified by the user
-static std::string
-getExplicitAndImplicitAMDGPUTargetFeatures(CompilerInstance &ci,
-                                           const TargetOptions &targetOpts,
-                                           const llvm::Triple triple) {
-  llvm::StringRef cpu = targetOpts.cpu;
-  llvm::StringMap<bool> implicitFeaturesMap;
-  std::string errorMsg;
-  // Get the set of implicit target features
-  llvm::AMDGPU::fillAMDGPUFeatureMap(cpu, triple, implicitFeaturesMap);
-
-  // Add target features specified by the user
-  for (auto &userFeature : targetOpts.featuresAsWritten) {
-    std::string userKeyString = userFeature.substr(1);
-    implicitFeaturesMap[userKeyString] = (userFeature[0] == '+');
-  }
-
-  if (!llvm::AMDGPU::insertWaveSizeFeature(cpu, triple, implicitFeaturesMap,
-                                           errorMsg)) {
-    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
-        clang::DiagnosticsEngine::Error, "Unsupported feature ID: %0");
-    ci.getDiagnostics().Report(diagID) << errorMsg.data();
-    return std::string();
-  }
-
-  llvm::SmallVector<std::string> featuresVec;
-  for (auto &implicitFeatureItem : implicitFeaturesMap) {
-    featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
-                           implicitFeatureItem.first().str())
-                              .str());
-  }
-  llvm::sort(featuresVec);
-  return llvm::join(featuresVec, ",");
-}
-
-// Get feature string which represents combined explicit target features
-// for NVPTX and the target features specified by the user/
-// TODO: Have a more robust target conf like `clang/lib/Basic/Targets/NVPTX.cpp`
-static std::string
-getExplicitAndImplicitNVPTXTargetFeatures(CompilerInstance &ci,
-                                          const TargetOptions &targetOpts,
-                                          const llvm::Triple triple) {
-  llvm::StringRef cpu = targetOpts.cpu;
-  llvm::StringMap<bool> implicitFeaturesMap;
-  std::string errorMsg;
-  bool ptxVer = false;
-
-  // Add target features specified by the user
-  for (auto &userFeature : targetOpts.featuresAsWritten) {
-    llvm::StringRef userKeyString(llvm::StringRef(userFeature).drop_front(1));
-    implicitFeaturesMap[userKeyString.str()] = (userFeature[0] == '+');
-    // Check if the user provided a PTX version
-    if (userKeyString.startswith("ptx"))
-      ptxVer = true;
-  }
-
-  // Set the default PTX version to `ptx61` if none was provided.
-  // TODO: set the default PTX version based on the chip.
-  if (!ptxVer)
-    implicitFeaturesMap["ptx61"] = true;
-
-  // Set the compute capability.
-  implicitFeaturesMap[cpu.str()] = true;
-
-  llvm::SmallVector<std::string> featuresVec;
-  for (auto &implicitFeatureItem : implicitFeaturesMap) {
-    featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
-                           implicitFeatureItem.first().str())
-                              .str());
-  }
-  llvm::sort(featuresVec);
-  return llvm::join(featuresVec, ",");
-}
-
-// Produces the string which represents target feature
-static std::string getTargetFeatures(CompilerInstance &ci) {
-  const TargetOptions &targetOpts = ci.getInvocation().getTargetOpts();
-  const llvm::Triple triple(targetOpts.triple);
-
-  // Clang does not append all target features to the clang -cc1 invocation.
-  // Some target features are parsed implicitly by clang::TargetInfo child
-  // class. Clang::TargetInfo classes are the basic clang classes and
-  // they cannot be reused by Flang.
-  // That's why we need to extract implicit target features and add
-  // them to the target features specified by the user
-  if (triple.isAMDGPU()) {
-    return getExplicitAndImplicitAMDGPUTargetFeatures(ci, targetOpts, triple);
-  } else if (triple.isNVPTX()) {
-    return getExplicitAndImplicitNVPTXTargetFeatures(ci, targetOpts, triple);
-  }
-  return llvm::join(targetOpts.featuresAsWritten.begin(),
-                    targetOpts.featuresAsWritten.end(), ",");
-}
-
-static void setMLIRDataLayout(mlir::ModuleOp &mlirModule,
-                              const llvm::DataLayout &dl) {
-  mlir::MLIRContext *context = mlirModule.getContext();
-  mlirModule->setAttr(
-      mlir::LLVM::LLVMDialect::getDataLayoutAttrName(),
-      mlir::StringAttr::get(context, dl.getStringRepresentation()));
-  mlir::DataLayoutSpecInterface dlSpec = mlir::translateDataLayout(dl, context);
-  mlirModule->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
-}
-
 static void addDepdendentLibs(mlir::ModuleOp &mlirModule,
                               CompilerInstance &ci) {
   const std::vector<std::string> &libs =
@@ -291,6 +185,9 @@ bool CodeGenAction::beginSourceFileAction() {
   fir::support::loadDialects(*mlirCtx);
   fir::support::registerLLVMTranslation(*mlirCtx);
 
+  const llvm::TargetMachine &targetMachine =
+      ci.getInvocation().getTargetMachine();
+
   // If the input is an MLIR file, just parse it and return.
   if (this->getCurrentInput().getKind().getLanguage() == Language::MLIR) {
     llvm::SourceMgr sourceMgr;
@@ -308,10 +205,8 @@ bool CodeGenAction::beginSourceFileAction() {
     }
 
     mlirModule = std::make_unique<mlir::ModuleOp>(module.release());
-    if (!setUpTargetMachine())
-      return false;
-    const llvm::DataLayout &dl = tm->createDataLayout();
-    setMLIRDataLayout(*mlirModule, dl);
+    const llvm::DataLayout &dl = targetMachine.createDataLayout();
+    fir::support::setMLIRDataLayout(*mlirModule, dl);
     return true;
   }
 
@@ -333,6 +228,8 @@ bool CodeGenAction::beginSourceFileAction() {
       ci.getInvocation().getSemanticsContext().defaultKinds();
   fir::KindMapping kindMap(mlirCtx.get(), llvm::ArrayRef<fir::KindTy>{
                                               fir::fromDefaultKinds(defKinds)});
+  const llvm::DataLayout &dl = targetMachine.createDataLayout();
+
   lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(
       *mlirCtx, ci.getInvocation().getSemanticsContext(), defKinds,
       ci.getInvocation().getSemanticsContext().intrinsics(),
@@ -340,27 +237,22 @@ bool CodeGenAction::beginSourceFileAction() {
       ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
       kindMap, ci.getInvocation().getLoweringOpts(),
       ci.getInvocation().getFrontendOpts().envDefaults,
-      ci.getInvocation().getFrontendOpts().features);
+      ci.getInvocation().getFrontendOpts().features, &dl);
 
   // Fetch module from lb, so we can set
   mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
 
-  if (!setUpTargetMachine())
-    return false;
-
   if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
           Fortran::common::LanguageFeature::OpenMP)) {
     setOffloadModuleInterfaceAttributes(*mlirModule,
                                         ci.getInvocation().getLangOpts());
-    setOffloadModuleInterfaceTargetAttribute(*mlirModule, tm->getTargetCPU(),
-                                             tm->getTargetFeatureString());
+    setOffloadModuleInterfaceTargetAttribute(
+        *mlirModule, targetMachine.getTargetCPU(),
+        targetMachine.getTargetFeatureString());
     setOpenMPVersionAttribute(*mlirModule,
                               ci.getInvocation().getLangOpts().OpenMPVersion);
   }
 
-  const llvm::DataLayout &dl = tm->createDataLayout();
-  setMLIRDataLayout(*mlirModule, dl);
-
   // Create a parse tree and lower it to FIR
   Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()};
   lb.lower(parseTree, ci.getInvocation().getSemanticsContext());
@@ -769,7 +661,8 @@ getVScaleRange(CompilerInstance &ci,
     return std::pair<unsigned, unsigned>(
         langOpts.VScaleMin ? langOpts.VScaleMin : 1, langOpts.VScaleMax);
 
-  std::string featuresStr = getTargetFeatures(ci);
+  std::string featuresStr =
+      ci.getInvocation().getTargetFeatures(ci.getDiagnostics());
   if (featuresStr.find("+sve") != std::string::npos)
     return std::pair<unsigned, unsigned>(1, 16);
 
@@ -850,38 +743,6 @@ void CodeGenAction::generateLLVMIR() {
   }
 }
 
-bool CodeGenAction::setUpTargetMachine() {
-  CompilerInstance &ci = this->getInstance();
-
-  const TargetOptions &targetOpts = ci.getInvocation().getTargetOpts();
-  const std::string &theTriple = targetOpts.triple;
-
-  // Create `Target`
-  std::string error;
-  const llvm::Target *theTarget =
-      llvm::TargetRegistry::lookupTarget(theTriple, error);
-  if (!theTarget) {
-    ci.getDiagnostics().Report(clang::diag::err_fe_unable_to_create_target)
-        << error;
-    return false;
-  }
-
-  // Create `TargetMachine`
-  const auto &CGOpts = ci.getInvocation().getCodeGenOpts();
-  std::optional<llvm::CodeGenOptLevel> OptLevelOrNone =
-      llvm::CodeGenOpt::getLevel(CGOpts.OptimizationLevel);
-  assert(OptLevelOrNone && "Invalid optimization level!");
-  llvm::CodeGenOptLevel OptLevel = *OptLevelOrNone;
-  std::string featuresStr = getTargetFeatures(ci);
-  tm.reset(theTarget->createTargetMachine(
-      theTriple, /*CPU=*/targetOpts.cpu,
-      /*Features=*/featuresStr, llvm::TargetOptions(),
-      /*Reloc::Model=*/CGOpts.getRelocationModel(),
-      /*CodeModel::Model=*/std::nullopt, OptLevel));
-  assert(tm && "Failed to create TargetMachine");
-  return true;
-}
-
 static std::unique_ptr<llvm::raw_pwrite_stream>
 getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
                 BackendActionTy action) {
@@ -959,6 +820,8 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
   auto &diags = getInstance().getDiagnostics();
   llvm::OptimizationLevel level = mapToLevel(opts);
 
+  llvm::TargetMachine *targetMachine =
+      &getInstance().getInvocation().getTargetMachine();
   // Create the analysis managers.
   llvm::LoopAnalysisManager lam;
   llvm::FunctionAnalysisManager fam;
@@ -972,7 +835,7 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
   llvm::StandardInstrumentations si(llvmModule->getContext(),
                                     opts.DebugPassManager);
   si.registerCallbacks(pic, &mam);
-  llvm::PassBuilder pb(tm.get(), pto, pgoOpt, &pic);
+  llvm::PassBuilder pb(targetMachine, pto, pgoOpt, &pic);
 
   // Attempt to load pass plugins and register their callbacks with PB.
   for (auto &pluginFile : opts.LLVMPassPlugins) {
@@ -1238,9 +1101,8 @@ void CodeGenAction::executeAction() {
   // Set the triple based on the targetmachine (this comes compiler invocation
   // and the command-line target option if specified, or the default if not
   // given on the command-line).
-  if (!setUpTargetMachine())
-    return;
-  const std::string &theTriple = tm->getTargetTriple().str();
+  llvm::TargetMachine &targetMachine = ci.getInvocation().getTargetMachine();
+  const std::string &theTriple = targetMachine.getTargetTriple().str();
 
   if (llvmModule->getTargetTriple() != theTriple) {
     diags.Report(clang::diag::warn_fe_override_module) << theTriple;
@@ -1250,7 +1112,7 @@ void CodeGenAction::executeAction() {
   // Note that this overwrites any datalayout stored in the LLVM-IR. This avoids
   // an assert for incompatible data layout when the code-generation happens.
   llvmModule->setTargetTriple(theTriple);
-  llvmModule->setDataLayout(tm->createDataLayout());
+  llvmModule->setDataLayout(targetMachine.createDataLayout());
 
   // Embed offload objects specified with -fembed-offload-object
   if (!codeGenOpts.OffloadObjects.empty())
@@ -1300,7 +1162,7 @@ void CodeGenAction::executeAction() {
   if (action == BackendActionTy::Backend_EmitAssembly ||
       action == BackendActionTy::Backend_EmitObj) {
     generateMachineCodeOrAssemblyImpl(
-        diags, *tm, action, *llvmModule, codeGenOpts,
+        diags, targetMachine, action, *llvmModule, codeGenOpts,
         ci.isOutputStreamNull() ? *os : ci.getOutputStream());
     return;
   }
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 23c48cc7bd97874..2523026d7b6a89e 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -44,6 +44,7 @@
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/Support/DataLayout.h"
 #include "flang/Optimizer/Support/FatalError.h"
 #include "flang/Optimizer/Support/InternalNames.h"
 #include "flang/Optimizer/Transforms/Passes.h"
@@ -4979,7 +4980,8 @@ Fortran::lower::LoweringBridge::LoweringBridge(
     fir::KindMapping &kindMap,
     const Fortran::lower::LoweringOptions &loweringOptions,
     const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
-    const Fortran::common::LanguageFeatureControl &languageFeatures)
+    const Fortran::common::LanguageFeatureControl &languageFeatures,
+    const llvm::DataLayout *dataLayout)
     : semanticsContext{semanticsContext}, defaultKinds{defaultKinds},
       intrinsics{intrinsics}, targetCharacteristics{targetCharacteristics},
       cooked{&cooked}, context{context}, kindMap{kindMap},
@@ -5035,4 +5037,6 @@ Fortran::lower::LoweringBridge::LoweringBridge(
   assert(module.get() && "module was not created");
   fir::setTargetTriple(*module.get(), triple);
   fir::setKindMapping(*module.get(), kindMap);
+  if (dataLayout)
+    fir::support::setMLIRDataLayout(*module.get(), *dataLayout);
 }
diff --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt
index 2d03bc52c83f01f..55f5718a90b8544 100644
--- a/flang/lib/Optimizer/Support/CMakeLists.txt
+++ b/flang/lib/Optimizer/Support/CMakeLists.txt
@@ -2,6 +2,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
 get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS)
 
 add_flang_library(FIRSupport
+  DataLayout.cpp
   InitFIR.cpp
   InternalNames.cpp
 
@@ -20,6 +21,7 @@ add_flang_library(FIRSupport
   MLIROpenMPToLLVMIRTranslation
   MLIRLLVMToLLVMIRTranslation
   MLIRTargetLLVMIRExport
+  MLIRTargetLLVMIRImport
 
   LINK_COMPONENTS
   TargetParser
diff --git a/flang/lib/Optimizer/Support/DataLayout.cpp b/flang/lib/Optimizer/Support/DataLayout.cpp
new file mode 100644
index 000000000000000..5cd9c01e8ce006c
--- /dev/null
+++ b/flang/lib/Optimizer/Support/DataLayout.cpp
@@ -0,0 +1,47 @@
+//===-- Optimizer/Support/DataLayout.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Support/DataLayout.h"
+#include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "flang/Optimizer/Support/FatalError.h"
+#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/Interfaces/DataLayoutInterfaces.h"
+#include "mlir/Support/LLVM.h"
+#include "mlir/Target/LLVMIR/Import.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+
+void fir::support::setMLIRDataLayout(mlir::ModuleOp mlirModule,
+                                     const llvm::DataLayout &dl) {
+  mlir::MLIRContext *context = mlirModule.getContext();
+  mlirModule->setAttr(
+      mlir::LLVM::LLVMDialect::getDataLayoutAttrName(),
+      mlir::StringAttr::get(context, dl.getStringRepresentation()));
+  mlir::DataLayoutSpecInterface dlSpec = mlir::translateDataLayout(dl, context);
+  mlirModule->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
+}
+
+void fir::support::setMLIRDataLayoutFromAttributes(mlir::ModuleOp mlirModule,
+                                                   bool allowDefaultLayout) {
+  if (mlirModule.getDataLayoutSpec())
+    return; // Already set.
+  if (auto dataLayoutString = mlirModule->getAttrOfType<mlir::StringAttr>(
+          mlir::LLVM::LLVMDialect::getDataLayoutAttrName())) {
+    llvm::DataLayout llvmDataLayout(dataLayoutString);
+    fir::support::setMLIRDataLayout(mlirModule, llvmDataLayout);
+    return;
+  }
+  if (!allowDefaultLayout)
+    return;
+  llvm::DataLayout llvmDataLayout("");
+  fir::support::setMLIRDataLayout(mlirModule, llvmDataLayout);
+}
diff --git a/flang/test/Fir/tco-default-datalayout.fir b/flang/test/Fir/tco-default-datalayout.fir
new file mode 100644
index 000000000000000..0741e820a8d19dc
--- /dev/null
+++ b/flang/test/Fir/tco-default-datalayout.fir
@@ -0,0 +1,12 @@
+// Test that tco tool sets a target independent data layout when none is
+// provided. LLVM default data layout aligns i64 with 32 bits.
+// RUN: tco -emit-fir %s | FileCheck %s
+
+module {
+}
+// CHECK: module attributes {
+// CHECK-SAME: dlti.dl_spec = #dlti.dl_spec<
+// ...
+// CHECK-SAME:    #dlti.dl_entry<i64, dense<[32, 64]> : vector<2xi64>>,
+// ...
+// CHECK-SAME:    llvm.data_layout = ""
diff --git a/flang/test/Fir/tco-explicit-datalayout.fir b/flang/test/Fir/tco-explicit-datalayout.fir
new file mode 100644
index 000000000000000..50d8d835a602f35
--- /dev/null
+++ b/flang/test/Fir/tco-explicit-datalayout.fir
@@ -0,0 +1,13 @@
+// Test that tco tool preserves incoming llvm.data_layout and creates a
+// related dlti.dl_spec attribute. This tests a weird datalayout where
+// i64 would be 128 bit aligned.
+// RUN: tco -emit-fir %s | FileCheck %s
+
+module attributes {llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:128-i128:128-f80:128-n8:16:32:64-S128"} {
+}
+// CHECK: module attributes {
+// CHECK-SAME: dlti.dl_spec = #dlti.dl_spec<
+// ...
+// CHECK-SAME:    #dlti.dl_entry<i64, dense<128> : vector<2xi64>>,
+// ...
+// CHECK-SAME:    llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:128-i128:128-f80:128-n8:16:32:64-S128"
diff --git a/flang/test/Lower/bbc-host-datalayout.f90 b/flang/test/Lower/bbc-host-datalayout.f90
new file mode 100644
index 000000000000000..1795022e3bf7f79
--- /dev/null
+++ b/flang/test/Lower/bbc-host-datalayout.f90
@@ -0,0 +1,9 @@
+! Test bbc set-up of the target data layout from the host.
+! RUN: bbc %s -o - | FileCheck %s
+subroutine test
+end subroutine
+
+! CHECK: module attributes {
+! CHECK-SAME: dlti.dl_spec = #dlti.dl_spec<
+! CHECK-SAME: llvm.data_layout = "{{[^"]}}
+! CHECK-SAME: llvm.target_triple = "{{[^"]}}
diff --git a/flang/test/Lower/bbc-target-datalayout.f90 b/flang/test/Lower/bbc-target-datalayout.f90
new file mode 100644
index 000000000000000..adc1ee77bdfba9d
--- /dev/null
+++ b/flang/test/Lower/bbc-target-datalayout.f90
@@ -0,0 +1,10 @@
+! Test bbc target override.
+! REQUIRES: x86-registered-target
+! RUN: bbc %s -target x86_64-unknown-linux-gnu -o - | FileCheck %s
+subroutine test
+end subroutine
+
+! CHECK: module attributes {
+! CHECK-SAME: dlti.dl_spec = #dlti.dl_spec<
+! CHECK-SAME: llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+! CHECK-SAME: llvm.target_triple = "x86_64-unknown-linux-gnu"
diff --git a/flang/test/Semantics/realkinds-aarch64-01.f90 b/flang/test/Semantics/realkinds-aarch64-01.f90
index c316e251032430e..e22920ff991e931 100644
--- a/flang/test/Semantics/realkinds-aarch64-01.f90
+++ b/flang/test/Semantics/realkinds-aarch64-01.f90
@@ -1,3 +1,4 @@
+! REQUIRES: aarch64-registered-target
 ! RUN: %python %S/test_modfile.py %s %flang_fc1 -triple aarch64-unknown-linux-gnu
 
 module m1
diff --git a/flang/tools/bbc/CMakeLists.txt b/flang/tools/bbc/CMakeLists.txt
index cbae4ecd56c96ca..f21fa3b7bae350c 100644
--- a/flang/tools/bbc/CMakeLists.txt
+++ b/flang/tools/bbc/CMakeLists.txt
@@ -1,5 +1,8 @@
 set(LLVM_LINK_COMPONENTS
 Passes
+AllTargetsCodeGens
+AllTargetsDescs
+AllTargetsInfos
 TargetParser
 )
 
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 0c35d5c8438c115..f73e35766f7e0ff 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -16,6 +16,7 @@
 
 #include "flang/Common/Fortran-features.h"
 #include "flang/Common/OpenMP-features.h"
+#include "flang/Common/Version.h"
 #include "flang/Common/default-kinds.h"
 #include "flang/Lower/Bridge.h"
 #include "flang/Lower/PFTBuilder.h"
@@ -39,6 +40,7 @@
 #include "flang/Semantics/semantics.h"
 #include "flang/Semantics/unparse-with-symbols.h"
 #include "flang/Tools/CrossToolHelpers.h"
+#include "flang/Tools/TargetSetup.h"
 #include "flang/Version.inc"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 #include "mlir/IR/AsmState.h"
@@ -202,6 +204,10 @@ static llvm::cl::opt<bool> enableCUDA("fcuda",
 static llvm::cl::opt<bool> fixedForm("ffixed-form",
                                      llvm::cl::desc("enable fixed form"),
                                      llvm::cl::init(false));
+static llvm::cl::opt<std::string>
+    targetTripleOverride("target",
+                         llvm::cl::desc("Override host target triple"),
+                         llvm::cl::init(""));
 
 #define FLANG_EXCLUDE_CODEGEN
 #include "flang/Tools/CLOptions.inc"
@@ -229,7 +235,8 @@ static mlir::LogicalResult convertFortranSourceToMLIR(
     std::string path, Fortran::parser::Options options,
     const ProgramName &programPrefix,
     Fortran::semantics::SemanticsContext &semanticsContext,
-    const mlir::PassPipelineCLParser &passPipeline) {
+    const mlir::PassPipelineCLParser &passPipeline,
+    const llvm::TargetMachine &targetMachine) {
 
   // prep for prescan and parse
   Fortran::parser::Parsing parsing{semanticsContext.allCookedSources()};
@@ -295,6 +302,8 @@ static mlir::LogicalResult convertFortranSourceToMLIR(
   auto &defKinds = semanticsContext.defaultKinds();
   fir::KindMapping kindMap(
       &ctx, llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
+  const llvm::DataLayout &dataLayout = targetMachine.createDataLayout();
+  std::string targetTriple = targetMachine.getTargetTriple().normalize();
   // Use default lowering options for bbc.
   Fortran::lower::LoweringOptions loweringOptions{};
   loweringOptions.setPolymorphicTypeImpl(enablePolymorphic);
@@ -302,8 +311,9 @@ static mlir::LogicalResult convertFortranSourceToMLIR(
   loweringOptions.setLowerToHighLevelFIR(useHLFIR || emitHLFIR);
   auto burnside = Fortran::lower::LoweringBridge::create(
       ctx, semanticsContext, defKinds, semanticsContext.intrinsics(),
-      semanticsContext.targetCharacteristics(), parsing.allCooked(), "",
-      kindMap, loweringOptions, {}, semanticsContext.languageFeatures());
+      semanticsContext.targetCharacteristics(), parsing.allCooked(),
+      targetTriple, kindMap, loweringOptions, {},
+      semanticsContext.languageFeatures(), &dataLayout);
   burnside.lower(parseTree, semanticsContext);
   mlir::ModuleOp mlirModule = burnside.getModule();
   if (enableOpenMP) {
@@ -388,6 +398,8 @@ static mlir::LogicalResult convertFortranSourceToMLIR(
 
 int main(int argc, char **argv) {
   [[maybe_unused]] llvm::InitLLVM y(argc, argv);
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllTargetMCs();
   registerAllPasses();
 
   mlir::registerMLIRContextCLOptions();
@@ -453,17 +465,21 @@ int main(int argc, char **argv) {
       .set_warnOnNonstandardUsage(warnStdViolation)
       .set_warningsAreErrors(warnIsError);
 
-  llvm::Triple targetTriple{llvm::Triple(
-      llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))};
-  // FIXME: Handle real(3) ?
-  if (targetTriple.getArch() != llvm::Triple::ArchType::x86 &&
-      targetTriple.getArch() != llvm::Triple::ArchType::x86_64) {
-    semanticsContext.targetCharacteristics().DisableType(
-        Fortran::common::TypeCategory::Real, /*kind=*/10);
+  std::string error;
+  // Create host target machine.
+  std::unique_ptr<llvm::TargetMachine> targetMachine =
+      Fortran::tools::createTargetMachine(targetTripleOverride, error);
+  if (!targetMachine) {
+    llvm::errs() << "failed to create target machine: " << error << "\n";
+    return mlir::failed(mlir::failure());
   }
-  if (targetTriple.isPPC())
-    semanticsContext.targetCharacteristics().set_isPPC(true);
-
-  return mlir::failed(convertFortranSourceToMLIR(
-      inputFilename, options, programPrefix, semanticsContext, passPipe));
+  std::string compilerVersion = Fortran::common::getFlangToolFullVersion("bbc");
+  std::string compilerOptions = "";
+  Fortran::tools::setUpTargetCharacteristics(
+      semanticsContext.targetCharacteristics(), *targetMachine, compilerVersion,
+      compilerOptions);
+
+  return mlir::failed(
+      convertFortranSourceToMLIR(inputFilename, options, programPrefix,
+                                 semanticsContext, passPipe, *targetMachine));
 }
diff --git a/flang/tools/f18/CMakeLists.txt b/flang/tools/f18/CMakeLists.txt
index 270124bce324046..ba6c6642c0b62e2 100644
--- a/flang/tools/f18/CMakeLists.txt
+++ b/flang/tools/f18/CMakeLists.txt
@@ -49,9 +49,14 @@ if (NOT CMAKE_CROSSCOMPILING)
 
     # The module contains PPC vector types that needs the PPC target.
     set(opts "")
-    if(${filename} STREQUAL "__ppc_intrinsics" OR
-       ${filename} STREQUAL "mma")
-      set(opts "--target=ppc64le")
+      if(${filename} STREQUAL "__ppc_intrinsics" OR
+         ${filename} STREQUAL "mma")
+      if (PowerPC IN_LIST LLVM_TARGETS_TO_BUILD)
+        set(opts "--target=ppc64le")
+      else()
+        # Do not compile PPC module if the target is not available.
+        continue()
+      endif()
     endif()
 
     add_custom_command(OUTPUT ${base}.mod
diff --git a/flang/tools/tco/tco.cpp b/flang/tools/tco/tco.cpp
index 31d6bac142dc421..f29e67fc5cf5cc1 100644
--- a/flang/tools/tco/tco.cpp
+++ b/flang/tools/tco/tco.cpp
@@ -14,6 +14,7 @@
 #include "flang/Optimizer/CodeGen/CodeGen.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
+#include "flang/Optimizer/Support/DataLayout.h"
 #include "flang/Optimizer/Support/InitFIR.h"
 #include "flang/Optimizer/Support/InternalNames.h"
 #include "flang/Optimizer/Transforms/Passes.h"
@@ -61,9 +62,8 @@ static cl::opt<bool> codeGenLLVM(
 
 #include "flang/Tools/CLOptions.inc"
 
-static void printModuleBody(mlir::ModuleOp mod, raw_ostream &output) {
-  for (auto &op : *mod.getBody())
-    output << op << '\n';
+static void printModule(mlir::ModuleOp mod, raw_ostream &output) {
+  output << mod << '\n';
 }
 
 // compile a .fir file
@@ -104,6 +104,10 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
   fir::KindMapping kindMap{&context};
   fir::setTargetTriple(*owningRef, targetTriple);
   fir::setKindMapping(*owningRef, kindMap);
+  // tco is a testing tool, so it will happily use the target independent
+  // data layout if none is on the module.
+  fir::support::setMLIRDataLayoutFromAttributes(*owningRef,
+                                                /*allowDefaultLayout=*/true);
   mlir::PassManager pm((*owningRef)->getName(),
                        mlir::OpPassManager::Nesting::Implicit);
   pm.enableVerifier(/*verifyPasses=*/true);
@@ -134,13 +138,13 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
   if (mlir::succeeded(pm.run(*owningRef))) {
     // passes ran successfully, so keep the output
     if ((emitFir || passPipeline.hasAnyOccurrences()) && !codeGenLLVM)
-      printModuleBody(*owningRef, out.os());
+      printModule(*owningRef, out.os());
     out.keep();
     return mlir::success();
   }
 
   // pass manager failed
-  printModuleBody(*owningRef, errs());
+  printModule(*owningRef, errs());
   errs() << "\n\nFAILED: " << inputFilename << '\n';
   return mlir::failure();
 }
diff --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp
index 2ef63e864b92912..d57154cb1001c72 100644
--- a/flang/unittests/Frontend/FrontendActionTest.cpp
+++ b/flang/unittests/Frontend/FrontendActionTest.cpp
@@ -64,6 +64,13 @@ class FrontendActionTest : public ::testing::Test {
     compInst.createDiagnostics();
     invoc = std::make_shared<CompilerInvocation>();
 
+    // Set-up default target triple and initialize LLVM Targets so that the
+    // target data layout can be passed to the frontend.
+    invoc->getTargetOpts().triple =
+        llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
+    llvm::InitializeAllTargets();
+    llvm::InitializeAllTargetMCs();
+
     compInst.setInvocation(std::move(invoc));
     compInst.getFrontendOpts().inputs.push_back(
         FrontendInputFile(inputFilePath, Language::Fortran));
@@ -174,13 +181,7 @@ TEST_F(FrontendActionTest, EmitLLVM) {
   // Set-up the action kind.
   compInst.getInvocation().getFrontendOpts().programAction = EmitLLVM;
 
-  // Set-up default target triple.
-  compInst.getInvocation().getTargetOpts().triple =
-      llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
-
   // Initialise LLVM backend
-  llvm::InitializeAllTargets();
-  llvm::InitializeAllTargetMCs();
   llvm::InitializeAllAsmPrinters();
 
   // Set-up the output stream. We are using output buffer wrapped as an output
@@ -209,13 +210,7 @@ TEST_F(FrontendActionTest, EmitAsm) {
   // Set-up the action kind.
   compInst.getInvocation().getFrontendOpts().programAction = EmitAssembly;
 
-  // Set-up default target triple.
-  compInst.getInvocation().getTargetOpts().triple =
-      llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
-
   // Initialise LLVM backend
-  llvm::InitializeAllTargets();
-  llvm::InitializeAllTargetMCs();
   llvm::InitializeAllAsmPrinters();
 
   // Set-up the output stream. We are using output buffer wrapped as an output

>From 9a291796192f5070e1bed3799b59ed8c9211668e Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 Nov 2023 06:14:39 -0800
Subject: [PATCH 2/3] update name in assert

---
 flang/lib/Frontend/CompilerInvocation.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index fe53f0f2116ad66..76dd82dd9d7404c 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1502,6 +1502,6 @@ bool CompilerInvocation::setUpTargetMachine(clang::DiagnosticsEngine &diags) {
       /*Features=*/featuresStr, llvm::TargetOptions(),
       /*Reloc::Model=*/CGOpts.getRelocationModel(),
       /*CodeModel::Model=*/std::nullopt, OptLevel));
-  assert(tm && "Failed to create TargetMachine");
+  assert(targetMachine && "Failed to create TargetMachine");
   return true;
 }

>From c7b99f721c3d76f282e2cbc095f6595a507de972 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Mon, 27 Nov 2023 00:47:26 -0800
Subject: [PATCH 3/3] move createTargetMachine to bbc

---
 flang/include/flang/Tools/TargetSetup.h | 23 -----------------------
 flang/tools/bbc/bbc.cpp                 | 25 ++++++++++++++++++++++++-
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/flang/include/flang/Tools/TargetSetup.h b/flang/include/flang/Tools/TargetSetup.h
index 0b09acefde98a3c..238d66c9241dd0e 100644
--- a/flang/include/flang/Tools/TargetSetup.h
+++ b/flang/include/flang/Tools/TargetSetup.h
@@ -10,10 +10,7 @@
 #define FORTRAN_TOOLS_TARGET_SETUP_H
 
 #include "flang/Evaluate/target.h"
-#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Target/TargetMachine.h"
-#include "llvm/TargetParser/Host.h"
-#include <memory>
 
 namespace Fortran::tools {
 
@@ -38,26 +35,6 @@ namespace Fortran::tools {
   // type size and alignment info.
 }
 
-/// Create a target machine that is at least sufficient to get data-layout
-/// information required by flang semantics and lowering. Note that it may not
-/// contain all the CPU feature information to get optimized assembly generation
-/// from LLVM IR. Drivers that needs to generate assembly from LLVM IR should
-/// create a target machine according to their specific options.
-[[maybe_unused]] inline static std::unique_ptr<llvm::TargetMachine>
-createTargetMachine(llvm::StringRef targetTriple, std::string &error) {
-  std::string triple{targetTriple};
-  if (triple.empty())
-    triple = llvm::sys::getDefaultTargetTriple();
-
-  const llvm::Target *theTarget =
-      llvm::TargetRegistry::lookupTarget(triple, error);
-  if (!theTarget)
-    return nullptr;
-  return std::unique_ptr<llvm::TargetMachine>{
-      theTarget->createTargetMachine(triple, /*CPU=*/"",
-          /*Features=*/"", llvm::TargetOptions(),
-          /*Reloc::Model=*/std::nullopt)};
-}
 } // namespace Fortran::tools
 
 #endif // FORTRAN_TOOLS_TARGET_SETUP_H
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index f73e35766f7e0ff..0122cf33b0b6779 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -52,6 +52,7 @@
 #include "mlir/Pass/PassRegistry.h"
 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
 #include "mlir/Transforms/Passes.h"
+#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Passes/OptimizationLevel.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ErrorOr.h"
@@ -65,6 +66,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Host.h"
 #include "llvm/TargetParser/Triple.h"
+#include <memory>
 
 //===----------------------------------------------------------------------===//
 // Some basic command-line options
@@ -227,6 +229,27 @@ static void registerAllPasses() {
   fir::registerOptTransformPasses();
 }
 
+/// Create a target machine that is at least sufficient to get data-layout
+/// information required by flang semantics and lowering. Note that it may not
+/// contain all the CPU feature information to get optimized assembly generation
+/// from LLVM IR. Drivers that needs to generate assembly from LLVM IR should
+/// create a target machine according to their specific options.
+static std::unique_ptr<llvm::TargetMachine>
+createTargetMachine(llvm::StringRef targetTriple, std::string &error) {
+  std::string triple{targetTriple};
+  if (triple.empty())
+    triple = llvm::sys::getDefaultTargetTriple();
+
+  const llvm::Target *theTarget =
+      llvm::TargetRegistry::lookupTarget(triple, error);
+  if (!theTarget)
+    return nullptr;
+  return std::unique_ptr<llvm::TargetMachine>{
+      theTarget->createTargetMachine(triple, /*CPU=*/"",
+                                     /*Features=*/"", llvm::TargetOptions(),
+                                     /*Reloc::Model=*/std::nullopt)};
+}
+
 //===----------------------------------------------------------------------===//
 // Translate Fortran input to FIR, a dialect of MLIR.
 //===----------------------------------------------------------------------===//
@@ -468,7 +491,7 @@ int main(int argc, char **argv) {
   std::string error;
   // Create host target machine.
   std::unique_ptr<llvm::TargetMachine> targetMachine =
-      Fortran::tools::createTargetMachine(targetTripleOverride, error);
+      createTargetMachine(targetTripleOverride, error);
   if (!targetMachine) {
     llvm::errs() << "failed to create target machine: " << error << "\n";
     return mlir::failed(mlir::failure());



More information about the flang-commits mailing list