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