diff --git a/toolchain/codegen/codegen.cpp b/toolchain/codegen/codegen.cpp index df9b7b5daf1c..45c35413d5bc 100644 --- a/toolchain/codegen/codegen.cpp +++ b/toolchain/codegen/codegen.cpp @@ -5,21 +5,17 @@ #include "toolchain/codegen/codegen.h" #include +#include #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 { // 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 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 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 diff --git a/toolchain/codegen/codegen.h b/toolchain/codegen/codegen.h index 2f76a7bbbf98..70f627cd63fa 100644 --- a/toolchain/codegen/codegen.h +++ b/toolchain/codegen/codegen.h @@ -8,15 +8,49 @@ #include #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; + + // 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_ diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index 325bfc0bb595..cd102659ad3c 100644 --- a/toolchain/driver/driver.cpp +++ b/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; } diff --git a/toolchain/driver/testdata/codegen.carbon b/toolchain/driver/testdata/codegen_assembly.carbon similarity index 100% rename from toolchain/driver/testdata/codegen.carbon rename to toolchain/driver/testdata/codegen_assembly.carbon diff --git a/toolchain/driver/testdata/error_codegen_target_triple.carbon b/toolchain/driver/testdata/error_codegen_assembly_target_triple.carbon similarity index 86% rename from toolchain/driver/testdata/error_codegen_target_triple.carbon rename to toolchain/driver/testdata/error_codegen_assembly_target_triple.carbon index 36755991fc1f..78b93b39b8d2 100644 --- a/toolchain/driver/testdata/error_codegen_target_triple.carbon +++ b/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; } diff --git a/toolchain/driver/testdata/error_codegen_objcode_no_output_file.carbon b/toolchain/driver/testdata/error_codegen_objcode_no_output_file.carbon new file mode 100644 index 000000000000..e358db4e60b4 --- /dev/null +++ b/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; } diff --git a/toolchain/driver/testdata/error_codegen_objcode_target_triple.carbon b/toolchain/driver/testdata/error_codegen_objcode_target_triple.carbon new file mode 100644 index 000000000000..d4d681ca6761 --- /dev/null +++ b/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; } diff --git a/toolchain/driver/testdata/success_codegen_objfile.carbon b/toolchain/driver/testdata/success_codegen_objfile.carbon new file mode 100644 index 000000000000..a7b66cedf872 --- /dev/null +++ b/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; }