Skip to content

Commit 344b6a1

Browse files
Rollup merge of rust-lang#130630 - taiki-e:s390x-clobber-abi, r=Amanieu
Support clobber_abi and vector/access registers (clobber-only) in s390x inline assembly This supports `clobber_abi` which is one of the requirements of stabilization mentioned in rust-lang#93335. This also supports vector registers (as `vreg`) and access registers (as `areg`) as clobber-only, which need to support clobbering of them to implement clobber_abi. Refs: - "1.2.1.1. Register Preservation Rules" section in ELF Application Binary Interface s390x Supplement, Version 1.6.1 (lzsabi_s390x.pdf in https://github.com/IBM/s390x-abi/releases/tag/v1.6.1) - Register definition in LLVM: - Vector registers https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/SystemZ/SystemZRegisterInfo.td#L249 - Access registers https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/SystemZ/SystemZRegisterInfo.td#L332 I have three questions: - ~~ELF Application Binary Interface s390x Supplement says that `cc` (condition code, bits 18-19 of PSW) is "Volatile". However, we do not have a register class for `cc` and instead mark `cc` as clobbered unless `preserves_flags` is specified (rust-lang#111331). Therefore, in the current implementation, if both `preserves_flags` and `clobber_abi` are specified, `cc` is not marked as clobbered. Is this okay? Or even if `preserves_flags` is used, should `cc` be marked as clobbered if `clobber_abi` is used?~~ UPDATE: resolved rust-lang#130630 (comment) - ~~ELF Application Binary Interface s390x Supplement says that `pm` (program mask, bits 20-23 of PSW) is "Cleared". There does not appear to be any registers associated with this in either [LLVM](https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/SystemZ/SystemZRegisterInfo.td) or [GCC](https://github.com/gcc-mirror/gcc/blob/33ccc1314dcdb0b988a9276ca6b6ce9b07bea21e/gcc/config/s390/s390.h#L407-L431), so at this point I don't see any way other than to just ignore it. Is this okay as-is?~~ UPDATE: resolved rust-lang#130630 (comment) - Is "areg" a good name for register class name for access registers? It may be a bit confusing between that and `reg_addr`, which uses the “a” constraint (rust-lang#119431)... Note: - GCC seems to [recognize only `a0` and `a1`](https://github.com/gcc-mirror/gcc/blob/33ccc1314dcdb0b988a9276ca6b6ce9b07bea21e/gcc/config/s390/s390.h#L428-L429), and using `a[2-15]` [causes errors](https://godbolt.org/z/a46vx8jjn). Given that cg_gcc has a similar problem with other architecture (rust-lang/rustc_codegen_gcc#485), I don't feel this is a blocker for this PR, but it is worth mentioning here. - `vreg` should be able to accept `#[repr(simd)]` types as input if the `vector` target feature added in rust-lang#127506 is enabled, but core_arch has no s390x vector type and both `#[repr(simd)]` and `core::simd` are unstable, so I have not implemented it in this PR. EDIT: And supporting it is probably more complex than doing the equivalent on other architectures... rust-lang#88245 (comment) cc `@uweigand` r? `@Amanieu` `@rustbot` label +O-SystemZ
2 parents c4f7176 + fa125e2 commit 344b6a1

File tree

7 files changed

+200
-12
lines changed

7 files changed

+200
-12
lines changed

compiler/rustc_codegen_gcc/src/asm.rs

+8
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
682682
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
683683
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a",
684684
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
685+
InlineAsmRegClass::S390x(
686+
S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg,
687+
) => {
688+
unreachable!("clobber-only")
689+
}
685690
InlineAsmRegClass::Err => unreachable!(),
686691
},
687692
};
@@ -757,6 +762,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
757762
S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr,
758763
) => cx.type_i32(),
759764
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
765+
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
766+
unreachable!("clobber-only")
767+
}
760768
InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
761769
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
762770
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),

compiler/rustc_codegen_llvm/src/asm.rs

