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