Przeglądaj źródła

Creates object file from the module. (#2955)

This pr creates the object file for the carbon code that returns either
0 or 1. The command to convert the object code to binary is `clang
<object_file_name> -o <binary_name>`

---------

Co-authored-by: Farzana Ahmed Siddique <fasiddique@google.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Farzana Ahmed Siddique 2 lat temu
rodzic
commit
037196f69f

+ 36 - 18
toolchain/codegen/codegen.cpp

@@ -5,21 +5,17 @@
 #include "toolchain/codegen/codegen.h"
 
 #include <cstdio>
+#include <memory>
 
 #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 {
+auto CodeGen::CreateTargetMachine() -> std::unique_ptr<llvm::TargetMachine> {
   // Initialize the target registry etc.
   llvm::InitializeAllTargetInfos();
   llvm::InitializeAllTargets();
@@ -37,8 +33,8 @@ auto PrintAssemblyFromModule(llvm::Module& module,
   const auto* target = llvm::TargetRegistry::lookupTarget(triple, error);
 
   if (!target) {
-    error_stream << "ERROR: " << error << "\n";
-    return false;
+    error_stream_ << "ERROR: Invalid -target_triple: " << error << "\n";
+    return nullptr;
   }
 
   constexpr llvm::StringLiteral CPU = "generic";
@@ -46,26 +42,48 @@ auto PrintAssemblyFromModule(llvm::Module& module,
 
   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);
+  std::unique_ptr<llvm::TargetMachine> target_machine(
+      target->createTargetMachine(target_triple, CPU, Features, target_opts,
+                                  reloc_model));
+  return target_machine;
+}
+
+auto CodeGen::EmitCode(llvm::raw_pwrite_stream& dest,
+                       llvm::TargetMachine* target_machine,
+                       llvm::CodeGenFileType file_type) -> bool {
+  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";
+  if (target_machine->addPassesToEmitFile(pass, dest, nullptr, file_type)) {
+    error_stream_ << "Error: Nothing to write to object file\n";
     return false;
   }
 
-  pass.run(module);
-  delete target_machine;
+  pass.run(Module);
   return true;
 }
+
+auto CodeGen::PrintAssembly() -> bool {
+  auto target_machine = CreateTargetMachine();
+  if (target_machine == nullptr) {
+    return false;
+  }
+  return EmitCode(output_stream_, target_machine.get(),
+                  llvm::CodeGenFileType::CGFT_AssemblyFile);
+}
+
+auto CodeGen::GenerateObjectCode() -> bool {
+  auto target_machine = CreateTargetMachine();
+  if (target_machine == nullptr) {
+    return false;
+  }
+  return EmitCode(output_stream_, target_machine.get(),
+                  llvm::CodeGenFileType::CGFT_ObjectFile);
+}
 }  // namespace Carbon

+ 41 - 7
toolchain/codegen/codegen.h

@@ -8,15 +8,49 @@
 #include <cstdint>
 
 #include "llvm/IR/Module.h"
+#include "llvm/Target/TargetMachine.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;
+
+class CodeGen {
+ public:
+  CodeGen(llvm::Module& module, llvm::StringRef triple,
+          llvm::raw_pwrite_stream& error_stream,
+          llvm::raw_pwrite_stream& output_stream)
+      : Module(module),
+        output_stream_(output_stream),
+        error_stream_(error_stream),
+        target_triple(triple){};
+
+  // Generates the object code file.
+  // Returns false in case of failure, and any information about the failure is
+  // printed to the error stream.
+  auto GenerateObjectCode() -> bool;
+
+  // Prints the assembly to stdout.
+  // Returns false in case of failure, and any information about the failure is
+  // printed to the error stream.
+  auto PrintAssembly() -> bool;
+
+ private:
+  llvm::Module& Module;
+  llvm::raw_pwrite_stream& output_stream_;
+  llvm::raw_pwrite_stream& error_stream_;
+  llvm::StringRef target_triple;
+
+  // Creates the target machine for triple.
+  // Returns nullptr in case of failure, and any information about the failure
+  // is printed to the error stream.
+  auto CreateTargetMachine() -> std::unique_ptr<llvm::TargetMachine>;
+
+  // Using the llvm pass emits either assembly or object code to dest.
+  // Returns false in case of failure, and any information about the failure is
+  // printed to the error stream.
+  auto EmitCode(llvm::raw_pwrite_stream& dest,
+                llvm::TargetMachine* target_machine,
+                llvm::CodeGenFileType file_type) -> bool;
+};
+
 }  // namespace Carbon
 
 #endif  // CARBON_TOOLCHAIN_CODEGEN_CODEGEN_H_

