Skip to content

Commit e1c3f5e

Browse files
committed
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.
1 parent 1e13a9b commit e1c3f5e

File tree

11 files changed

+238
-27
lines changed

11 files changed

+238
-27
lines changed

compiler/rustc_ast_lowering/src/asm.rs

+16
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
199199
}
200200
);
201201

202+
// Some register classes can only be used as clobbers. This
203+
// means that we disallow passing a value in/out of the asm and
204+
// require that the operand name an explicit register, not a
205+
// register class.
206+
if reg_class.is_clobber_only(asm_arch.unwrap())
207+
&& !(is_clobber && matches!(reg, asm::InlineAsmRegOrRegClass::Reg(_)))
208+
{
209+
let msg = format!(
210+
"register class `{}` can only be used as a clobber, \
211+
not as an input or output",
212+
reg_class.name()
213+
);
214+
sess.struct_span_err(op_sp, &msg).emit();
215+
continue;
216+
}
217+
202218
if !is_clobber {
203219
// Validate register classes against currently enabled target
204220
// features. We check that at least one type is available for

compiler/rustc_codegen_llvm/src/asm.rs

+49-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
128128
let mut clobbers = vec![];
129129
let mut output_types = vec![];
130130
let mut op_idx = FxHashMap::default();
131+
let mut clobbered_x87 = false;
131132
for (idx, op) in operands.iter().enumerate() {
132133
match *op {
133134
InlineAsmOperandRef::Out { reg, late, place } => {
@@ -150,7 +151,27 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
150151
let ty = if let Some(ref place) = place {
151152
layout = Some(&place.layout);
152153
llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout)
153-
} else if !is_target_supported(reg.reg_class()) {
154+
} else if matches!(
155+
reg.reg_class(),
156+
InlineAsmRegClass::X86(
157+
X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::x87_reg
158+
)
159+
) {
160+
// Special handling for x87/mmx registers: we always
161+
// clobber the whole set if one register is marked as
162+
// clobbered. This is due to the way LLVM handles the
163+
// FP stack in inline assembly.
164+
if !clobbered_x87 {
165+
clobbered_x87 = true;
166+
clobbers.push("~{st}".to_string());
167+
for i in 1..=7 {
168+
clobbers.push(format!("~{{st({})}}", i));
169+
}
170+
}
171+
continue;
172+
} else if !is_target_supported(reg.reg_class())
173+
|| reg.reg_class().is_clobber_only(asm_arch)
174+
{
154175
// We turn discarded outputs into clobber constraints
155176
// if the target feature needed by the register class is
156177
// disabled. This is necessary otherwise LLVM will try
@@ -564,6 +585,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
564585
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r",
565586
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w",
566587
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x",
588+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
589+
unreachable!("clobber-only")
590+
}
567591
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
568592
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l",
569593
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -585,13 +609,19 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
585609
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
586610
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r",
587611
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f",
612+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
613+
unreachable!("clobber-only")
614+
}
588615
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
589616
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
590617
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
591618
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
592619
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
593620
InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
594621
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk",
622+
InlineAsmRegClass::X86(
623+
X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
624+
) => unreachable!("clobber-only"),
595625
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
596626
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
597627
bug!("LLVM backend does not support SPIR-V")
@@ -614,6 +644,9 @@ fn modifier_to_llvm(
614644
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
615645
if modifier == Some('v') { None } else { modifier }
616646
}
647+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
648+
unreachable!("clobber-only")
649+
}
617650
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
618651
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None,
619652
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -636,6 +669,9 @@ fn modifier_to_llvm(
636669
InlineAsmRegClass::PowerPC(_) => None,
637670
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
638671
| InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None,
672+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
673+
unreachable!("clobber-only")
674+
}
639675
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
640676
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
641677
None if arch == InlineAsmArch::X86_64 => Some('q'),
@@ -660,6 +696,9 @@ fn modifier_to_llvm(
660696
_ => unreachable!(),
661697
},
662698
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
699+
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
700+
unreachable!("clobber-only")
701+
}
663702
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
664703
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
665704
bug!("LLVM backend does not support SPIR-V")
@@ -677,6 +716,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
677716
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
678717
cx.type_vector(cx.type_i64(), 2)
679718
}
719+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
720+
unreachable!("clobber-only")
721+
}
680722
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
681723
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
682724
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -700,13 +742,19 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
700742
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
701743
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
702744
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
745+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
746+
unreachable!("clobber-only")
747+
}
703748
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
704749
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
705750
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
706751
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
707752
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
708753
| InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
709754
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
755+
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
756+
unreachable!("clobber-only")
757+
}
710758
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
711759
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
712760
bug!("LLVM backend does not support SPIR-V")

