2021-12-31 16:26:32 +01:00
use std ::{ env , fs } ;
2020-05-10 10:54:30 -04:00
2024-12-20 19:28:02 -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-05 15:16:27 -05:00
use rustc_session ::config ::{ Lto , OutputType } ;
2020-05-10 10:54:30 -04:00
use rustc_span ::fatal_error ::FatalError ;
use rustc_target ::spec ::SplitDebuginfo ;
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 ;
let module_name = module . name . clone ( ) ;
2023-10-09 15:53:34 -04:00
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 [ .. ] ) ;
2023-10-09 15:53:34 -04:00
// 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).
2024-07-10 12:44:23 +02:00
// TODO: remove this environment variable.
2023-10-09 15:53:34 -04:00
let fat_lto = env ::var ( " EMBED_LTO_BITCODE " ) . as_deref ( ) = = Ok ( " 1 " ) ;
2024-12-20 19:28:02 -05:00
if cgcx . msvc_imps_needed {
println! ( " ************************************************** Imps needed " ) ;
create_msvc_imps ( cgcx , context ) ;
}
2023-10-09 15:53:34 -04: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 {
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 {
let _timer = cgcx
. prof
. generic_activity_with_arg ( " GCC_module_codegen_emit_bitcode " , & * module . name ) ;
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 " ) ;
context
. compile_to_file ( OutputKind ::ObjectFile , bc_out . to_str ( ) . expect ( " path to str " ) ) ;
}
if config . emit_obj = = EmitObj ::ObjectCode ( BitcodeSection ::Full ) {
let _timer = cgcx
. prof
. generic_activity_with_arg ( " GCC_module_codegen_embed_bitcode " , & * module . name ) ;
// 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).
context
. compile_to_file ( OutputKind ::ObjectFile , bc_out . to_str ( ) . expect ( " path to str " ) ) ;
}
2023-10-09 15:53:34 -04:00
}
2025-01-05 15:16:27 -05:00
else {
if config . emit_bc | | config . emit_obj = = EmitObj ::Bitcode {
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 " ) ) ;
}
2023-10-09 15:53:34 -04: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.
unreachable! ( ) ;
let _timer = cgcx
. prof
. generic_activity_with_arg ( " GCC_module_codegen_embed_bitcode " , & * module . name ) ;
// 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).
context
. compile_to_file ( OutputKind ::ObjectFile , bc_out . to_str ( ) . expect ( " path to str " ) ) ;
}
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 {
2024-03-05 19:58:36 +01:00
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 {
2024-03-05 19:58:36 +01:00
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
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
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 " ) ;
// NOTE: this doesn't actually generate an executable. With the above flags, it combines the .o files together in another .o.
2024-03-05 19:58:36 +01:00
context . compile_to_file (
OutputKind ::Executable ,
obj_out . to_str ( ) . expect ( " path to str " ) ,
) ;
} 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 ( ) ) ;
} * /
}
2024-12-20 19:28:02 -05:00
fn create_msvc_imps < ' gcc > (
cgcx : & CodegenContext < GccCodegenBackend > ,
context : & Context < ' gcc > ,
) {
if ! cgcx . msvc_imps_needed {
return ;
}
// The x86 ABI seems to require that leading underscores are added to symbol
// names, so we need an extra underscore on x86. There's also a leading
// '\x01' here which disables LLVM's symbol mangling (e.g., no extra
// underscores added in front).
let prefix = if cgcx . target_arch = = " x86 " { " \x01 __imp__ " } else { " \x01 __imp_ " } ;
/* 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 ) ;
}
} * /
}