ソースを参照

Codegen: Given a carbon file prints the assembly to the stdout (#2944)

Co-authored-by: Farzana Ahmed Siddique <fasiddique@google.com>
Farzana Ahmed Siddique 2 年 前
コミット
aad4ed2083

+ 20 - 0
toolchain/codegen/BUILD

@@ -0,0 +1,20 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+    name = "codegen",
+    srcs = ["codegen.cpp"],
+    hdrs = ["codegen.h"],
+    deps = [
+        "@llvm-project//llvm:AllTargetsAsmParsers",
+        "@llvm-project//llvm:AllTargetsCodeGens",
+        "@llvm-project//llvm:Core",
+        "@llvm-project//llvm:MC",
+        "@llvm-project//llvm:Support",
+        "@llvm-project//llvm:Target",
+        "@llvm-project//llvm:TargetParser",
+    ],
+)

+ 71 - 0
toolchain/codegen/codegen.cpp

@@ -0,0 +1,71 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/codegen/codegen.h"
+
+#include <cstdio>
+
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/TargetParser/Host.h"
+
+namespace Carbon {
+
+auto PrintAssemblyFromModule(llvm::Module& module,
+                             llvm::StringRef target_triple,
+                             llvm::raw_pwrite_stream& error_stream,
+                             llvm::raw_pwrite_stream& output_stream) -> bool {
+  // Initialize the target registry etc.
+  llvm::InitializeAllTargetInfos();
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllTargetMCs();
+  llvm::InitializeAllAsmParsers();
+  llvm::InitializeAllAsmPrinters();
+
+  std::string error;
+  llvm::StringRef triple = target_triple;
+  std::string host_triple;
+  if (target_triple.empty()) {
+    host_triple = llvm::sys::getDefaultTargetTriple();
+    triple = host_triple;
+  }
+  const auto* target = llvm::TargetRegistry::lookupTarget(triple, error);
+
+  if (!target) {
+    error_stream << "ERROR: " << error << "\n";
+    return false;
+  }
+
+  constexpr llvm::StringLiteral CPU = "generic";
+  constexpr llvm::StringLiteral Features = "";
+
+  llvm::TargetOptions target_opts;
+  std::optional<llvm::Reloc::Model> reloc_model;
+  auto* target_machine = target->createTargetMachine(
+      target_triple, CPU, Features, target_opts, reloc_model);
+  module.setDataLayout(target_machine->createDataLayout());
+  module.setTargetTriple(target_triple);
+
+  // Using the legacy PM to generate the assembly since the new PM
+  // does not work with this yet.
+  // TODO: make the new PM work with the codegen pipeline.
+
+  llvm::legacy::PassManager pass;
+  auto file_type = llvm::CGFT_AssemblyFile;
+
+  if (target_machine->addPassesToEmitFile(pass, output_stream, nullptr,
+                                          file_type)) {
+    error_stream << "Nothing to write to object file\n";
+    return false;
+  }
+
+  pass.run(module);
+  delete target_machine;
+  return true;
+}
+}  // namespace Carbon

+ 22 - 0
toolchain/codegen/codegen.h

@@ -0,0 +1,22 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CODEGEN_CODEGEN_H_
+#define CARBON_TOOLCHAIN_CODEGEN_CODEGEN_H_
+
+#include <cstdint>
+
+#include "llvm/IR/Module.h"
+
+namespace Carbon {
+// Prints the assembly to stdout for the given llvm module.
+// If print fails, this returns false and any information about the failure is
+// printed to the error stream.
+auto PrintAssemblyFromModule(llvm::Module& module,
+                             llvm::StringRef target_triple,
+                             llvm::raw_pwrite_stream& error_stream,
+                             llvm::raw_pwrite_stream& output_stream) -> bool;
+}  // namespace Carbon
+
+#endif  // CARBON_TOOLCHAIN_CODEGEN_CODEGEN_H_

+ 1 - 0
toolchain/driver/BUILD

@@ -14,6 +14,7 @@ cc_library(
     textual_hdrs = ["flags.def"],
     deps = [
         "//common:vlog",
+        "//toolchain/codegen",
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/diagnostics:sorting_diagnostic_consumer",
         "//toolchain/lexer:tokenized_buffer",

+ 18 - 1
toolchain/driver/driver.cpp

@@ -11,6 +11,7 @@
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/Support/Format.h"
+#include "toolchain/codegen/codegen.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/diagnostics/sorting_diagnostic_consumer.h"
 #include "toolchain/lexer/tokenized_buffer.h"
@@ -116,6 +117,7 @@ enum class DumpMode {
   ParseTree,
   SemanticsIR,
   LLVMIR,
+  Assembly,
   Unknown
 };
 
@@ -131,10 +133,11 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
                        .Case("parse-tree", DumpMode::ParseTree)
                        .Case("semantics-ir", DumpMode::SemanticsIR)
                        .Case("llvm-ir", DumpMode::LLVMIR)
+                       .Case("assembly", DumpMode::Assembly)
                        .Default(DumpMode::Unknown);
   if (dump_mode == DumpMode::Unknown) {
     error_stream_ << "ERROR: Dump mode should be one of tokens, parse-tree, "
-                     "semantics-ir, or llvm-ir.\n";
+                     "semantics-ir, llvm-ir, or assembly.\n";
     return false;
   }
   args = args.drop_front();
@@ -153,6 +156,13 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
     semantics_ir_include_builtins = true;
   }
 
+  llvm::StringRef target_triple;
+  if (dump_mode == DumpMode::Assembly && !args.empty() &&
+      args.front().starts_with("--target_triple=")) {
+    target_triple = args.front().split("=").second;
+    args = args.drop_front();
+  }
+
   if (args.empty()) {
     error_stream_ << "ERROR: No input file specified.\n";
     return false;
@@ -236,6 +246,13 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
                   /*IsForDebug=*/true);
   }
 
+  if (dump_mode == DumpMode::Assembly) {
+    consumer.Flush();
+    has_errors |= !Carbon::PrintAssemblyFromModule(
+        *module, target_triple, error_stream_, output_stream_);
+    return !has_errors;
+  }
+
   llvm_unreachable("should handle all dump modes");
 }
 

+ 8 - 0
toolchain/driver/testdata/codegen.carbon

@@ -0,0 +1,8 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{carbon} dump assembly --target_triple=x86_64-unknown-linux-gnu %s | %{FileCheck-allow-unmatched}
+// CHECK:STDOUT: Main:
+
+fn Main() -> i32 { return 0; }

+ 8 - 0
toolchain/driver/testdata/error_codegen_target_triple.carbon

@@ -0,0 +1,8 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{not} %{carbon} dump assembly --target_triple=x86_687-unknown-linux-gnu %s | %{FileCheck-strict}
+// CHECK:STDERR: ERROR: {{.*}}
+
+fn Main() -> i32 { return 0; }