compiler/rustc_span/src/symbol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ symbols! {
752752
minnumf64,
753753
mips_target_feature,
754754
misc,
755+
mmx_reg,
755756
modifiers,
756757
module,
757758
module_path,
@@ -894,6 +895,7 @@ symbols! {
894895
prefetch_read_instruction,
895896
prefetch_write_data,
896897
prefetch_write_instruction,
898+
preg,
897899
prelude,
898900
prelude_import,
899901
preserves_flags,
@@ -1333,6 +1335,7 @@ symbols! {
13331335
wrapping_mul,
13341336
wrapping_sub,
13351337
write_bytes,
1338+
x87_reg,
13361339
xmm_reg,
13371340
ymm_reg,
13381341
zmm_reg,

compiler/rustc_target/src/asm/aarch64.rs

+22
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def_reg_class! {
77
reg,
88
vreg,
99
vreg_low16,
10+
preg,
1011
}
1112
}
1213

@@ -15,6 +16,7 @@ impl AArch64InlineAsmRegClass {
1516
match self {
1617
Self::reg => &['w', 'x'],
1718
Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'],
19+
Self::preg => &[],
1820
}
1921
}
2022

@@ -40,13 +42,15 @@ impl AArch64InlineAsmRegClass {
4042
128 => Some(('q', "q0")),
4143
_ => None,
4244
},
45+
Self::preg => None,
4346
}
4447
}
4548

4649
pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
4750
match self {
4851
Self::reg => Some(('x', "x0")),
4952
Self::vreg | Self::vreg_low16 => Some(('v', "v0")),
53+
Self::preg => None,
5054
}
5155
}
5256

@@ -61,6 +65,7 @@ impl AArch64InlineAsmRegClass {
6165
VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1),
6266
VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
6367
},
68+
Self::preg => &[],
6469
}
6570
}
6671
}
@@ -127,6 +132,23 @@ def_regs! {
127132
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"],
128133
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"],
129134
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"],
135+
p0: preg = ["p0"],
136+
p1: preg = ["p1"],
137+
p2: preg = ["p2"],
138+
p3: preg = ["p3"],
139+
p4: preg = ["p4"],
140+
p5: preg = ["p5"],
141+
p6: preg = ["p6"],
142+
p7: preg = ["p7"],
143+
p8: preg = ["p8"],
144+
p9: preg = ["p9"],
145+
p10: preg = ["p10"],
146+
p11: preg = ["p11"],
147+
p12: preg = ["p12"],
148+
p13: preg = ["p13"],
149+
p14: preg = ["p14"],
150+
p15: preg = ["p15"],
151+
ffr: preg = ["ffr"],
130152
#error = ["x18", "w18"] =>
131153
"x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm",
132154
#error = ["x19", "w19"] =>

compiler/rustc_target/src/asm/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,12 @@ impl InlineAsmRegClass {
513513
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
514514
}
515515
}
516+
517+
/// Returns whether registers in this class can only be used as clobbers
518+
/// and not as inputs/outputs.
519+
pub fn is_clobber_only(self, arch: InlineAsmArch) -> bool {
520+
self.supported_types(arch).is_empty()
521+
}
516522
}
517523

518524
#[derive(

compiler/rustc_target/src/asm/riscv.rs

+34
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def_reg_class! {
77
RiscV RiscVInlineAsmRegClass {
88
reg,
99
freg,
10+
vreg,
1011
}
1112
}
1213

