1
Fork 0

Auto merge of #86416 - Amanieu:asm_clobber_only, r=nagisa

Add clobber-only register classes for asm!

These are needed to properly express a function call ABI using a clobber
list, even though we don't support passing actual values into/out of
these registers.
This commit is contained in:
bors 2021-07-11 01:06:58 +00:00
commit 99f8efec46
11 changed files with 273 additions and 62 deletions

View file

@ -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 { if !is_clobber {
// Validate register classes against currently enabled target // Validate register classes against currently enabled target
// features. We check that at least one type is available for // features. We check that at least one type is available for

View file

@ -128,6 +128,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let mut clobbers = vec![]; let mut clobbers = vec![];
let mut output_types = vec![]; let mut output_types = vec![];
let mut op_idx = FxHashMap::default(); let mut op_idx = FxHashMap::default();
let mut clobbered_x87 = false;
for (idx, op) in operands.iter().enumerate() { for (idx, op) in operands.iter().enumerate() {
match *op { match *op {
InlineAsmOperandRef::Out { reg, late, place } => { InlineAsmOperandRef::Out { reg, late, place } => {
@ -150,7 +151,27 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let ty = if let Some(ref place) = place { let ty = if let Some(ref place) = place {
layout = Some(&place.layout); layout = Some(&place.layout);
llvm_fixup_output_type(self.cx, reg.reg_class(), &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 // We turn discarded outputs into clobber constraints
// if the target feature needed by the register class is // if the target feature needed by the register class is
// disabled. This is necessary otherwise LLVM will try // 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::reg) => "r",
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w",
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x",
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l", InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l",
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@ -586,6 +610,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "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::ymm_reg) => "x",
InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk",
InlineAsmRegClass::X86(
X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
) => unreachable!("clobber-only"),
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
@ -617,6 +647,9 @@ fn modifier_to_llvm(
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
if modifier == Some('v') { None } else { modifier } if modifier == Some('v') { None } else { modifier }
} }
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None, | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None,
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@ -639,6 +672,9 @@ fn modifier_to_llvm(
InlineAsmRegClass::PowerPC(_) => None, InlineAsmRegClass::PowerPC(_) => None,
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
| InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None,
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
None if arch == InlineAsmArch::X86_64 => Some('q'), None if arch == InlineAsmArch::X86_64 => Some('q'),
@ -663,6 +699,9 @@ fn modifier_to_llvm(
_ => unreachable!(), _ => unreachable!(),
}, },
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None, InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
InlineAsmRegClass::Bpf(_) => None, InlineAsmRegClass::Bpf(_) => None,
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
@ -681,6 +720,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
cx.type_vector(cx.type_i64(), 2) cx.type_vector(cx.type_i64(), 2)
} }
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(), | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) 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::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), 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::ymm_reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), 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::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(),
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(),

View file

@ -756,6 +756,7 @@ symbols! {
minnumf64, minnumf64,
mips_target_feature, mips_target_feature,
misc, misc,
mmx_reg,
modifiers, modifiers,
module, module,
module_path, module_path,
@ -899,6 +900,7 @@ symbols! {
prefetch_read_instruction, prefetch_read_instruction,
prefetch_write_data, prefetch_write_data,
prefetch_write_instruction, prefetch_write_instruction,
preg,
prelude, prelude,
prelude_import, prelude_import,
preserves_flags, preserves_flags,
@ -1343,6 +1345,7 @@ symbols! {
wrapping_sub, wrapping_sub,
wreg, wreg,
write_bytes, write_bytes,
x87_reg,
xmm_reg, xmm_reg,
ymm_reg, ymm_reg,
zmm_reg, zmm_reg,

View file

@ -7,6 +7,7 @@ def_reg_class! {
reg, reg,
vreg, vreg,
vreg_low16, vreg_low16,
preg,
} }
} }
@ -15,6 +16,7 @@ impl AArch64InlineAsmRegClass {
match self { match self {
Self::reg => &['w', 'x'], Self::reg => &['w', 'x'],
Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'], Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'],
Self::preg => &[],
} }
} }
@ -40,6 +42,7 @@ impl AArch64InlineAsmRegClass {
128 => Some(('q', "q0")), 128 => Some(('q', "q0")),
_ => None, _ => None,
}, },
Self::preg => None,
} }
} }
@ -47,6 +50,7 @@ impl AArch64InlineAsmRegClass {
match self { match self {
Self::reg => Some(('x', "x0")), Self::reg => Some(('x', "x0")),
Self::vreg | Self::vreg_low16 => Some(('v', "v0")), Self::vreg | Self::vreg_low16 => Some(('v', "v0")),
Self::preg => None,
} }
} }
@ -61,6 +65,7 @@ impl AArch64InlineAsmRegClass {
VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1), VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1),
VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
}, },
Self::preg => &[],
} }
} }
} }
@ -95,38 +100,55 @@ def_regs! {
x27: reg = ["x27", "w27"], x27: reg = ["x27", "w27"],
x28: reg = ["x28", "w28"], x28: reg = ["x28", "w28"],
x30: reg = ["x30", "w30", "lr", "wlr"], x30: reg = ["x30", "w30", "lr", "wlr"],
v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"], v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0", "z0"],
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"], v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1", "z1"],
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"], v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2", "z2"],
v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3"], v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3", "z3"],
v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4"], v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4", "z4"],
v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5"], v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5", "z5"],
v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6"], v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6", "z6"],
v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7"], v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7", "z7"],
v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8"], v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8", "z8"],
v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9"], v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9", "z9"],
v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10"], v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10", "z10"],
v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11"], v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11", "z11"],
v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12"], v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12", "z12"],
v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13"], v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13", "z13"],
v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14"], v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14", "z14"],
v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15"], v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15", "z15"],
v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16"], v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"],
v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17"], v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"],
v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18"], v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"],
v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19"], v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"],
v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20"], v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"],
v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21"], v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"],
v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22"], v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"],
v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23"], v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"],
v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24"], v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"],
v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25"], v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"],
v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26"], v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"],
v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27"], v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"],
v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28"], v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"],
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"], v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"],
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"], v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"],
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"], 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"] => #error = ["x18", "w18"] =>
"x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm", "x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm",
#error = ["x19", "w19"] => #error = ["x19", "w19"] =>

