Skip to content

Commit d23239b

Browse files
committed
msvc: Get codegen-units working
This commit alters the implementation of multiple codegen units slightly to be compatible with the MSVC linker. Currently the implementation will take the N object files created by each codegen unit and will run `ld -r` to create a new object file which is then passed along. The MSVC linker, however, is not able to do this operation. The compiler will now no longer attempt to assemble object files together but will instead just pass through all the object files as usual. This implies that rlibs may not contain more than one object file (if the library is compiled with more than one codegen unit) and the output of `-C save-temps` will have changed slightly as object files with the extension `0.o` will not be renamed to `o` unless requested otherwise.
1 parent e6a9be1 commit d23239b

File tree

3 files changed

+60
-123
lines changed

3 files changed

+60
-123
lines changed

src/librustc_trans/back/link.rs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -417,11 +417,10 @@ pub fn link_binary(sess: &Session,
417417

418418
// Remove the temporary object file and metadata if we aren't saving temps
419419
if !sess.opts.cg.save_temps {
420-
let obj_filename = outputs.temp_path(OutputTypeObject);
421-
if !sess.opts.output_types.contains(&OutputTypeObject) {
422-
remove(sess, &obj_filename);
420+
for obj in object_filenames(sess, outputs) {
421+
remove(sess, &obj);
423422
}
424-
remove(sess, &obj_filename.with_extension("metadata.o"));
423+
remove(sess, &outputs.with_extension("metadata.o"));
425424
}
426425

427426
out_filenames
@@ -499,7 +498,7 @@ fn link_binary_output(sess: &Session,
499498
crate_type: config::CrateType,
500499
outputs: &OutputFilenames,
501500
crate_name: &str) -> PathBuf {
502-
let obj_filename = outputs.temp_path(OutputTypeObject);
501+
let objects = object_filenames(sess, outputs);
503502
let out_filename = match outputs.single_output_file {
504503
Some(ref file) => file.clone(),
505504
None => {
@@ -508,41 +507,41 @@ fn link_binary_output(sess: &Session,
508507
}
509508
};
510509

511-
// Make sure the output and obj_filename are both writeable.
512-
// Mac, FreeBSD, and Windows system linkers check this already --
513-
// however, the Linux linker will happily overwrite a read-only file.
514-
// We should be consistent.
515-
let obj_is_writeable = is_writeable(&obj_filename);
516-
let out_is_writeable = is_writeable(&out_filename);
517-
if !out_is_writeable {
518-
sess.fatal(&format!("output file {} is not writeable -- check its \
519-
permissions.",
520-
out_filename.display()));
521-
}
522-
else if !obj_is_writeable {
523-
sess.fatal(&format!("object file {} is not writeable -- check its \
524-
permissions.",
525-
obj_filename.display()));
510+
// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
511+
// check this already -- however, the Linux linker will happily overwrite a
512+
// read-only file. We should be consistent.
513+
for file in objects.iter().chain(Some(&out_filename)) {
514+
if !is_writeable(file) {
515+
sess.fatal(&format!("output file {} is not writeable -- check its \
516+
permissions", file.display()));
517+
}
526518
}
527519

528520
match crate_type {
529521
config::CrateTypeRlib => {
530-
link_rlib(sess, Some(trans), &obj_filename, &out_filename).build();
522+
link_rlib(sess, Some(trans), &objects, &out_filename).build();
531523
}
532524
config::CrateTypeStaticlib => {
533-
link_staticlib(sess, &obj_filename, &out_filename);
525+
link_staticlib(sess, &objects, &out_filename);
534526
}
535527
config::CrateTypeExecutable => {
536-
link_natively(sess, trans, false, &obj_filename, &out_filename);
528+
link_natively(sess, trans, false, &objects, &out_filename, outputs);
537529
}
538530
config::CrateTypeDylib => {
539-
link_natively(sess, trans, true, &obj_filename, &out_filename);
531+
link_natively(sess, trans, true, &objects, &out_filename, outputs);
540532
}
541533
}
542534

543535
out_filename
544536
}
545537

538+
fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec<PathBuf> {
539+
(0..sess.opts.cg.codegen_units).map(|i| {
540+
let ext = format!("{}.o", i);
541+
outputs.temp_path(OutputTypeObject).with_extension(&ext)
542+
}).collect()
543+
}
544+
546545
fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
547546
let mut search = Vec::new();
548547
sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| {
@@ -560,9 +559,9 @@ fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
560559
// native libraries and inserting all of the contents into this archive.
561560
fn link_rlib<'a>(sess: &'a Session,
562561
trans: Option<&CrateTranslation>, // None == no metadata/bytecode
563-
obj_filename: &Path,
562+
objects: &[PathBuf],
564563
out_filename: &Path) -> ArchiveBuilder<'a> {
565-
info!("preparing rlib from {:?} to {:?}", obj_filename, out_filename);
564+
info!("preparing rlib from {:?} to {:?}", objects, out_filename);
566565
let handler = &sess.diagnostic().handler;
567566
let config = ArchiveConfig {
568567
handler: handler,
@@ -574,7 +573,9 @@ fn link_rlib<'a>(sess: &'a Session,
574573
command_path: command_path(sess),
575574
};
576575
let mut ab = ArchiveBuilder::create(config);
577-
ab.add_file(obj_filename).unwrap();
576+
for obj in objects {
577+
ab.add_file(obj).unwrap();
578+
}
578579

579580
for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() {
580581
match kind {
@@ -600,7 +601,7 @@ fn link_rlib<'a>(sess: &'a Session,
600601
// this is as follows:
601602
//
602603
// * When performing LTO, this archive will be modified to remove
603-
// obj_filename from above. The reason for this is described below.
604+
// objects from above. The reason for this is described below.
604605
//
605606
// * When the system linker looks at an archive, it will attempt to
606607
// determine the architecture of the archive in order to see whether its
@@ -639,15 +640,14 @@ fn link_rlib<'a>(sess: &'a Session,
639640
// For LTO purposes, the bytecode of this library is also inserted
640641
// into the archive. If codegen_units > 1, we insert each of the
641642
// bitcode files.
642-
for i in 0..sess.opts.cg.codegen_units {
643+
for obj in objects {
643644
// Note that we make sure that the bytecode filename in the
644645
// archive is never exactly 16 bytes long by adding a 16 byte
645646
// extension to it. This is to work around a bug in LLDB that
646647
// would cause it to crash if the name of a file in an archive
647648
// was exactly 16 bytes.
648-
let bc_filename = obj_filename.with_extension(&format!("{}.bc", i));
649-
let bc_deflated_filename = obj_filename.with_extension(
650-
&format!("{}.bytecode.deflate", i));
649+
let bc_filename = obj.with_extension("bc");
650+
let bc_deflated_filename = obj.with_extension("bytecode.deflate");
651651

652652
let mut bc_data = Vec::new();
653653
match fs::File::open(&bc_filename).and_then(|mut f| {
@@ -750,8 +750,8 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write,
750750
// There's no need to include metadata in a static archive, so ensure to not
751751
// link in the metadata object file (and also don't prepare the archive with a
752752
// metadata file).
753-
fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
754-
let ab = link_rlib(sess, None, obj_filename, out_filename);
753+
fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) {
754+
let ab = link_rlib(sess, None, objects, out_filename);
755755
let mut ab = match sess.target.target.options.is_like_osx {
756756
true => ab.build().extend(),
757757
false => ab,
@@ -806,8 +806,9 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
806806
// This will invoke the system linker/cc to create the resulting file. This
807807
// links to all upstream files as well.
808808
fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
809-
obj_filename: &Path, out_filename: &Path) {
810-
info!("preparing dylib? ({}) from {:?} to {:?}", dylib, obj_filename,
809+
objects: &[PathBuf], out_filename: &Path,
810+
outputs: &OutputFilenames) {
811+
info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects,
811812
out_filename);
812813
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
813814

@@ -828,7 +829,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
828829
Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
829830
};
830831
link_args(&mut *linker, sess, dylib, tmpdir.path(),
831-
trans, obj_filename, out_filename);
832+
trans, objects, out_filename, outputs);
832833
if !sess.target.target.options.no_compiler_rt {
833834
linker.link_staticlib("compiler-rt");
834835
}
@@ -884,8 +885,9 @@ fn link_args(cmd: &mut Linker,
884885
dylib: bool,
885886
tmpdir: &Path,
886887
trans: &CrateTranslation,
887-
obj_filename: &Path,
888-
out_filename: &Path) {
888+
objects: &[PathBuf],
889+
out_filename: &Path,
890+
outputs: &OutputFilenames) {
889891

890892
// The default library location, we need this to find the runtime.
891893
// The location of crates will be determined as needed.
@@ -895,7 +897,9 @@ fn link_args(cmd: &mut Linker,
895897
let t = &sess.target.target;
896898

897899
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
898-
cmd.add_object(obj_filename);
900+
for obj in objects {
901+
cmd.add_object(obj);
902+
}
899903
cmd.output_filename(out_filename);
900904

901905
// Stack growth requires statically linking a __morestack function. Note
@@ -922,7 +926,7 @@ fn link_args(cmd: &mut Linker,
922926
// executable. This metadata is in a separate object file from the main
923927
// object file, so we link that in here.
924928
if dylib {
925-
cmd.add_object(&obj_filename.with_extension("metadata.o"));
929+
cmd.add_object(&outputs.with_extension("metadata.o"));
926930
}
927931

928932
// Try to strip as much out of the generated object by removing unused

src/librustc_trans/back/write.rs

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ use std::ffi::{CStr, CString};
2727
use std::fs;
2828
use std::mem;
2929
use std::path::Path;
30-
use std::process::Stdio;
3130
use std::ptr;
3231
use std::str;
3332
use std::sync::{Arc, Mutex};
@@ -619,6 +618,8 @@ pub fn run_passes(sess: &Session,
619618
let needs_crate_bitcode =
620619
sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
621620
sess.opts.output_types.contains(&config::OutputTypeExe);
621+
let needs_crate_object =
622+
sess.opts.output_types.contains(&config::OutputTypeExe);
622623
if needs_crate_bitcode {
623624
modules_config.emit_bc = true;
624625
}
@@ -696,7 +697,8 @@ pub fn run_passes(sess: &Session,
696697
if sess.opts.cg.codegen_units == 1 {
697698
// 1) Only one codegen unit. In this case it's no difficulty
698699
// to copy `foo.0.x` to `foo.x`.
699-
copy_gracefully(&crate_output.with_extension(ext), &crate_output.path(output_type));
700+
copy_gracefully(&crate_output.with_extension(ext),
701+
&crate_output.path(output_type));
700702
if !sess.opts.cg.save_temps && !keep_numbered {
701703
// The user just wants `foo.x`, not `foo.0.x`.
702704
remove(sess, &crate_output.with_extension(ext));
@@ -715,76 +717,11 @@ pub fn run_passes(sess: &Session,
715717
}
716718
};
717719

718-
let link_obj = |output_path: &Path| {
719-
// Running `ld -r` on a single input is kind of pointless.
720-
if sess.opts.cg.codegen_units == 1 {
721-
copy_gracefully(&crate_output.with_extension("0.o"), output_path);
722-
// Leave the .0.o file around, to mimic the behavior of the normal
723-
// code path.
724-
return;
725-
}
726-
727-
// Some builds of MinGW GCC will pass --force-exe-suffix to ld, which
728-
// will automatically add a .exe extension if the extension is not
729-
// already .exe or .dll. To ensure consistent behavior on Windows, we
730-
// add the .exe suffix explicitly and then rename the output file to
731-
// the desired path. This will give the correct behavior whether or
732-
// not GCC adds --force-exe-suffix.
733-
let windows_output_path =
734-
if sess.target.target.options.is_like_windows {
735-
Some(output_path.with_extension("o.exe"))
736-
} else {
737-
None
738-
};
739-
740-
let (pname, mut cmd) = get_linker(sess);
741-
742-
cmd.args(&sess.target.target.options.pre_link_args);
743-
cmd.arg("-nostdlib");
744-
745-
for index in 0..trans.modules.len() {
746-
cmd.arg(&crate_output.with_extension(&format!("{}.o", index)));
747-
}
748-
749-
cmd.arg("-r").arg("-o")
750-
.arg(windows_output_path.as_ref().map(|s| &**s).unwrap_or(output_path));
751-
752-
cmd.args(&sess.target.target.options.post_link_args);
753-
754-
if sess.opts.debugging_opts.print_link_args {
755-
println!("{:?}", &cmd);
756-
}
757-
758-
cmd.stdin(Stdio::null());
759-
match cmd.status() {
760-
Ok(status) => {
761-
if !status.success() {
762-
sess.err(&format!("linking of {} with `{:?}` failed",
763-
output_path.display(), cmd));
764-
sess.abort_if_errors();
765-
}
766-
},
767-
Err(e) => {
768-
sess.err(&format!("could not exec the linker `{}`: {}",
769-
pname, e));
770-
sess.abort_if_errors();
771-
},
772-
}
773-
774-
match windows_output_path {
775-
Some(ref windows_path) => {
776-
fs::rename(windows_path, output_path).unwrap();
777-
},
778-
None => {
779-
// The file is already named according to `output_path`.
780-
}
781-
}
782-
};
783-
784720
// Flag to indicate whether the user explicitly requested bitcode.
785721
// Otherwise, we produced it only as a temporary output, and will need
786722
// to get rid of it.
787723
let mut user_wants_bitcode = false;
724+
let mut user_wants_objects = false;
788725
for output_type in output_types {
789726
match *output_type {
790727
config::OutputTypeBitcode => {
@@ -801,17 +738,10 @@ pub fn run_passes(sess: &Session,
801738
copy_if_one_unit("0.s", config::OutputTypeAssembly, false);
802739
}
803740
config::OutputTypeObject => {
804-
link_obj(&crate_output.path(config::OutputTypeObject));
805-
}
806-
config::OutputTypeExe => {
807-
// If config::OutputTypeObject is already in the list, then
808-
// `crate.o` will be handled by the config::OutputTypeObject case.
809-
// Otherwise, we need to create the temporary object so we
810-
// can run the linker.
811-
if !sess.opts.output_types.contains(&config::OutputTypeObject) {
812-
link_obj(&crate_output.temp_path(config::OutputTypeObject));
813-
}
741+
user_wants_objects = true;
742+
copy_if_one_unit("0.o", config::OutputTypeObject, true);
814743
}
744+
config::OutputTypeExe |
815745
config::OutputTypeDepInfo => {}
816746
}
817747
}
@@ -848,15 +778,18 @@ pub fn run_passes(sess: &Session,
848778
let keep_numbered_bitcode = needs_crate_bitcode ||
849779
(user_wants_bitcode && sess.opts.cg.codegen_units > 1);
850780

781+
let keep_numbered_objects = needs_crate_object ||
782+
(user_wants_objects && sess.opts.cg.codegen_units > 1);
783+
851784
for i in 0..trans.modules.len() {
852-
if modules_config.emit_obj {
785+
if modules_config.emit_obj && !keep_numbered_objects {
853786
let ext = format!("{}.o", i);
854-
remove(sess, &crate_output.with_extension(&ext[..]));
787+
remove(sess, &crate_output.with_extension(&ext));
855788
}
856789

857790
if modules_config.emit_bc && !keep_numbered_bitcode {
858791
let ext = format!("{}.bc", i);
859-
remove(sess, &crate_output.with_extension(&ext[..]));
792+
remove(sess, &crate_output.with_extension(&ext));
860793
}
861794
}
862795

src/test/run-make/extra-filename-with-temp-outputs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
all:
44
$(RUSTC) -C extra-filename=bar foo.rs -C save-temps
5-
rm $(TMPDIR)/foobar.o
5+
rm $(TMPDIR)/foobar.0.o
66
rm $(TMPDIR)/$(call BIN,foobar)

0 commit comments

Comments
 (0)