+6
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
708708
S390x(S390xInlineAsmRegClass::reg) => "r",
709709
S390x(S390xInlineAsmRegClass::reg_addr) => "a",
710710
S390x(S390xInlineAsmRegClass::freg) => "f",
711+
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
712+
unreachable!("clobber-only")
713+
}
711714
Msp430(Msp430InlineAsmRegClass::reg) => "r",
712715
M68k(M68kInlineAsmRegClass::reg) => "r",
713716
M68k(M68kInlineAsmRegClass::reg_addr) => "a",
@@ -866,6 +869,9 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
866869
Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
867870
S390x(S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr) => cx.type_i32(),
868871
S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
872+
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
873+
unreachable!("clobber-only")
874+
}
869875
Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
870876
M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
871877
M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ symbols! {
411411
arbitrary_enum_discriminant,
412412
arbitrary_self_types,
413413
arbitrary_self_types_pointers,
414+
areg,
414415
args,
415416
arith_offset,
416417
arm,

compiler/rustc_target/src/asm/mod.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ impl InlineAsmReg {
439439
Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))),
440440
Self::LoongArch(_) => cb(self),
441441
Self::Mips(_) => cb(self),
442-
Self::S390x(_) => cb(self),
442+
Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))),
443443
Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
444444
Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
445445
Self::Msp430(_) => cb(self),
@@ -892,6 +892,7 @@ pub enum InlineAsmClobberAbi {
892892
AArch64NoX18,
893893
RiscV,
894894
LoongArch,
895+
S390x,
895896
}
896897

897898
impl InlineAsmClobberAbi {
@@ -941,6 +942,10 @@ impl InlineAsmClobberAbi {
941942
"C" | "system" => Ok(InlineAsmClobberAbi::LoongArch),
942943
_ => Err(&["C", "system"]),
943944
},
945+
InlineAsmArch::S390x => match name {
946+
"C" | "system" => Ok(InlineAsmClobberAbi::S390x),
947+
_ => Err(&["C", "system"]),
948+
},
944949
_ => Err(&[]),
945950
}
946951
}
@@ -1098,6 +1103,28 @@ impl InlineAsmClobberAbi {
10981103
f16, f17, f18, f19, f20, f21, f22, f23,
10991104
}
11001105
},
1106+
InlineAsmClobberAbi::S390x => clobbered_regs! {
1107+
S390x S390xInlineAsmReg {
1108+
r0, r1, r2, r3, r4, r5,
1109+
r14,
1110+
1111+
// f0-f7, v0-v7
1112+
f0, f1, f2, f3, f4, f5, f6, f7,
1113+
v0, v1, v2, v3, v4, v5, v6, v7,
1114+
1115+
// Technically the left halves of v8-v15 (i.e., f8-f15) are saved, but
1116+
// we have no way of expressing this using clobbers.
1117+
v8, v9, v10, v11, v12, v13, v14, v15,
1118+
1119+
// Other vector registers are volatile
1120+
v16, v17, v18, v19, v20, v21, v22, v23,
1121+
v24, v25, v26, v27, v28, v29, v30, v31,
1122+
1123+
// a0-a1 are reserved, other access registers are volatile
1124+
a2, a3, a4, a5, a6, a7,
1125+
a8, a9, a10, a11, a12, a13, a14, a15,
1126+
}
1127+
},
11011128
}
11021129
}
11031130
}

compiler/rustc_target/src/asm/s390x.rs

+100-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ def_reg_class! {
99
reg,
1010
reg_addr,
1111
freg,
12+
vreg,
13+
areg,
1214
}
1315
}
1416

