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