]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/global_asm.rs
Merge commit '7d53619064ab7045c383644cb445052d2a3d46db' into sync_cg_clif-2023-02-09
[rust.git] / compiler / rustc_codegen_cranelift / src / global_asm.rs
1 //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a
2 //! standalone executable.
3
4 use std::io::Write;
5 use std::path::PathBuf;
6 use std::process::{Command, Stdio};
7 use std::sync::Arc;
8
9 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
10 use rustc_hir::{InlineAsmOperand, ItemId};
11 use rustc_session::config::{OutputFilenames, OutputType};
12
13 use crate::prelude::*;
14
15 pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) {
16     let item = tcx.hir().item(item_id);
17     if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
18         if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
19             global_asm.push_str("\n.intel_syntax noprefix\n");
20         } else {
21             global_asm.push_str("\n.att_syntax\n");
22         }
23         for piece in asm.template {
24             match *piece {
25                 InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s),
26                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: op_sp } => {
27                     match asm.operands[operand_idx].0 {
28                         InlineAsmOperand::Const { ref anon_const } => {
29                             let const_value =
30                                 tcx.const_eval_poly(anon_const.def_id.to_def_id()).unwrap_or_else(
31                                     |_| span_bug!(op_sp, "asm const cannot be resolved"),
32                                 );
33                             let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
34                             let string = rustc_codegen_ssa::common::asm_const_to_str(
35                                 tcx,
36                                 op_sp,
37                                 const_value,
38                                 RevealAllLayoutCx(tcx).layout_of(ty),
39                             );
40                             global_asm.push_str(&string);
41                         }
42                         InlineAsmOperand::SymFn { anon_const } => {
43                             let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
44                             let instance = match ty.kind() {
45                                 &ty::FnDef(def_id, substs) => Instance::new(def_id, substs),
46                                 _ => span_bug!(op_sp, "asm sym is not a function"),
47                             };
48                             let symbol = tcx.symbol_name(instance);
49                             // FIXME handle the case where the function was made private to the
50                             // current codegen unit
51                             global_asm.push_str(symbol.name);
52                         }
53                         InlineAsmOperand::SymStatic { path: _, def_id } => {
54                             let instance = Instance::mono(tcx, def_id).polymorphize(tcx);
55                             let symbol = tcx.symbol_name(instance);
56                             global_asm.push_str(symbol.name);
57                         }
58                         InlineAsmOperand::In { .. }
59                         | InlineAsmOperand::Out { .. }
60                         | InlineAsmOperand::InOut { .. }
61                         | InlineAsmOperand::SplitInOut { .. } => {
62                             span_bug!(op_sp, "invalid operand type for global_asm!")
63                         }
64                     }
65                 }
66             }
67         }
68         global_asm.push_str("\n.att_syntax\n\n");
69     } else {
70         bug!("Expected GlobalAsm found {:?}", item);
71     }
72 }
73
74 #[derive(Debug)]
75 pub(crate) struct GlobalAsmConfig {
76     asm_enabled: bool,
77     assembler: PathBuf,
78     pub(crate) output_filenames: Arc<OutputFilenames>,
79 }
80
81 impl GlobalAsmConfig {
82     pub(crate) fn new(tcx: TyCtxt<'_>) -> Self {
83         let asm_enabled = cfg!(feature = "inline_asm") && !tcx.sess.target.is_like_windows;
84
85         GlobalAsmConfig {
86             asm_enabled,
87             assembler: crate::toolchain::get_toolchain_binary(tcx.sess, "as"),
88             output_filenames: tcx.output_filenames(()).clone(),
89         }
90     }
91 }
92
93 pub(crate) fn compile_global_asm(
94     config: &GlobalAsmConfig,
95     cgu_name: &str,
96     global_asm: &str,
97 ) -> Result<Option<PathBuf>, String> {
98     if global_asm.is_empty() {
99         return Ok(None);
100     }
101
102     if !config.asm_enabled {
103         if global_asm.contains("__rust_probestack") {
104             return Ok(None);
105         }
106
107         // FIXME fix linker error on macOS
108         if cfg!(not(feature = "inline_asm")) {
109             return Err(
110                 "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift"
111                     .to_owned(),
112             );
113         } else {
114             return Err("asm! and global_asm! are not yet supported on Windows".to_owned());
115         }
116     }
117
118     // Remove all LLVM style comments
119     let global_asm = global_asm
120         .lines()
121         .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line })
122         .collect::<Vec<_>>()
123         .join("\n");
124
125     let output_object_file = config.output_filenames.temp_path(OutputType::Object, Some(cgu_name));
126
127     // Assemble `global_asm`
128     let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm");
129     let mut child = Command::new(&config.assembler)
130         .arg("-o")
131         .arg(&global_asm_object_file)
132         .stdin(Stdio::piped())
133         .spawn()
134         .expect("Failed to spawn `as`.");
135     child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap();
136     let status = child.wait().expect("Failed to wait for `as`.");
137     if !status.success() {
138         return Err(format!("Failed to assemble `{}`", global_asm));
139     }
140
141     Ok(Some(global_asm_object_file))
142 }
143
144 pub(crate) fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf {
145     let mut new_filename = path.file_stem().unwrap().to_owned();
146     new_filename.push(postfix);
147     if let Some(extension) = path.extension() {
148         new_filename.push(".");
149         new_filename.push(extension);
150     }
151     path.set_file_name(new_filename);
152     path
153 }