View file

@ -533,6 +533,12 @@ impl InlineAsmRegClass {
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), 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( #[derive(

View file

@ -7,6 +7,7 @@ def_reg_class! {
RiscV RiscVInlineAsmRegClass { RiscV RiscVInlineAsmRegClass {
reg, reg,
freg, freg,
vreg,
} }
} }
@ -44,6 +45,7 @@ impl RiscVInlineAsmRegClass {
} }
} }
Self::freg => types! { "f": F32; "d": F64; }, Self::freg => types! { "f": F32; "d": F64; },
Self::vreg => &[],
} }
} }
} }
@ -120,6 +122,38 @@ def_regs! {
f29: freg = ["f29", "ft9"], f29: freg = ["f29", "ft9"],
f30: freg = ["f30", "ft10"], f30: freg = ["f30", "ft10"],
f31: freg = ["f31", "ft11"], 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"] => #error = ["x9", "s1"] =>
"s1 is used internally by LLVM and cannot be used as an operand for inline asm", "s1 is used internally by LLVM and cannot be used as an operand for inline asm",
#error = ["x8", "s0", "fp"] => #error = ["x8", "s0", "fp"] =>

View file

@ -12,6 +12,8 @@ def_reg_class! {
ymm_reg, ymm_reg,
zmm_reg, zmm_reg,
kreg, kreg,
mmx_reg,
x87_reg,
} }
} }
@ -35,6 +37,7 @@ impl X86InlineAsmRegClass {
Self::reg_byte => &[], Self::reg_byte => &[],
Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'],
Self::kreg => &[], Self::kreg => &[],
Self::mmx_reg | Self::x87_reg => &[],
} }
} }
@ -73,6 +76,7 @@ impl X86InlineAsmRegClass {
_ => Some(('x', "xmm0")), _ => Some(('x', "xmm0")),
}, },
Self::kreg => None, Self::kreg => None,
Self::mmx_reg | Self::x87_reg => None,
} }
} }
@ -90,6 +94,7 @@ impl X86InlineAsmRegClass {
Self::ymm_reg => Some(('y', "ymm0")), Self::ymm_reg => Some(('y', "ymm0")),
Self::zmm_reg => Some(('z', "zmm0")), Self::zmm_reg => Some(('z', "zmm0")),
Self::kreg => None, Self::kreg => None,
Self::mmx_reg | Self::x87_reg => None,
} }
} }
@ -125,6 +130,7 @@ impl X86InlineAsmRegClass {
"avx512f": I8, I16; "avx512f": I8, I16;
"avx512bw": I32, I64; "avx512bw": I32, I64;
}, },
Self::mmx_reg | Self::x87_reg => &[],
} }
} }
} }
@ -285,16 +291,28 @@ def_regs! {
k5: kreg = ["k5"], k5: kreg = ["k5"],
k6: kreg = ["k6"], k6: kreg = ["k6"],
k7: kreg = ["k7"], 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"] => #error = ["bp", "bpl", "ebp", "rbp"] =>
"the frame pointer cannot be used as an operand for inline asm", "the frame pointer cannot be used as an operand for inline asm",
#error = ["sp", "spl", "esp", "rsp"] => #error = ["sp", "spl", "esp", "rsp"] =>
"the stack pointer cannot be used as an operand for inline asm", "the stack pointer cannot be used as an operand for inline asm",
#error = ["ip", "eip", "rip"] => #error = ["ip", "eip", "rip"] =>
"the instruction pointer cannot be used as an operand for inline asm", "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"] => #error = ["k0"] =>
"the k0 AVX mask register cannot be used as an operand for inline asm", "the k0 AVX mask register cannot be used as an operand for inline asm",
} }

View file