@@ -35,11 +37,13 @@ impl S390xInlineAsmRegClass {
3537

3638
pub fn supported_types(
3739
self,
38-
arch: InlineAsmArch,
40+
_arch: InlineAsmArch,
3941
) -> &'static [(InlineAsmType, Option<Symbol>)] {
40-
match (self, arch) {
41-
(Self::reg | Self::reg_addr, _) => types! { _: I8, I16, I32, I64; },
42-
(Self::freg, _) => types! { _: F32, F64; },
42+
match self {
43+
Self::reg | Self::reg_addr => types! { _: I8, I16, I32, I64; },
44+
Self::freg => types! { _: F32, F64; },
45+
Self::vreg => &[],
46+
Self::areg => &[],
4347
}
4448
}
4549
}
@@ -76,6 +80,52 @@ def_regs! {
7680
f13: freg = ["f13"],
7781
f14: freg = ["f14"],
7882
f15: freg = ["f15"],
83+
v0: vreg = ["v0"],
84+
v1: vreg = ["v1"],
85+
v2: vreg = ["v2"],
86+
v3: vreg = ["v3"],
87+
v4: vreg = ["v4"],
88+
v5: vreg = ["v5"],
89+
v6: vreg = ["v6"],
90+
v7: vreg = ["v7"],
91+
v8: vreg = ["v8"],
92+
v9: vreg = ["v9"],
93+
v10: vreg = ["v10"],
94+
v11: vreg = ["v11"],
95+
v12: vreg = ["v12"],
96+
v13: vreg = ["v13"],
97+
v14: vreg = ["v14"],
98+
v15: vreg = ["v15"],
99+
v16: vreg = ["v16"],
100+
v17: vreg = ["v17"],
101+
v18: vreg = ["v18"],
102+
v19: vreg = ["v19"],
103+
v20: vreg = ["v20"],
104+
v21: vreg = ["v21"],
105+
v22: vreg = ["v22"],
106+
v23: vreg = ["v23"],
107+
v24: vreg = ["v24"],
108+
v25: vreg = ["v25"],
109+
v26: vreg = ["v26"],
110+
v27: vreg = ["v27"],
111+
v28: vreg = ["v28"],
112+
v29: vreg = ["v29"],
113+
v30: vreg = ["v30"],
114+
v31: vreg = ["v31"],
115+
a2: areg = ["a2"],
116+
a3: areg = ["a3"],
117+
a4: areg = ["a4"],
118+
a5: areg = ["a5"],
119+
a6: areg = ["a6"],
120+
a7: areg = ["a7"],
121+
a8: areg = ["a8"],
122+
a9: areg = ["a9"],
123+
a10: areg = ["a10"],
124+
a11: areg = ["a11"],
125+
a12: areg = ["a12"],
126+
a13: areg = ["a13"],
127+
a14: areg = ["a14"],
128+
a15: areg = ["a15"],
79129
#error = ["r11"] =>
80130
"The frame pointer cannot be used as an operand for inline asm",
81131
#error = ["r15"] =>
@@ -87,13 +137,8 @@ def_regs! {
87137
"c12", "c13", "c14", "c15"
88138
] =>
89139
"control registers are reserved by the kernel and cannot be used as operands for inline asm",
90-
#error = [
91-
"a0", "a1", "a2", "a3",
92-
"a4", "a5", "a6", "a7",
93-
"a8", "a9", "a10", "a11",
94-
"a12", "a13", "a14", "a15"
95-
] =>
96-
"access registers are not supported and cannot be used as operands for inline asm",
140+
#error = ["a0", "a1"] =>
141+
"a0 and a1 are reserved for system use and cannot be used as operands for inline asm",
97142
}
98143
}
99144

@@ -106,4 +151,48 @@ impl S390xInlineAsmReg {
106151
) -> fmt::Result {
107152
write!(out, "%{}", self.name())
108153
}
154+
155+
pub fn overlapping_regs(self, mut cb: impl FnMut(S390xInlineAsmReg)) {
156+
macro_rules! reg_conflicts {
157+
(
158+
$(
159+
$full:ident : $($field:ident)*
160+
),*;
161+
) => {
162+
match self {
163+
$(
164+
Self::$full => {
165+
cb(Self::$full);
166+
$(cb(Self::$field);)*
167+
}
168+
$(Self::$field)|* => {
169+
cb(Self::$full);
170+
cb(self);
171+
}
172+
)*
173+
r => cb(r),
174+
}
175+
};
176+
}
177+
178+
// The left halves of v0-v15 are aliased to f0-f15.
179+
reg_conflicts! {
180+
v0 : f0,
181+
v1 : f1,
182+
v2 : f2,
183+
v3 : f3,
184+
v4 : f4,
185+
v5 : f5,
186+
v6 : f6,
187+
v7 : f7,
188+
v8 : f8,
189+
v9 : f9,
190+
v10 : f10,
191+
v11 : f11,
192+
v12 : f12,
193+
v13 : f13,
194+
v14 : f14,
195+
v15 : f15;
196+
}
197+
}
109198
}

src/doc/unstable-book/src/language-features/asm-experimental-arch.md

