]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/inline_asm.rs
Auto merge of #78833 - CDirkx:parse_prefix, r=dtolnay
[rust.git] / compiler / rustc_codegen_cranelift / src / inline_asm.rs
1 //! Codegen of [`asm!`] invocations.
2
3 use crate::prelude::*;
4
5 use std::fmt::Write;
6
7 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
8 use rustc_middle::mir::InlineAsmOperand;
9 use rustc_target::asm::*;
10
11 pub(crate) fn codegen_inline_asm<'tcx>(
12     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
13     _span: Span,
14     template: &[InlineAsmTemplatePiece],
15     operands: &[InlineAsmOperand<'tcx>],
16     options: InlineAsmOptions,
17 ) {
18     // FIXME add .eh_frame unwind info directives
19
20     if template.is_empty() {
21         // Black box
22         return;
23     }
24
25     let mut slot_size = Size::from_bytes(0);
26     let mut clobbered_regs = Vec::new();
27     let mut inputs = Vec::new();
28     let mut outputs = Vec::new();
29
30     let mut new_slot = |reg_class: InlineAsmRegClass| {
31         let reg_size = reg_class
32             .supported_types(InlineAsmArch::X86_64)
33             .iter()
34             .map(|(ty, _)| ty.size())
35             .max()
36             .unwrap();
37         let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
38         slot_size = slot_size.align_to(align);
39         let offset = slot_size;
40         slot_size += reg_size;
41         offset
42     };
43
44     // FIXME overlap input and output slots to save stack space
45     for operand in operands {
46         match *operand {
47             InlineAsmOperand::In { reg, ref value } => {
48                 let reg = expect_reg(reg);
49                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
50                 inputs.push((
51                     reg,
52                     new_slot(reg.reg_class()),
53                     crate::base::codegen_operand(fx, value).load_scalar(fx),
54                 ));
55             }
56             InlineAsmOperand::Out {
57                 reg,
58                 late: _,
59                 place,
60             } => {
61                 let reg = expect_reg(reg);
62                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
63                 if let Some(place) = place {
64                     outputs.push((
65                         reg,
66                         new_slot(reg.reg_class()),
67                         crate::base::codegen_place(fx, place),
68                     ));
69                 }
70             }
71             InlineAsmOperand::InOut {
72                 reg,
73                 late: _,
74                 ref in_value,
75                 out_place,
76             } => {
77                 let reg = expect_reg(reg);
78                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
79                 inputs.push((
80                     reg,
81                     new_slot(reg.reg_class()),
82                     crate::base::codegen_operand(fx, in_value).load_scalar(fx),
83                 ));
84                 if let Some(out_place) = out_place {
85                     outputs.push((
86                         reg,
87                         new_slot(reg.reg_class()),
88                         crate::base::codegen_place(fx, out_place),
89                     ));
90                 }
91             }
92             InlineAsmOperand::Const { value: _ } => todo!(),
93             InlineAsmOperand::SymFn { value: _ } => todo!(),
94             InlineAsmOperand::SymStatic { def_id: _ } => todo!(),
95         }
96     }
97
98     let inline_asm_index = fx.inline_asm_index;
99     fx.inline_asm_index += 1;
100     let asm_name = format!(
101         "{}__inline_asm_{}",
102         fx.tcx.symbol_name(fx.instance).name,
103         inline_asm_index
104     );
105
106     let generated_asm = generate_asm_wrapper(
107         &asm_name,
108         InlineAsmArch::X86_64,
109         options,
110         template,
111         clobbered_regs,
112         &inputs,
113         &outputs,
114     );
115     fx.cx.global_asm.push_str(&generated_asm);
116
117     call_inline_asm(fx, &asm_name, slot_size, inputs, outputs);
118 }
119
120 fn generate_asm_wrapper(
121     asm_name: &str,
122     arch: InlineAsmArch,
123     options: InlineAsmOptions,
124     template: &[InlineAsmTemplatePiece],
125     clobbered_regs: Vec<(InlineAsmReg, Size)>,
126     inputs: &[(InlineAsmReg, Size, Value)],
127     outputs: &[(InlineAsmReg, Size, CPlace<'_>)],
128 ) -> String {
129     let mut generated_asm = String::new();
130     writeln!(generated_asm, ".globl {}", asm_name).unwrap();
131     writeln!(generated_asm, ".type {},@function", asm_name).unwrap();
132     writeln!(
133         generated_asm,
134         ".section .text.{},\"ax\",@progbits",
135         asm_name
136     )
137     .unwrap();
138     writeln!(generated_asm, "{}:", asm_name).unwrap();
139
140     generated_asm.push_str(".intel_syntax noprefix\n");
141     generated_asm.push_str("    push rbp\n");
142     generated_asm.push_str("    mov rbp,rdi\n");
143
144     // Save clobbered registers
145     if !options.contains(InlineAsmOptions::NORETURN) {
146         // FIXME skip registers saved by the calling convention
147         for &(reg, offset) in &clobbered_regs {
148             save_register(&mut generated_asm, arch, reg, offset);
149         }
150     }
151
152     // Write input registers
153     for &(reg, offset, _value) in inputs {
154         restore_register(&mut generated_asm, arch, reg, offset);
155     }
156
157     if options.contains(InlineAsmOptions::ATT_SYNTAX) {
158         generated_asm.push_str(".att_syntax\n");
159     }
160
161     // The actual inline asm
162     for piece in template {
163         match piece {
164             InlineAsmTemplatePiece::String(s) => {
165                 generated_asm.push_str(s);
166             }
167             InlineAsmTemplatePiece::Placeholder {
168                 operand_idx: _,
169                 modifier: _,
170                 span: _,
171             } => todo!(),
172         }
173     }
174     generated_asm.push('\n');
175
176     if options.contains(InlineAsmOptions::ATT_SYNTAX) {
177         generated_asm.push_str(".intel_syntax noprefix\n");
178     }
179
180     if !options.contains(InlineAsmOptions::NORETURN) {
181         // Read output registers
182         for &(reg, offset, _place) in outputs {
183             save_register(&mut generated_asm, arch, reg, offset);
184         }
185
186         // Restore clobbered registers
187         for &(reg, offset) in clobbered_regs.iter().rev() {
188             restore_register(&mut generated_asm, arch, reg, offset);
189         }
190
191         generated_asm.push_str("    pop rbp\n");
192         generated_asm.push_str("    ret\n");
193     } else {
194         generated_asm.push_str("    ud2\n");
195     }
196
197     generated_asm.push_str(".att_syntax\n");
198     writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap();
199     generated_asm.push_str(".text\n");
200     generated_asm.push_str("\n\n");
201
202     generated_asm
203 }
204
205 fn call_inline_asm<'tcx>(
206     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
207     asm_name: &str,
208     slot_size: Size,
209     inputs: Vec<(InlineAsmReg, Size, Value)>,
210     outputs: Vec<(InlineAsmReg, Size, CPlace<'tcx>)>,
211 ) {
212     let stack_slot = fx.bcx.func.create_stack_slot(StackSlotData {
213         kind: StackSlotKind::ExplicitSlot,
214         offset: None,
215         size: u32::try_from(slot_size.bytes()).unwrap(),
216     });
217     #[cfg(debug_assertions)]
218     fx.add_comment(stack_slot, "inline asm scratch slot");
219
220     let inline_asm_func = fx
221         .cx
222         .module
223         .declare_function(
224             asm_name,
225             Linkage::Import,
226             &Signature {
227                 call_conv: CallConv::SystemV,
228                 params: vec![AbiParam::new(fx.pointer_type)],
229                 returns: vec![],
230             },
231         )
232         .unwrap();
233     let inline_asm_func = fx
234         .cx
235         .module
236         .declare_func_in_func(inline_asm_func, &mut fx.bcx.func);
237     #[cfg(debug_assertions)]
238     fx.add_comment(inline_asm_func, asm_name);
239
240     for (_reg, offset, value) in inputs {
241         fx.bcx
242             .ins()
243             .stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap());
244     }
245
246     let stack_slot_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0);
247     fx.bcx.ins().call(inline_asm_func, &[stack_slot_addr]);
248
249     for (_reg, offset, place) in outputs {
250         let ty = fx.clif_type(place.layout().ty).unwrap();
251         let value = fx
252             .bcx
253             .ins()
254             .stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap());
255         place.write_cvalue(fx, CValue::by_val(value, place.layout()));
256     }
257 }
258
259 fn expect_reg(reg_or_class: InlineAsmRegOrRegClass) -> InlineAsmReg {
260     match reg_or_class {
261         InlineAsmRegOrRegClass::Reg(reg) => reg,
262         InlineAsmRegOrRegClass::RegClass(class) => unimplemented!("{:?}", class),
263     }
264 }
265
266 fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsmReg, offset: Size) {
267     match arch {
268         InlineAsmArch::X86_64 => {
269             write!(generated_asm, "    mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
270             reg.emit(generated_asm, InlineAsmArch::X86_64, None)
271                 .unwrap();
272             generated_asm.push('\n');
273         }
274         _ => unimplemented!("save_register for {:?}", arch),
275     }
276 }
277
278 fn restore_register(
279     generated_asm: &mut String,
280     arch: InlineAsmArch,
281     reg: InlineAsmReg,
282     offset: Size,
283 ) {
284     match arch {
285         InlineAsmArch::X86_64 => {
286             generated_asm.push_str("    mov ");
287             reg.emit(generated_asm, InlineAsmArch::X86_64, None)
288                 .unwrap();
289             writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
290         }
291         _ => unimplemented!("restore_register for {:?}", arch),
292     }
293 }