diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 1c3fae2afe7..9ea09a2cf31 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -199,6 +199,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } ); + // Some register classes can only be used as clobbers. This + // means that we disallow passing a value in/out of the asm and + // require that the operand name an explicit register, not a + // register class. + if reg_class.is_clobber_only(asm_arch.unwrap()) + && !(is_clobber && matches!(reg, asm::InlineAsmRegOrRegClass::Reg(_))) + { + let msg = format!( + "register class `{}` can only be used as a clobber, \ + not as an input or output", + reg_class.name() + ); + sess.struct_span_err(op_sp, &msg).emit(); + continue; + } + if !is_clobber { // Validate register classes against currently enabled target // features. We check that at least one type is available for diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index ecf62ed213d..7bd9397d649 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -128,6 +128,7 @@ fn codegen_inline_asm( let mut clobbers = vec![]; let mut output_types = vec![]; let mut op_idx = FxHashMap::default(); + let mut clobbered_x87 = false; for (idx, op) in operands.iter().enumerate() { match *op { InlineAsmOperandRef::Out { reg, late, place } => { @@ -150,7 +151,27 @@ fn codegen_inline_asm( let ty = if let Some(ref place) = place { layout = Some(&place.layout); llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout) - } else if !is_target_supported(reg.reg_class()) { + } else if matches!( + reg.reg_class(), + InlineAsmRegClass::X86( + X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::x87_reg + ) + ) { + // Special handling for x87/mmx registers: we always + // clobber the whole set if one register is marked as + // clobbered. This is due to the way LLVM handles the + // FP stack in inline assembly. + if !clobbered_x87 { + clobbered_x87 = true; + clobbers.push("~{st}".to_string()); + for i in 1..=7 { + clobbers.push(format!("~{{st({})}}", i)); + } + } + continue; + } else if !is_target_supported(reg.reg_class()) + || reg.reg_class().is_clobber_only(asm_arch) + { // We turn discarded outputs into clobber constraints // if the target feature needed by the register class is // disabled. This is necessary otherwise LLVM will try @@ -565,6 +586,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l", InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) @@ -586,6 +610,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", @@ -593,6 +620,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", + InlineAsmRegClass::X86( + X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg, + ) => unreachable!("clobber-only"), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", @@ -617,6 +647,9 @@ fn modifier_to_llvm( | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { if modifier == Some('v') { None } else { modifier } } + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None, InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) @@ -639,6 +672,9 @@ fn modifier_to_llvm( InlineAsmRegClass::PowerPC(_) => None, InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { None if arch == InlineAsmArch::X86_64 => Some('q'), @@ -663,6 +699,9 @@ fn modifier_to_llvm( _ => unreachable!(), }, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None, InlineAsmRegClass::Bpf(_) => None, InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { @@ -681,6 +720,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { cx.type_vector(cx.type_i64(), 2) } + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(), InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) @@ -704,6 +746,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), @@ -711,6 +756,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9051c9d69b5..cfa0c79004c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -756,6 +756,7 @@ minnumf64, mips_target_feature, misc, + mmx_reg, modifiers, module, module_path, @@ -899,6 +900,7 @@ prefetch_read_instruction, prefetch_write_data, prefetch_write_instruction, + preg, prelude, prelude_import, preserves_flags, @@ -1343,6 +1345,7 @@ wrapping_sub, wreg, write_bytes, + x87_reg, xmm_reg, ymm_reg, zmm_reg, diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index f180eea01a3..76e50678314 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -7,6 +7,7 @@ reg, vreg, vreg_low16, + preg, } } @@ -15,6 +16,7 @@ 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 => &[], } } @@ -40,6 +42,7 @@ pub fn suggest_modifier( 128 => Some(('q', "q0")), _ => None, }, + Self::preg => None, } } @@ -47,6 +50,7 @@ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static st match self { Self::reg => Some(('x', "x0")), Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::preg => None, } } @@ -61,6 +65,7 @@ pub fn supported_types( 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 => &[], } } } @@ -95,38 +100,55 @@ pub fn supported_types( x27: reg = ["x27", "w27"], x28: reg = ["x28", "w28"], x30: reg = ["x30", "w30", "lr", "wlr"], - v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"], - v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"], - v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"], - v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3"], - v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4"], - v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5"], - v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6"], - v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7"], - v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8"], - v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9"], - v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10"], - v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11"], - v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12"], - v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13"], - v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14"], - v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15"], - v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16"], - v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17"], - v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18"], - v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19"], - v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20"], - v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21"], - v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22"], - v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23"], - v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24"], - v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25"], - v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26"], - v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27"], - v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28"], - v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"], - v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"], - v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"], + 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 = ["x18", "w18"] => "x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm", #error = ["x19", "w19"] => diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 305ea7d50e6..b52fa5bbcb2 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -533,6 +533,12 @@ pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { 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( diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index e276a9175f9..314bd01de12 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -7,6 +7,7 @@ RiscV RiscVInlineAsmRegClass { reg, freg, + vreg, } } @@ -44,6 +45,7 @@ pub fn supported_types( } } Self::freg => types! { "f": F32; "d": F64; }, + Self::vreg => &[], } } } @@ -120,6 +122,38 @@ fn not_e( 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"] => diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 48f83ca7cd4..5e3828d7d85 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -12,6 +12,8 @@ ymm_reg, zmm_reg, kreg, + mmx_reg, + x87_reg, } } @@ -35,6 +37,7 @@ pub fn valid_modifiers(self, arch: super::InlineAsmArch) -> &'static [char] { Self::reg_byte => &[], Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], Self::kreg => &[], + Self::mmx_reg | Self::x87_reg => &[], } } @@ -73,6 +76,7 @@ pub fn suggest_modifier( _ => Some(('x', "xmm0")), }, Self::kreg => None, + Self::mmx_reg | Self::x87_reg => None, } } @@ -90,6 +94,7 @@ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str Self::ymm_reg => Some(('y', "ymm0")), Self::zmm_reg => Some(('z', "zmm0")), Self::kreg => None, + Self::mmx_reg | Self::x87_reg => None, } } @@ -125,6 +130,7 @@ pub fn supported_types( "avx512f": I8, I16; "avx512bw": I32, I64; }, + Self::mmx_reg | Self::x87_reg => &[], } } } @@ -285,16 +291,28 @@ fn esi_reserved( 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)"], #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", - #error = ["st", "st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"] => - "x87 registers are not currently supported as operands for inline asm", - #error = ["mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"] => - "MMX registers are not currently supported as operands for inline asm", #error = ["k0"] => "the k0 AVX mask register cannot be used as an operand for inline asm", } diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 2cf6801ad1c..e950891ef92 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -545,9 +545,12 @@ Here is the list of currently supported register classes: | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | | x86 | `kreg` | `k[1-7]` | `Yk` | +| x86 | `x87_reg` | `st([0-7])` | Only clobbers | +| x86 | `mmx_reg` | `mm[0-7]` | Only clobbers | | AArch64 | `reg` | `x[0-30]` | `r` | | AArch64 | `vreg` | `v[0-31]` | `w` | | AArch64 | `vreg_low16` | `v[0-15]` | `x` | +| AArch64 | `preg` | `p[0-15]`, `ffr` | Only clobbers | | ARM | `reg` | `r[0-12]`, `r14` | `r` | | ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | | ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` | @@ -566,6 +569,7 @@ Here is the list of currently supported register classes: | NVPTX | `reg64` | None\* | `l` | | RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | | RISC-V | `freg` | `f[0-31]` | `f` | +| RISC-V | `vreg` | `v[0-31]` | Only clobbers | | Hexagon | `reg` | `r[0-28]` | `r` | | PowerPC | `reg` | `r[0-31]` | `r` | | PowerPC | `reg_nonzero` | | `r[1-31]` | `b` | @@ -581,6 +585,8 @@ Here is the list of currently supported register classes: > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. > > Note #4: WebAssembly doesn't have registers, so named registers are not supported. +> +> Note #5: Some register classes are marked as "Only clobbers" which means that they cannot be used for inputs or outputs, only clobbers of the form `out("reg") _` or `lateout("reg") _`. Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). @@ -596,8 +602,11 @@ Each register class has constraints on which value types they can be used with. | x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | | x86 | `kreg` | `axv512f` | `i8`, `i16` | | x86 | `kreg` | `axv512bw` | `i32`, `i64` | +| x86 | `mmx_reg` | N/A | Only clobbers | +| x86 | `x87_reg` | N/A | Only clobbers | | AArch64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| AArch64 | `preg` | N/A | Only clobbers | | ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` | | ARM | `sreg` | `vfp2` | `i32`, `f32` | | ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | @@ -613,6 +622,7 @@ Each register class has constraints on which value types they can be used with. | RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | RISC-V | `freg` | `f` | `f32` | | RISC-V | `freg` | `d` | `f64` | +| RISC-V | `vreg` | N/A | Only clobbers | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | PowerPC | `reg` | None | `i8`, `i16`, `i32` | | PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` | diff --git a/src/test/codegen/asm-clobbers.rs b/src/test/codegen/asm-clobbers.rs new file mode 100644 index 00000000000..9d7c8b5f155 --- /dev/null +++ b/src/test/codegen/asm-clobbers.rs @@ -0,0 +1,19 @@ +// compile-flags: -O +// only-x86_64 + +#![crate_type = "rlib"] +#![feature(asm)] + +// CHECK-LABEL: @x87_clobber +// CHECK: ~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)} +#[no_mangle] +pub unsafe fn x87_clobber() { + asm!("foo", out("st") _); +} + +// CHECK-LABEL: @mmx_clobber +// CHECK: ~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)} +#[no_mangle] +pub unsafe fn mmx_clobber() { + asm!("bar", out("mm0") _, out("mm1") _); +} diff --git a/src/test/ui/asm/bad-reg.rs b/src/test/ui/asm/bad-reg.rs index da302b24876..06af08fab80 100644 --- a/src/test/ui/asm/bad-reg.rs +++ b/src/test/ui/asm/bad-reg.rs @@ -31,15 +31,26 @@ fn main() { //~^ ERROR invalid register `rsp`: the stack pointer cannot be used as an operand asm!("", in("ip") foo); //~^ ERROR invalid register `ip`: the instruction pointer cannot be used as an operand - asm!("", in("st(2)") foo); - //~^ ERROR invalid register `st(2)`: x87 registers are not currently supported as operands - asm!("", in("mm0") foo); - //~^ ERROR invalid register `mm0`: MMX registers are not currently supported as operands asm!("", in("k0") foo); //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand asm!("", in("ah") foo); //~^ ERROR invalid register `ah`: high byte registers cannot be used as an operand + asm!("", in("st(2)") foo); + //~^ ERROR register class `x87_reg` can only be used as a clobber, not as an input or output + asm!("", in("mm0") foo); + //~^ ERROR register class `mmx_reg` can only be used as a clobber, not as an input or output + asm!("", out("st(2)") _); + asm!("", out("mm0") _); + asm!("{}", in(x87_reg) foo); + //~^ ERROR register class `x87_reg` can only be used as a clobber, not as an input or output + asm!("{}", in(mmx_reg) foo); + //~^ ERROR register class `mmx_reg` can only be used as a clobber, not as an input or output + asm!("{}", out(x87_reg) _); + //~^ ERROR register class `x87_reg` can only be used as a clobber, not as an input or output + asm!("{}", out(mmx_reg) _); + //~^ ERROR register class `mmx_reg` can only be used as a clobber, not as an input or output + // Explicit register conflicts // (except in/lateout which don't conflict) diff --git a/src/test/ui/asm/bad-reg.stderr b/src/test/ui/asm/bad-reg.stderr index 2bfb4854c34..14740bf62f8 100644 --- a/src/test/ui/asm/bad-reg.stderr +++ b/src/test/ui/asm/bad-reg.stderr @@ -76,32 +76,56 @@ error: invalid register `ip`: the instruction pointer cannot be used as an opera LL | asm!("", in("ip") foo); | ^^^^^^^^^^^^ -error: invalid register `st(2)`: x87 registers are not currently supported as operands for inline asm - --> $DIR/bad-reg.rs:34:18 - | -LL | asm!("", in("st(2)") foo); - | ^^^^^^^^^^^^^^^ - -error: invalid register `mm0`: MMX registers are not currently supported as operands for inline asm - --> $DIR/bad-reg.rs:36:18 - | -LL | asm!("", in("mm0") foo); - | ^^^^^^^^^^^^^ - error: invalid register `k0`: the k0 AVX mask register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", in("k0") foo); | ^^^^^^^^^^^^ error: invalid register `ah`: high byte registers cannot be used as an operand on x86_64 - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", in("ah") foo); | ^^^^^^^^^^^^ +error: register class `x87_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:39:18 + | +LL | asm!("", in("st(2)") foo); + | ^^^^^^^^^^^^^^^ + +error: register class `mmx_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:41:18 + | +LL | asm!("", in("mm0") foo); + | ^^^^^^^^^^^^^ + +error: register class `x87_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:45:20 + | +LL | asm!("{}", in(x87_reg) foo); + | ^^^^^^^^^^^^^^^ + +error: register class `mmx_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:47:20 + | +LL | asm!("{}", in(mmx_reg) foo); + | ^^^^^^^^^^^^^^^ + +error: register class `x87_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:49:20 + | +LL | asm!("{}", out(x87_reg) _); + | ^^^^^^^^^^^^^^ + +error: register class `mmx_reg` can only be used as a clobber, not as an input or output + --> $DIR/bad-reg.rs:51:20 + | +LL | asm!("{}", out(mmx_reg) _); + | ^^^^^^^^^^^^^^ + error: register `al` conflicts with register `ax` - --> $DIR/bad-reg.rs:46:33 + --> $DIR/bad-reg.rs:57:33 | LL | asm!("", in("eax") foo, in("al") bar); | ------------- ^^^^^^^^^^^^ register `al` @@ -109,7 +133,7 @@ LL | asm!("", in("eax") foo, in("al") bar); | register `ax` error: register `ax` conflicts with register `ax` - --> $DIR/bad-reg.rs:48:33 + --> $DIR/bad-reg.rs:59:33 | LL | asm!("", in("rax") foo, out("rax") bar); | ------------- ^^^^^^^^^^^^^^ register `ax` @@ -117,13 +141,13 @@ LL | asm!("", in("rax") foo, out("rax") bar); | register `ax` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:59:18 | LL | asm!("", in("rax") foo, out("rax") bar); | ^^^^^^^^^^^^^ error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:51:34 + --> $DIR/bad-reg.rs:62:34 | LL | asm!("", in("xmm0") foo, in("ymm0") bar); | -------------- ^^^^^^^^^^^^^^ register `ymm0` @@ -131,7 +155,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar); | register `xmm0` error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:53:34 + --> $DIR/bad-reg.rs:64:34 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | -------------- ^^^^^^^^^^^^^^^ register `ymm0` @@ -139,10 +163,10 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar); | register `xmm0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:53:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | ^^^^^^^^^^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors