Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 548c46f

Browse files
authored
Merge pull request rust-lang#1062 from bjorn3/global_asm
Implement global_asm! using an external assembler
2 parents 1987a3b + 8cf3818 commit 548c46f

File tree

8 files changed

+252
-19
lines changed

8 files changed

+252
-19
lines changed

example/mini_core.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ pub macro line() { /* compiler built-in */ }
562562
#[rustc_macro_transparency = "semitransparent"]
563563
pub macro cfg() { /* compiler built-in */ }
564564

565+
#[rustc_builtin_macro]
566+
#[rustc_macro_transparency = "semitransparent"]
567+
pub macro global_asm() { /* compiler built-in */ }
568+
565569
pub static A_STATIC: u8 = 42;
566570

567571
#[lang = "panic_location"]

example/mini_core_hello_world.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,26 @@ fn main() {
284284

285285
#[cfg(not(jit))]
286286
test_tls();
287+
288+
#[cfg(all(not(jit), target_os = "linux"))]
289+
unsafe {
290+
global_asm_test();
291+
}
292+
}
293+
294+
#[cfg(all(not(jit), target_os = "linux"))]
295+
extern "C" {
296+
fn global_asm_test();
297+
}
298+
299+
#[cfg(all(not(jit), target_os = "linux"))]
300+
global_asm! {
301+
"
302+
.global global_asm_test
303+
global_asm_test:
304+
// comment that would normally be removed by LLVM
305+
ret
306+
"
287307
}
288308

289309
#[repr(C)]

src/archive.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,10 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
220220
std::mem::drop(builder);
221221

222222
if self.update_symbols {
223+
let ranlib = crate::toolchain::get_toolchain_binary(self.config.sess, "ranlib");
224+
223225
// Run ranlib to be able to link the archive
224-
let status = std::process::Command::new("ranlib")
226+
let status = std::process::Command::new(ranlib)
225227
.arg(self.config.dst)
226228
.status()
227229
.expect("Couldn't run ranlib");

src/driver/aot.rs

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::path::PathBuf;
2+
13
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
24
use rustc_middle::middle::cstore::EncodedMetadata;
35
use rustc_middle::mir::mono::CodegenUnit;
@@ -110,19 +112,33 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege
110112

111113
let module = new_module(tcx, cgu_name.as_str().to_string());
112114

115+
let mut global_asm = Vec::new();
113116
let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None);
114-
super::codegen_mono_items(&mut cx, mono_items);
117+
super::codegen_mono_items(&mut cx, &mut global_asm, mono_items);
115118
let (mut module, debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize());
116119
crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context);
117120

118-
emit_module(
121+
let global_asm = global_asm.into_iter().map(|hir_id| {
122+
let item = tcx.hir().expect_item(hir_id);
123+
if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind {
124+
asm.as_str().to_string()
125+
} else {
126+
bug!("Expected GlobalAsm found {:?}", item);
127+
}
128+
}).collect::<Vec<String>>().join("\n");
129+
130+
let codegen_result = emit_module(
119131
tcx,
120132
cgu.name().as_str().to_string(),
121133
ModuleKind::Regular,
122134
module,
123135
debug,
124136
unwind_context,
125-
)
137+
);
138+
139+
codegen_global_asm(tcx, &cgu.name().as_str(), &global_asm);
140+
141+
codegen_result
126142
}
127143

128144
pub(super) fn run_aot(
@@ -253,6 +269,85 @@ pub(super) fn run_aot(
253269
}, work_products))
254270
}
255271

