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_hir::LlvmInlineAsmInner;
8 use rustc_middle::{bug, ty::Instance};
9 use rustc_span::{Span, Symbol};
10 use rustc_target::asm::*;
11 
12 use std::borrow::Cow;
13 
14 use crate::builder::Builder;
15 use crate::context::CodegenCx;
16 use crate::type_of::LayoutGccExt;
17 
18 
19 // Rust asm! and GCC Extended Asm semantics differ substantially.
20 //
21 // 1. Rust asm operands go along as one list of operands. Operands themselves indicate
22 //    if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
23 //    both "in" and "out" (`inout(reg)`).
24 //
25 //    GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
26 //    this means that all "out" operands must go before "in" operands. "In" and "out" operands
27 //    cannot interleave.
28 //
29 // 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
30 //    because the asm template refers to operands by index.
31 //
32 //    Mapping from Rust to GCC index would be 1-1 if it wasn't for...
33 //
34 // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
35 //    Contrary, Rust expresses clobbers through "out" operands that aren't tied to
36 //    a variable (`_`),  and such "clobbers" do have index.
37 //
38 // 4. Furthermore, GCC Extended Asm does not support explicit register constraints
39 //    (like `out("eax")`) directly, offering so-called "local register variables"
40 //    as a workaround. These variables need to be declared and initialized *before*
41 //    the Extended Asm block but *after* normal local variables
42 //    (see comment in `codegen_inline_asm` for explanation).
43 //
44 // With that in mind, let's see how we translate Rust syntax to GCC
45 // (from now on, `CC` stands for "constraint code"):
46 //
47 // * `out(reg_class) var`   -> translated to output operand: `"=CC"(var)`
48 // * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)`
49 // * `in(reg_class) var`    -> translated to input operand: `"CC"(var)`
50 //
51 // * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
52 //
53 // * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
54 //
55 // * `inout(reg_class) in_var => out_var` -> translated to two operands:
56 //                              output: `"=CC"(in_var)`
57 //                              input:  `"num"(out_var)` where num is the GCC index
58 //                                       of the corresponding output operand
59 //
60 // * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
61 //                                      where "tmp" is a temporary unused variable
62 //
63 // * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
64 //                                              with `"r"(var)` constraint,
65 //                                              and one register variable assigned to the desired register.
66 //
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_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, _inputs: Vec<RValue<'gcc>>, span: Span) -> bool110     fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, _inputs: Vec<RValue<'gcc>>, span: Span) -> bool {
111         self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`")
112             .help("consider using the `asm!` macro instead")
113             .emit();
114 
115         // We return `true` even if we've failed to generate the asm
116         // because we want to suppress the "malformed inline assembly" error
117         // generated by the frontend.
118         true
119     }
120 
codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span], _instance: Instance<'_>)121     fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span], _instance: Instance<'_>) {
122         let asm_arch = self.tcx.sess.asm_arch.unwrap();
123         let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
124         let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
125         let intel_dialect = is_x86 && !options.contains(InlineAsmOptions::ATT_SYNTAX);
126 
127         // GCC index of an output operand equals its position in the array
128         let mut outputs = vec![];
129 
130         // GCC index of an input operand equals its position in the array
131         // added to `outputs.len()`
132         let mut inputs = vec![];
133 
134         // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
135         let mut clobbers = vec![];
136 
137         // We're trying to preallocate space for the template
138         let mut constants_len = 0;
139 
140         // There are rules we must adhere to if we want GCC to do the right thing:
141         //
142         // * Every local variable that the asm block uses as an output must be declared *before*
143         //   the asm block.
144         // * There must be no instructions whatsoever between the register variables and the asm.
145         //
146         // Therefore, the backend must generate the instructions strictly in this order:
147         //
148         // 1. Output variables.
149         // 2. Register variables.
150         // 3. The asm block.
151         //
152         // We also must make sure that no input operands are emitted before output operands.
153         //
154         // This is why we work in passes, first emitting local vars, then local register vars.
155         // Also, we don't emit any asm operands immediately; we save them to
156         // the one of the buffers to be emitted later.
157 
158         // 1. Normal variables (and saving operands to buffers).
159         for (rust_idx, op) in rust_operands.iter().enumerate() {
160             match *op {
161                 InlineAsmOperandRef::Out { reg, late, place } => {
162                     use ConstraintOrRegister::*;
163 
164                     let (constraint, ty) = match (reg_to_gcc(reg), place) {
165                         (Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx, false)),
166                         // When `reg` is a class and not an explicit register but the out place is not specified,
167                         // we need to create an unused output variable to assign the output to. This var
168                         // needs to be of a type that's "compatible" with the register class, but specific type
169                         // doesn't matter.
170                         (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
171                         (Register(_), Some(_)) => {
172                             // left for the next pass
173                             continue
174                         },
175                         (Register(reg_name), None) => {
176                             // `clobber_abi` can add lots of clobbers that are not supported by the target,
177                             // such as AVX-512 registers, so we just ignore unsupported registers
178                             let is_target_supported = reg.reg_class().supported_types(asm_arch).iter()
179                                 .any(|&(_, feature)| {
180                                     if let Some(feature) = feature {
181                                         self.tcx.sess.target_features.contains(&Symbol::intern(feature))
182                                     } else {
183                                         true // Register class is unconditionally supported
184                                     }
185                                 });
186 
187                             if is_target_supported && !clobbers.contains(&reg_name) {
188                                 clobbers.push(reg_name);
189                             }
190                             continue
191                         }
192                     };
193 
194                     let tmp_var = self.current_func().new_local(None, ty, "output_register");
195                     outputs.push(AsmOutOperand {
196                         constraint,
197                         rust_idx,
198                         late,
199                         readwrite: false,
200                         tmp_var,
201                         out_place: place
202                     });
203                 }
204 
205                 InlineAsmOperandRef::In { reg, value } => {
206                     if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
207                         inputs.push(AsmInOperand {
208                             constraint: Cow::Borrowed(constraint),
209                             rust_idx,
210                             val: value.immediate()
211                         });
212                     }
213                     else {
214                         // left for the next pass
215                         continue
216                     }
217                 }
218 
219                 InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
220                     let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
221                         constraint
222                     }
223                     else {
224                         // left for the next pass
225                         continue
226                     };
227 
228                     // Rustc frontend guarantees that input and output types are "compatible",
229                     // so we can just use input var's type for the output variable.
230                     //
231                     // This decision is also backed by the fact that LLVM needs in and out
232                     // values to be of *exactly the same type*, not just "compatible".
233                     // I'm not sure if GCC is so picky too, but better safe than sorry.
234                     let ty = in_value.layout.gcc_type(self.cx, false);
235                     let tmp_var = self.current_func().new_local(None, ty, "output_register");
236 
237                     // If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
238                     // it to one "readwrite (+) output variable", otherwise we translate it to two
239                     // "out and tied in" vars as described above.
240                     let readwrite = out_place.is_none();
241                     outputs.push(AsmOutOperand {
242                         constraint,
243                         rust_idx,
244                         late,
245                         readwrite,
246                         tmp_var,
247                         out_place,
248                     });
249 
250                     if !readwrite {
251                         let out_gcc_idx = outputs.len() - 1;
252                         let constraint = Cow::Owned(out_gcc_idx.to_string());
253 
254                         inputs.push(AsmInOperand {
255                             constraint,
256                             rust_idx,
257                             val: in_value.immediate()
258                         });
259                     }
260                 }
261 
262                 InlineAsmOperandRef::Const { ref string } => {
263                     constants_len += string.len() + att_dialect as usize;
264                 }
265 
266                 InlineAsmOperandRef::SymFn { instance } => {
267                     constants_len += self.tcx.symbol_name(instance).name.len();
268                 }
269                 InlineAsmOperandRef::SymStatic { def_id } => {
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, false);
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, false);
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                         let out_place = if let Some(place) = out_place {
328                             place
329                         }
330                         else {
331                             // processed in the previous pass
332                             continue
333                         };
334 
335                         // See explanation in the first pass.
336                         let ty = in_value.layout.gcc_type(self.cx, false);
337                         let tmp_var = self.current_func().new_local(None, ty, "output_register");
338                         tmp_var.set_register_name(reg_name);
339 
340                         outputs.push(AsmOutOperand {
341                             constraint: "r".into(),
342                             rust_idx,
343                             late,
344                             readwrite: false,
345                             tmp_var,
346                             out_place: Some(out_place)
347                         });
348 
349                         let constraint = Cow::Owned((outputs.len() - 1).to_string());
350                         inputs.push(AsmInOperand {
351                             constraint,
352                             rust_idx,
353                             val: in_value.immediate()
354                         });
355                     }
356 
357                     // processed in the previous pass
358                 }
359 
360                 InlineAsmOperandRef::Const { .. }
361                 | InlineAsmOperandRef::SymFn { .. }
362                 | InlineAsmOperandRef::SymStatic { .. } => {
363                     // processed in the previous pass
364                 }
365             }
366         }
367 
368         // 3. Build the template string
369 
370         let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
371         if !intel_dialect {
372             template_str.push_str(ATT_SYNTAX_INS);
373         }
374 
375         for piece in template {
376             match *piece {
377                 InlineAsmTemplatePiece::String(ref string) => {
378                     // TODO(@Commeownist): switch to `Iterator::intersperse` once it's stable
379                     let mut iter = string.split('%');
380                     if let Some(s) = iter.next() {
381                         template_str.push_str(s);
382                     }
383 
384                     for s in iter {
385                         template_str.push_str("%%");
386                         template_str.push_str(s);
387                     }
388                 }
389                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
390                     let mut push_to_template = |modifier, gcc_idx| {
391                         use std::fmt::Write;
392 
393                         template_str.push('%');
394                         if let Some(modifier) = modifier {
395                             template_str.push(modifier);
396                         }
397                         write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
398                     };
399 
400                     match rust_operands[operand_idx] {
401                         InlineAsmOperandRef::Out { reg, ..  } => {
402                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
403                             let gcc_index = outputs.iter()
404                                 .position(|op| operand_idx == op.rust_idx)
405                                 .expect("wrong rust index");
406                             push_to_template(modifier, gcc_index);
407                         }
408 
409                         InlineAsmOperandRef::In { reg, .. } => {
410                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
411                             let in_gcc_index = inputs.iter()
412                                 .position(|op| operand_idx == op.rust_idx)
413                                 .expect("wrong rust index");
414                             let gcc_index = in_gcc_index + outputs.len();
415                             push_to_template(modifier, gcc_index);
416                         }
417 
418                         InlineAsmOperandRef::InOut { reg, .. } => {
419                             let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
420 
421                             // The input register is tied to the output, so we can just use the index of the output register
422                             let gcc_index = outputs.iter()
423                                 .position(|op| operand_idx == op.rust_idx)
424                                 .expect("wrong rust index");
425                             push_to_template(modifier, gcc_index);
426                         }
427 
428                         InlineAsmOperandRef::SymFn { instance } => {
429                             let name = self.tcx.symbol_name(instance).name;
430                             template_str.push_str(name);
431                         }
432 
433                         InlineAsmOperandRef::SymStatic { def_id } => {
434                             // TODO(@Commeownist): This may not be sufficient for all kinds of statics.
435                             // Some statics may need the `@plt` suffix, like thread-local vars.
436                             let instance = Instance::mono(self.tcx, def_id);
437                             let name = self.tcx.symbol_name(instance).name;
438                             template_str.push_str(name);
439                         }
440 
441                         InlineAsmOperandRef::Const { ref string } => {
442                             // Const operands get injected directly into the template
443                             if att_dialect {
444                                 template_str.push('$');
445                             }
446                             template_str.push_str(string);
447                         }
448                     }
449                 }
450             }
451         }
452 
453         if !intel_dialect {
454             template_str.push_str(INTEL_SYNTAX_INS);
455         }
456 
457         // 4. Generate Extended Asm block
458 
459         let block = self.llbb();
460         let extended_asm = block.add_extended_asm(None, &template_str);
461 
462         for op in &outputs {
463             extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
464         }
465 
466         for op in &inputs {
467             extended_asm.add_input_operand(None, &op.constraint, op.val);
468         }
469 
470         for clobber in clobbers.iter() {
471             extended_asm.add_clobber(clobber);
472         }
473 
474         if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
475             // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
476             // on all architectures. For instance, what about FP stack?
477             extended_asm.add_clobber("cc");
478         }
479         if !options.contains(InlineAsmOptions::NOMEM) {
480             extended_asm.add_clobber("memory");
481         }
482         if !options.contains(InlineAsmOptions::PURE) {
483             extended_asm.set_volatile_flag(true);
484         }
485         if !options.contains(InlineAsmOptions::NOSTACK) {
486             // TODO(@Commeownist): figure out how to align stack
487         }
488         if options.contains(InlineAsmOptions::NORETURN) {
489             let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
490             let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
491             self.call(self.type_void(), builtin_unreachable, &[], None);
492         }
493 
494         // Write results to outputs.
495         //
496         // We need to do this because:
497         //  1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
498         //     (especially with current `rustc_backend_ssa` API).
499         //  2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
500         //
501         // Instead, we generate a temporary output variable for each output operand, and then this loop,
502         // generates `out_place = tmp_var;` assignments if out_place exists.
503         for op in &outputs {
504             if let Some(place) = op.out_place {
505                 OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
506             }
507         }
508 
509     }
510 }
511 
estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize512 fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
513     let len: usize = template.iter().map(|piece| {
514         match *piece {
515             InlineAsmTemplatePiece::String(ref string) => {
516                 string.len()
517             }
518             InlineAsmTemplatePiece::Placeholder { .. } => {
519                 // '%' + 1 char modifier + 1 char index
520                 3
521             }
522         }
523     })
524     .sum();
525 
526     // increase it by 5% to account for possible '%' signs that'll be duplicated
527     // I pulled the number out of blue, but should be fair enough
528     // as the upper bound
529     let mut res = (len as f32 * 1.05) as usize + constants_len;
530 
531     if att_dialect {
532         res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
533     }
534     res
535 }
536 
537 /// Converts a register class to a GCC constraint code.
reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister538 fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
539     let constraint = match reg {
540         // For vector registers LLVM wants the register name to match the type size.
541         InlineAsmRegOrRegClass::Reg(reg) => {
542             match reg {
543                 InlineAsmReg::X86(_) => {
544                     // TODO(antoyo): add support for vector register.
545                     //
546                     // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
547                     return ConstraintOrRegister::Register(match reg.name() {
548                         // Some of registers' names does not map 1-1 from rust to gcc
549                         "st(0)" => "st",
550 
551                         name => name,
552                     });
553                 }
554 
555                 _ => unimplemented!(),
556             }
557         },
558         InlineAsmRegOrRegClass::RegClass(reg) => match reg {
559             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
560             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => unimplemented!(),
561             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => unimplemented!(),
562             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => unimplemented!(),
563             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => unimplemented!(),
564             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
565             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
566             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
567             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => unimplemented!(),
568             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16)
569             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8)
570             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(),
571             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
572             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(),
573             InlineAsmRegClass::Bpf(_) => unimplemented!(),
574             InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
575             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
576             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(),
577             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(),
578             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(),
579             InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(),
580             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(),
581             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(),
582             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(),
583             InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
584             | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
585                 unreachable!("clobber-only")
586             },
587             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(),
588             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
589             InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
590             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
591             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
592             InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
593             InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
594             | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
595             InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
596             InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
597             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
598             InlineAsmRegClass::X86(
599                 X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
600             ) => unreachable!("clobber-only"),
601             InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
602                 bug!("GCC backend does not support SPIR-V")
603             }
604             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
605             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
606             InlineAsmRegClass::Err => unreachable!(),
607         }
608     };
609 
610     ConstraintOrRegister::Constraint(constraint)
611 }
612 
613 /// Type to use for outputs that are discarded. It doesn't really matter what
614 /// 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>615 fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> {
616     match reg {
617         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(),
618         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
619         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
620         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
621             unimplemented!()
622         }
623         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
624         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
625         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
626         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
627         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
628         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
629         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(),
630         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
631         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
632         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
633             unimplemented!()
634         }
635         InlineAsmRegClass::Bpf(_) => unimplemented!(),
636         InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
637         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
638         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
639         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
640         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
641         InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
642         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
643         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
644         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
645         InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
646         | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
647             unreachable!("clobber-only")
648         },
649         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
650         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
651         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
652         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
653         | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
654         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
655         InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
656         InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
657         | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
658         | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
659         InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
660         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
661         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
662         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
663             bug!("LLVM backend does not support SPIR-V")
664         },
665         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
666         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
667         InlineAsmRegClass::Err => unreachable!(),
668     }
669 }
670 
671 impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> {
codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef], options: InlineAsmOptions, _line_spans: &[Span])672     fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef], options: InlineAsmOptions, _line_spans: &[Span]) {
673         let asm_arch = self.tcx.sess.asm_arch.unwrap();
674 
675         // Default to Intel syntax on x86
676         let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
677             && !options.contains(InlineAsmOptions::ATT_SYNTAX);
678 
679         // Build the template string
680         let mut template_str = String::new();
681         for piece in template {
682             match *piece {
683                 InlineAsmTemplatePiece::String(ref string) => {
684                     for line in string.lines() {
685                         // NOTE: gcc does not allow inline comment, so remove them.
686                         let line =
687                             if let Some(index) = line.rfind("//") {
688                                 &line[..index]
689                             }
690                             else {
691                                 line
692                             };
693                         template_str.push_str(line);
694                         template_str.push('\n');
695                     }
696                 },
697                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
698                     match operands[operand_idx] {
699                         GlobalAsmOperandRef::Const { ref string } => {
700                             // Const operands get injected directly into the
701                             // template. Note that we don't need to escape %
702                             // here unlike normal inline assembly.
703                             template_str.push_str(string);
704                         }
705                     }
706                 }
707             }
708         }
709 
710         let template_str =
711             if intel_syntax {
712                 format!("{}\n\t.intel_syntax noprefix", template_str)
713             }
714             else {
715                 format!(".att_syntax\n\t{}\n\t.intel_syntax noprefix", template_str)
716             };
717         // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually.
718         let template_str = format!(".pushsection .text\n{}\n.popsection", template_str);
719         self.context.add_top_level_asm(None, &template_str);
720     }
721 }
722 
modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char>723 fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char> {
724     match reg {
725         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier,
726         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => modifier,
727         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
728         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
729             unimplemented!()
730         }
731         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
732         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
733         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
734         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => unimplemented!(),
735         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
736         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
737         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => unimplemented!(),
738         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
739         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
740         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
741             unimplemented!()
742         }
743         InlineAsmRegClass::Bpf(_) => unimplemented!(),
744         InlineAsmRegClass::Hexagon(_) => unimplemented!(),
745         InlineAsmRegClass::Mips(_) => unimplemented!(),
746         InlineAsmRegClass::Nvptx(_) => unimplemented!(),
747         InlineAsmRegClass::PowerPC(_) => unimplemented!(),
748         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
749         | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
750         InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
751         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
752         | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
753             None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') },
754             Some('l') => Some('b'),
755             Some('h') => Some('h'),
756             Some('x') => Some('w'),
757             Some('e') => Some('k'),
758             Some('r') => Some('q'),
759             _ => unreachable!(),
760         },
761         InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None,
762         InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg)
763         | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg)
764         | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) {
765             (X86InlineAsmRegClass::xmm_reg, None) => Some('x'),
766             (X86InlineAsmRegClass::ymm_reg, None) => Some('t'),
767             (X86InlineAsmRegClass::zmm_reg, None) => Some('g'),
768             (_, Some('x')) => Some('x'),
769             (_, Some('y')) => Some('t'),
770             (_, Some('z')) => Some('g'),
771             _ => unreachable!(),
772         },
773         InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
774         InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
775             unreachable!("clobber-only")
776         }
777         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
778         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
779             bug!("LLVM backend does not support SPIR-V")
780         },
781         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
782         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
783         InlineAsmRegClass::Err => unreachable!(),
784     }
785 }
786