+ 40 - 3
toolchain/driver/driver.cpp

@@ -118,6 +118,7 @@ enum class DumpMode {
   SemanticsIR,
   LLVMIR,
   Assembly,
+  ObjectCode,
   Unknown
 };
 
@@ -134,10 +135,11 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
                        .Case("semantics-ir", DumpMode::SemanticsIR)
                        .Case("llvm-ir", DumpMode::LLVMIR)
                        .Case("assembly", DumpMode::Assembly)
+                       .Case("objcode", DumpMode::ObjectCode)
                        .Default(DumpMode::Unknown);
   if (dump_mode == DumpMode::Unknown) {
     error_stream_ << "ERROR: Dump mode should be one of tokens, parse-tree, "
-                     "semantics-ir, llvm-ir, or assembly.\n";
+                     "semantics-ir, llvm-ir, assembly, or objcode.\n";
     return false;
   }
   args = args.drop_front();
@@ -163,6 +165,26 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
     args = args.drop_front();
   }
 
+  llvm::StringRef output_file;
+  if (dump_mode == DumpMode::ObjectCode) {
+    while (!args.empty() && (args.front().starts_with("--target_triple=") ||
+                             args.front().starts_with("--output_file="))) {
+      if (args.front().starts_with("--target_triple=")) {
+        target_triple = args.front().split("=").second;
+        args = args.drop_front();
+      }
+      if (args.front().starts_with("--output_file=")) {
+        output_file = args.front().split("=").second;
+        args = args.drop_front();
+      }
+    }
+
+    if (output_file.empty()) {
+      error_stream_ << "ERROR: Must provide an output file.\n";
+      return false;
+    }
+  }
+
   if (args.empty()) {
     error_stream_ << "ERROR: No input file specified.\n";
     return false;
@@ -248,8 +270,23 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
 
   if (dump_mode == DumpMode::Assembly) {
     consumer.Flush();
-    has_errors |= !Carbon::PrintAssemblyFromModule(
-        *module, target_triple, error_stream_, output_stream_);
+    CodeGen codegen(*module, target_triple, error_stream_, output_stream_);
+    has_errors |= !codegen.PrintAssembly();
+    return !has_errors;
+  }
+
+  if (dump_mode == DumpMode::ObjectCode) {
+    std::error_code ec;
+    llvm::raw_fd_ostream dest(output_file, ec, llvm::sys::fs::OF_None);
+    if (ec) {
+      error_stream_ << "Error: Could not open file: " << ec.message() << "\n";
+      return false;
+    }
+    CodeGen codegen(*module, target_triple, error_stream_, dest);
+    has_errors |= !codegen.GenerateObjectCode();
+    if (!has_errors) {
+      output_stream_ << "Success: Object file is generated!\n";
+    }
     return !has_errors;
   }
 

+ 0 - 0
toolchain/driver/testdata/codegen.carbon → toolchain/driver/testdata/codegen_assembly.carbon


+ 1 - 1
toolchain/driver/testdata/error_codegen_target_triple.carbon → toolchain/driver/testdata/error_codegen_assembly_target_triple.carbon

@@ -3,6 +3,6 @@
 // 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: {{.*}}
+// CHECK:STDERR: ERROR: Invalid -target_triple:{{.*}}
 
 fn Main() -> i32 { return 0; }

+ 8 - 0
toolchain/driver/testdata/error_codegen_objcode_no_output_file.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 objcode --target_triple=x86_64-unknown-linux-gnu %s | %{FileCheck-strict}
+// CHECK:STDERR: ERROR: Must provide an output file.
+
+fn Main() -> i32 { return 0; }

+ 8 - 0
toolchain/driver/testdata/error_codegen_objcode_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 objcode --target_triple=x86_684-unknown-linux-gnu --output_file=output.o %s | %{FileCheck-strict}
+// CHECK:STDERR: ERROR: Invalid -target_triple:{{.*}}
+
+fn Main() -> i32 { return 0; }

+ 8 - 0
toolchain/driver/testdata/success_codegen_objfile.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 objcode --target_triple=x86_64-unknown-linux-gnu --output_file=objfile.o %s | %{FileCheck-strict}
+// CHECK:STDOUT: Success: Object file is generated!
+
+fn Main() -> i32 { return 0; }