272+
fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) {
273+
use std::io::Write;
274+
use std::process::{Command, Stdio};
275+
276+
if global_asm.is_empty() {
277+
return;
278+
}
279+
280+
if tcx.sess.target.target.options.is_like_osx || tcx.sess.target.target.options.is_like_windows {
281+
if global_asm.contains("__rust_probestack") {
282+
return;
283+
}
284+
285+
// FIXME fix linker error on macOS
286+
tcx.sess.fatal("global_asm! is not yet supported on macOS and Windows");
287+
}
288+
289+
let assembler = crate::toolchain::get_toolchain_binary(tcx.sess, "as");
290+
let linker = crate::toolchain::get_toolchain_binary(tcx.sess, "ld");
291+
292+
// Remove all LLVM style comments
293+
let global_asm = global_asm.lines().map(|line| {
294+
if let Some(index) = line.find("//") {
295+
&line[0..index]
296+
} else {
297+
line
298+
}
299+
}).collect::<Vec<_>>().join("\n");
300+
301+
let output_object_file = tcx
302+
.output_filenames(LOCAL_CRATE)
303+
.temp_path(OutputType::Object, Some(cgu_name));
304+
305+
// Assemble `global_asm`
306+
let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm");
307+
let mut child = Command::new(assembler)
308+
.arg("-o").arg(&global_asm_object_file)
309+
.stdin(Stdio::piped())
310+
.spawn()
311+
.expect("Failed to spawn `as`.");
312+
child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap();
313+
let status = child.wait().expect("Failed to wait for `as`.");
314+
if !status.success() {
315+
tcx.sess.fatal(&format!("Failed to assemble `{}`", global_asm));
316+
}
317+
318+
// Link the global asm and main object file together
319+
let main_object_file = add_file_stem_postfix(output_object_file.clone(), ".main");
320+
std::fs::rename(&output_object_file, &main_object_file).unwrap();
321+
let status = Command::new(linker)
322+
.arg("-r") // Create a new object file
323+
.arg("-o").arg(output_object_file)
324+
.arg(&main_object_file)
325+
.arg(&global_asm_object_file)
326+
.status()
327+
.unwrap();
328+
if !status.success() {
329+
tcx.sess.fatal(&format!(
330+
"Failed to link `{}` and `{}` together",
331+
main_object_file.display(),
332+
global_asm_object_file.display(),
333+
));
334+
}
335+
336+
std::fs::remove_file(global_asm_object_file).unwrap();
337+
std::fs::remove_file(main_object_file).unwrap();
338+
}
339+
340+
fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf {
341+
let mut new_filename = path.file_stem().unwrap().to_owned();
342+
new_filename.push(postfix);
343+
if let Some(extension) = path.extension() {
344+
new_filename.push(".");
345+
new_filename.push(extension);
346+
}
347+
path.set_file_name(new_filename);
348+
path
349+
}
350+
256351
// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
257352
fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
258353
if !tcx.dep_graph.is_fully_enabled() {

src/driver/jit.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! {
5555
let mut cx = crate::CodegenCx::new(tcx, jit_module, false);
5656

5757
let (mut jit_module, _debug, mut unwind_context) = super::time(tcx, "codegen mono items", || {
58-
super::codegen_mono_items(&mut cx, mono_items);
58+
let mut global_asm = Vec::new();
59+
super::codegen_mono_items(&mut cx, &mut global_asm, mono_items);
60+
for hir_id in global_asm {
61+
let item = tcx.hir().expect_item(hir_id);
62+
tcx.sess.span_err(item.span, "Global asm is not supported in JIT mode");
63+
}
5964
tcx.sess.time("finalize CodegenCx", || cx.finalize())
6065
});
6166
crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context);

src/driver/mod.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::any::Any;
22

3+
use rustc_hir::HirId;
34
use rustc_middle::middle::cstore::EncodedMetadata;
45
use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility};
56