@@ -44,6 +45,7 @@ impl RiscVInlineAsmRegClass {
4445
}
4546
}
4647
Self::freg => types! { "f": F32; "d": F64; },
48+
Self::vreg => &[],
4749
}
4850
}
4951
}
@@ -120,6 +122,38 @@ def_regs! {
120122
f29: freg = ["f29", "ft9"],
121123
f30: freg = ["f30", "ft10"],
122124
f31: freg = ["f31", "ft11"],
125+
v0: vreg = ["v0"],
126+
v1: vreg = ["v1"],
127+
v2: vreg = ["v2"],
128+
v3: vreg = ["v3"],
129+
v4: vreg = ["v4"],
130+
v5: vreg = ["v5"],
131+
v6: vreg = ["v6"],
132+
v7: vreg = ["v7"],
133+
v8: vreg = ["v8"],
134+
v9: vreg = ["v9"],
135+
v10: vreg = ["v10"],
136+
v11: vreg = ["v11"],
137+
v12: vreg = ["v12"],
138+
v13: vreg = ["v13"],
139+
v14: vreg = ["v14"],
140+
v15: vreg = ["v15"],
141+
v16: vreg = ["v16"],
142+
v17: vreg = ["v17"],
143+
v18: vreg = ["v18"],
144+
v19: vreg = ["v19"],
145+
v20: vreg = ["v20"],
146+
v21: vreg = ["v21"],
147+
v22: vreg = ["v22"],
148+
v23: vreg = ["v23"],
149+
v24: vreg = ["v24"],
150+
v25: vreg = ["v25"],
151+
v26: vreg = ["v26"],
152+
v27: vreg = ["v27"],
153+
v28: vreg = ["v28"],
154+
v29: vreg = ["v29"],
155+
v30: vreg = ["v30"],
156+
v31: vreg = ["v31"],
123157
#error = ["x9", "s1"] =>
124158
"s1 is used internally by LLVM and cannot be used as an operand for inline asm",
125159
#error = ["x8", "s0", "fp"] =>

compiler/rustc_target/src/asm/x86.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ def_reg_class! {
1212
ymm_reg,
1313
zmm_reg,
1414
kreg,
15+
mmx_reg,
16+
x87_reg,
1517
}
1618
}
1719

@@ -35,6 +37,7 @@ impl X86InlineAsmRegClass {
3537
Self::reg_byte => &[],
3638
Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'],
3739
Self::kreg => &[],
40+
Self::mmx_reg | Self::x87_reg => &[],
3841
}
3942
}
4043

@@ -73,6 +76,7 @@ impl X86InlineAsmRegClass {
7376
_ => Some(('x', "xmm0")),
7477
},
7578
Self::kreg => None,
79+
Self::mmx_reg | Self::x87_reg => None,
7680
}
7781
}
7882

@@ -90,6 +94,7 @@ impl X86InlineAsmRegClass {
9094
Self::ymm_reg => Some(('y', "ymm0")),
9195
Self::zmm_reg => Some(('z', "zmm0")),
9296
Self::kreg => None,
97+
Self::mmx_reg | Self::x87_reg => None,
9398
}
9499
}
95100

@@ -125,6 +130,7 @@ impl X86InlineAsmRegClass {
125130
"avx512f": I8, I16;
126131
"avx512bw": I32, I64;
127132
},
133+
Self::mmx_reg | Self::x87_reg => &[],
128134
}
129135
}
130136
}
@@ -285,16 +291,28 @@ def_regs! {
285291
k5: kreg = ["k5"],
286292
k6: kreg = ["k6"],
287293
k7: kreg = ["k7"],
294+
mm0: mmx_reg = ["mm0"],
295+
mm1: mmx_reg = ["mm1"],
296+
mm2: mmx_reg = ["mm2"],
297+
mm3: mmx_reg = ["mm3"],
298+
mm4: mmx_reg = ["mm4"],
299+
mm5: mmx_reg = ["mm5"],
300+
mm6: mmx_reg = ["mm6"],
301+
mm7: mmx_reg = ["mm7"],
302+
st0: x87_reg = ["st(0)", "st"],
303+
st1: x87_reg = ["st(1)"],
304+
st2: x87_reg = ["st(2)"],
305+
st3: x87_reg = ["st(3)"],
306+
st4: x87_reg = ["st(4)"],
307+
st5: x87_reg = ["st(5)"],
308+
st6: x87_reg = ["st(6)"],
309+
st7: x87_reg = ["st(7)"],
288310
#error = ["bp", "bpl", "ebp", "rbp"] =>
289311
"the frame pointer cannot be used as an operand for inline asm",
290312
#error = ["sp", "spl", "esp", "rsp"] =>
291313
"the stack pointer cannot be used as an operand for inline asm",
292314
#error = ["ip", "eip", "rip"] =>
293315
"the instruction pointer cannot be used as an operand for inline asm",
294-
#error = ["st", "st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"] =>
295-
"x87 registers are not currently supported as operands for inline asm",
296-
#error = ["mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"] =>
297-
"MMX registers are not currently supported as operands for inline asm",
298316
#error = ["k0"] =>
299317
"the k0 AVX mask register cannot be used as an operand for inline asm",
300318
}

0 commit comments

Comments
 (0)