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