@@ -31,6 +32,7 @@ pub(crate) fn codegen_crate(
3132

3233
fn codegen_mono_items<'tcx>(
3334
cx: &mut crate::CodegenCx<'tcx, impl Backend + 'static>,
35+
global_asm: &mut Vec<HirId>,
3436
mono_items: Vec<(MonoItem<'tcx>, (RLinkage, Visibility))>,
3537
) {
3638
cx.tcx.sess.time("predefine functions", || {
@@ -49,12 +51,13 @@ fn codegen_mono_items<'tcx>(
4951

5052
for (mono_item, (linkage, visibility)) in mono_items {
5153
let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
52-
trans_mono_item(cx, mono_item, linkage);
54+
trans_mono_item(cx, global_asm, mono_item, linkage);
5355
}
5456
}
5557

5658
fn trans_mono_item<'tcx, B: Backend + 'static>(
5759
cx: &mut crate::CodegenCx<'tcx, B>,
60+
global_asm: &mut Vec<HirId>,
5861
mono_item: MonoItem<'tcx>,
5962
linkage: Linkage,
6063
) {
@@ -91,19 +94,7 @@ fn trans_mono_item<'tcx, B: Backend + 'static>(
9194
crate::constant::codegen_static(&mut cx.constants_cx, def_id);
9295
}
9396
MonoItem::GlobalAsm(hir_id) => {
94-
let item = tcx.hir().expect_item(hir_id);
95-
if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind {
96-
// FIXME implement global asm using an external assembler
97-
if asm.as_str().contains("__rust_probestack") {
98-
return;
99-
} else {
100-
tcx
101-
.sess
102-
.fatal(&format!("Unimplemented global asm mono item \"{}\"", asm));
103-
}
104-
} else {
105-
bug!("Expected GlobalAsm found {:?}", item);
106-
}
97+
global_asm.push(hir_id);
10798
}
10899
}
109100
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ mod optimize;
6464
mod pointer;
6565
mod pretty_clif;
6666
mod target_features_whitelist;
67+
mod toolchain;
6768
mod trap;
6869
mod unsize;
6970
mod value_and_place;

src/toolchain.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use std::path::PathBuf;
2+
3+
use rustc_middle::bug;
4+
use rustc_session::Session;
5+
use rustc_target::spec::LinkerFlavor;
6+
7+
/// Tries to infer the path of a binary for the target toolchain from the linker name.
8+
pub(crate) fn get_toolchain_binary(sess: &Session, tool: &str) -> PathBuf {
9+
let (mut linker, _linker_flavor) = linker_and_flavor(sess);
10+
let linker_file_name = linker.file_name().and_then(|name| name.to_str()).unwrap_or_else(|| {
11+
sess.fatal("couldn't extract file name from specified linker")
12+
});
13+
14+
if linker_file_name == "ld.lld" {
15+
if tool != "ld" {
16+
linker.set_file_name(tool)
17+
}
18+
} else {
19+
let tool_file_name = linker_file_name
20+
.replace("ld", tool)
21+
.replace("gcc", tool)
22+
.replace("clang", tool)
23+
.replace("cc", tool);
24+
25+
linker.set_file_name(tool_file_name)
26+
}
27+
28+
linker
29+
}
30+
31+
// Adapted from https://github.com/rust-lang/rust/blob/5db778affee7c6600c8e7a177c48282dab3f6292/src/librustc_codegen_ssa/back/link.rs#L848-L931
32+
fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
33+
fn infer_from(
34+
sess: &Session,
35+
linker: Option<PathBuf>,
36+
flavor: Option<LinkerFlavor>,
37+
) -> Option<(PathBuf, LinkerFlavor)> {
38+
match (linker, flavor) {
39+
(Some(linker), Some(flavor)) => Some((linker, flavor)),
40+
// only the linker flavor is known; use the default linker for the selected flavor
41+
(None, Some(flavor)) => Some((
42+
PathBuf::from(match flavor {
43+
LinkerFlavor::Em => {
44+
if cfg!(windows) {
45+
"emcc.bat"
46+
} else {
47+
"emcc"
48+
}
49+
}
50+
LinkerFlavor::Gcc => {
51+
if cfg!(any(target_os = "solaris", target_os = "illumos")) {
52+
// On historical Solaris systems, "cc" may have
53+
// been Sun Studio, which is not flag-compatible
54+
// with "gcc". This history casts a long shadow,
55+
// and many modern illumos distributions today
56+
// ship GCC as "gcc" without also making it
57+
// available as "cc".
58+
"gcc"
59+
} else {
60+
"cc"
61+
}
62+
}
63+
LinkerFlavor::Ld => "ld",
64+
LinkerFlavor::Msvc => "link.exe",
65+
LinkerFlavor::Lld(_) => "lld",
66+
LinkerFlavor::PtxLinker => "rust-ptx-linker",
67+
}),
68+
flavor,
69+
)),
70+
(Some(linker), None) => {
71+
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
72+
sess.fatal("couldn't extract file stem from specified linker")
73+
});
74+
75+
let flavor = if stem == "emcc" {
76+
LinkerFlavor::Em
77+
} else if stem == "gcc"
78+
|| stem.ends_with("-gcc")
79+
|| stem == "clang"
80+
|| stem.ends_with("-clang")
81+
{
82+
LinkerFlavor::Gcc
83+
} else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
84+
LinkerFlavor::Ld
85+
} else if stem == "link" || stem == "lld-link" {
86+
LinkerFlavor::Msvc
87+
} else if stem == "lld" || stem == "rust-lld" {
88+
LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
89+
} else {
90+
// fall back to the value in the target spec
91+
sess.target.target.linker_flavor
92+
};
93+
94+
Some((linker, flavor))
95+
}
96+
(None, None) => None,
97+
}
98+
}
99+
100+
// linker and linker flavor specified via command line have precedence over what the target
101+
// specification specifies
102+
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) {
103+
return ret;
104+
}
105+
106+
if let Some(ret) = infer_from(
107+
sess,
108+
sess.target.target.options.linker.clone().map(PathBuf::from),
109+
Some(sess.target.target.linker_flavor),
110+
) {
111+
return ret;
112+
}
113+
114+
bug!("Not enough information provided to determine how to invoke the linker");
115+
}

0 commit comments

Comments
 (0)