+7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
5151
| CSKY | `reg` | `r[0-31]` | `r` |
5252
| CSKY | `freg` | `f[0-31]` | `f` |
5353
| s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` |
54+
| s390x | `reg_addr` | `r[1-10]`, `r[12-14]` | `a` |
5455
| s390x | `freg` | `f[0-15]` | `f` |
56+
| s390x | `vreg` | `v[0-31]` | Only clobbers |
57+
| s390x | `areg` | `a[2-15]` | Only clobbers |
5558
| Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` |
5659
| Arm64EC | `vreg` | `v[0-15]` | `w` |
5760
| Arm64EC | `vreg_low16` | `v[0-15]` | `x` |
@@ -90,6 +93,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
9093
| CSKY | `freg` | None | `f32`, |
9194
| s390x | `reg`, `reg_addr` | None | `i8`, `i16`, `i32`, `i64` |
9295
| s390x | `freg` | None | `f32`, `f64` |
96+
| s390x | `vreg` | N/A | Only clobbers |
97+
| s390x | `areg` | N/A | Only clobbers |
9398
| Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
9499
| Arm64EC | `vreg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |
95100

@@ -157,6 +162,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
157162
| CSKY | `r15` | This is the link register. |
158163
| CSKY | `r[26-30]` | Reserved by its ABI. |
159164
| CSKY | `r31` | This is the TLS register. |
165+
| s390x | `c[0-15]` | Reserved by the kernel. |
166+
| s390x | `a[0-1]` | Reserved for system use. |
160167
| Arm64EC | `xzr` | This is a constant zero register which can't be modified. |
161168
| Arm64EC | `x18` | This is an OS-reserved register. |
162169
| Arm64EC | `x13`, `x14`, `x23`, `x24`, `x28`, `v[16-31]` | These are AArch64 registers that are not supported for Arm64EC. |

tests/codegen/asm-s390x-clobbers.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//@ revisions: s390x
2+
//@[s390x] compile-flags: --target s390x-unknown-linux-gnu
3+
//@[s390x] needs-llvm-components: systemz
4+
5+
#![crate_type = "rlib"]
6+
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
7+
#![no_core]
8+
9+
#[lang = "sized"]
10+
trait Sized {}
11+
12+
#[rustc_builtin_macro]
13+
macro_rules! asm {
14+
() => {};
15+
}
16+
17+
// CHECK-LABEL: @cc_clobber
18+
// CHECK: call void asm sideeffect "", "~{cc}"()
19+
#[no_mangle]
20+
pub unsafe fn cc_clobber() {
21+
asm!("", options(nostack, nomem));
22+
}
23+
24+
// CHECK-LABEL: @no_clobber
25+
// CHECK: call void asm sideeffect "", ""()
26+
#[no_mangle]
27+
pub unsafe fn no_clobber() {
28+
asm!("", options(nostack, nomem, preserves_flags));
29+
}
30+
31+
// CHECK-LABEL: @a2_clobber
32+
// CHECK: call void asm sideeffect "", "~{a2}"()
33+
#[no_mangle]
34+
pub unsafe fn a2_clobber() {
35+
asm!("", out("a2") _, options(nostack, nomem, preserves_flags));
36+
}
37+
38+
// CHECK-LABEL: @v0_clobber
39+
// CHECK: call void asm sideeffect "", "~{v0}"()
40+
#[no_mangle]
41+
pub unsafe fn v0_clobber() {
42+
asm!("", out("v0") _, options(nostack, nomem, preserves_flags));
43+
}
44+
45+
// CHECK-LABEL: @clobber_abi
46+
// CHECK: asm sideeffect "", "={r0},={r1},={r2},={r3},={r4},={r5},={r14},={f0},={f1},={f2},={f3},={f4},={f5},={f6},={f7},~{v0},~{v1},~{v2},~{v3},~{v4},~{v5},~{v6},~{v7},~{v8},~{v9},~{v10},~{v11},~{v12},~{v13},~{v14},~{v15},~{v16},~{v17},~{v18},~{v19},~{v20},~{v21},~{v22},~{v23},~{v24},~{v25},~{v26},~{v27},~{v28},~{v29},~{v30},~{v31},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{a8},~{a9},~{a10},~{a11},~{a12},~{a13},~{a14},~{a15}"()
47+
#[no_mangle]
48+
pub unsafe fn clobber_abi() {
49+
asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags));
50+
}

0 commit comments

Comments
 (0)