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