2021-12-31 16:26:32 +01:00
use std ::{ env , fs } ;
2020-05-10 10:54:30 -04:00
2025-01-09 18:41:32 -05:00
use gccjit ::{ Context , OutputKind } ;
2023-10-09 15:53:34 -04:00
use rustc_codegen_ssa ::back ::link ::ensure_removed ;
use rustc_codegen_ssa ::back ::write ::{ BitcodeSection , CodegenContext , EmitObj , ModuleConfig } ;
2024-03-05 19:58:36 +01:00
use rustc_codegen_ssa ::{ CompiledModule , ModuleCodegen } ;
2024-06-18 10:35:56 +00:00
use rustc_errors ::DiagCtxtHandle ;
2023-10-09 15:53:34 -04:00
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 ;
2025-01-10 20:51:23 -05:00
use crate ::base ::add_pic_option ;
2023-10-09 15:53:34 -04:00
use crate ::errors ::CopyBitcode ;
2024-03-05 19:58:36 +01:00
use crate ::{ GccCodegenBackend , GccContext } ;
2020-05-10 10:54:30 -04:00
2024-03-05 19:58:36 +01:00
pub ( crate ) unsafe fn codegen (
cgcx : & CodegenContext < GccCodegenBackend > ,
2024-06-18 10:35:56 +00:00
dcx : DiagCtxtHandle < '_ > ,
2024-03-05 19:58:36 +01:00
module : ModuleCodegen < GccContext > ,
config : & ModuleConfig ,
) -> Result < CompiledModule , FatalError > {
2023-10-09 15:53:34 -04:00
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 ;
2023-10-09 15:53:34 -04:00
let should_combine_object_files = module . module_llvm . should_combine_object_files ;
// 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).
2025-01-10 14:32:01 -05:00
// TODO(antoyo): remove this environment variable.
2023-10-09 15:53:34 -04:00
let fat_lto = env ::var ( " EMBED_LTO_BITCODE " ) . as_deref ( ) = = Ok ( " 1 " ) ;
2025-04-06 23:37:30 +00:00
let bc_out = cgcx . output_filenames . temp_path_for_cgu ( OutputType ::Bitcode , & module . name ) ;
let obj_out = cgcx . output_filenames . temp_path_for_cgu ( OutputType ::Object , & module . name ) ;
2020-05-10 10:54:30 -04:00
2025-01-05 15:16:27 -05:00
if config . bitcode_needed ( ) {
if fat_lto {
2023-10-09 15:53:34 -04:00
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 " ) ;
2025-01-10 14:32:01 -05:00
// TODO(antoyo): remove since we don't want fat objects when it is for Bitcode only.
2025-01-05 15:16:27 -05:00
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
}
2023-10-09 15:53:34 -04:00
2025-01-05 15:16:27 -05:00
if config . emit_obj = = EmitObj ::ObjectCode ( BitcodeSection ::Full ) {
2025-01-10 14:32:01 -05:00
// TODO(antoyo): we might want to emit to emit an error here, saying to set the
2025-01-05 15:16:27 -05:00
// 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);
// 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
}
2023-10-09 15:53:34 -04:00
}
2021-08-15 08:28:46 -04:00
}
2020-05-10 10:54:30 -04:00
if config . emit_ir {
2025-04-06 23:37:30 +00:00
let out =
cgcx . output_filenames . temp_path_for_cgu ( OutputType ::LlvmAssembly , & module . name ) ;
2024-03-05 19:58:36 +01:00
std ::fs ::write ( out , " " ) . expect ( " write file " ) ;
2020-05-10 10:54:30 -04:00
}
if config . emit_asm {
2024-03-05 19:58:36 +01:00
let _timer =
cgcx . prof . generic_activity_with_arg ( " GCC_module_codegen_emit_asm " , & * module . name ) ;
2025-04-06 23:37:30 +00:00
let path = cgcx . output_filenames . temp_path_for_cgu ( OutputType ::Assembly , & module . name ) ;
2020-05-10 10:54:30 -04:00
context . compile_to_file ( OutputKind ::Assembler , path . to_str ( ) . expect ( " path to str " ) ) ;
}
match config . emit_obj {
EmitObj ::ObjectCode ( _ ) = > {
let _timer = cgcx
. prof
2023-10-09 15:53:34 -04:00
. generic_activity_with_arg ( " GCC_module_codegen_emit_obj " , & * module . name ) ;
2021-12-31 16:26:32 +01:00
if env ::var ( " CG_GCCJIT_DUMP_MODULE_NAMES " ) . as_deref ( ) = = Ok ( " 1 " ) {
println! ( " Module {} " , module . name ) ;
}
2024-03-05 19:58:36 +01:00
if env ::var ( " CG_GCCJIT_DUMP_ALL_MODULES " ) . as_deref ( ) = = Ok ( " 1 " )
| | env ::var ( " CG_GCCJIT_DUMP_MODULE " ) . as_deref ( ) = = Ok ( & module . name )
{
2021-12-31 16:26:32 +01:00
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
2024-07-10 12:44:23 +02:00
context . dump_reproducer_to_file ( format! ( " /tmp/reproducers/ {} .c " , module . name ) ) ;
2021-12-31 16:26:32 +01:00
println! ( " Dumped reproducer {} " , module . name ) ;
2021-08-15 08:28:46 -04:00
}
2022-03-26 18:29:37 +01: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 ) ;
2023-03-05 12:03:19 -05:00
context . set_debug_info ( true ) ;
2022-03-26 18:29:37 +01:00
context . dump_to_file ( path , true ) ;
}
2024-07-10 12:44:23 +02:00
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 20:51:23 -05:00
// TODO(antoyo): since we do not do LTO when the linker is invoked anymore, perhaps
2025-01-10 11:10:58 -05:00
// the following flag is not necessary anymore.
2024-07-10 12:44:23 +02:00
context . add_driver_option ( " -fuse-linker-plugin " ) ;
}
2023-10-09 15:53:34 -04:00
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 " ) ;
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 ) ;
2025-01-10 20:51:23 -05:00
let context = Context ::default ( ) ;
2025-01-09 18:41:32 -05:00
if cgcx . target_arch = = " x86 " | | cgcx . target_arch = = " x86_64 " {
// 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.
context . add_driver_option ( " -x " ) ;
context . add_driver_option ( " lto " ) ;
2025-01-10 20:51:23 -05:00
add_pic_option ( & context , module . module_llvm . relocation_model ) ;
2025-01-09 18:41:32 -05:00
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 ) ;
}
2024-03-05 19:58:36 +01:00
} else {
context . compile_to_file (
OutputKind ::ObjectFile ,
obj_out . to_str ( ) . expect ( " path to str " ) ,
) ;
2023-10-09 15:53:34 -04:00
}
2020-05-10 10:54:30 -04:00
}
EmitObj ::Bitcode = > {
2023-10-09 15:53:34 -04:00
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 } ) ;
2023-10-09 15:53:34 -04:00
}
if ! config . emit_bc {
debug! ( " removing_bitcode {:?} " , bc_out ) ;
2023-12-18 10:15:45 +11:00
ensure_removed ( dcx , & bc_out ) ;
2023-10-09 15:53:34 -04:00
}
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 ,
2024-04-06 09:07:54 -04:00
config . emit_asm ,
config . emit_ir ,
2020-05-10 10:54:30 -04:00
& cgcx . output_filenames ,
) )
}
2024-03-05 19:58:36 +01:00
pub ( crate ) fn link (
_cgcx : & CodegenContext < GccCodegenBackend > ,
2024-06-18 10:35:56 +00:00
_dcx : DiagCtxtHandle < '_ > ,
2024-03-05 19:58:36 +01:00
mut _modules : Vec < ModuleCodegen < GccContext > > ,
) -> Result < ModuleCodegen < GccContext > , FatalError > {
2020-05-10 10:54:30 -04:00
unimplemented! ( ) ;
}
2023-10-09 15:53:34 -04:00
2024-03-05 19:58:36 +01:00
pub ( crate ) fn save_temp_bitcode (
cgcx : & CodegenContext < GccCodegenBackend > ,
_module : & ModuleCodegen < GccContext > ,
_name : & str ,
) {
2023-10-09 15:53:34 -04:00
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 ( ) ) ;
} * /
}