Skip to content

Commit 99f8efe

Browse files
committed
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.
2 parents dfd7b8d + d2a1d04 commit 99f8efe

File tree

11 files changed

+270
-59
lines changed

11 files changed

+270
-59
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
@@ -565,6 +586,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
565586
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r",
566587
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w",
567588
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x",
589+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
590+
unreachable!("clobber-only")
591+
}
568592
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
569593
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l",
570594
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -586,13 +610,19 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
586610
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
587611
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r",
588612
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f",
613+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
614+
unreachable!("clobber-only")
615+
}
589616
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
590617
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
591618
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
592619
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
593620
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
594621
InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
595622
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk",
623+
InlineAsmRegClass::X86(
624+
X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
625+
) => unreachable!("clobber-only"),
596626
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
597627
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
598628
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
@@ -617,6 +647,9 @@ fn modifier_to_llvm(
617647
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
618648
if modifier == Some('v') { None } else { modifier }
619649
}
650+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
651+
unreachable!("clobber-only")
652+
}
620653
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
621654
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None,
622655
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -639,6 +672,9 @@ fn modifier_to_llvm(
639672
InlineAsmRegClass::PowerPC(_) => None,
640673
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
641674
| InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None,
675+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
676+
unreachable!("clobber-only")
677+
}
642678
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
643679
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
644680
None if arch == InlineAsmArch::X86_64 => Some('q'),
@@ -663,6 +699,9 @@ fn modifier_to_llvm(
663699
_ => unreachable!(),
664700
},
665701
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
702+
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
703+
unreachable!("clobber-only")
704+
}
666705
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
667706
InlineAsmRegClass::Bpf(_) => None,
668707
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
@@ -681,6 +720,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
681720
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
682721
cx.type_vector(cx.type_i64(), 2)
683722
}
723+
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
724+
unreachable!("clobber-only")
725+
}
684726
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
685727
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
686728
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -704,13 +746,19 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
704746
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
705747
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
706748
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
749+
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
750+
unreachable!("clobber-only")
751+
}
707752
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
708753
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
709754
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
710755
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
711756
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
712757
| InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
713758
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
759+
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
760+
unreachable!("clobber-only")
761+
}
714762
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
715763
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(),
716764
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(),

compiler/rustc_span/src/symbol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ symbols! {
756756
minnumf64,
757757
mips_target_feature,
758758
misc,
759+
mmx_reg,
759760
modifiers,
760761
module,
761762
module_path,
@@ -899,6 +900,7 @@ symbols! {
899900
prefetch_read_instruction,
900901
prefetch_write_data,
901902
prefetch_write_instruction,
903+
preg,
902904
prelude,
903905
prelude_import,
904906
preserves_flags,
@@ -1343,6 +1345,7 @@ symbols! {
13431345
wrapping_sub,
13441346
wreg,
13451347
write_bytes,
1348+
x87_reg,
13461349
xmm_reg,
13471350
ymm_reg,
13481351
zmm_reg,

compiler/rustc_target/src/asm/aarch64.rs

+54-32
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
}
@@ -95,38 +100,55 @@ def_regs! {
95100
x27: reg = ["x27", "w27"],
96101
x28: reg = ["x28", "w28"],
97102
x30: reg = ["x30", "w30", "lr", "wlr"],
98-
v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"],
99-
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"],
100-
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"],
101-
v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3"],
102-
v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4"],
103-
v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5"],
104-
v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6"],
105-
v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7"],
106-
v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8"],
107-
v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9"],
108-
v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10"],
109-
v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11"],
110-
v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12"],
111-
v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13"],
112-
v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14"],
113-
v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15"],
114-
v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16"],
115-
v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17"],
116-
v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18"],
117-
v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19"],
118-
v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20"],
119-
v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21"],
120-
v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22"],
121-
v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23"],
122-
v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24"],
123-
v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25"],
124-
v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26"],
125-
v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27"],
126-
v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28"],
127-
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"],
128-
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"],
129-
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"],
103+
v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0", "z0"],
104+
v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1", "z1"],
105+
v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2", "z2"],
106+
v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3", "z3"],
107+
v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4", "z4"],
108+
v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5", "z5"],
109+
v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6", "z6"],
110+
v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7", "z7"],
111+
v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8", "z8"],
112+
v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9", "z9"],
113+
v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10", "z10"],
114+
v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11", "z11"],
115+
v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12", "z12"],
116+
v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13", "z13"],
117+
v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14", "z14"],
118+
v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15", "z15"],
119+
v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"],
120+
v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"],
121+
v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"],
122+
v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"],
123+
v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"],
124+
v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"],
125+
v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"],
126+
v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"],
127+
v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"],
128+
v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"],
129+
v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"],
130+
v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"],
131+
v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"],
132+
v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"],
133+
v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"],
134+
v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"],
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
@@ -533,6 +533,12 @@ impl InlineAsmRegClass {
533533
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
534534
}
535535
}
536+
537+
/// Returns whether registers in this class can only be used as clobbers
538+
/// and not as inputs/outputs.
539+
pub fn is_clobber_only(self, arch: InlineAsmArch) -> bool {
540+
self.supported_types(arch).is_empty()
541+
}
536542
}
537543

538544
#[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"] =>

0 commit comments

Comments
 (0)