rust/src/back/write.rs

326 lines
15 KiB
Rust
Raw Normal View History

use std::{env, fs};
2020-05-10 10:54:30 -04:00
2025-01-09 18:41:32 -05:00
use gccjit::{Context, OutputKind};
use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
use rustc_errors::DiagCtxtHandle;
use rustc_fs_util::link_or_copy;
2025-01-08 21:31:32 -05:00
use rustc_session::config::OutputType;
2020-05-10 10:54:30 -04:00
use rustc_span::fatal_error::FatalError;
use rustc_target::spec::SplitDebuginfo;
use crate::errors::CopyBitcode;
use crate::{GccCodegenBackend, GccContext};
2020-05-10 10:54:30 -04:00
pub(crate) unsafe fn codegen(
cgcx: &CodegenContext<GccCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
module: ModuleCodegen<GccContext>,
config: &ModuleConfig,
) -> Result<CompiledModule, FatalError> {
let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
2020-05-10 10:54:30 -04:00
{
let context = &module.module_llvm.context;
let module_name = module.name.clone();
let should_combine_object_files = module.module_llvm.should_combine_object_files;
2020-05-10 10:54:30 -04:00
let module_name = Some(&module_name[..]);
// NOTE: Only generate object files with GIMPLE when this environment variable is set for
// now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit).
// TODO: remove this environment variable.
let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1");
2025-01-08 21:31:32 -05:00
/*if cgcx.msvc_imps_needed {
println!("************************************************** Imps needed");
create_msvc_imps(cgcx, context);
2025-01-08 21:31:32 -05:00
}*/
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
2020-05-10 10:54:30 -04:00
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
2025-01-05 15:16:27 -05:00
if config.bitcode_needed() {
if fat_lto {
let _timer = cgcx
.prof
2025-01-05 15:16:27 -05:00
.generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name);
// TODO(antoyo)
/*if let Some(bitcode_filename) = bc_out.file_name() {
cgcx.prof.artifact_size(
"llvm_bitcode",
bitcode_filename.to_string_lossy(),
data.len() as u64,
);
}*/
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
2025-01-08 21:31:32 -05:00
let _timer = cgcx.prof.generic_activity_with_arg(
"GCC_module_codegen_emit_bitcode",
&*module.name,
);
2025-01-05 15:16:27 -05:00
context.add_command_line_option("-flto=auto");
context.add_command_line_option("-flto-partition=one");
// TODO: remove since we don't want fat objects when it is for Bitcode only.
context.add_command_line_option("-ffat-lto-objects");
2025-01-08 21:31:32 -05:00
context.compile_to_file(
OutputKind::ObjectFile,
bc_out.to_str().expect("path to str"),
);
2025-01-05 15:16:27 -05:00
}
if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
2025-01-08 21:31:32 -05:00
let _timer = cgcx.prof.generic_activity_with_arg(
"GCC_module_codegen_embed_bitcode",
&*module.name,
);
2025-01-05 15:16:27 -05:00
// TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes?
//embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data);
context.add_command_line_option("-flto=auto");
context.add_command_line_option("-flto-partition=one");
context.add_command_line_option("-ffat-lto-objects");
// TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument).
2025-01-08 21:31:32 -05:00
context.compile_to_file(
OutputKind::ObjectFile,
bc_out.to_str().expect("path to str"),
);
2025-01-05 15:16:27 -05:00
}
2025-01-08 21:31:32 -05:00
} else {
2025-01-05 15:16:27 -05:00
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
2025-01-08 21:31:32 -05:00
let _timer = cgcx.prof.generic_activity_with_arg(
"GCC_module_codegen_emit_bitcode",
&*module.name,
);
context.compile_to_file(
OutputKind::ObjectFile,
bc_out.to_str().expect("path to str"),
);
2025-01-05 15:16:27 -05:00
}
2025-01-05 15:16:27 -05:00
if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
// TODO: we might want to emit to emit an error here, saying to set the
// environment variable EMBED_LTO_BITCODE.
2025-01-08 21:31:32 -05:00
let _timer = cgcx.prof.generic_activity_with_arg(
"GCC_module_codegen_embed_bitcode",
&*module.name,
);
2025-01-05 15:16:27 -05:00
// TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes?
//embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data);
2025-01-10 12:05:37 -05:00
// TODO: check if this condition makes sense.
if fat_lto {
context.add_command_line_option("-flto=auto");
context.add_command_line_option("-flto-partition=one");
context.add_command_line_option("-ffat-lto-objects");
}
2025-01-05 15:16:27 -05:00
// TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument).
2025-01-08 21:31:32 -05:00
context.compile_to_file(
OutputKind::ObjectFile,
bc_out.to_str().expect("path to str"),
);
2025-01-05 15:16:27 -05:00
}
}
2021-08-15 08:28:46 -04:00
}
2020-05-10 10:54:30 -04:00
if config.emit_ir {
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
std::fs::write(out, "").expect("write file");
2020-05-10 10:54:30 -04:00
}
if config.emit_asm {
let _timer =
cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
2020-05-10 10:54:30 -04:00
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
}
match config.emit_obj {
EmitObj::ObjectCode(_) => {
let _timer = cgcx
.prof
.generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name);
if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") {
println!("Module {}", module.name);
}
if env::var("CG_GCCJIT_DUMP_ALL_MODULES").as_deref() == Ok("1")
|| env::var("CG_GCCJIT_DUMP_MODULE").as_deref() == Ok(&module.name)
{
println!("Dumping reproducer {}", module.name);
let _ = fs::create_dir("/tmp/reproducers");
// FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
// transmuting an rvalue to an lvalue.
// Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue
context.dump_reproducer_to_file(format!("/tmp/reproducers/{}.c", module.name));
println!("Dumped reproducer {}", module.name);
2021-08-15 08:28:46 -04:00
}
if env::var("CG_GCCJIT_DUMP_TO_FILE").as_deref() == Ok("1") {
let _ = fs::create_dir("/tmp/gccjit_dumps");
let path = &format!("/tmp/gccjit_dumps/{}.c", module.name);
context.set_debug_info(true);
context.dump_to_file(path, true);
}
if should_combine_object_files {
if fat_lto {
context.add_command_line_option("-flto=auto");
context.add_command_line_option("-flto-partition=one");
// NOTE: without -fuse-linker-plugin, we get the following error:
// lto1: internal compiler error: decompressed stream: Destination buffer is too small
2025-01-10 11:10:58 -05:00
// TODO: since we do not do LTO when the linker is invoked anymore, perhaps
// the following flag is not necessary anymore.
// TODO: also, perhaps compiling the gcc driver in the CI is not necessary
// anymore.
context.add_driver_option("-fuse-linker-plugin");
}
context.add_driver_option("-Wl,-r");
// NOTE: we need -nostdlib, otherwise, we get the following error:
// /usr/bin/ld: cannot find -lgcc_s: No such file or directory
context.add_driver_option("-nostdlib");
// NOTE: this doesn't actually generate an executable. With the above flags, it combines the .o files together in another .o.
2025-01-09 18:41:32 -05:00
// FIXME FIXME: this produces an object file with GIMPLE IR, but it should
// produce an object file with machine code.
//println!("LTO-ed object file: {:?}", obj_out);
/*context.compile_to_file(
OutputKind::Executable,
obj_out.to_str().expect("path to str"),
2025-01-09 18:41:32 -05:00
);*/
let path = obj_out.to_str().expect("path to str");
if fat_lto {
let lto_path = format!("{}.lto", path);
// FIXME(antoyo): The LTO frontend generates the following warning:
// ../build_sysroot/sysroot_src/library/core/src/num/dec2flt/lemire.rs:150:15: warning: type of _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E does not match original declaration [-Wlto-type-mismatch]
// 150 | let (lo5, hi5) = POWER_OF_FIVE_128[index];
// | ^
// lto1: note: _ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E was previously declared here
//
// This option is to mute it to make the UI tests pass with LTO enabled.
context.add_driver_option("-Wno-lto-type-mismatch");
// NOTE: this doesn't actually generate an executable. With the above
// flags, it combines the .o files together in another .o.
context.compile_to_file(OutputKind::Executable, &lto_path);
let context = Context::default(); // TODO: might need to set some other flags from new_context.
//context.add_driver_option("-v");
//println!("*** Arch: {}", cgcx.target_arch);
if cgcx.target_arch == "x86" || cgcx.target_arch == "x86_64" {
//println!("**** Added -masm=intel");
//context.add_command_line_option("-masm=intel");
// NOTE: it seems we need to use add_driver_option instead of
// add_command_line_option here because we use the LTO frontend via gcc.
context.add_driver_option("-masm=intel");
}
// NOTE: these two options are needed to invoke LTO to produce an object file.
// We need to initiate a second compilation because the arguments "-x lto"
// needs to be at the very beginning.
// TODO TODO: check that LTO still does the optimizations across different
// object files with this change.
// TODO: this should probably be in a condition `if fat_lto`.
context.add_driver_option("-x");
context.add_driver_option("lto");
context.add_driver_option("-fPIC"); // TODO TODO: only add this flag when necessary.
context.add_driver_option(lto_path);
context.compile_to_file(OutputKind::ObjectFile, path);
} else {
// NOTE: this doesn't actually generate an executable. With the above
// flags, it combines the .o files together in another .o.
context.compile_to_file(OutputKind::Executable, path);
}
} else {
context.compile_to_file(
OutputKind::ObjectFile,
obj_out.to_str().expect("path to str"),
);
}
2020-05-10 10:54:30 -04:00
}
EmitObj::Bitcode => {
debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out);
if let Err(err) = link_or_copy(&bc_out, &obj_out) {
2023-12-18 10:15:45 +11:00
dcx.emit_err(CopyBitcode { err });
}
if !config.emit_bc {
debug!("removing_bitcode {:?}", bc_out);
2023-12-18 10:15:45 +11:00
ensure_removed(dcx, &bc_out);
}
2020-05-10 10:54:30 -04:00
}
EmitObj::None => {}
}
}
Ok(module.into_compiled_module(
config.emit_obj != EmitObj::None,
cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked,
config.emit_bc,
config.emit_asm,
config.emit_ir,
2020-05-10 10:54:30 -04:00
&cgcx.output_filenames,
))
}
pub(crate) fn link(
_cgcx: &CodegenContext<GccCodegenBackend>,
_dcx: DiagCtxtHandle<'_>,
mut _modules: Vec<ModuleCodegen<GccContext>>,
) -> Result<ModuleCodegen<GccContext>, FatalError> {
2020-05-10 10:54:30 -04:00
unimplemented!();
}
pub(crate) fn save_temp_bitcode(
cgcx: &CodegenContext<GccCodegenBackend>,
_module: &ModuleCodegen<GccContext>,
_name: &str,
) {
if !cgcx.save_temps {
return;
}
unimplemented!();
/*unsafe {
let ext = format!("{}.bc", name);
let cgu = Some(&module.name[..]);
let path = cgcx.output_filenames.temp_path_ext(&ext, cgu);
let cstr = path_to_c_string(&path);
let llmod = module.module_llvm.llmod();
llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
}*/
}
2025-01-08 21:31:32 -05:00
/*fn create_msvc_imps<'gcc>(cgcx: &CodegenContext<GccCodegenBackend>, _context: &Context<'gcc>) {
if !cgcx.msvc_imps_needed {
return;
}
/*unsafe {
let ptr_ty = Type::ptr_llcx(llcx);
let globals = base::iter_globals(llmod)
.filter(|&val| {
llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage
&& llvm::LLVMIsDeclaration(val) == 0
})
.map(move |(val, name)| {
let mut imp_name = prefix.as_bytes().to_vec();
imp_name.extend(name);
let imp_name = CString::new(imp_name).unwrap();
(imp_name, val)
})
.collect::<Vec<_>>();
for (imp_name, val) in globals {
let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr());
llvm::LLVMSetInitializer(imp, val);
llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage);
}
}*/
2025-01-08 21:31:32 -05:00
}*/