diff options
Diffstat (limited to 'compiler/rustc_target/src/asm')
-rw-r--r-- | compiler/rustc_target/src/asm/aarch64.rs | 200 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/arm.rs | 340 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/avr.rs | 197 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/bpf.rs | 118 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/hexagon.rs | 95 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/mips.rs | 135 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/mod.rs | 976 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/msp430.rs | 81 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/nvptx.rs | 50 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/powerpc.rs | 204 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/riscv.rs | 185 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/s390x.rs | 107 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/spirv.rs | 47 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/wasm.rs | 47 | ||||
-rw-r--r-- | compiler/rustc_target/src/asm/x86.rs | 492 |
15 files changed, 3274 insertions, 0 deletions
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs new file mode 100644 index 000000000..62a0f9fb0 --- /dev/null +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -0,0 +1,200 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::{RelocModel, Target}; +use rustc_data_structures::fx::FxHashSet; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + AArch64 AArch64InlineAsmRegClass { + reg, + vreg, + vreg_low16, + preg, + } +} + +impl AArch64InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => &['w', 'x'], + Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'], + Self::preg => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 64 => None, + _ => Some(('w', "w0")), + }, + Self::vreg | Self::vreg_low16 => match ty.size().bits() { + 8 => Some(('b', "b0")), + 16 => Some(('h', "h0")), + 32 => Some(('s', "s0")), + 64 => Some(('d', "d0")), + 128 => Some(('q', "q0")), + _ => None, + }, + Self::preg => None, + } + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg => Some(('x', "x0")), + Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::preg => None, + } + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, + Self::vreg | Self::vreg_low16 => types! { + neon: I8, I16, I32, I64, F32, F64, + VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1), + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + Self::preg => &[], + } + } +} + +pub fn target_reserves_x18(target: &Target) -> bool { + target.os == "android" || target.os == "fuchsia" || target.is_like_osx || target.is_like_windows +} + +fn reserved_x18( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxHashSet<Symbol>, + target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_reserves_x18(target) { + Err("x18 is a reserved register on this target") + } else { + Ok(()) + } +} + +def_regs! { + AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass { + x0: reg = ["x0", "w0"], + x1: reg = ["x1", "w1"], + x2: reg = ["x2", "w2"], + x3: reg = ["x3", "w3"], + x4: reg = ["x4", "w4"], + x5: reg = ["x5", "w5"], + x6: reg = ["x6", "w6"], + x7: reg = ["x7", "w7"], + x8: reg = ["x8", "w8"], + x9: reg = ["x9", "w9"], + x10: reg = ["x10", "w10"], + x11: reg = ["x11", "w11"], + x12: reg = ["x12", "w12"], + x13: reg = ["x13", "w13"], + x14: reg = ["x14", "w14"], + x15: reg = ["x15", "w15"], + x16: reg = ["x16", "w16"], + x17: reg = ["x17", "w17"], + x18: reg = ["x18", "w18"] % reserved_x18, + x20: reg = ["x20", "w20"], + x21: reg = ["x21", "w21"], + x22: reg = ["x22", "w22"], + x23: reg = ["x23", "w23"], + x24: reg = ["x24", "w24"], + x25: reg = ["x25", "w25"], + x26: reg = ["x26", "w26"], + x27: reg = ["x27", "w27"], + x28: reg = ["x28", "w28"], + x30: reg = ["x30", "w30", "lr", "wlr"], + v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0", "z0"], + v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1", "z1"], + v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2", "z2"], + v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3", "z3"], + v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4", "z4"], + v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5", "z5"], + v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6", "z6"], + v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7", "z7"], + v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8", "z8"], + v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9", "z9"], + v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10", "z10"], + v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11", "z11"], + v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12", "z12"], + v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13", "z13"], + v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14", "z14"], + v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15", "z15"], + v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"], + v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"], + v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"], + v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"], + v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"], + v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"], + v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"], + v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"], + v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"], + v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"], + v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"], + v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"], + v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"], + v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"], + v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"], + v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"], + p0: preg = ["p0"], + p1: preg = ["p1"], + p2: preg = ["p2"], + p3: preg = ["p3"], + p4: preg = ["p4"], + p5: preg = ["p5"], + p6: preg = ["p6"], + p7: preg = ["p7"], + p8: preg = ["p8"], + p9: preg = ["p9"], + p10: preg = ["p10"], + p11: preg = ["p11"], + p12: preg = ["p12"], + p13: preg = ["p13"], + p14: preg = ["p14"], + p15: preg = ["p15"], + ffr: preg = ["ffr"], + #error = ["x19", "w19"] => + "x19 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["x29", "w29", "fp", "wfp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "wsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["xzr", "wzr"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl AArch64InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + let (prefix, index) = if (self as u32) < Self::v0 as u32 { + (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32) + } else { + (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) + }; + assert!(index < 32); + write!(out, "{}{}", prefix, index) + } +} diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs new file mode 100644 index 000000000..0db3eb6fc --- /dev/null +++ b/compiler/rustc_target/src/asm/arm.rs @@ -0,0 +1,340 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::{RelocModel, Target}; +use rustc_data_structures::fx::FxHashSet; +use rustc_macros::HashStable_Generic; +use rustc_span::{sym, Symbol}; +use std::fmt; + +def_reg_class! { + Arm ArmInlineAsmRegClass { + reg, + sreg, + sreg_low16, + dreg, + dreg_low16, + dreg_low8, + qreg, + qreg_low8, + qreg_low4, + } +} + +impl ArmInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'], + _ => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => types! { _: I8, I16, I32, F32; }, + Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F32; }, + Self::dreg_low16 | Self::dreg_low8 => types! { + vfp2: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); + }, + Self::dreg => types! { + d32: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); + }, + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! { + neon: VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4); + }, + } + } +} + +// This uses the same logic as useR7AsFramePointer in LLVM +fn frame_pointer_is_r7(target_features: &FxHashSet<Symbol>, target: &Target) -> bool { + target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode)) +} + +fn frame_pointer_r11( + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + target: &Target, + is_clobber: bool, +) -> Result<(), &'static str> { + not_thumb1(arch, reloc_model, target_features, target, is_clobber)?; + + if !frame_pointer_is_r7(target_features, target) { + Err("the frame pointer (r11) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn frame_pointer_r7( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if frame_pointer_is_r7(target_features, target) { + Err("the frame pointer (r7) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn not_thumb1( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + _target: &Target, + is_clobber: bool, +) -> Result<(), &'static str> { + if !is_clobber + && target_features.contains(&sym::thumb_mode) + && !target_features.contains(&sym::thumb2) + { + Err("high registers (r8+) can only be used as clobbers in Thumb-1 code") + } else { + Ok(()) + } +} + +fn reserved_r9( + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + target: &Target, + is_clobber: bool, +) -> Result<(), &'static str> { + not_thumb1(arch, reloc_model, target_features, target, is_clobber)?; + + match reloc_model { + RelocModel::Rwpi | RelocModel::RopiRwpi => { + Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") + } + _ => Ok(()), + } +} + +def_regs! { + Arm ArmInlineAsmReg ArmInlineAsmRegClass { + r0: reg = ["r0", "a1"], + r1: reg = ["r1", "a2"], + r2: reg = ["r2", "a3"], + r3: reg = ["r3", "a4"], + r4: reg = ["r4", "v1"], + r5: reg = ["r5", "v2"], + r7: reg = ["r7", "v4"] % frame_pointer_r7, + r8: reg = ["r8", "v5"] % not_thumb1, + r9: reg = ["r9", "v6", "rfp"] % reserved_r9, + r10: reg = ["r10", "sl"] % not_thumb1, + r11: reg = ["r11", "fp"] % frame_pointer_r11, + r12: reg = ["r12", "ip"] % not_thumb1, + r14: reg = ["r14", "lr"] % not_thumb1, + s0: sreg, sreg_low16 = ["s0"], + s1: sreg, sreg_low16 = ["s1"], + s2: sreg, sreg_low16 = ["s2"], + s3: sreg, sreg_low16 = ["s3"], + s4: sreg, sreg_low16 = ["s4"], + s5: sreg, sreg_low16 = ["s5"], + s6: sreg, sreg_low16 = ["s6"], + s7: sreg, sreg_low16 = ["s7"], + s8: sreg, sreg_low16 = ["s8"], + s9: sreg, sreg_low16 = ["s9"], + s10: sreg, sreg_low16 = ["s10"], + s11: sreg, sreg_low16 = ["s11"], + s12: sreg, sreg_low16 = ["s12"], + s13: sreg, sreg_low16 = ["s13"], + s14: sreg, sreg_low16 = ["s14"], + s15: sreg, sreg_low16 = ["s15"], + s16: sreg = ["s16"], + s17: sreg = ["s17"], + s18: sreg = ["s18"], + s19: sreg = ["s19"], + s20: sreg = ["s20"], + s21: sreg = ["s21"], + s22: sreg = ["s22"], + s23: sreg = ["s23"], + s24: sreg = ["s24"], + s25: sreg = ["s25"], + s26: sreg = ["s26"], + s27: sreg = ["s27"], + s28: sreg = ["s28"], + s29: sreg = ["s29"], + s30: sreg = ["s30"], + s31: sreg = ["s31"], + d0: dreg, dreg_low16, dreg_low8 = ["d0"], + d1: dreg, dreg_low16, dreg_low8 = ["d1"], + d2: dreg, dreg_low16, dreg_low8 = ["d2"], + d3: dreg, dreg_low16, dreg_low8 = ["d3"], + d4: dreg, dreg_low16, dreg_low8 = ["d4"], + d5: dreg, dreg_low16, dreg_low8 = ["d5"], + d6: dreg, dreg_low16, dreg_low8 = ["d6"], + d7: dreg, dreg_low16, dreg_low8 = ["d7"], + d8: dreg, dreg_low16 = ["d8"], + d9: dreg, dreg_low16 = ["d9"], + d10: dreg, dreg_low16 = ["d10"], + d11: dreg, dreg_low16 = ["d11"], + d12: dreg, dreg_low16 = ["d12"], + d13: dreg, dreg_low16 = ["d13"], + d14: dreg, dreg_low16 = ["d14"], + d15: dreg, dreg_low16 = ["d15"], + d16: dreg = ["d16"], + d17: dreg = ["d17"], + d18: dreg = ["d18"], + d19: dreg = ["d19"], + d20: dreg = ["d20"], + d21: dreg = ["d21"], + d22: dreg = ["d22"], + d23: dreg = ["d23"], + d24: dreg = ["d24"], + d25: dreg = ["d25"], + d26: dreg = ["d26"], + d27: dreg = ["d27"], + d28: dreg = ["d28"], + d29: dreg = ["d29"], + d30: dreg = ["d30"], + d31: dreg = ["d31"], + q0: qreg, qreg_low8, qreg_low4 = ["q0"], + q1: qreg, qreg_low8, qreg_low4 = ["q1"], + q2: qreg, qreg_low8, qreg_low4 = ["q2"], + q3: qreg, qreg_low8, qreg_low4 = ["q3"], + q4: qreg, qreg_low8 = ["q4"], + q5: qreg, qreg_low8 = ["q5"], + q6: qreg, qreg_low8 = ["q6"], + q7: qreg, qreg_low8 = ["q7"], + q8: qreg = ["q8"], + q9: qreg = ["q9"], + q10: qreg = ["q10"], + q11: qreg = ["q11"], + q12: qreg = ["q12"], + q13: qreg = ["q13"], + q14: qreg = ["q14"], + q15: qreg = ["q15"], + #error = ["r6", "v3"] => + "r6 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["r13", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r15", "pc"] => + "the program pointer cannot be used as an operand for inline asm", + } +} + +impl ArmInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + // Only qreg is allowed to have modifiers. This should have been + // validated already by now. + if let Some(modifier) = modifier { + let index = self as u32 - Self::q0 as u32; + assert!(index < 16); + let index = index * 2 + (modifier == 'f') as u32; + write!(out, "d{}", index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) { + cb(self); + + macro_rules! reg_conflicts { + ( + $( + $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident + ),*; + $( + $q_high:ident : $d0_high:ident $d1_high:ident + ),*; + ) => { + match self { + $( + Self::$q => { + cb(Self::$d0); + cb(Self::$d1); + cb(Self::$s0); + cb(Self::$s1); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$d0 => { + cb(Self::$q); + cb(Self::$s0); + cb(Self::$s1); + } + Self::$d1 => { + cb(Self::$q); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$s0 | Self::$s1 => { + cb(Self::$q); + cb(Self::$d0); + } + Self::$s2 | Self::$s3 => { + cb(Self::$q); + cb(Self::$d1); + } + )* + $( + Self::$q_high => { + cb(Self::$d0_high); + cb(Self::$d1_high); + } + Self::$d0_high | Self::$d1_high => { + cb(Self::$q_high); + } + )* + _ => {}, + } + }; + } + + // ARM's floating-point register file is interesting in that it can be + // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit + // registers. Because these views overlap, the registers of different + // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1 + // overlaps with d2 and d3). + // + // See section E1.3.1 of the ARM Architecture Reference Manual for + // ARMv8-A for more details. + reg_conflicts! { + q0 : d0 d1 : s0 s1 s2 s3, + q1 : d2 d3 : s4 s5 s6 s7, + q2 : d4 d5 : s8 s9 s10 s11, + q3 : d6 d7 : s12 s13 s14 s15, + q4 : d8 d9 : s16 s17 s18 s19, + q5 : d10 d11 : s20 s21 s22 s23, + q6 : d12 d13 : s24 s25 s26 s27, + q7 : d14 d15 : s28 s29 s30 s31; + q8 : d16 d17, + q9 : d18 d19, + q10 : d20 d21, + q11 : d22 d23, + q12 : d24 d25, + q13 : d26 d27, + q14 : d28 d29, + q15 : d30 d31; + } + } +} diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs new file mode 100644 index 000000000..9a96a61f5 --- /dev/null +++ b/compiler/rustc_target/src/asm/avr.rs @@ -0,0 +1,197 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Avr AvrInlineAsmRegClass { + reg, + reg_upper, + reg_pair, + reg_iw, + reg_ptr, + } +} + +impl AvrInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] { + match self { + Self::reg_pair | Self::reg_iw | Self::reg_ptr => &['h', 'l'], + _ => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => types! { _: I8; }, + Self::reg_upper => types! { _: I8; }, + Self::reg_pair => types! { _: I16; }, + Self::reg_iw => types! { _: I16; }, + Self::reg_ptr => types! { _: I16; }, + } + } +} + +def_regs! { + Avr AvrInlineAsmReg AvrInlineAsmRegClass { + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + r16: reg, reg_upper = ["r16"], + r17: reg, reg_upper = ["r17"], + r18: reg, reg_upper = ["r18"], + r19: reg, reg_upper = ["r19"], + r20: reg, reg_upper = ["r20"], + r21: reg, reg_upper = ["r21"], + r22: reg, reg_upper = ["r22"], + r23: reg, reg_upper = ["r23"], + r24: reg, reg_upper = ["r24"], + r25: reg, reg_upper = ["r25"], + r26: reg, reg_upper = ["r26", "XL"], + r27: reg, reg_upper = ["r27", "XH"], + r30: reg, reg_upper = ["r30", "ZL"], + r31: reg, reg_upper = ["r31", "ZH"], + + r3r2: reg_pair = ["r3r2"], + r5r4: reg_pair = ["r5r4"], + r7r6: reg_pair = ["r7r6"], + r9r8: reg_pair = ["r9r8"], + r11r10: reg_pair = ["r11r10"], + r13r12: reg_pair = ["r13r12"], + r15r14: reg_pair = ["r15r14"], + r17r16: reg_pair = ["r17r16"], + r19r18: reg_pair = ["r19r18"], + r21r20: reg_pair = ["r21r20"], + r23r22: reg_pair = ["r23r22"], + + r25r24: reg_iw, reg_pair = ["r25r24"], + + X: reg_ptr, reg_iw, reg_pair = ["r27r26", "X"], + Z: reg_ptr, reg_iw, reg_pair = ["r31r30", "Z"], + + #error = ["Y", "YL", "YH"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["SP", "SPL", "SPH"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r0", "r1", "r1r0"] => + "r0 and r1 are not available due to an issue in LLVM", + } +} + +macro_rules! emit_pairs { + ( + $self:ident $modifier:ident, + $($pair:ident $name:literal $hi:literal $lo:literal,)* + ) => { + match ($self, $modifier) { + $( + (AvrInlineAsmReg::$pair, Some('h')) => $hi, + (AvrInlineAsmReg::$pair, Some('l')) => $lo, + (AvrInlineAsmReg::$pair, _) => $name, + )* + _ => $self.name(), + } + }; +} + +impl AvrInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + let name = emit_pairs! { + self modifier, + Z "Z" "ZH" "ZL", + X "X" "XH" "XL", + r25r24 "r25:r24" "r25" "r24", + r23r22 "r23:r22" "r23" "r22", + r21r20 "r21:r20" "r21" "r20", + r19r18 "r19:r18" "r19" "r18", + r17r16 "r17:r16" "r17" "r16", + r15r14 "r15:r14" "r15" "r14", + r13r12 "r13:r12" "r13" "r12", + r11r10 "r11:r10" "r11" "r10", + r9r8 "r9:r8" "r9" "r8", + r7r6 "r7:r6" "r7" "r6", + r5r4 "r5:r4" "r5" "r4", + r3r2 "r3:r2" "r3" "r2", + }; + out.write_str(name) + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(AvrInlineAsmReg)) { + cb(self); + + macro_rules! reg_conflicts { + ( + $( + $pair:ident : $hi:ident $lo:ident, + )* + ) => { + match self { + $( + Self::$pair => { + cb(Self::$hi); + cb(Self::$lo); + } + Self::$hi => { + cb(Self::$pair); + } + Self::$lo => { + cb(Self::$pair); + } + )* + } + }; + } + + reg_conflicts! { + Z : r31 r30, + X : r27 r26, + r25r24 : r25 r24, + r23r22 : r23 r22, + r21r20 : r21 r20, + r19r18 : r19 r18, + r17r16 : r17 r16, + r15r14 : r15 r14, + r13r12 : r13 r12, + r11r10 : r11 r10, + r9r8 : r9 r8, + r7r6 : r7 r6, + r5r4 : r5 r4, + r3r2 : r3 r2, + } + } +} diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs new file mode 100644 index 000000000..3b03766a0 --- /dev/null +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -0,0 +1,118 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Bpf BpfInlineAsmRegClass { + reg, + wreg, + } +} + +impl BpfInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => types! { _: I8, I16, I32, I64; }, + Self::wreg => types! { alu32: I8, I16, I32; }, + } + } +} + +def_regs! { + Bpf BpfInlineAsmReg BpfInlineAsmRegClass { + r0: reg = ["r0"], + r1: reg = ["r1"], + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + w0: wreg = ["w0"], + w1: wreg = ["w1"], + w2: wreg = ["w2"], + w3: wreg = ["w3"], + w4: wreg = ["w4"], + w5: wreg = ["w5"], + w6: wreg = ["w6"], + w7: wreg = ["w7"], + w8: wreg = ["w8"], + w9: wreg = ["w9"], + + #error = ["r10", "w10"] => + "the stack pointer cannot be used as an operand for inline asm", + } +} + +impl BpfInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(BpfInlineAsmReg)) { + cb(self); + + macro_rules! reg_conflicts { + ( + $( + $r:ident : $w:ident + ),* + ) => { + match self { + $( + Self::$r => { + cb(Self::$w); + } + Self::$w => { + cb(Self::$r); + } + )* + } + }; + } + + reg_conflicts! { + r0 : w0, + r1 : w1, + r2 : w2, + r3 : w3, + r4 : w4, + r5 : w5, + r6 : w6, + r7 : w7, + r8 : w8, + r9 : w9 + } + } +} diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs new file mode 100644 index 000000000..d20270ac9 --- /dev/null +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -0,0 +1,95 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Hexagon HexagonInlineAsmRegClass { + reg, + } +} + +impl HexagonInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => types! { _: I8, I16, I32, F32; }, + } + } +} + +def_regs! { + Hexagon HexagonInlineAsmReg HexagonInlineAsmRegClass { + r0: reg = ["r0"], + r1: reg = ["r1"], + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + r16: reg = ["r16"], + r17: reg = ["r17"], + r18: reg = ["r18"], + r20: reg = ["r20"], + r21: reg = ["r21"], + r22: reg = ["r22"], + r23: reg = ["r23"], + r24: reg = ["r24"], + r25: reg = ["r25"], + r26: reg = ["r26"], + r27: reg = ["r27"], + r28: reg = ["r28"], + #error = ["r19"] => + "r19 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["r29", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r30", "fr"] => + "the frame register cannot be used as an operand for inline asm", + #error = ["r31", "lr"] => + "the link register cannot be used as an operand for inline asm", + } +} + +impl HexagonInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } + + pub fn overlapping_regs(self, mut _cb: impl FnMut(HexagonInlineAsmReg)) {} +} diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs new file mode 100644 index 000000000..4e7c2eb1b --- /dev/null +++ b/compiler/rustc_target/src/asm/mips.rs @@ -0,0 +1,135 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Mips MipsInlineAsmRegClass { + reg, + freg, + } +} + +impl MipsInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match (self, arch) { + (Self::reg, InlineAsmArch::Mips64) => types! { _: I8, I16, I32, I64, F32, F64; }, + (Self::reg, _) => types! { _: I8, I16, I32, F32; }, + (Self::freg, _) => types! { _: F32, F64; }, + } + } +} + +// The reserved registers are somewhat taken from +// <https://github.com/llvm/llvm-project/blob/deb8f8bcf31540c657716ea5242183b0792702a1/llvm/lib/Target/Mips/MipsRegisterInfo.cpp#L150>. +def_regs! { + Mips MipsInlineAsmReg MipsInlineAsmRegClass { + r2: reg = ["$2"], + r3: reg = ["$3"], + r4: reg = ["$4"], + r5: reg = ["$5"], + r6: reg = ["$6"], + r7: reg = ["$7"], + // FIXME: Reserve $t0, $t1 if in mips16 mode. + r8: reg = ["$8"], + r9: reg = ["$9"], + r10: reg = ["$10"], + r11: reg = ["$11"], + r12: reg = ["$12"], + r13: reg = ["$13"], + r14: reg = ["$14"], + r15: reg = ["$15"], + r16: reg = ["$16"], + r17: reg = ["$17"], + r18: reg = ["$18"], + r19: reg = ["$19"], + r20: reg = ["$20"], + r21: reg = ["$21"], + r22: reg = ["$22"], + r23: reg = ["$23"], + r24: reg = ["$24"], + r25: reg = ["$25"], + f0: freg = ["$f0"], + f1: freg = ["$f1"], + f2: freg = ["$f2"], + f3: freg = ["$f3"], + f4: freg = ["$f4"], + f5: freg = ["$f5"], + f6: freg = ["$f6"], + f7: freg = ["$f7"], + f8: freg = ["$f8"], + f9: freg = ["$f9"], + f10: freg = ["$f10"], + f11: freg = ["$f11"], + f12: freg = ["$f12"], + f13: freg = ["$f13"], + f14: freg = ["$f14"], + f15: freg = ["$f15"], + f16: freg = ["$f16"], + f17: freg = ["$f17"], + f18: freg = ["$f18"], + f19: freg = ["$f19"], + f20: freg = ["$f20"], + f21: freg = ["$f21"], + f22: freg = ["$f22"], + f23: freg = ["$f23"], + f24: freg = ["$f24"], + f25: freg = ["$f25"], + f26: freg = ["$f26"], + f27: freg = ["$f27"], + f28: freg = ["$f28"], + f29: freg = ["$f29"], + f30: freg = ["$f30"], + f31: freg = ["$f31"], + #error = ["$0"] => + "constant zero cannot be used as an operand for inline asm", + #error = ["$1"] => + "reserved for assembler (Assembler Temp)", + #error = ["$26"] => + "OS-reserved register cannot be used as an operand for inline asm", + #error = ["$27"] => + "OS-reserved register cannot be used as an operand for inline asm", + #error = ["$28"] => + "the global pointer cannot be used as an operand for inline asm", + #error = ["$29"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["$30"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["$31"] => + "the return address register cannot be used as an operand for inline asm", + } +} + +impl MipsInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs new file mode 100644 index 000000000..65d2cd64b --- /dev/null +++ b/compiler/rustc_target/src/asm/mod.rs @@ -0,0 +1,976 @@ +use crate::spec::Target; +use crate::{abi::Size, spec::RelocModel}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; +use std::str::FromStr; + +macro_rules! def_reg_class { + ($arch:ident $arch_regclass:ident { + $( + $class:ident, + )* + }) => { + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_regclass { + $($class,)* + } + + impl $arch_regclass { + pub fn name(self) -> rustc_span::Symbol { + match self { + $(Self::$class => rustc_span::symbol::sym::$class,)* + } + } + + pub fn parse(name: rustc_span::Symbol) -> Result<Self, &'static str> { + match name { + $( + rustc_span::sym::$class => Ok(Self::$class), + )* + _ => Err("unknown register class"), + } + } + } + + pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>, + > { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use super::InlineAsmRegClass; + let mut map = FxHashMap::default(); + $( + map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default()); + )* + map + } + } +} + +macro_rules! def_regs { + ($arch:ident $arch_reg:ident $arch_regclass:ident { + $( + $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, + )* + $( + #error = [$($bad_reg:literal),+] => $error:literal, + )* + }) => { + #[allow(unreachable_code)] + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_reg { + $($reg,)* + } + + impl $arch_reg { + pub fn name(self) -> &'static str { + match self { + $(Self::$reg => $reg_name,)* + } + } + + pub fn reg_class(self) -> $arch_regclass { + match self { + $(Self::$reg => $arch_regclass::$class,)* + } + } + + pub fn parse(name: &str) -> Result<Self, &'static str> { + match name { + $( + $($alias)|* | $reg_name => Ok(Self::$reg), + )* + $( + $($bad_reg)|* => Err($error), + )* + _ => Err("unknown register"), + } + } + + pub fn validate(self, + _arch: super::InlineAsmArch, + _reloc_model: crate::spec::RelocModel, + _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>, + _target: &crate::spec::Target, + _is_clobber: bool, + ) -> Result<(), &'static str> { + match self { + $( + Self::$reg => { + $($filter( + _arch, + _reloc_model, + _target_features, + _target, + _is_clobber + )?;)? + Ok(()) + } + )* + } + } + } + + pub(super) fn fill_reg_map( + _arch: super::InlineAsmArch, + _reloc_model: crate::spec::RelocModel, + _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>, + _target: &crate::spec::Target, + _map: &mut rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>, + >, + ) { + #[allow(unused_imports)] + use super::{InlineAsmReg, InlineAsmRegClass}; + $( + if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)? true { + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + $( + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + )* + } + )* + } + } +} + +macro_rules! types { + ( + $(_ : $($ty:expr),+;)? + $($feature:ident: $($ty2:expr),+;)* + ) => { + { + use super::InlineAsmType::*; + &[ + $($( + ($ty, None), + )*)? + $($( + ($ty2, Some(rustc_span::sym::$feature)), + )*)* + ] + } + }; +} + +mod aarch64; +mod arm; +mod avr; +mod bpf; +mod hexagon; +mod mips; +mod msp430; +mod nvptx; +mod powerpc; +mod riscv; +mod s390x; +mod spirv; +mod wasm; +mod x86; + +pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; +pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass}; +pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass}; +pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; +pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; +pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass}; +pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; +pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass}; +pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use s390x::{S390xInlineAsmReg, S390xInlineAsmRegClass}; +pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; +pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass}; +pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmArch { + X86, + X86_64, + Arm, + AArch64, + RiscV32, + RiscV64, + Nvptx64, + Hexagon, + Mips, + Mips64, + PowerPC, + PowerPC64, + S390x, + SpirV, + Wasm32, + Wasm64, + Bpf, + Avr, + Msp430, +} + +impl FromStr for InlineAsmArch { + type Err = (); + + fn from_str(s: &str) -> Result<InlineAsmArch, ()> { + match s { + "x86" => Ok(Self::X86), + "x86_64" => Ok(Self::X86_64), + "arm" => Ok(Self::Arm), + "aarch64" => Ok(Self::AArch64), + "riscv32" => Ok(Self::RiscV32), + "riscv64" => Ok(Self::RiscV64), + "nvptx64" => Ok(Self::Nvptx64), + "powerpc" => Ok(Self::PowerPC), + "powerpc64" => Ok(Self::PowerPC64), + "hexagon" => Ok(Self::Hexagon), + "mips" => Ok(Self::Mips), + "mips64" => Ok(Self::Mips64), + "s390x" => Ok(Self::S390x), + "spirv" => Ok(Self::SpirV), + "wasm32" => Ok(Self::Wasm32), + "wasm64" => Ok(Self::Wasm64), + "bpf" => Ok(Self::Bpf), + "avr" => Ok(Self::Avr), + "msp430" => Ok(Self::Msp430), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum InlineAsmReg { + X86(X86InlineAsmReg), + Arm(ArmInlineAsmReg), + AArch64(AArch64InlineAsmReg), + RiscV(RiscVInlineAsmReg), + Nvptx(NvptxInlineAsmReg), + PowerPC(PowerPCInlineAsmReg), + Hexagon(HexagonInlineAsmReg), + Mips(MipsInlineAsmReg), + S390x(S390xInlineAsmReg), + SpirV(SpirVInlineAsmReg), + Wasm(WasmInlineAsmReg), + Bpf(BpfInlineAsmReg), + Avr(AvrInlineAsmReg), + Msp430(Msp430InlineAsmReg), + // Placeholder for invalid register constraints for the current target + Err, +} + +impl InlineAsmReg { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::PowerPC(r) => r.name(), + Self::Hexagon(r) => r.name(), + Self::Mips(r) => r.name(), + Self::S390x(r) => r.name(), + Self::Bpf(r) => r.name(), + Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), + Self::Err => "<reg>", + } + } + + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), + Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), + Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), + Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + Self::PowerPC(r) => InlineAsmRegClass::PowerPC(r.reg_class()), + Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), + Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), + Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()), + Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), + Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()), + Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()), + Self::Err => InlineAsmRegClass::Err, + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> { + // FIXME: use direct symbol comparison for register names + // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. + let name = name.as_str(); + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => Self::X86(X86InlineAsmReg::parse(name)?), + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse(name)?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmReg::parse(name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse(name)?), + InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { + Self::PowerPC(PowerPCInlineAsmReg::parse(name)?) + } + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse(name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmReg::parse(name)?) + } + InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?), + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?), + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { + Self::Wasm(WasmInlineAsmReg::parse(name)?) + } + InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmReg::parse(name)?), + InlineAsmArch::Avr => Self::Avr(AvrInlineAsmReg::parse(name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse(name)?), + }) + } + + pub fn validate( + self, + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + target: &Target, + is_clobber: bool, + ) -> Result<(), &'static str> { + match self { + Self::X86(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Arm(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::AArch64(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::RiscV(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::PowerPC(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Hexagon(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Mips(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::S390x(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Bpf(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Avr(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Err => unreachable!(), + } + } + + // NOTE: This function isn't used at the moment, but is needed to support + // falling back to an external assembler. + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + match self { + Self::X86(r) => r.emit(out, arch, modifier), + Self::Arm(r) => r.emit(out, arch, modifier), + Self::AArch64(r) => r.emit(out, arch, modifier), + Self::RiscV(r) => r.emit(out, arch, modifier), + Self::PowerPC(r) => r.emit(out, arch, modifier), + Self::Hexagon(r) => r.emit(out, arch, modifier), + Self::Mips(r) => r.emit(out, arch, modifier), + Self::S390x(r) => r.emit(out, arch, modifier), + Self::Bpf(r) => r.emit(out, arch, modifier), + Self::Avr(r) => r.emit(out, arch, modifier), + Self::Msp430(r) => r.emit(out, arch, modifier), + Self::Err => unreachable!("Use of InlineAsmReg::Err"), + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { + match self { + Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), + Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), + Self::AArch64(_) => cb(self), + Self::RiscV(_) => cb(self), + Self::PowerPC(r) => r.overlapping_regs(|r| cb(Self::PowerPC(r))), + Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), + Self::Mips(_) => cb(self), + Self::S390x(_) => cb(self), + Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), + Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), + Self::Msp430(_) => cb(self), + Self::Err => unreachable!("Use of InlineAsmReg::Err"), + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum InlineAsmRegClass { + X86(X86InlineAsmRegClass), + Arm(ArmInlineAsmRegClass), + AArch64(AArch64InlineAsmRegClass), + RiscV(RiscVInlineAsmRegClass), + Nvptx(NvptxInlineAsmRegClass), + PowerPC(PowerPCInlineAsmRegClass), + Hexagon(HexagonInlineAsmRegClass), + Mips(MipsInlineAsmRegClass), + S390x(S390xInlineAsmRegClass), + SpirV(SpirVInlineAsmRegClass), + Wasm(WasmInlineAsmRegClass), + Bpf(BpfInlineAsmRegClass), + Avr(AvrInlineAsmRegClass), + Msp430(Msp430InlineAsmRegClass), + // Placeholder for invalid register constraints for the current target + Err, +} + +impl InlineAsmRegClass { + pub fn name(self) -> Symbol { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), + Self::PowerPC(r) => r.name(), + Self::Hexagon(r) => r.name(), + Self::Mips(r) => r.name(), + Self::S390x(r) => r.name(), + Self::SpirV(r) => r.name(), + Self::Wasm(r) => r.name(), + Self::Bpf(r) => r.name(), + Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), + Self::Err => rustc_span::symbol::sym::reg, + } + } + + /// Returns a suggested register class to use for this type. This is called + /// when `supported_types` fails to give a better error + /// message to the user. + pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> { + match self { + Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), + Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), + Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), + Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), + Self::PowerPC(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::PowerPC), + Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), + Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), + Self::S390x(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::S390x), + Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), + Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), + Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), + Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr), + Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), + } + } + + /// Returns a suggested template modifier to use for this type and an + /// example of a register named formatted with it. + /// + /// Such suggestions are useful if a type smaller than the full register + /// size is used and a modifier can be used to point to the subregister of + /// the correct size. + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.suggest_modifier(arch, ty), + Self::Arm(r) => r.suggest_modifier(arch, ty), + Self::AArch64(r) => r.suggest_modifier(arch, ty), + Self::RiscV(r) => r.suggest_modifier(arch, ty), + Self::Nvptx(r) => r.suggest_modifier(arch, ty), + Self::PowerPC(r) => r.suggest_modifier(arch, ty), + Self::Hexagon(r) => r.suggest_modifier(arch, ty), + Self::Mips(r) => r.suggest_modifier(arch, ty), + Self::S390x(r) => r.suggest_modifier(arch, ty), + Self::SpirV(r) => r.suggest_modifier(arch, ty), + Self::Wasm(r) => r.suggest_modifier(arch, ty), + Self::Bpf(r) => r.suggest_modifier(arch, ty), + Self::Avr(r) => r.suggest_modifier(arch, ty), + Self::Msp430(r) => r.suggest_modifier(arch, ty), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), + } + } + + /// Returns the default modifier for this register and an example of a + /// register named formatted with it. + /// + /// This is only needed when the register class can suggest a modifier, so + /// that the user can be shown how to get the default behavior without a + /// warning. + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.default_modifier(arch), + Self::Arm(r) => r.default_modifier(arch), + Self::AArch64(r) => r.default_modifier(arch), + Self::RiscV(r) => r.default_modifier(arch), + Self::Nvptx(r) => r.default_modifier(arch), + Self::PowerPC(r) => r.default_modifier(arch), + Self::Hexagon(r) => r.default_modifier(arch), + Self::Mips(r) => r.default_modifier(arch), + Self::S390x(r) => r.default_modifier(arch), + Self::SpirV(r) => r.default_modifier(arch), + Self::Wasm(r) => r.default_modifier(arch), + Self::Bpf(r) => r.default_modifier(arch), + Self::Avr(r) => r.default_modifier(arch), + Self::Msp430(r) => r.default_modifier(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), + } + } + + /// Returns a list of supported types for this register class, each with an + /// options target feature required to use this type. + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::X86(r) => r.supported_types(arch), + Self::Arm(r) => r.supported_types(arch), + Self::AArch64(r) => r.supported_types(arch), + Self::RiscV(r) => r.supported_types(arch), + Self::Nvptx(r) => r.supported_types(arch), + Self::PowerPC(r) => r.supported_types(arch), + Self::Hexagon(r) => r.supported_types(arch), + Self::Mips(r) => r.supported_types(arch), + Self::S390x(r) => r.supported_types(arch), + Self::SpirV(r) => r.supported_types(arch), + Self::Wasm(r) => r.supported_types(arch), + Self::Bpf(r) => r.supported_types(arch), + Self::Avr(r) => r.supported_types(arch), + Self::Msp430(r) => r.supported_types(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(name)?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(name)?), + InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { + Self::PowerPC(PowerPCInlineAsmRegClass::parse(name)?) + } + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmRegClass::parse(name)?) + } + InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(name)?), + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(name)?), + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { + Self::Wasm(WasmInlineAsmRegClass::parse(name)?) + } + InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(name)?), + InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(name)?), + }) + } + + /// Returns the list of template modifiers that can be used with this + /// register class. + pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { + match self { + Self::X86(r) => r.valid_modifiers(arch), + Self::Arm(r) => r.valid_modifiers(arch), + Self::AArch64(r) => r.valid_modifiers(arch), + Self::RiscV(r) => r.valid_modifiers(arch), + Self::Nvptx(r) => r.valid_modifiers(arch), + Self::PowerPC(r) => r.valid_modifiers(arch), + Self::Hexagon(r) => r.valid_modifiers(arch), + Self::Mips(r) => r.valid_modifiers(arch), + Self::S390x(r) => r.valid_modifiers(arch), + Self::SpirV(r) => r.valid_modifiers(arch), + Self::Wasm(r) => r.valid_modifiers(arch), + Self::Bpf(r) => r.valid_modifiers(arch), + Self::Avr(r) => r.valid_modifiers(arch), + Self::Msp430(r) => r.valid_modifiers(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), + } + } + + /// Returns whether registers in this class can only be used as clobbers + /// and not as inputs/outputs. + pub fn is_clobber_only(self, arch: InlineAsmArch) -> bool { + self.supported_types(arch).is_empty() + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum InlineAsmRegOrRegClass { + Reg(InlineAsmReg), + RegClass(InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::Reg(r) => r.reg_class(), + Self::RegClass(r) => r, + } + } +} + +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reg(r) => write!(f, "\"{}\"", r.name()), + Self::RegClass(r) => write!(f, "{}", r.name()), + } + } +} + +/// Set of types which can be used with a particular register class. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InlineAsmType { + I8, + I16, + I32, + I64, + I128, + F32, + F64, + VecI8(u64), + VecI16(u64), + VecI32(u64), + VecI64(u64), + VecI128(u64), + VecF32(u64), + VecF64(u64), +} + +impl InlineAsmType { + pub fn is_integer(self) -> bool { + matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128) + } + + pub fn size(self) -> Size { + Size::from_bytes(match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 4, + Self::I64 => 8, + Self::I128 => 16, + Self::F32 => 4, + Self::F64 => 8, + Self::VecI8(n) => n * 1, + Self::VecI16(n) => n * 2, + Self::VecI32(n) => n * 4, + Self::VecI64(n) => n * 8, + Self::VecI128(n) => n * 16, + Self::VecF32(n) => n * 4, + Self::VecF64(n) => n * 8, + }) + } +} + +impl fmt::Display for InlineAsmType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::I8 => f.write_str("i8"), + Self::I16 => f.write_str("i16"), + Self::I32 => f.write_str("i32"), + Self::I64 => f.write_str("i64"), + Self::I128 => f.write_str("i128"), + Self::F32 => f.write_str("f32"), + Self::F64 => f.write_str("f64"), + Self::VecI8(n) => write!(f, "i8x{}", n), + Self::VecI16(n) => write!(f, "i16x{}", n), + Self::VecI32(n) => write!(f, "i32x{}", n), + Self::VecI64(n) => write!(f, "i64x{}", n), + Self::VecI128(n) => write!(f, "i128x{}", n), + Self::VecF32(n) => write!(f, "f32x{}", n), + Self::VecF64(n) => write!(f, "f64x{}", n), + } + } +} + +/// Returns the full set of allocatable registers for a given architecture. +/// +/// The registers are structured as a map containing the set of allocatable +/// registers in each register class. A particular register may be allocatable +/// from multiple register classes, in which case it will appear multiple times +/// in the map. +// NOTE: This function isn't used at the moment, but is needed to support +// falling back to an external assembler. +pub fn allocatable_registers( + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + target: &crate::spec::Target, +) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> { + match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + let mut map = x86::regclass_map(); + x86::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Arm => { + let mut map = arm::regclass_map(); + arm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::AArch64 => { + let mut map = aarch64::regclass_map(); + aarch64::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + let mut map = riscv::regclass_map(); + riscv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Nvptx64 => { + let mut map = nvptx::regclass_map(); + nvptx::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { + let mut map = powerpc::regclass_map(); + powerpc::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Hexagon => { + let mut map = hexagon::regclass_map(); + hexagon::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + let mut map = mips::regclass_map(); + mips::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::S390x => { + let mut map = s390x::regclass_map(); + s390x::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::SpirV => { + let mut map = spirv::regclass_map(); + spirv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { + let mut map = wasm::regclass_map(); + wasm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Bpf => { + let mut map = bpf::regclass_map(); + bpf::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Avr => { + let mut map = avr::regclass_map(); + avr::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + InlineAsmArch::Msp430 => { + let mut map = msp430::regclass_map(); + msp430::fill_reg_map(arch, reloc_model, target_features, target, &mut map); + map + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum InlineAsmClobberAbi { + X86, + X86_64Win, + X86_64SysV, + Arm, + AArch64, + AArch64NoX18, + RiscV, +} + +impl InlineAsmClobberAbi { + /// Parses a clobber ABI for the given target, or returns a list of supported + /// clobber ABIs for the target. + pub fn parse( + arch: InlineAsmArch, + target: &Target, + name: Symbol, + ) -> Result<Self, &'static [&'static str]> { + let name = name.as_str(); + match arch { + InlineAsmArch::X86 => match name { + "C" | "system" | "efiapi" | "cdecl" | "stdcall" | "fastcall" => { + Ok(InlineAsmClobberAbi::X86) + } + _ => Err(&["C", "system", "efiapi", "cdecl", "stdcall", "fastcall"]), + }, + InlineAsmArch::X86_64 => match name { + "C" | "system" if !target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64SysV), + "C" | "system" if target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64Win), + "win64" | "efiapi" => Ok(InlineAsmClobberAbi::X86_64Win), + "sysv64" => Ok(InlineAsmClobberAbi::X86_64SysV), + _ => Err(&["C", "system", "efiapi", "win64", "sysv64"]), + }, + InlineAsmArch::Arm => match name { + "C" | "system" | "efiapi" | "aapcs" => Ok(InlineAsmClobberAbi::Arm), + _ => Err(&["C", "system", "efiapi", "aapcs"]), + }, + InlineAsmArch::AArch64 => match name { + "C" | "system" | "efiapi" => Ok(if aarch64::target_reserves_x18(target) { + InlineAsmClobberAbi::AArch64NoX18 + } else { + InlineAsmClobberAbi::AArch64 + }), + _ => Err(&["C", "system", "efiapi"]), + }, + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { + "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV), + _ => Err(&["C", "system", "efiapi"]), + }, + _ => Err(&[]), + } + } + + /// Returns the set of registers which are clobbered by this ABI. + pub fn clobbered_regs(self) -> &'static [InlineAsmReg] { + macro_rules! clobbered_regs { + ($arch:ident $arch_reg:ident { + $( + $reg:ident, + )* + }) => { + &[ + $(InlineAsmReg::$arch($arch_reg::$reg),)* + ] + }; + } + match self { + InlineAsmClobberAbi::X86 => clobbered_regs! { + X86 X86InlineAsmReg { + ax, cx, dx, + + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, + + k0, k1, k2, k3, k4, k5, k6, k7, + + mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, + st0, st1, st2, st3, st4, st5, st6, st7, + } + }, + InlineAsmClobberAbi::X86_64SysV => clobbered_regs! { + X86 X86InlineAsmReg { + ax, cx, dx, si, di, r8, r9, r10, r11, + + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, + xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, + zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23, + zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31, + + k0, k1, k2, k3, k4, k5, k6, k7, + + mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, + st0, st1, st2, st3, st4, st5, st6, st7, + tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, + } + }, + InlineAsmClobberAbi::X86_64Win => clobbered_regs! { + X86 X86InlineAsmReg { + // rdi and rsi are callee-saved on windows + ax, cx, dx, r8, r9, r10, r11, + + // xmm6-xmm15 are callee-saved on windows, but we need to + // mark them as clobbered anyways because the upper portions + // of ymm6-ymm15 are volatile. + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, + xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, + zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23, + zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31, + + k0, k1, k2, k3, k4, k5, k6, k7, + + mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, + st0, st1, st2, st3, st4, st5, st6, st7, + tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, + } + }, + InlineAsmClobberAbi::AArch64 => clobbered_regs! { + AArch64 AArch64InlineAsmReg { + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x30, + + // Technically the low 64 bits of v8-v15 are preserved, but + // we have no way of expressing this using clobbers. + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + + p0, p1, p2, p3, p4, p5, p6, p7, + p8, p9, p10, p11, p12, p13, p14, p15, + ffr, + + } + }, + InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! { + AArch64 AArch64InlineAsmReg { + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x30, + + // Technically the low 64 bits of v8-v15 are preserved, but + // we have no way of expressing this using clobbers. + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + + p0, p1, p2, p3, p4, p5, p6, p7, + p8, p9, p10, p11, p12, p13, p14, p15, + ffr, + + } + }, + InlineAsmClobberAbi::Arm => clobbered_regs! { + Arm ArmInlineAsmReg { + // r9 is either platform-reserved or callee-saved. Either + // way we don't need to clobber it. + r0, r1, r2, r3, r12, r14, + + // The finest-grained register variant is used here so that + // partial uses of larger registers are properly handled. + s0, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s11, s12, s13, s14, s15, + // s16-s31 are callee-saved + d16, d17, d18, d19, d20, d21, d22, d23, + d24, d25, d26, d27, d28, d29, d30, d31, + } + }, + InlineAsmClobberAbi::RiscV => clobbered_regs! { + RiscV RiscVInlineAsmReg { + // ra + x1, + // t0-t2 + x5, x6, x7, + // a0-a7 + x10, x11, x12, x13, x14, x15, x16, x17, + // t3-t6 + x28, x29, x30, x31, + // ft0-ft7 + f0, f1, f2, f3, f4, f5, f6, f7, + // fa0-fa7 + f10, f11, f12, f13, f14, f15, f16, f17, + // ft8-ft11 + f28, f29, f30, f31, + + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + } + }, + } + } +} diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs new file mode 100644 index 000000000..a27d6390a --- /dev/null +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -0,0 +1,81 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Msp430 Msp430InlineAsmRegClass { + reg, + } +} + +impl Msp430InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match (self, arch) { + (Self::reg, _) => types! { _: I8, I16; }, + } + } +} + +// The reserved registers are taken from: +// https://github.com/llvm/llvm-project/blob/36cb29cbbe1b22dcd298ad65e1fabe899b7d7249/llvm/lib/Target/MSP430/MSP430RegisterInfo.cpp#L73. +def_regs! { + Msp430 Msp430InlineAsmReg Msp430InlineAsmRegClass { + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + + #error = ["r0", "pc"] => + "the program counter cannot be used as an operand for inline asm", + #error = ["r1", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r2", "sr"] => + "the status register cannot be used as an operand for inline asm", + #error = ["r3", "cg"] => + "the constant generator cannot be used as an operand for inline asm", + #error = ["r4", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + } +} + +impl Msp430InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs new file mode 100644 index 000000000..8e1e91e7c --- /dev/null +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -0,0 +1,50 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; + +def_reg_class! { + Nvptx NvptxInlineAsmRegClass { + reg16, + reg32, + reg64, + } +} + +impl NvptxInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg16 => types! { _: I8, I16; }, + Self::reg32 => types! { _: I8, I16, I32, F32; }, + Self::reg64 => types! { _: I8, I16, I32, F32, I64, F64; }, + } + } +} + +def_regs! { + // Registers in PTX are declared in the assembly. + // There are no predefined registers that one can use. + Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {} +} diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs new file mode 100644 index 000000000..d3ccb3035 --- /dev/null +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -0,0 +1,204 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + PowerPC PowerPCInlineAsmRegClass { + reg, + reg_nonzero, + freg, + cr, + xer, + } +} + +impl PowerPCInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg | Self::reg_nonzero => { + if arch == InlineAsmArch::PowerPC { + types! { _: I8, I16, I32; } + } else { + types! { _: I8, I16, I32, I64; } + } + } + Self::freg => types! { _: F32, F64; }, + Self::cr | Self::xer => &[], + } + } +} + +def_regs! { + PowerPC PowerPCInlineAsmReg PowerPCInlineAsmRegClass { + r0: reg = ["r0", "0"], + r3: reg, reg_nonzero = ["r3", "3"], + r4: reg, reg_nonzero = ["r4", "4"], + r5: reg, reg_nonzero = ["r5", "5"], + r6: reg, reg_nonzero = ["r6", "6"], + r7: reg, reg_nonzero = ["r7", "7"], + r8: reg, reg_nonzero = ["r8", "8"], + r9: reg, reg_nonzero = ["r9", "9"], + r10: reg, reg_nonzero = ["r10", "10"], + r11: reg, reg_nonzero = ["r11", "11"], + r12: reg, reg_nonzero = ["r12", "12"], + r14: reg, reg_nonzero = ["r14", "14"], + r15: reg, reg_nonzero = ["r15", "15"], + r16: reg, reg_nonzero = ["r16", "16"], + r17: reg, reg_nonzero = ["r17", "17"], + r18: reg, reg_nonzero = ["r18", "18"], + r19: reg, reg_nonzero = ["r19", "19"], + r20: reg, reg_nonzero = ["r20", "20"], + r21: reg, reg_nonzero = ["r21", "21"], + r22: reg, reg_nonzero = ["r22", "22"], + r23: reg, reg_nonzero = ["r23", "23"], + r24: reg, reg_nonzero = ["r24", "24"], + r25: reg, reg_nonzero = ["r25", "25"], + r26: reg, reg_nonzero = ["r26", "26"], + r27: reg, reg_nonzero = ["r27", "27"], + r28: reg, reg_nonzero = ["r28", "28"], + f0: freg = ["f0", "fr0"], + f1: freg = ["f1", "fr1"], + f2: freg = ["f2", "fr2"], + f3: freg = ["f3", "fr3"], + f4: freg = ["f4", "fr4"], + f5: freg = ["f5", "fr5"], + f6: freg = ["f6", "fr6"], + f7: freg = ["f7", "fr7"], + f8: freg = ["f8", "fr8"], + f9: freg = ["f9", "fr9"], + f10: freg = ["f10", "fr10"], + f11: freg = ["f11", "fr11"], + f12: freg = ["f12", "fr12"], + f13: freg = ["f13", "fr13"], + f14: freg = ["f14", "fr14"], + f15: freg = ["f15", "fr15"], + f16: freg = ["f16", "fr16"], + f17: freg = ["f17", "fr17"], + f18: freg = ["f18", "fr18"], + f19: freg = ["f19", "fr19"], + f20: freg = ["f20", "fr20"], + f21: freg = ["f21", "fr21"], + f22: freg = ["f22", "fr22"], + f23: freg = ["f23", "fr23"], + f24: freg = ["f24", "fr24"], + f25: freg = ["f25", "fr25"], + f26: freg = ["f26", "fr26"], + f27: freg = ["f27", "fr27"], + f28: freg = ["f28", "fr28"], + f29: freg = ["f29", "fr29"], + f30: freg = ["f30", "fr30"], + f31: freg = ["f31", "fr31"], + cr: cr = ["cr"], + cr0: cr = ["cr0"], + cr1: cr = ["cr1"], + cr2: cr = ["cr2"], + cr3: cr = ["cr3"], + cr4: cr = ["cr4"], + cr5: cr = ["cr5"], + cr6: cr = ["cr6"], + cr7: cr = ["cr7"], + xer: xer = ["xer"], + #error = ["r1", "1", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r2", "2"] => + "r2 is a system reserved register and cannot be used as an operand for inline asm", + #error = ["r13", "13"] => + "r13 is a system reserved register and cannot be used as an operand for inline asm", + #error = ["r29", "29"] => + "r29 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["r30", "30"] => + "r30 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["r31", "31", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["lr"] => + "the link register cannot be used as an operand for inline asm", + #error = ["ctr"] => + "the counter register cannot be used as an operand for inline asm", + #error = ["vrsave"] => + "the vrsave register cannot be used as an operand for inline asm", + } +} + +impl PowerPCInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + macro_rules! do_emit { + ( + $($(($reg:ident, $value:literal)),*;)* + ) => { + out.write_str(match self { + $($(Self::$reg => $value,)*)* + }) + }; + } + // Strip off the leading prefix. + do_emit! { + (r0, "0"), (r3, "3"), (r4, "4"), (r5, "5"), (r6, "6"), (r7, "7"); + (r8, "8"), (r9, "9"), (r10, "10"), (r11, "11"), (r12, "12"), (r14, "14"), (r15, "15"); + (r16, "16"), (r17, "17"), (r18, "18"), (r19, "19"), (r20, "20"), (r21, "21"), (r22, "22"), (r23, "23"); + (r24, "24"), (r25, "25"), (r26, "26"), (r27, "27"), (r28, "28"); + (f0, "0"), (f1, "1"), (f2, "2"), (f3, "3"), (f4, "4"), (f5, "5"), (f6, "6"), (f7, "7"); + (f8, "8"), (f9, "9"), (f10, "10"), (f11, "11"), (f12, "12"), (f13, "13"), (f14, "14"), (f15, "15"); + (f16, "16"), (f17, "17"), (f18, "18"), (f19, "19"), (f20, "20"), (f21, "21"), (f22, "22"), (f23, "23"); + (f24, "24"), (f25, "25"), (f26, "26"), (f27, "27"), (f28, "28"), (f29, "29"), (f30, "30"), (f31, "31"); + (cr, "cr"); + (cr0, "0"), (cr1, "1"), (cr2, "2"), (cr3, "3"), (cr4, "4"), (cr5, "5"), (cr6, "6"), (cr7, "7"); + (xer, "xer"); + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(PowerPCInlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $full:ident : $($field:ident)* + ),*; + ) => { + match self { + $( + Self::$full => { + cb(Self::$full); + $(cb(Self::$field);)* + } + $(Self::$field)|* => { + cb(Self::$full); + cb(self); + } + )* + r => cb(r), + } + }; + } + reg_conflicts! { + cr : cr0 cr1 cr2 cr3 cr4 cr5 cr6 cr7; + } + } +} diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs new file mode 100644 index 000000000..e41bdc9a5 --- /dev/null +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -0,0 +1,185 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::{RelocModel, Target}; +use rustc_data_structures::fx::FxHashSet; +use rustc_macros::HashStable_Generic; +use rustc_span::{sym, Symbol}; +use std::fmt; + +def_reg_class! { + RiscV RiscVInlineAsmRegClass { + reg, + freg, + vreg, + } +} + +impl RiscVInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => { + if arch == InlineAsmArch::RiscV64 { + types! { _: I8, I16, I32, I64, F32, F64; } + } else { + types! { _: I8, I16, I32, F32; } + } + } + Self::freg => types! { f: F32; d: F64; }, + Self::vreg => &[], + } + } +} + +fn not_e( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxHashSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::e) { + Err("register can't be used with the `e` target feature") + } else { + Ok(()) + } +} + +def_regs! { + RiscV RiscVInlineAsmReg RiscVInlineAsmRegClass { + x1: reg = ["x1", "ra"], + x5: reg = ["x5", "t0"], + x6: reg = ["x6", "t1"], + x7: reg = ["x7", "t2"], + x10: reg = ["x10", "a0"], + x11: reg = ["x11", "a1"], + x12: reg = ["x12", "a2"], + x13: reg = ["x13", "a3"], + x14: reg = ["x14", "a4"], + x15: reg = ["x15", "a5"], + x16: reg = ["x16", "a6"] % not_e, + x17: reg = ["x17", "a7"] % not_e, + x18: reg = ["x18", "s2"] % not_e, + x19: reg = ["x19", "s3"] % not_e, + x20: reg = ["x20", "s4"] % not_e, + x21: reg = ["x21", "s5"] % not_e, + x22: reg = ["x22", "s6"] % not_e, + x23: reg = ["x23", "s7"] % not_e, + x24: reg = ["x24", "s8"] % not_e, + x25: reg = ["x25", "s9"] % not_e, + x26: reg = ["x26", "s10"] % not_e, + x27: reg = ["x27", "s11"] % not_e, + x28: reg = ["x28", "t3"] % not_e, + x29: reg = ["x29", "t4"] % not_e, + x30: reg = ["x30", "t5"] % not_e, + x31: reg = ["x31", "t6"] % not_e, + f0: freg = ["f0", "ft0"], + f1: freg = ["f1", "ft1"], + f2: freg = ["f2", "ft2"], + f3: freg = ["f3", "ft3"], + f4: freg = ["f4", "ft4"], + f5: freg = ["f5", "ft5"], + f6: freg = ["f6", "ft6"], + f7: freg = ["f7", "ft7"], + f8: freg = ["f8", "fs0"], + f9: freg = ["f9", "fs1"], + f10: freg = ["f10", "fa0"], + f11: freg = ["f11", "fa1"], + f12: freg = ["f12", "fa2"], + f13: freg = ["f13", "fa3"], + f14: freg = ["f14", "fa4"], + f15: freg = ["f15", "fa5"], + f16: freg = ["f16", "fa6"], + f17: freg = ["f17", "fa7"], + f18: freg = ["f18", "fs2"], + f19: freg = ["f19", "fs3"], + f20: freg = ["f20", "fs4"], + f21: freg = ["f21", "fs5"], + f22: freg = ["f22", "fs6"], + f23: freg = ["f23", "fs7"], + f24: freg = ["f24", "fs8"], + f25: freg = ["f25", "fs9"], + f26: freg = ["f26", "fs10"], + f27: freg = ["f27", "fs11"], + f28: freg = ["f28", "ft8"], + f29: freg = ["f29", "ft9"], + f30: freg = ["f30", "ft10"], + f31: freg = ["f31", "ft11"], + v0: vreg = ["v0"], + v1: vreg = ["v1"], + v2: vreg = ["v2"], + v3: vreg = ["v3"], + v4: vreg = ["v4"], + v5: vreg = ["v5"], + v6: vreg = ["v6"], + v7: vreg = ["v7"], + v8: vreg = ["v8"], + v9: vreg = ["v9"], + v10: vreg = ["v10"], + v11: vreg = ["v11"], + v12: vreg = ["v12"], + v13: vreg = ["v13"], + v14: vreg = ["v14"], + v15: vreg = ["v15"], + v16: vreg = ["v16"], + v17: vreg = ["v17"], + v18: vreg = ["v18"], + v19: vreg = ["v19"], + v20: vreg = ["v20"], + v21: vreg = ["v21"], + v22: vreg = ["v22"], + v23: vreg = ["v23"], + v24: vreg = ["v24"], + v25: vreg = ["v25"], + v26: vreg = ["v26"], + v27: vreg = ["v27"], + v28: vreg = ["v28"], + v29: vreg = ["v29"], + v30: vreg = ["v30"], + v31: vreg = ["v31"], + #error = ["x9", "s1"] => + "s1 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["x8", "s0", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["x2", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["x3", "gp"] => + "the global pointer cannot be used as an operand for inline asm", + #error = ["x4", "tp"] => + "the thread pointer cannot be used as an operand for inline asm" , + #error = ["x0", "zero"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl RiscVInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs new file mode 100644 index 000000000..0a50064f5 --- /dev/null +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -0,0 +1,107 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + S390x S390xInlineAsmRegClass { + reg, + freg, + } +} + +impl S390xInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match (self, arch) { + (Self::reg, _) => types! { _: I8, I16, I32, I64; }, + (Self::freg, _) => types! { _: F32, F64; }, + } + } +} + +def_regs! { + S390x S390xInlineAsmReg S390xInlineAsmRegClass { + r0: reg = ["r0"], + r1: reg = ["r1"], + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + f0: freg = ["f0"], + f1: freg = ["f1"], + f2: freg = ["f2"], + f3: freg = ["f3"], + f4: freg = ["f4"], + f5: freg = ["f5"], + f6: freg = ["f6"], + f7: freg = ["f7"], + f8: freg = ["f8"], + f9: freg = ["f9"], + f10: freg = ["f10"], + f11: freg = ["f11"], + f12: freg = ["f12"], + f13: freg = ["f13"], + f14: freg = ["f14"], + f15: freg = ["f15"], + #error = ["r11"] => + "The frame pointer cannot be used as an operand for inline asm", + #error = ["r15"] => + "The stack pointer cannot be used as an operand for inline asm", + #error = [ + "c0", "c1", "c2", "c3", + "c4", "c5", "c6", "c7", + "c8", "c9", "c10", "c11", + "c12", "c13", "c14", "c15" + ] => + "control registers are reserved by the kernel and cannot be used as operands for inline asm", + #error = [ + "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", + "a8", "a9", "a10", "a11", + "a12", "a13", "a14", "a15" + ] => + "access registers are not supported and cannot be used as operands for inline asm", + } +} + +impl S390xInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + write!(out, "%{}", self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs new file mode 100644 index 000000000..31073da10 --- /dev/null +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -0,0 +1,47 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; + +def_reg_class! { + SpirV SpirVInlineAsmRegClass { + reg, + } +} + +impl SpirVInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg => { + types! { _: I8, I16, I32, I64, F32, F64; } + } + } + } +} + +def_regs! { + // SPIR-V is SSA-based, it does not have registers. + SpirV SpirVInlineAsmReg SpirVInlineAsmRegClass {} +} diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs new file mode 100644 index 000000000..f095b7c6e --- /dev/null +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -0,0 +1,47 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; + +def_reg_class! { + Wasm WasmInlineAsmRegClass { + local, + } +} + +impl WasmInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::local => { + types! { _: I8, I16, I32, I64, F32, F64; } + } + } + } +} + +def_regs! { + // WebAssembly doesn't have registers. + Wasm WasmInlineAsmReg WasmInlineAsmRegClass {} +} diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs new file mode 100644 index 000000000..238c36509 --- /dev/null +++ b/compiler/rustc_target/src/asm/x86.rs @@ -0,0 +1,492 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::{RelocModel, Target}; +use rustc_data_structures::fx::FxHashSet; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + X86 X86InlineAsmRegClass { + reg, + reg_abcd, + reg_byte, + xmm_reg, + ymm_reg, + zmm_reg, + kreg, + kreg0, + mmx_reg, + x87_reg, + tmm_reg, + } +} + +impl X86InlineAsmRegClass { + pub fn valid_modifiers(self, arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => { + if arch == InlineAsmArch::X86_64 { + &['l', 'x', 'e', 'r'] + } else { + &['x', 'e'] + } + } + Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + &['l', 'h', 'x', 'e', 'r'] + } else { + &['l', 'h', 'x', 'e'] + } + } + Self::reg_byte => &[], + Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], + Self::kreg | Self::kreg0 => &[], + Self::mmx_reg | Self::x87_reg => &[], + Self::tmm_reg => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> { + match self { + Self::reg | Self::reg_abcd if ty.size().bits() == 8 => Some(Self::reg_byte), + _ => None, + } + } + + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_abcd => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_byte => None, + Self::xmm_reg => None, + Self::ymm_reg => match ty.size().bits() { + 256 => None, + _ => Some(('x', "xmm0")), + }, + Self::zmm_reg => match ty.size().bits() { + 512 => None, + 256 => Some(('y', "ymm0")), + _ => Some(('x', "xmm0")), + }, + Self::kreg | Self::kreg0 => None, + Self::mmx_reg | Self::x87_reg => None, + Self::tmm_reg => None, + } + } + + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + Some(('r', "rax")) + } else { + Some(('e', "eax")) + } + } + Self::reg_byte => None, + Self::xmm_reg => Some(('x', "xmm0")), + Self::ymm_reg => Some(('y', "ymm0")), + Self::zmm_reg => Some(('z', "zmm0")), + Self::kreg | Self::kreg0 => None, + Self::mmx_reg | Self::x87_reg => None, + Self::tmm_reg => None, + } + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<Symbol>)] { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + types! { _: I16, I32, I64, F32, F64; } + } else { + types! { _: I16, I32, F32; } + } + } + Self::reg_byte => types! { _: I8; }, + Self::xmm_reg => types! { + sse: I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + Self::ymm_reg => types! { + avx: I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + }, + Self::zmm_reg => types! { + avx512f: I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8); + }, + Self::kreg => types! { + avx512f: I8, I16; + avx512bw: I32, I64; + }, + Self::kreg0 => &[], + Self::mmx_reg | Self::x87_reg => &[], + Self::tmm_reg => &[], + } + } +} + +fn x86_64_only( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxHashSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86 => Err("register is only available on x86_64"), + InlineAsmArch::X86_64 => Ok(()), + _ => unreachable!(), + } +} + +fn high_byte( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxHashSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"), + _ => Ok(()), + } +} + +fn rbx_reserved( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxHashSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86 => Ok(()), + InlineAsmArch::X86_64 => { + Err("rbx is used internally by LLVM and cannot be used as an operand for inline asm") + } + _ => unreachable!(), + } +} + +fn esi_reserved( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxHashSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86 => { + Err("esi is used internally by LLVM and cannot be used as an operand for inline asm") + } + InlineAsmArch::X86_64 => Ok(()), + _ => unreachable!(), + } +} + +def_regs! { + X86 X86InlineAsmReg X86InlineAsmRegClass { + ax: reg, reg_abcd = ["ax", "eax", "rax"], + bx: reg, reg_abcd = ["bx", "ebx", "rbx"] % rbx_reserved, + cx: reg, reg_abcd = ["cx", "ecx", "rcx"], + dx: reg, reg_abcd = ["dx", "edx", "rdx"], + si: reg = ["si", "esi", "rsi"] % esi_reserved, + di: reg = ["di", "edi", "rdi"], + r8: reg = ["r8", "r8w", "r8d"] % x86_64_only, + r9: reg = ["r9", "r9w", "r9d"] % x86_64_only, + r10: reg = ["r10", "r10w", "r10d"] % x86_64_only, + r11: reg = ["r11", "r11w", "r11d"] % x86_64_only, + r12: reg = ["r12", "r12w", "r12d"] % x86_64_only, + r13: reg = ["r13", "r13w", "r13d"] % x86_64_only, + r14: reg = ["r14", "r14w", "r14d"] % x86_64_only, + r15: reg = ["r15", "r15w", "r15d"] % x86_64_only, + al: reg_byte = ["al"], + ah: reg_byte = ["ah"] % high_byte, + bl: reg_byte = ["bl"], + bh: reg_byte = ["bh"] % high_byte, + cl: reg_byte = ["cl"], + ch: reg_byte = ["ch"] % high_byte, + dl: reg_byte = ["dl"], + dh: reg_byte = ["dh"] % high_byte, + sil: reg_byte = ["sil"] % x86_64_only, + dil: reg_byte = ["dil"] % x86_64_only, + r8b: reg_byte = ["r8b"] % x86_64_only, + r9b: reg_byte = ["r9b"] % x86_64_only, + r10b: reg_byte = ["r10b"] % x86_64_only, + r11b: reg_byte = ["r11b"] % x86_64_only, + r12b: reg_byte = ["r12b"] % x86_64_only, + r13b: reg_byte = ["r13b"] % x86_64_only, + r14b: reg_byte = ["r14b"] % x86_64_only, + r15b: reg_byte = ["r15b"] % x86_64_only, + xmm0: xmm_reg = ["xmm0"], + xmm1: xmm_reg = ["xmm1"], + xmm2: xmm_reg = ["xmm2"], + xmm3: xmm_reg = ["xmm3"], + xmm4: xmm_reg = ["xmm4"], + xmm5: xmm_reg = ["xmm5"], + xmm6: xmm_reg = ["xmm6"], + xmm7: xmm_reg = ["xmm7"], + xmm8: xmm_reg = ["xmm8"] % x86_64_only, + xmm9: xmm_reg = ["xmm9"] % x86_64_only, + xmm10: xmm_reg = ["xmm10"] % x86_64_only, + xmm11: xmm_reg = ["xmm11"] % x86_64_only, + xmm12: xmm_reg = ["xmm12"] % x86_64_only, + xmm13: xmm_reg = ["xmm13"] % x86_64_only, + xmm14: xmm_reg = ["xmm14"] % x86_64_only, + xmm15: xmm_reg = ["xmm15"] % x86_64_only, + ymm0: ymm_reg = ["ymm0"], + ymm1: ymm_reg = ["ymm1"], + ymm2: ymm_reg = ["ymm2"], + ymm3: ymm_reg = ["ymm3"], + ymm4: ymm_reg = ["ymm4"], + ymm5: ymm_reg = ["ymm5"], + ymm6: ymm_reg = ["ymm6"], + ymm7: ymm_reg = ["ymm7"], + ymm8: ymm_reg = ["ymm8"] % x86_64_only, + ymm9: ymm_reg = ["ymm9"] % x86_64_only, + ymm10: ymm_reg = ["ymm10"] % x86_64_only, + ymm11: ymm_reg = ["ymm11"] % x86_64_only, + ymm12: ymm_reg = ["ymm12"] % x86_64_only, + ymm13: ymm_reg = ["ymm13"] % x86_64_only, + ymm14: ymm_reg = ["ymm14"] % x86_64_only, + ymm15: ymm_reg = ["ymm15"] % x86_64_only, + zmm0: zmm_reg = ["zmm0"], + zmm1: zmm_reg = ["zmm1"], + zmm2: zmm_reg = ["zmm2"], + zmm3: zmm_reg = ["zmm3"], + zmm4: zmm_reg = ["zmm4"], + zmm5: zmm_reg = ["zmm5"], + zmm6: zmm_reg = ["zmm6"], + zmm7: zmm_reg = ["zmm7"], + zmm8: zmm_reg = ["zmm8"] % x86_64_only, + zmm9: zmm_reg = ["zmm9"] % x86_64_only, + zmm10: zmm_reg = ["zmm10"] % x86_64_only, + zmm11: zmm_reg = ["zmm11"] % x86_64_only, + zmm12: zmm_reg = ["zmm12"] % x86_64_only, + zmm13: zmm_reg = ["zmm13"] % x86_64_only, + zmm14: zmm_reg = ["zmm14"] % x86_64_only, + zmm15: zmm_reg = ["zmm15"] % x86_64_only, + zmm16: zmm_reg = ["zmm16", "xmm16", "ymm16"] % x86_64_only, + zmm17: zmm_reg = ["zmm17", "xmm17", "ymm17"] % x86_64_only, + zmm18: zmm_reg = ["zmm18", "xmm18", "ymm18"] % x86_64_only, + zmm19: zmm_reg = ["zmm19", "xmm19", "ymm19"] % x86_64_only, + zmm20: zmm_reg = ["zmm20", "xmm20", "ymm20"] % x86_64_only, + zmm21: zmm_reg = ["zmm21", "xmm21", "ymm21"] % x86_64_only, + zmm22: zmm_reg = ["zmm22", "xmm22", "ymm22"] % x86_64_only, + zmm23: zmm_reg = ["zmm23", "xmm23", "ymm23"] % x86_64_only, + zmm24: zmm_reg = ["zmm24", "xmm24", "ymm24"] % x86_64_only, + zmm25: zmm_reg = ["zmm25", "xmm25", "ymm25"] % x86_64_only, + zmm26: zmm_reg = ["zmm26", "xmm26", "ymm26"] % x86_64_only, + zmm27: zmm_reg = ["zmm27", "xmm27", "ymm27"] % x86_64_only, + zmm28: zmm_reg = ["zmm28", "xmm28", "ymm28"] % x86_64_only, + zmm29: zmm_reg = ["zmm29", "xmm29", "ymm29"] % x86_64_only, + zmm30: zmm_reg = ["zmm30", "xmm30", "ymm30"] % x86_64_only, + zmm31: zmm_reg = ["zmm31", "xmm31", "ymm31"] % x86_64_only, + k0: kreg0 = ["k0"], + k1: kreg = ["k1"], + k2: kreg = ["k2"], + k3: kreg = ["k3"], + k4: kreg = ["k4"], + k5: kreg = ["k5"], + k6: kreg = ["k6"], + k7: kreg = ["k7"], + mm0: mmx_reg = ["mm0"], + mm1: mmx_reg = ["mm1"], + mm2: mmx_reg = ["mm2"], + mm3: mmx_reg = ["mm3"], + mm4: mmx_reg = ["mm4"], + mm5: mmx_reg = ["mm5"], + mm6: mmx_reg = ["mm6"], + mm7: mmx_reg = ["mm7"], + st0: x87_reg = ["st(0)", "st"], + st1: x87_reg = ["st(1)"], + st2: x87_reg = ["st(2)"], + st3: x87_reg = ["st(3)"], + st4: x87_reg = ["st(4)"], + st5: x87_reg = ["st(5)"], + st6: x87_reg = ["st(6)"], + st7: x87_reg = ["st(7)"], + tmm0: tmm_reg = ["tmm0"] % x86_64_only, + tmm1: tmm_reg = ["tmm1"] % x86_64_only, + tmm2: tmm_reg = ["tmm2"] % x86_64_only, + tmm3: tmm_reg = ["tmm3"] % x86_64_only, + tmm4: tmm_reg = ["tmm4"] % x86_64_only, + tmm5: tmm_reg = ["tmm5"] % x86_64_only, + tmm6: tmm_reg = ["tmm6"] % x86_64_only, + tmm7: tmm_reg = ["tmm7"] % x86_64_only, + #error = ["bp", "bpl", "ebp", "rbp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "spl", "esp", "rsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["ip", "eip", "rip"] => + "the instruction pointer cannot be used as an operand for inline asm", + } +} + +impl X86InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + let reg_default_modifier = match arch { + InlineAsmArch::X86 => 'e', + InlineAsmArch::X86_64 => 'r', + _ => unreachable!(), + }; + if self as u32 <= Self::dx as u32 { + let root = ['a', 'b', 'c', 'd'][self as usize - Self::ax as usize]; + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'h' => write!(out, "{}h", root), + 'x' => write!(out, "{}x", root), + 'e' => write!(out, "e{}x", root), + 'r' => write!(out, "r{}x", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::di as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'x' => write!(out, "{}", root), + 'e' => write!(out, "e{}", root), + 'r' => write!(out, "r{}", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15 as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}b", root), + 'x' => write!(out, "{}w", root), + 'e' => write!(out, "{}d", root), + 'r' => out.write_str(root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15b as u32 { + out.write_str(self.name()) + } else if self as u32 <= Self::xmm15 as u32 { + let prefix = modifier.unwrap_or('x'); + let index = self as u32 - Self::xmm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::ymm15 as u32 { + let prefix = modifier.unwrap_or('y'); + let index = self as u32 - Self::ymm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::zmm31 as u32 { + let prefix = modifier.unwrap_or('z'); + let index = self as u32 - Self::zmm0 as u32; + write!(out, "{}{}", prefix, index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(X86InlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $w:ident : $l:ident $h:ident + ),*; + $( + $w2:ident : $l2:ident + ),*; + $( + $x:ident : $y:ident : $z:ident + ),*; + ) => { + match self { + $( + Self::$w => { + cb(Self::$w); + cb(Self::$l); + cb(Self::$h); + } + Self::$l => { + cb(Self::$w); + cb(Self::$l); + } + Self::$h => { + cb(Self::$w); + cb(Self::$h); + } + )* + $( + Self::$w2 | Self::$l2 => { + cb(Self::$w2); + cb(Self::$l2); + } + )* + $( + Self::$x | Self::$y | Self::$z => { + cb(Self::$x); + cb(Self::$y); + cb(Self::$z); + } + )* + r => cb(r), + } + }; + } + + // XMM*, YMM* and ZMM* are all different views of the same register. + // + // See section 15.5 of the combined Intel® 64 and IA-32 Architectures + // Software Developer’s Manual for more details. + // + // We don't need to specify conflicts for [x,y,z]mm[16-31] since these + // registers are only available with AVX-512, so we just specify them + // as aliases directly. + reg_conflicts! { + ax : al ah, + bx : bl bh, + cx : cl ch, + dx : dl dh; + si : sil, + di : dil, + r8 : r8b, + r9 : r9b, + r10 : r10b, + r11 : r11b, + r12 : r12b, + r13 : r13b, + r14 : r14b, + r15 : r15b; + xmm0 : ymm0 : zmm0, + xmm1 : ymm1 : zmm1, + xmm2 : ymm2 : zmm2, + xmm3 : ymm3 : zmm3, + xmm4 : ymm4 : zmm4, + xmm5 : ymm5 : zmm5, + xmm6 : ymm6 : zmm6, + xmm7 : ymm7 : zmm7, + xmm8 : ymm8 : zmm8, + xmm9 : ymm9 : zmm9, + xmm10 : ymm10 : zmm10, + xmm11 : ymm11 : zmm11, + xmm12 : ymm12 : zmm12, + xmm13 : ymm13 : zmm13, + xmm14 : ymm14 : zmm14, + xmm15 : ymm15 : zmm15; + } + } +} |