]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/inline_asm.rs
Sync rustc_codegen_cranelift 'ddd4ce25535cf71203ba3700896131ce55fde795'
[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>,
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     } else if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) {
24         let true_ = fx.bcx.ins().iconst(types::I32, 1);
25         fx.bcx.ins().trapnz(true_, TrapCode::User(1));
26         return;
27     } else if template[0] == InlineAsmTemplatePiece::String("mov rsi, rbx".to_string())
28         && template[1] == InlineAsmTemplatePiece::String("\n".to_string())
29         && template[2] == InlineAsmTemplatePiece::String("cpuid".to_string())
30         && template[3] == InlineAsmTemplatePiece::String("\n".to_string())
31         && template[4] == InlineAsmTemplatePiece::String("xchg rsi, rbx".to_string())
32     {
33         assert_eq!(operands.len(), 4);
34         let (leaf, eax_place) = match operands[0] {
35             InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
36                 let reg = expect_reg(reg);
37                 assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::ax));
38                 (
39                     crate::base::codegen_operand(fx, in_value).load_scalar(fx),
40                     crate::base::codegen_place(fx, out_place.unwrap()),
41                 )
42             }
43             _ => unreachable!(),
44         };
45         let ebx_place = match operands[1] {
46             InlineAsmOperand::Out { reg, late: true, place } => {
47                 let reg = expect_reg(reg);
48                 assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::si));
49                 crate::base::codegen_place(fx, place.unwrap())
50             }
51             _ => unreachable!(),
52         };
53         let (sub_leaf, ecx_place) = match operands[2] {
54             InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
55                 let reg = expect_reg(reg);
56                 assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::cx));
57                 (
58                     crate::base::codegen_operand(fx, in_value).load_scalar(fx),
59                     crate::base::codegen_place(fx, out_place.unwrap()),
60                 )
61             }
62             _ => unreachable!(),
63         };
64         let edx_place = match operands[3] {
65             InlineAsmOperand::Out { reg, late: true, place } => {
66                 let reg = expect_reg(reg);
67                 assert_eq!(reg, InlineAsmReg::X86(X86InlineAsmReg::dx));
68                 crate::base::codegen_place(fx, place.unwrap())
69             }
70             _ => unreachable!(),
71         };
72
73         let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf);
74
75         eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32)));
76         ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32)));
77         ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32)));
78         edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32)));
79         return;
80     } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") {
81         // ___chkstk, ___chkstk_ms and __alloca are only used on Windows
82         crate::trap::trap_unimplemented(fx, "Stack probes are not supported");
83     } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" {
84         crate::trap::trap_unimplemented(fx, "Alloca is not supported");
85     }
86
87     let mut slot_size = Size::from_bytes(0);
88     let mut clobbered_regs = Vec::new();
89     let mut inputs = Vec::new();
90     let mut outputs = Vec::new();
91
92     let mut new_slot = |reg_class: InlineAsmRegClass| {
93         let reg_size = reg_class
94             .supported_types(InlineAsmArch::X86_64)
95             .iter()
96             .map(|(ty, _)| ty.size())
97             .max()
98             .unwrap();
99         let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap();
100         slot_size = slot_size.align_to(align);
101         let offset = slot_size;
102         slot_size += reg_size;
103         offset
104     };
105
106     // FIXME overlap input and output slots to save stack space
107     for operand in operands {
108         match *operand {
109             InlineAsmOperand::In { reg, ref value } => {
110                 let reg = expect_reg(reg);
111                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
112                 inputs.push((
113                     reg,
114                     new_slot(reg.reg_class()),
115                     crate::base::codegen_operand(fx, value).load_scalar(fx),
116                 ));
117             }
118             InlineAsmOperand::Out { reg, late: _, place } => {
119                 let reg = expect_reg(reg);
120                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
121                 if let Some(place) = place {
122                     outputs.push((
123                         reg,
124                         new_slot(reg.reg_class()),
125                         crate::base::codegen_place(fx, place),
126                     ));
127                 }
128             }
129             InlineAsmOperand::InOut { reg, late: _, ref in_value, out_place } => {
130                 let reg = expect_reg(reg);
131                 clobbered_regs.push((reg, new_slot(reg.reg_class())));
132                 inputs.push((
133                     reg,
134                     new_slot(reg.reg_class()),
135                     crate::base::codegen_operand(fx, in_value).load_scalar(fx),
136                 ));
137                 if let Some(out_place) = out_place {
138                     outputs.push((
139                         reg,
140                         new_slot(reg.reg_class()),
141                         crate::base::codegen_place(fx, out_place),
142                     ));
143                 }
144             }
145             InlineAsmOperand::Const { value: _ } => todo!(),
146             InlineAsmOperand::SymFn { value: _ } => todo!(),
147             InlineAsmOperand::SymStatic { def_id: _ } => todo!(),
148         }
149     }
150
151     let inline_asm_index = fx.inline_asm_index;
152     fx.inline_asm_index += 1;
153     let asm_name = format!("{}__inline_asm_{}", fx.symbol_name, inline_asm_index);
154
155     let generated_asm = generate_asm_wrapper(
156         &asm_name,
157         InlineAsmArch::X86_64,
158         options,
159         template,
160         clobbered_regs,
161         &inputs,
162         &outputs,
163     );
164     fx.cx.global_asm.push_str(&generated_asm);
165
166     call_inline_asm(fx, &asm_name, slot_size, inputs, outputs);
167 }
168
169 fn generate_asm_wrapper(
170     asm_name: &str,
171     arch: InlineAsmArch,
172     options: InlineAsmOptions,
173     template: &[InlineAsmTemplatePiece],
174     clobbered_regs: Vec<(InlineAsmReg, Size)>,
175     inputs: &[(InlineAsmReg, Size, Value)],
176     outputs: &[(InlineAsmReg, Size, CPlace<'_>)],
177 ) -> String {
178     let mut generated_asm = String::new();
179     writeln!(generated_asm, ".globl {}", asm_name).unwrap();
180     writeln!(generated_asm, ".type {},@function", asm_name).unwrap();
181     writeln!(generated_asm, ".section .text.{},\"ax\",@progbits", asm_name).unwrap();
182     writeln!(generated_asm, "{}:", asm_name).unwrap();
183
184     generated_asm.push_str(".intel_syntax noprefix\n");
185     generated_asm.push_str("    push rbp\n");
186     generated_asm.push_str("    mov rbp,rdi\n");
187
188     // Save clobbered registers
189     if !options.contains(InlineAsmOptions::NORETURN) {
190         // FIXME skip registers saved by the calling convention
191         for &(reg, offset) in &clobbered_regs {
192             save_register(&mut generated_asm, arch, reg, offset);
193         }
194     }
195
196     // Write input registers
197     for &(reg, offset, _value) in inputs {
198         restore_register(&mut generated_asm, arch, reg, offset);
199     }
200
201     if options.contains(InlineAsmOptions::ATT_SYNTAX) {
202         generated_asm.push_str(".att_syntax\n");
203     }
204
205     // The actual inline asm
206     for piece in template {
207         match piece {
208             InlineAsmTemplatePiece::String(s) => {
209                 generated_asm.push_str(s);
210             }
211             InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span: _ } => todo!(),
212         }
213     }
214     generated_asm.push('\n');
215
216     if options.contains(InlineAsmOptions::ATT_SYNTAX) {
217         generated_asm.push_str(".intel_syntax noprefix\n");
218     }
219
220     if !options.contains(InlineAsmOptions::NORETURN) {
221         // Read output registers
222         for &(reg, offset, _place) in outputs {
223             save_register(&mut generated_asm, arch, reg, offset);
224         }
225
226         // Restore clobbered registers
227         for &(reg, offset) in clobbered_regs.iter().rev() {
228             restore_register(&mut generated_asm, arch, reg, offset);
229         }
230
231         generated_asm.push_str("    pop rbp\n");
232         generated_asm.push_str("    ret\n");
233     } else {
234         generated_asm.push_str("    ud2\n");
235     }
236
237     generated_asm.push_str(".att_syntax\n");
238     writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap();
239     generated_asm.push_str(".text\n");
240     generated_asm.push_str("\n\n");
241
242     generated_asm
243 }
244
245 fn call_inline_asm<'tcx>(
246     fx: &mut FunctionCx<'_, '_, 'tcx>,
247     asm_name: &str,
248     slot_size: Size,
249     inputs: Vec<(InlineAsmReg, Size, Value)>,
250     outputs: Vec<(InlineAsmReg, Size, CPlace<'tcx>)>,
251 ) {
252     let stack_slot = fx.bcx.func.create_stack_slot(StackSlotData {
253         kind: StackSlotKind::ExplicitSlot,
254         offset: None,
255         size: u32::try_from(slot_size.bytes()).unwrap(),
256     });
257     if fx.clif_comments.enabled() {
258         fx.add_comment(stack_slot, "inline asm scratch slot");
259     }
260
261     let inline_asm_func = fx
262         .module
263         .declare_function(
264             asm_name,
265             Linkage::Import,
266             &Signature {
267                 call_conv: CallConv::SystemV,
268                 params: vec![AbiParam::new(fx.pointer_type)],
269                 returns: vec![],
270             },
271         )
272         .unwrap();
273     let inline_asm_func = fx.module.declare_func_in_func(inline_asm_func, &mut fx.bcx.func);
274     if fx.clif_comments.enabled() {
275         fx.add_comment(inline_asm_func, asm_name);
276     }
277
278     for (_reg, offset, value) in inputs {
279         fx.bcx.ins().stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap());
280     }
281
282     let stack_slot_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0);
283     fx.bcx.ins().call(inline_asm_func, &[stack_slot_addr]);
284
285     for (_reg, offset, place) in outputs {
286         let ty = fx.clif_type(place.layout().ty).unwrap();
287         let value = fx.bcx.ins().stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap());
288         place.write_cvalue(fx, CValue::by_val(value, place.layout()));
289     }
290 }
291
292 fn expect_reg(reg_or_class: InlineAsmRegOrRegClass) -> InlineAsmReg {
293     match reg_or_class {
294         InlineAsmRegOrRegClass::Reg(reg) => reg,
295         InlineAsmRegOrRegClass::RegClass(class) => unimplemented!("{:?}", class),
296     }
297 }
298
299 fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsmReg, offset: Size) {
300     match arch {
301         InlineAsmArch::X86_64 => {
302             write!(generated_asm, "    mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
303             reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
304             generated_asm.push('\n');
305         }
306         _ => unimplemented!("save_register for {:?}", arch),
307     }
308 }
309
310 fn restore_register(
311     generated_asm: &mut String,
312     arch: InlineAsmArch,
313     reg: InlineAsmReg,
314     offset: Size,
315 ) {
316     match arch {
317         InlineAsmArch::X86_64 => {
318             generated_asm.push_str("    mov ");
319             reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
320             writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
321         }
322         _ => unimplemented!("restore_register for {:?}", arch),
323     }
324 }