• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use gccjit::{LValue, RValue, ToRValue, Type};
2 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
3 use rustc_codegen_ssa::mir::operand::OperandValue;
4 use rustc_codegen_ssa::mir::place::PlaceRef;
5 use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
6 
7 use rustc_middle::{bug, ty::Instance};
8 use rustc_span::Span;
9 use rustc_target::asm::*;
10 
11 use std::borrow::Cow;
12 
13 use crate::builder::Builder;
14 use crate::context::CodegenCx;
15 use crate::errors::UnwindingInlineAsm;
16 use crate::type_of::LayoutGccExt;
17 use crate::callee::get_fn;
18 
19 
20 // Rust asm! and GCC Extended Asm semantics differ substantially.
21 //
22 // 1. Rust asm operands go along as one list of operands. Operands themselves indicate
23 //    if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
24 //    both "in" and "out" (`inout(reg)`).
25 //
26 //    GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
27 //    this means that all "out" operands must go before "in" operands. "In" and "out" operands
28 //    cannot interleave.
29 //
30 // 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
31 //    because the asm template refers to operands by index.
32 //
33 //    Mapping from Rust to GCC index would be 1-1 if it wasn't for...
34 //
35 // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
36 //    Contrary, Rust expresses clobbers through "out" operands that aren't tied to
37 //    a variable (`_`),  and such "clobbers" do have index.
38 //
39 // 4. Furthermore, GCC Extended Asm does not support explicit register constraints
40 //    (like `out("eax")`) directly, offering so-called "local register variables"
41 //    as a workaround. These variables need to be declared and initialized *before*
42 //    the Extended Asm block but *after* normal local variables
43 //    (see comment in `codegen_inline_asm` for explanation).
44 //
45 // With that in mind, let's see how we translate Rust syntax to GCC
46 // (from now on, `CC` stands for "constraint code"):
47 //
48 // * `out(reg_class) var`   -> translated to output operand: `"=CC"(var)`
49 // * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)`
50 // * `in(reg_class) var`    -> translated to input operand: `"CC"(var)`
51 //
52 // * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
53 //
54 // * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
55 //
56 // * `inout(reg_class) in_var => out_var` -> translated to two operands:
57 //                              output: `"=CC"(in_var)`
58 //                              input:  `"num"(out_var)` where num is the GCC index
59 //                                       of the corresponding output operand
60 //
61 // * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
62 //                                      where "tmp" is a temporary unused variable
63 //
64 // * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
65 //                                              with `"r"(var)` constraint,
66 //                                              and one register variable assigned to the desired register.
67 
68 const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
69 const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
70 
71 
72 struct AsmOutOperand<'a, 'tcx, 'gcc> {
73     rust_idx: usize,
74     constraint: &'a str,
75     late: bool,
76     readwrite: bool,
77 
78     tmp_var: LValue<'gcc>,
79     out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
80 }
81 
82 struct AsmInOperand<'a, 'tcx> {
83     rust_idx: usize,
84     constraint: Cow<'a, str>,
85     val: RValue<'tcx>
86 }
87 
88 impl AsmOutOperand<'_, '_, '_> {
to_constraint(&self) -> String89     fn to_constraint(&self) -> String {
90         let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1);
91 
92         let sign = if self.readwrite { '+' } else { '=' };
93         res.push(sign);
94         if !self.late {
95             res.push('&');
96         }
97 
98         res.push_str(&self.constraint);
99         res
100     }
101 }
102 
103 enum ConstraintOrRegister {
104     Constraint(&'static str),
105     Register(&'static str)
106 }
107 
108 
109 impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], _instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>)110     fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], _instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) {
111         if options.contains(InlineAsmOptions::MAY_UNWIND) {
112             self.sess()
113                 .create_err(UnwindingInlineAsm { span: span[0] })
114                 .emit();
115             return;
116         }
117 
118         let asm_arch = self.tcx.sess.asm_arch.unwrap();
119         let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
120         let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
121 
122         // GCC index of an output operand equals its position in the array
123         let mut outputs = vec![];
124 
125         // GCC index of an input operand equals its position in the array
126         // added to `outputs.len()`
127         let mut inputs = vec![];
128 
129         // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
130         let mut clobbers = vec![];
131 
132         // We're trying to preallocate space for the template
133         let mut constants_len = 0;
134 
135         // There are rules we must adhere to if we want GCC to do the right thing:
136         //
137         // * Every local variable that the asm block uses as an output must be declared *before*
138         //   the asm block.
139         // * There must be no instructions whatsoever between the register variables and the asm.
140         //
141         // Therefore, the backend must generate the instructions strictly in this order:
142         //
143         // 1. Output variables.
144         // 2. Register variables.
145         // 3. The asm block.
146         //
147         // We also must make sure that no input operands are emitted before output operands.
148         //
149         // This is why we work in passes, first emitting local vars, then local register vars.
150         // Also, we don't emit any asm operands immediately; we save them to
151         // the one of the buffers to be emitted later.
152 
153         // 1. Normal variables (and saving operands to buffers).
154         for (rust_idx, op) in rust_operands.iter().enumerate() {
155             match *op {
156                 InlineAsmOperandRef::Out { reg, late, place } => {
157                     use ConstraintOrRegister::*;
158 
159                     let (constraint, ty) = match (reg_to_gcc(reg), place) {
160                         (Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx)),
161                         // When `reg` is a class and not an explicit register but the out place is not specified,
162                         // we need to create an unused output variable to assign the output to. This var
163                         // needs to be of a type that's "compatible" with the register class, but specific type
164                         // doesn't matter.
165                         (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
166                         (Register(_), Some(_)) => {
167                             // left for the next pass
168                             continue
169                         },
170                         (Register(reg_name), None) => {
171                             // `clobber_abi` can add lots of clobbers that are not supported by the target,
172                             // such as AVX-512 registers, so we just ignore unsupported registers
173                             let is_target_supported = reg.reg_class().supported_types(asm_arch).iter()
174                                 .any(|&(_, feature)| {
175                                     if let Some(feature) = feature {
176                                         self.tcx.sess.target_features.contains(&feature)
177                                     } else {
178                                         true // Register class is unconditionally supported
179                                     }
180                                 });
181 
182                             if is_target_supported && !clobbers.contains(&reg_name) {
183                                 clobbers.push(reg_name);
184                             }
185                             continue
186                         }
187                     };
188 
189                     let tmp_var = self.current_func().new_local(None, ty, "output_register");
190                     outputs.push(AsmOutOperand {
191                         constraint,
192                         rust_idx,
193                         late,
194                         readwrite: false,
195                         tmp_var,
196                         out_place: place
197                     });
198                 }
199 
200                 InlineAsmOperandRef::In { reg, value } => {
201                     if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
202                         inputs.push(AsmInOperand {
203                             constraint: Cow::Borrowed(constraint),
204                             rust_idx,
205                             val: value.immediate()
206                         });
207                     }
208                     else {
209                         // left for the next pass
210                         continue
211                     }
212                 }
213 
214                 InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
215                     let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
216                         constraint
217                     }
218                     else {
219                         // left for the next pass
220                         continue
221                     };
222 
223                     // Rustc frontend guarantees that input and output types are "compatible",
224                     // so we can just use input var's type for the output variable.
225                     //
226                     // This decision is also backed by the fact that LLVM needs in and out
227                     // values to be of *exactly the same type*, not just "compatible".
228                     // I'm not sure if GCC is so picky too, but better safe than sorry.
229                     let ty = in_value.layout.gcc_type(self.cx);
230                     let tmp_var = self.current_func().new_local(None, ty, "output_register");
231 
232                     // If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
233                     // it to one "readwrite (+) output variable", otherwise we translate it to two
234                     // "out and tied in" vars as described above.
235                     let readwrite = out_place.is_none();
236                     outputs.push(AsmOutOperand {
237                         constraint,
238                         rust_idx,
239                         late,
240                         readwrite,
241                         tmp_var,
242                         out_place,
243                     });
244 
245                     if !readwrite {
246                         let out_gcc_idx = outputs.len() - 1;
247                         let constraint = Cow::Owned(out_gcc_idx.to_string());
248 
249                         inputs.push(AsmInOperand {
250                             constraint,
251                             rust_idx,
252                             val: in_value.immediate()
253                         });
254                     }
255                 }
256 
257                 InlineAsmOperandRef::Const { ref string } => {
258                     constants_len += string.len() + att_dialect as usize;
259                 }
260 
261                 InlineAsmOperandRef::SymFn { instance } => {
262                     // TODO(@Amanieu): Additional mangling is needed on
263                     // some targets to add a leading underscore (Mach-O)
264                     // or byte count suffixes (x86 Windows).
265                     constants_len += self.tcx.symbol_name(instance).name.len();
266                 }
267                 InlineAsmOperandRef::SymStatic { def_id } => {
268                     // TODO(@Amanieu): Additional mangling is needed on
269                     // some targets to add a leading underscore (Mach-O).
270                     constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
271                 }
272             }
273         }
274 
275         // 2. Register variables.
276         for (rust_idx, op) in rust_operands.iter().enumerate() {
277             match *op {
278                 // `out("explicit register") var`
279                 InlineAsmOperandRef::Out { reg, late, place } => {
280                     if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
281                         let out_place = if let Some(place) = place {
282                             place
283                         }
284                         else {
285                             // processed in the previous pass
286                             continue
287                         };
288 
289                         let ty = out_place.layout.gcc_type(self.cx);
290                         let tmp_var = self.current_func().new_local(None, ty, "output_register");
291                         tmp_var.set_register_name(reg_name);
292 
293                         outputs.push(AsmOutOperand {
294                             constraint: "r".into(),
295                             rust_idx,
296                             late,
297                             readwrite: false,
298                             tmp_var,
299                             out_place: Some(out_place)
300                         });
301                     }
302 
303                     // processed in the previous pass
304                 }
305 
306                 // `in("explicit register") var`
307                 InlineAsmOperandRef::In { reg, value } => {
308                     if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
309                         let ty = value.layout.gcc_type(self.cx);
310                         let reg_var = self.current_func().new_local(None, ty, "input_register");
311                         reg_var.set_register_name(reg_name);
312                         self.llbb().add_assignment(None, reg_var, value.immediate());
313 
314                         inputs.push(AsmInOperand {
315                             constraint: "r".into(),
316                             rust_idx,
317                             val: reg_var.to_rvalue()
318                         });
319                     }
320 
321                     // processed in the previous pass
322                 }
323 
324                 // `inout("explicit register") in_var => out_var`
325                 InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
326                     if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
327                         // See explanation in the first pass.
328                         let ty = in_value.layout.gcc_type(self.cx);
329                         let tmp_var = self.current_func().new_local(None, ty, "output_register");
330                         tmp_var.set_register_name(reg_name);
331 
332                         outputs.push(AsmOutOperand {
333                             constraint: "r".into(),
334                             rust_idx,
335                             late,
336                             readwrite: false,
337                             tmp_var,
338                             out_place,
339                         });
340 
341                         let constraint = Cow::Owned((outputs.len() - 1).to_string());
342                         inputs.push(AsmInOperand {
343                             constraint,
344                             rust_idx,
345                             val: in_value.immediate()
346                         });
347                     }
348 
349                     // processed in the previous pass
350                 }
351 
352                 InlineAsmOperandRef::SymFn { instance } => {
353                     inputs.push(AsmInOperand {
354                         constraint: "X".into(),
355                         rust_idx,
356                         val: get_fn(self.cx, instance).get_address(None),
357                     });
358                 }
359 
360                 InlineAsmOperandRef::SymStatic { def_id } => {
361                     inputs.push(AsmInOperand {
362                         constraint: "X".into(),
363                         rust_idx,
364                         val: self.cx.get_static(def_id).get_address(None),
365                     });
366                 }
367 
368                 InlineAsmOperandRef::Const { .. } => {
369                     // processed in the previous pass
370                 }
371             }
372         }
373 
374         // 3. Build the template string
375 
376         let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
377         if att_dialect {
378             template_str.push_str(ATT_SYNTAX_INS);
379         }
380 
381         for piece in template {
382             match *piece {
383                 InlineAsmTemplatePiece::String(ref string) => {
384                     for char in string.chars() {
385                         // TODO(antoyo): might also need to escape | if rustc doesn't do it.
386                         let escaped_char =
387                             match char {
388                                 '%' => "%%",
389                                 '{' => "%{",
390                                 '}' => "%}",
391                                 _ => {
392                                     template_str.push(char);
393                                     continue;
394                                 },
395                             };
396                         template_str.push_str(escaped_char);
397                     }
398                 }
399                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
400                     let mut push_to_template = |modifier, gcc_idx| {
401                         use std::fmt::Write;
402 
403                         template_str.push('%');
404                         if let Some(modifier) = modifier {
405                             template_str.push(modifier);
406                         }
407                         write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
408                     };
409 
410                     match rust_operands[operand_idx] {
411                         InlineAsmOperandRef::Out { reg, ..  } => {
412                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
413                             let gcc_index = outputs.iter()
414                                 .position(|op| operand_idx == op.rust_idx)
415                                 .expect("wrong rust index");
416                             push_to_template(modifier, gcc_index);
417                         }
418 
419                         InlineAsmOperandRef::In { reg, .. } => {
420                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
421                             let in_gcc_index = inputs.iter()
422                                 .position(|op| operand_idx == op.rust_idx)
423                                 .expect("wrong rust index");
424                             let gcc_index = in_gcc_index + outputs.len();
425                             push_to_template(modifier, gcc_index);
426                         }
427 
428                         InlineAsmOperandRef::InOut { reg, .. } => {
429                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
430 
431                             // The input register is tied to the output, so we can just use the index of the output register
432                             let gcc_index = outputs.iter()
433                                 .position(|op| operand_idx == op.rust_idx)
434                                 .expect("wrong rust index");
435                             push_to_template(modifier, gcc_index);
436                         }
437 
438                         InlineAsmOperandRef::SymFn { instance } => {
439                             // TODO(@Amanieu): Additional mangling is needed on
440                             // some targets to add a leading underscore (Mach-O)
441                             // or byte count suffixes (x86 Windows).
442                             let name = self.tcx.symbol_name(instance).name;
443                             template_str.push_str(name);
444                         }
445 
446                         InlineAsmOperandRef::SymStatic { def_id } => {
447                             // TODO(@Amanieu): Additional mangling is needed on
448                             // some targets to add a leading underscore (Mach-O).
449                             let instance = Instance::mono(self.tcx, def_id);
450                             let name = self.tcx.symbol_name(instance).name;
451                             template_str.push_str(name);
452                         }
453 
454                         InlineAsmOperandRef::Const { ref string } => {
455                             // Const operands get injected directly into the template
456                             if att_dialect {
457                                 template_str.push('$');
458                             }
459                             template_str.push_str(string);
460                         }
461                     }
462                 }
463             }
464         }
465 
466         if att_dialect {
467             template_str.push_str(INTEL_SYNTAX_INS);
468         }
469 
470         // 4. Generate Extended Asm block
471 
472         let block = self.llbb();
473         let extended_asm = block.add_extended_asm(None, &template_str);
474 
475         for op in &outputs {
476             extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
477         }
478 
479         for op in &inputs {
480             extended_asm.add_input_operand(None, &op.constraint, op.val);
481         }
482 
483         for clobber in clobbers.iter() {
484             extended_asm.add_clobber(clobber);
485         }
486 
487         if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
488             // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
489             // on all architectures. For instance, what about FP stack?
490             extended_asm.add_clobber("cc");
491         }
492         if !options.contains(InlineAsmOptions::NOMEM) {
493             extended_asm.add_clobber("memory");
494         }
495         if !options.contains(InlineAsmOptions::PURE) {
496             extended_asm.set_volatile_flag(true);
497         }
498         if !options.contains(InlineAsmOptions::NOSTACK) {
499             // TODO(@Commeownist): figure out how to align stack
500         }
501         if options.contains(InlineAsmOptions::NORETURN) {
502             let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
503             let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
504             self.call(self.type_void(), None, None, builtin_unreachable, &[], None);
505         }
506 
507         // Write results to outputs.
508         //
509         // We need to do this because:
510         //  1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
511         //     (especially with current `rustc_backend_ssa` API).
512         //  2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
513         //
514         // Instead, we generate a temporary output variable for each output operand, and then this loop,
515         // generates `out_place = tmp_var;` assignments if out_place exists.
516         for op in &outputs {
517             if let Some(place) = op.out_place {
518                 OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
519             }
520         }
521     }
522 }
523 
estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize524 fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
525     let len: usize = template.iter().map(|piece| {
526         match *piece {
527             InlineAsmTemplatePiece::String(ref string) => {
528                 string.len()
529             }
530             InlineAsmTemplatePiece::Placeholder { .. } => {
531                 // '%' + 1 char modifier + 1 char index
532                 3
533             }
534         }
535     })
536     .sum();
537 
538     // increase it by 5% to account for possible '%' signs that'll be duplicated
539     // I pulled the number out of blue, but should be fair enough
540     // as the upper bound
541     let mut res = (len as f32 * 1.05) as usize + constants_len;
542 
543     if att_dialect {
544         res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
545     }
546     res
547 }
548 
549 /// Converts a register class to a GCC constraint code.
reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister550 fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
551     let constraint = match reg {
552         // For vector registers LLVM wants the register name to match the type size.
553         InlineAsmRegOrRegClass::Reg(reg) => {
554             match reg {
555                 InlineAsmReg::X86(_) => {
556                     // TODO(antoyo): add support for vector register.
557                     //
558                     // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
559                     return ConstraintOrRegister::Register(match reg.name() {
560                         // Some of registers' names does not map 1-1 from rust to gcc
561                         "st(0)" => "st",
562 
563                         name => name,
564                     });
565                 }
566 
567                 _ => unimplemented!(),
568             }
569         },
570         // They can be retrieved from https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html
571         InlineAsmRegOrRegClass::RegClass(reg) => match reg {
572             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r",
573             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w",
574             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x",
575             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
576                 unreachable!("clobber-only")
577             }
578             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
579             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
580             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
581             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
582             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16)
583             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8)
584             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4)
585             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
586             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "t",
587             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r",
588             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d",
589             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r",
590             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w",
591             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e",
592             InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
593             InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
594             InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r",
595             InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r",
596             InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f",
597             InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r",
598             InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a",
599             InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d",
600             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "d", // more specific than "r"
601             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f",
602             InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r",
603             // https://github.com/gcc-mirror/gcc/blob/master/gcc/config/nvptx/nvptx.md -> look for
604             // "define_constraint".
605             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h",
606             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r",
607             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l",
608 
609             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r",
610             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b",
611             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
612             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
613             | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
614                 unreachable!("clobber-only")
615             },
616             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r",
617             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f",
618             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
619                 unreachable!("clobber-only")
620             }
621             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
622             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
623             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
624             InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
625             | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
626             InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
627             InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "Yk",
628             InlineAsmRegClass::X86(
629                 X86InlineAsmRegClass::kreg0
630                 | X86InlineAsmRegClass::x87_reg
631                 | X86InlineAsmRegClass::mmx_reg
632                 | X86InlineAsmRegClass::tmm_reg,
633             ) => unreachable!("clobber-only"),
634             InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
635                 bug!("GCC backend does not support SPIR-V")
636             }
637             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
638             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
639             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
640             InlineAsmRegClass::Err => unreachable!(),
641         }
642     };
643 
644     ConstraintOrRegister::Constraint(constraint)
645 }
646 
647 /// Type to use for outputs that are discarded. It doesn't really matter what
648 /// the type is, as long as it is valid for the constraint code.
dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc>649 fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> {
650     match reg {
651         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(),
652         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
653         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
654         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
655             unimplemented!()
656         }
657         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)=> cx.type_i32(),
658         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
659         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
660         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
661         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
662         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(),
663         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
664         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
665         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
666             unimplemented!()
667         }
668         InlineAsmRegClass::Avr(_) => unimplemented!(),
669         InlineAsmRegClass::Bpf(_) => unimplemented!(),
670         InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
671         InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(),
672         InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(),
673         InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
674         InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
675         InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => cx.type_i32(),
676         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
677         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
678         InlineAsmRegClass::Msp430(_) => unimplemented!(),
679         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
680         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
681         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
682         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
683         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
684         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
685         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
686         | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
687             unreachable!("clobber-only")
688         },
689         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
690         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
691         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
692         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
693         | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
694         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
695         InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
696         InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
697         | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
698         | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
699         InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
700         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
701         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => cx.type_i16(),
702         InlineAsmRegClass::X86(X86InlineAsmRegClass::tmm_reg) => unimplemented!(),
703         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
704         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
705             bug!("LLVM backend does not support SPIR-V")
706         },
707         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
708         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
709         InlineAsmRegClass::Err => unreachable!(),
710     }
711 }
712 
713 impl<'gcc, 'tcx> AsmMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span])714     fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span]) {
715         let asm_arch = self.tcx.sess.asm_arch.unwrap();
716 
717         // Default to Intel syntax on x86
718         let att_dialect = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
719             && options.contains(InlineAsmOptions::ATT_SYNTAX);
720 
721         // Build the template string
722         let mut template_str = ".pushsection .text\n".to_owned();
723         if att_dialect {
724             template_str.push_str(".att_syntax\n");
725         }
726         for piece in template {
727             match *piece {
728                 InlineAsmTemplatePiece::String(ref string) => {
729                     let mut index = 0;
730                     while index < string.len() {
731                         // NOTE: gcc does not allow inline comment, so remove them.
732                         let comment_index = string[index..].find("//")
733                             .map(|comment_index| comment_index + index)
734                             .unwrap_or(string.len());
735                         template_str.push_str(&string[index..comment_index]);
736                         index = string[comment_index..].find('\n')
737                             .map(|index| index + comment_index)
738                             .unwrap_or(string.len());
739                     }
740                 },
741                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
742                     match operands[operand_idx] {
743                         GlobalAsmOperandRef::Const { ref string } => {
744                             // Const operands get injected directly into the
745                             // template. Note that we don't need to escape %
746                             // here unlike normal inline assembly.
747                             template_str.push_str(string);
748                         }
749 
750                         GlobalAsmOperandRef::SymFn { instance } => {
751                             let function = get_fn(self, instance);
752                             self.add_used_function(function);
753                             // TODO(@Amanieu): Additional mangling is needed on
754                             // some targets to add a leading underscore (Mach-O)
755                             // or byte count suffixes (x86 Windows).
756                             let name = self.tcx.symbol_name(instance).name;
757                             template_str.push_str(name);
758                         }
759 
760                         GlobalAsmOperandRef::SymStatic { def_id } => {
761                             // TODO(antoyo): set the global variable as used.
762                             // TODO(@Amanieu): Additional mangling is needed on
763                             // some targets to add a leading underscore (Mach-O).
764                             let instance = Instance::mono(self.tcx, def_id);
765                             let name = self.tcx.symbol_name(instance).name;
766                             template_str.push_str(name);
767                         }
768                     }
769                 }
770             }
771         }
772 
773         if att_dialect {
774             template_str.push_str("\n\t.intel_syntax noprefix");
775         }
776         // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually.
777         template_str.push_str("\n.popsection");
778         self.context.add_top_level_asm(None, &template_str);
779     }
780 }
781 
modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char>782 fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char> {
783     // The modifiers can be retrieved from
784     // https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html#Modifiers
785     match reg {
786         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier,
787         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
788         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
789             if modifier == Some('v') { None } else { modifier }
790         }
791         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
792             unreachable!("clobber-only")
793         }
794         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => None,
795         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
796         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => None,
797         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
798         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
799         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => Some('P'),
800         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
801         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
802         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
803             if modifier.is_none() {
804                 Some('q')
805             } else {
806                 modifier
807             }
808         }
809         InlineAsmRegClass::Hexagon(_) => None,
810         InlineAsmRegClass::LoongArch(_) => None,
811         InlineAsmRegClass::Mips(_) => None,
812         InlineAsmRegClass::Nvptx(_) => None,
813         InlineAsmRegClass::PowerPC(_) => None,
814         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
815         | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None,
816         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
817             unreachable!("clobber-only")
818         }
819         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
820         | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
821             None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') },
822             Some('l') => Some('b'),
823             Some('h') => Some('h'),
824             Some('x') => Some('w'),
825             Some('e') => Some('k'),
826             Some('r') => Some('q'),
827             _ => unreachable!(),
828         },
829         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None,
830         InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg)
831         | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg)
832         | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) {
833             (X86InlineAsmRegClass::xmm_reg, None) => Some('x'),
834             (X86InlineAsmRegClass::ymm_reg, None) => Some('t'),
835             (X86InlineAsmRegClass::zmm_reg, None) => Some('g'),
836             (_, Some('x')) => Some('x'),
837             (_, Some('y')) => Some('t'),
838             (_, Some('z')) => Some('g'),
839             _ => unreachable!(),
840         },
841         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
842         InlineAsmRegClass::X86(
843             X86InlineAsmRegClass::x87_reg
844             | X86InlineAsmRegClass::mmx_reg
845             | X86InlineAsmRegClass::kreg0
846             | X86InlineAsmRegClass::tmm_reg,
847         ) => {
848             unreachable!("clobber-only")
849         }
850         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
851         InlineAsmRegClass::Bpf(_) => None,
852         InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair)
853         | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw)
854         | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => match modifier {
855             Some('h') => Some('B'),
856             Some('l') => Some('A'),
857             _ => None,
858         },
859         InlineAsmRegClass::Avr(_) => None,
860         InlineAsmRegClass::S390x(_) => None,
861         InlineAsmRegClass::Msp430(_) => None,
862         InlineAsmRegClass::M68k(_) => None,
863         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
864             bug!("LLVM backend does not support SPIR-V")
865         }
866         InlineAsmRegClass::Err => unreachable!(),
867     }
868 }
869