Skip to content

Commit

Permalink
Make s390x non-clobber-only vector register support unstable
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Nov 24, 2024
1 parent 2c8f6de commit d2357fb
Show file tree
Hide file tree
Showing 18 changed files with 775 additions and 146 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on thi
ast_lowering_unstable_inline_assembly_label_operands =
label operands for inline assembly are unstable
ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
ast_lowering_register_class_only_clobber_stable =
this register class can only be used as a clobber in stable
ast_lowering_use_angle_brackets = use angle brackets instead
Expand Down
26 changes: 21 additions & 5 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.emit();
}
}
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
&& !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
&& !self.tcx.sess.opts.actually_rustdoc
Expand Down Expand Up @@ -333,11 +334,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// 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()) && !op.is_clobber() {
self.dcx().emit_err(RegisterClassOnlyClobber {
op_span: op_sp,
reg_class_name: reg_class.name(),
});
if reg_class.is_clobber_only(asm_arch.unwrap(), allow_experimental_reg)
&& !op.is_clobber()
{
if allow_experimental_reg || reg_class.is_clobber_only(asm_arch.unwrap(), true)
{
// always clobber-only
self.dcx().emit_err(RegisterClassOnlyClobber {
op_span: op_sp,
reg_class_name: reg_class.name(),
});
} else {
// clobber-only in stable
feature_err(
&self.tcx.sess,
sym::asm_experimental_reg,
sp,
fluent::ast_lowering_register_class_only_clobber_stable,
)
.emit();
}
continue;
}

Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_codegen_cranelift/src/inline_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
let mut asm_gen = InlineAssemblyGenerator {
tcx: fx.tcx,
arch: fx.tcx.sess.asm_arch.unwrap(),
allow_experimental_reg: fx.tcx.features().asm_experimental_reg(),
enclosing_def_id: fx.instance.def_id(),
template,
operands,
Expand Down Expand Up @@ -296,6 +297,7 @@ pub(crate) fn codegen_naked_asm<'tcx>(
let asm_gen = InlineAssemblyGenerator {
tcx,
arch: tcx.sess.asm_arch.unwrap(),
allow_experimental_reg: tcx.features().asm_experimental_reg(),
enclosing_def_id: instance.def_id(),
template,
operands: &operands,
Expand All @@ -315,6 +317,7 @@ pub(crate) fn codegen_naked_asm<'tcx>(
struct InlineAssemblyGenerator<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
arch: InlineAsmArch,
allow_experimental_reg: bool,
enclosing_def_id: DefId,
template: &'a [InlineAsmTemplatePiece],
operands: &'a [CInlineAsmOperand<'tcx>],
Expand Down Expand Up @@ -462,8 +465,12 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
let mut slots_output = vec![None; self.operands.len()];

let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| {
let reg_size =
reg_class.supported_types(self.arch).iter().map(|(ty, _)| ty.size()).max().unwrap();
let reg_size = reg_class
.supported_types(self.arch, self.allow_experimental_reg)
.iter()
.map(|(ty, _)| ty.size())
.max()
.unwrap();
let align = rustc_abi::Align::from_bytes(reg_size.bytes()).unwrap();
let offset = slot_size.align_to(align);
*slot_size = offset + reg_size;
Expand Down
26 changes: 14 additions & 12 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
let asm_arch = self.tcx.sess.asm_arch.unwrap();
let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();

// GCC index of an output operand equals its position in the array
let mut outputs = vec![];
Expand Down Expand Up @@ -185,18 +186,19 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
(Register(reg_name), None) => {
// `clobber_abi` can add lots of clobbers that are not supported by the target,
// such as AVX-512 registers, so we just ignore unsupported registers
let is_target_supported =
reg.reg_class().supported_types(asm_arch).iter().any(
|&(_, feature)| {
if let Some(feature) = feature {
self.tcx
.asm_target_features(instance.def_id())
.contains(&feature)
} else {
true // Register class is unconditionally supported
}
},
);
let is_target_supported = reg
.reg_class()
.supported_types(asm_arch, allow_experimental_reg)
.iter()
.any(|&(_, feature)| {
if let Some(feature) = feature {
self.tcx
.asm_target_features(instance.def_id())
.contains(&feature)
} else {
true // Register class is unconditionally supported
}
});

if is_target_supported && !clobbers.contains(&reg_name) {
clobbers.push(reg_name);
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>,
) {
let asm_arch = self.tcx.sess.asm_arch.unwrap();
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();

// Collect the types of output operands
let mut constraints = vec![];
Expand All @@ -45,7 +46,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
match *op {
InlineAsmOperandRef::Out { reg, late, place } => {
let is_target_supported = |reg_class: InlineAsmRegClass| {
for &(_, feature) in reg_class.supported_types(asm_arch) {
for &(_, feature) in
reg_class.supported_types(asm_arch, allow_experimental_reg)
{
if let Some(feature) = feature {
if self
.tcx
Expand Down Expand Up @@ -85,7 +88,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
}
continue;
} else if !is_target_supported(reg.reg_class())
|| reg.reg_class().is_clobber_only(asm_arch)
|| reg.reg_class().is_clobber_only(asm_arch, allow_experimental_reg)
{
// We turn discarded outputs into clobber constraints
// if the target feature needed by the register class is
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ declare_features! (
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
/// Enables experimental inline assembly support for additional architectures.
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
/// Enables experimental register support in inline assembly.
(unstable, asm_experimental_reg, "CURRENT_RUSTC_VERSION", Some(133416)),
/// Allows using `label` operands in inline assembly.
(unstable, asm_goto, "1.78.0", Some(119364)),
/// Allows the `may_unwind` option in inline assembly.
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_hir_analysis/src/check/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
// Check the type against the list of types supported by the selected
// register class.
let asm_arch = self.tcx.sess.asm_arch.unwrap();
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
let reg_class = reg.reg_class();
let supported_tys = reg_class.supported_types(asm_arch);
let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
let msg = format!("type `{ty}` cannot be used with this register class");
let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
Expand Down Expand Up @@ -313,6 +314,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
self.tcx.dcx().delayed_bug("target architecture does not support asm");
return;
};
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
Expand Down Expand Up @@ -352,7 +354,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
if let InlineAsmRegClass::Err = reg_class {
continue;
}
for &(_, feature) in reg_class.supported_types(asm_arch) {
for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg)
{
match feature {
Some(feature) => {
if target_features.contains(&feature) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ symbols! {
asm,
asm_const,
asm_experimental_arch,
asm_experimental_reg,
asm_goto,
asm_sym,
asm_unwind,
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_target/src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ impl InlineAsmRegClass {
pub fn supported_types(
self,
arch: InlineAsmArch,
allow_experimental_reg: bool,
) -> &'static [(InlineAsmType, Option<Symbol>)] {
match self {
Self::X86(r) => r.supported_types(arch),
Expand All @@ -618,7 +619,7 @@ impl InlineAsmRegClass {
Self::Hexagon(r) => r.supported_types(arch),
Self::LoongArch(r) => r.supported_types(arch),
Self::Mips(r) => r.supported_types(arch),
Self::S390x(r) => r.supported_types(arch),
Self::S390x(r) => r.supported_types(arch, allow_experimental_reg),
Self::Sparc(r) => r.supported_types(arch),
Self::SpirV(r) => r.supported_types(arch),
Self::Wasm(r) => r.supported_types(arch),
Expand Down Expand Up @@ -696,8 +697,8 @@ impl InlineAsmRegClass {

/// 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()
pub fn is_clobber_only(self, arch: InlineAsmArch, allow_experimental_reg: bool) -> bool {
self.supported_types(arch, allow_experimental_reg).is_empty()
}
}

Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_target/src/asm/s390x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,22 @@ impl S390xInlineAsmRegClass {
pub fn supported_types(
self,
_arch: InlineAsmArch,
allow_experimental_reg: bool,
) -> &'static [(InlineAsmType, Option<Symbol>)] {
match self {
Self::reg | Self::reg_addr => types! { _: I8, I16, I32, I64; },
Self::freg => types! { _: F32, F64; },
Self::vreg => types! {
vector: I32, F32, I64, F64, I128, F128,
VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
},
Self::vreg => {
if allow_experimental_reg {
// non-clobber-only vector register support is unstable.
types! {
vector: I32, F32, I64, F64, I128, F128,
VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
}
} else {
&[]
}
}
Self::areg => &[],
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# `asm_experimental_arch`

The tracking issue for this feature is: [#133416]

[#133416]: https://github.com/rust-lang/rust/issues/133416

------------------------

This tracks support for additional registers in architectures where inline assembly is already stable.

## Register classes

| Architecture | Register class | Registers | LLVM constraint code |
| ------------ | -------------- | --------- | -------------------- |
| s390x | `vreg` | `v[0-31]` | `v` |

> **Notes**:
> - s390x `vreg` is clobber-only in stable.
## Register class supported types

| Architecture | Register class | Target feature | Allowed types |
| ------------ | -------------- | -------------- | ------------- |
| s390x | `vreg` | `vector` | `i32`, `f32`, `i64`, `f64`, `i128`, `f128`, `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |

## Register aliases

| Architecture | Base register | Aliases |
| ------------ | ------------- | ------- |

## Unsupported registers

| Architecture | Unsupported register | Reason |
| ------------ | -------------------- | ------ |

## Template modifiers

| Architecture | Register class | Modifier | Example output | LLVM modifier |
| ------------ | -------------- | -------- | -------------- | ------------- |
| s390x | `vreg` | None | `%v0` | None |
1 change: 1 addition & 0 deletions tests/assembly/asm/s390x-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//@ compile-flags: -Zmerge-functions=disabled

#![feature(no_core, lang_items, rustc_attrs, repr_simd, f128)]
#![cfg_attr(s390x_vector, feature(asm_experimental_reg))]
#![crate_type = "rlib"]
#![no_core]
#![allow(asm_sub_register, non_camel_case_types)]
Expand Down
38 changes: 30 additions & 8 deletions tests/ui/asm/s390x/bad-reg.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//@ add-core-stubs
//@ needs-asm-support
//@ revisions: s390x s390x_vector
//@ revisions: s390x s390x_vector s390x_vector_stable
//@[s390x] compile-flags: --target s390x-unknown-linux-gnu
//@[s390x] needs-llvm-components: systemz
//@[s390x_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector
//@[s390x_vector] needs-llvm-components: systemz
//@[s390x_vector_stable] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector
//@[s390x_vector_stable] needs-llvm-components: systemz

#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, repr_simd)]
#![cfg_attr(not(s390x_vector_stable), feature(asm_experimental_reg))]
#![no_core]
#![allow(non_camel_case_types)]

Expand Down Expand Up @@ -68,29 +71,48 @@ fn f() {

// vreg
asm!("", out("v0") _); // always ok
asm!("", in("v0") v); // requires vector
asm!("", in("v0") v); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
asm!("", out("v0") v); // requires vector
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i64x2` cannot be used with this register class
asm!("", out("v0") v); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
asm!("", in("v0") x); // requires vector
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i64x2` cannot be used with this register class
asm!("", in("v0") x); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
asm!("", out("v0") x); // requires vector
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i32` cannot be used with this register class
asm!("", out("v0") x); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i32` cannot be used with this register class
asm!("", in("v0") b);
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector]~^^ ERROR type `u8` cannot be used with this register class
//[s390x_vector_stable]~^^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `u8` cannot be used with this register class
asm!("", out("v0") b);
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector]~^^ ERROR type `u8` cannot be used with this register class
asm!("/* {} */", in(vreg) v); // requires vector
//[s390x_vector_stable]~^^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `u8` cannot be used with this register class
asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
asm!("/* {} */", in(vreg) x); // requires vector
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i64x2` cannot be used with this register class
asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `i32` cannot be used with this register class
asm!("/* {} */", in(vreg) b);
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector]~^^ ERROR type `u8` cannot be used with this register class
asm!("/* {} */", out(vreg) _); // requires vector
//[s390x_vector_stable]~^^^ ERROR this register class can only be used as a clobber in stable [E0658]
//[s390x_vector_stable]~| ERROR type `u8` cannot be used with this register class
asm!("/* {} */", out(vreg) _); // requires vector & asm_experimental_reg
//[s390x]~^ ERROR register class `vreg` requires the `vector` target feature
//[s390x_vector_stable]~^^ ERROR this register class can only be used as a clobber in stable [E0658]

// Clobber-only registers
// areg
Expand Down
Loading

0 comments on commit d2357fb

Please sign in to comment.