@ -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 | `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 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
| x86 | `kreg` | `k[1-7]` | `Yk` | | 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 | `reg` | `x[0-30]` | `r` |
| AArch64 | `vreg` | `v[0-31]` | `w` | | AArch64 | `vreg` | `v[0-31]` | `w` |
| AArch64 | `vreg_low16` | `v[0-15]` | `x` | | AArch64 | `vreg_low16` | `v[0-15]` | `x` |
| AArch64 | `preg` | `p[0-15]`, `ffr` | Only clobbers |
| ARM | `reg` | `r[0-12]`, `r14` | `r` | | ARM | `reg` | `r[0-12]`, `r14` | `r` |
| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | | ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `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` | | NVPTX | `reg64` | None\* | `l` |
| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | | 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 | `freg` | `f[0-31]` | `f` |
| RISC-V | `vreg` | `v[0-31]` | Only clobbers |
| Hexagon | `reg` | `r[0-28]` | `r` | | Hexagon | `reg` | `r[0-28]` | `r` |
| PowerPC | `reg` | `r[0-31]` | `r` | | PowerPC | `reg` | `r[0-31]` | `r` |
| PowerPC | `reg_nonzero` | | `r[1-31]` | `b` | | 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 #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 #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). 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`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` <br> `i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | | x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` <br> `i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` |
| x86 | `kreg` | `axv512f` | `i8`, `i16` | | x86 | `kreg` | `axv512f` | `i8`, `i16` |
| x86 | `kreg` | `axv512bw` | `i32`, `i64` | | 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 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
| AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | | AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |
| AArch64 | `preg` | N/A | Only clobbers |
| ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` | | ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` |
| ARM | `sreg` | `vfp2` | `i32`, `f32` | | ARM | `sreg` | `vfp2` | `i32`, `f32` |
| ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | | 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-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
| RISC-V | `freg` | `f` | `f32` | | RISC-V | `freg` | `f` | `f32` |
| RISC-V | `freg` | `d` | `f64` | | RISC-V | `freg` | `d` | `f64` |
| RISC-V | `vreg` | N/A | Only clobbers |
| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` |
| PowerPC | `reg` | None | `i8`, `i16`, `i32` | | PowerPC | `reg` | None | `i8`, `i16`, `i32` |
| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` | | PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` |

View file

@ -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") _);
}

View file

@ -31,15 +31,26 @@ fn main() {
//~^ ERROR invalid register `rsp`: the stack pointer cannot be used as an operand //~^ ERROR invalid register `rsp`: the stack pointer cannot be used as an operand
asm!("", in("ip") foo); asm!("", in("ip") foo);
//~^ ERROR invalid register `ip`: the instruction pointer cannot be used as an operand //~^ 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); asm!("", in("k0") foo);
//~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand
asm!("", in("ah") foo); asm!("", in("ah") foo);
//~^ ERROR invalid register `ah`: high byte registers cannot be used as an operand //~^ 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 // Explicit register conflicts
// (except in/lateout which don't conflict) // (except in/lateout which don't conflict)

View file

@ -76,32 +76,56 @@ error: invalid register `ip`: the instruction pointer cannot be used as an opera
LL | asm!("", in("ip") foo); 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 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); LL | asm!("", in("k0") foo);
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: invalid register `ah`: high byte registers cannot be used as an operand on x86_64 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); 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` 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); LL | asm!("", in("eax") foo, in("al") bar);
| ------------- ^^^^^^^^^^^^ register `al` | ------------- ^^^^^^^^^^^^ register `al`
@ -109,7 +133,7 @@ LL | asm!("", in("eax") foo, in("al") bar);
| register `ax` | register `ax`
error: register `ax` conflicts with 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); LL | asm!("", in("rax") foo, out("rax") bar);
| ------------- ^^^^^^^^^^^^^^ register `ax` | ------------- ^^^^^^^^^^^^^^ register `ax`
@ -117,13 +141,13 @@ LL | asm!("", in("rax") foo, out("rax") bar);
| register `ax` | register `ax`
| |
help: use `lateout` instead of `out` to avoid conflict 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); LL | asm!("", in("rax") foo, out("rax") bar);
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: register `ymm0` conflicts with register `xmm0` 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); LL | asm!("", in("xmm0") foo, in("ymm0") bar);
| -------------- ^^^^^^^^^^^^^^ register `ymm0` | -------------- ^^^^^^^^^^^^^^ register `ymm0`
@ -131,7 +155,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar);
| register `xmm0` | register `xmm0`
error: register `ymm0` conflicts with 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); LL | asm!("", in("xmm0") foo, out("ymm0") bar);
| -------------- ^^^^^^^^^^^^^^^ register `ymm0` | -------------- ^^^^^^^^^^^^^^^ register `ymm0`
@ -139,10 +163,10 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar);
| register `xmm0` | register `xmm0`
| |
help: use `lateout` instead of `out` to avoid conflict 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); LL | asm!("", in("xmm0") foo, out("ymm0") bar);
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: aborting due to 19 previous errors error: aborting due to 23 previous errors