diff --git a/toolchain/codegen/BUILD b/toolchain/codegen/BUILD new file mode 100644 index 000000000000..39dc612cc226 --- /dev/null +++ b/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", + ], +) diff --git a/toolchain/codegen/codegen.cpp b/toolchain/codegen/codegen.cpp new file mode 100644 index 000000000000..df9b7b5daf1c --- /dev/null +++ b/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 + +#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 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 diff --git a/toolchain/codegen/codegen.h b/toolchain/codegen/codegen.h new file mode 100644 index 000000000000..2f76a7bbbf98 --- /dev/null +++ b/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 + +#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_ diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 865cae7b8ec9..3b2b74e5aa72 100644 --- a/toolchain/driver/BUILD +++ b/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", diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index 8e10a4659162..325bfc0bb595 100644 --- a/toolchain/driver/driver.cpp +++ b/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"); } diff --git a/toolchain/driver/testdata/codegen.carbon b/toolchain/driver/testdata/codegen.carbon new file mode 100644 index 000000000000..845dffe2b2db --- /dev/null +++ b/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; } diff --git a/toolchain/driver/testdata/error_codegen_target_triple.carbon b/toolchain/driver/testdata/error_codegen_target_triple.carbon new file mode 100644 index 000000000000..36755991fc1f --- /dev/null +++ b/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; }