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};
7 use rustc_hir::LlvmInlineAsmInner;
8 use rustc_middle::{bug, ty::Instance};
9 use rustc_span::{Span, Symbol};
10 use rustc_target::asm::*;
14 use crate::builder::Builder;
15 use crate::context::CodegenCx;
16 use crate::type_of::LayoutGccExt;
19 // Rust asm! and GCC Extended Asm semantics differ substantially.
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)`).
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
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.
32 // Mapping from Rust to GCC index would be 1-1 if it wasn't for...
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.
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).
44 // With that in mind, let's see how we translate Rust syntax to GCC
45 // (from now on, `CC` stands for "constraint code"):
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)`
51 // * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
53 // * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
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
60 // * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
61 // where "tmp" is a temporary unused variable
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.
68 const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
69 const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
72 struct AsmOutOperand<'a, 'tcx, 'gcc> {
78 tmp_var: LValue<'gcc>,
79 out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
82 struct AsmInOperand<'a, 'tcx> {
84 constraint: Cow<'a, str>,
88 impl AsmOutOperand<'_, '_, '_> {
89 fn to_constraint(&self) -> String {
90 let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1);
92 let sign = if self.readwrite { '+' } else { '=' };
98 res.push_str(&self.constraint);
103 enum ConstraintOrRegister {
104 Constraint(&'static str),
105 Register(&'static str)
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")
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.
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) {
124 .struct_span_err(span[0], "GCC backend does not support unwinding from inline asm")
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);
134 // GCC index of an output operand equals its position in the array
135 let mut outputs = vec![];
137 // GCC index of an input operand equals its position in the array
138 // added to `outputs.len()`
139 let mut inputs = vec![];
141 // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
142 let mut clobbers = vec![];
144 // We're trying to preallocate space for the template
145 let mut constants_len = 0;
147 // There are rules we must adhere to if we want GCC to do the right thing:
149 // * Every local variable that the asm block uses as an output must be declared *before*
151 // * There must be no instructions whatsoever between the register variables and the asm.
153 // Therefore, the backend must generate the instructions strictly in this order:
155 // 1. Output variables.
156 // 2. Register variables.
159 // We also must make sure that no input operands are emitted before output operands.
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.
165 // 1. Normal variables (and saving operands to buffers).
166 for (rust_idx, op) in rust_operands.iter().enumerate() {
168 InlineAsmOperandRef::Out { reg, late, place } => {
169 use ConstraintOrRegister::*;
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
177 (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
178 (Register(_), Some(_)) => {
179 // left for the next pass
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))
190 true // Register class is unconditionally supported
194 if is_target_supported && !clobbers.contains(®_name) {
195 clobbers.push(reg_name);
201 let tmp_var = self.current_func().new_local(None, ty, "output_register");
202 outputs.push(AsmOutOperand {
212 InlineAsmOperandRef::In { reg, value } => {
213 if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
214 inputs.push(AsmInOperand {
215 constraint: Cow::Borrowed(constraint),
217 val: value.immediate()
221 // left for the next pass
226 InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
227 let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
231 // left for the next pass
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.
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");
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 {
258 let out_gcc_idx = outputs.len() - 1;
259 let constraint = Cow::Owned(out_gcc_idx.to_string());
261 inputs.push(AsmInOperand {
264 val: in_value.immediate()
269 InlineAsmOperandRef::Const { ref string } => {
270 constants_len += string.len() + att_dialect as usize;
273 InlineAsmOperandRef::SymFn { instance } => {
274 constants_len += self.tcx.symbol_name(instance).name.len();
276 InlineAsmOperandRef::SymStatic { def_id } => {
277 constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
282 // 2. Register variables.
283 for (rust_idx, op) in rust_operands.iter().enumerate() {
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 {
292 // processed in the previous pass
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);
300 outputs.push(AsmOutOperand {
301 constraint: "r".into(),
306 out_place: Some(out_place)
310 // processed in the previous pass
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());
321 inputs.push(AsmInOperand {
322 constraint: "r".into(),
324 val: reg_var.to_rvalue()
328 // processed in the previous pass
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 {
338 // processed in the previous pass
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);
347 outputs.push(AsmOutOperand {
348 constraint: "r".into(),
353 out_place: Some(out_place)
356 let constraint = Cow::Owned((outputs.len() - 1).to_string());
357 inputs.push(AsmInOperand {
360 val: in_value.immediate()
364 // processed in the previous pass
367 InlineAsmOperandRef::Const { .. }
368 | InlineAsmOperandRef::SymFn { .. }
369 | InlineAsmOperandRef::SymStatic { .. } => {
370 // processed in the previous pass
375 // 3. Build the template string
377 let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
379 template_str.push_str(ATT_SYNTAX_INS);
382 for piece in template {
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);
392 template_str.push_str("%%");
393 template_str.push_str(s);
396 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
397 let mut push_to_template = |modifier, gcc_idx| {
400 template_str.push('%');
401 if let Some(modifier) = modifier {
402 template_str.push(modifier);
404 write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
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);
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);
425 InlineAsmOperandRef::InOut { reg, .. } => {
426 let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
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);
435 InlineAsmOperandRef::SymFn { instance } => {
436 let name = self.tcx.symbol_name(instance).name;
437 template_str.push_str(name);
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);
448 InlineAsmOperandRef::Const { ref string } => {
449 // Const operands get injected directly into the template
451 template_str.push('$');
453 template_str.push_str(string);
461 template_str.push_str(INTEL_SYNTAX_INS);
464 // 4. Generate Extended Asm block
466 let block = self.llbb();
467 let extended_asm = block.add_extended_asm(None, &template_str);
470 extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
474 extended_asm.add_input_operand(None, &op.constraint, op.val);
477 for clobber in clobbers.iter() {
478 extended_asm.add_clobber(clobber);
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");
486 if !options.contains(InlineAsmOptions::NOMEM) {
487 extended_asm.add_clobber("memory");
489 if !options.contains(InlineAsmOptions::PURE) {
490 extended_asm.set_volatile_flag(true);
492 if !options.contains(InlineAsmOptions::NOSTACK) {
493 // TODO(@Commeownist): figure out how to align stack
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);
501 // Write results to outputs.
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`.
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.
511 if let Some(place) = op.out_place {
512 OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
519 fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
520 let len: usize = template.iter().map(|piece| {
522 InlineAsmTemplatePiece::String(ref string) => {
525 InlineAsmTemplatePiece::Placeholder { .. } => {
526 // '%' + 1 char modifier + 1 char index
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;
539 res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
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) => {
550 InlineAsmReg::X86(_) => {
551 // TODO(antoyo): add support for vector register.
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
562 _ => unimplemented!(),
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")
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")
611 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
612 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
613 InlineAsmRegClass::Err => unreachable!(),
617 ConstraintOrRegister::Constraint(constraint)
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> {
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) => {
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) => {
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")
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")
672 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
673 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
674 InlineAsmRegClass::Err => unreachable!(),
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();
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);
686 // Build the template string
687 let mut template_str = String::new();
688 for piece in template {
690 InlineAsmTemplatePiece::String(ref string) => {
691 for line in string.lines() {
692 // NOTE: gcc does not allow inline comment, so remove them.
694 if let Some(index) = line.rfind("//") {
700 template_str.push_str(line);
701 template_str.push('\n');
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);
719 format!("{}\n\t.intel_syntax noprefix", template_str)
722 format!(".att_syntax\n\t{}\n\t.intel_syntax noprefix", template_str)
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);
730 fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char> {
732 InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier,
733 InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => modifier,
734 InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
735 | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
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) => {
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'),
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'),
780 InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
781 InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
782 unreachable!("clobber-only")
784 InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
785 InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
786 bug!("LLVM backend does not support SPIR-V")
788 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
789 InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
790 InlineAsmRegClass::Err => unreachable!(),