diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index f801f845ac1..1eb852e6b01 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -259,7 +259,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} InlineAsmArch::Nvptx64 => {} InlineAsmArch::Hexagon => {} - InlineAsmArch::Mips => {} + InlineAsmArch::Mips | InlineAsmArch::Mips64 => {} } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -710,6 +710,7 @@ fn llvm_fixup_input( // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => bx.zext(value, bx.cx.type_i32()), Primitive::F32 => bx.bitcast(value, bx.cx.type_i32()), + Primitive::F64 => bx.bitcast(value, bx.cx.type_i64()), _ => value, }, _ => value, @@ -785,6 +786,7 @@ fn llvm_fixup_output( Primitive::Int(Integer::I8, _) => bx.trunc(value, bx.cx.type_i8()), Primitive::Int(Integer::I16, _) => bx.trunc(value, bx.cx.type_i16()), Primitive::F32 => bx.bitcast(value, bx.cx.type_f32()), + Primitive::F64 => bx.bitcast(value, bx.cx.type_f64()), _ => value, }, _ => value, @@ -854,6 +856,7 @@ fn llvm_fixup_output_type( // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => cx.type_i32(), Primitive::F32 => cx.type_i32(), + Primitive::F64 => cx.type_i64(), _ => layout.llvm_type(cx), }, _ => layout.llvm_type(cx), diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index f6e3b7d1921..0cad241efdf 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -32,11 +32,12 @@ impl MipsInlineAsmRegClass { pub fn supported_types( self, - _arch: InlineAsmArch, + arch: InlineAsmArch, ) -> &'static [(InlineAsmType, Option<&'static str>)] { - match self { - Self::reg => types! { _: I8, I16, I32, F32; }, - Self::freg => types! { _: F32, F64; }, + match (self, arch) { + (Self::reg, InlineAsmArch::Mips64) => types! { _: I8, I16, I32, I64, F32, F64; }, + (Self::reg, _) => types! { _: I8, I16, I32, F32; }, + (Self::freg, _) => types! { _: F32, F64; }, } } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e2f8e91fa95..0d691dc441e 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -176,6 +176,7 @@ pub enum InlineAsmArch { Nvptx64, Hexagon, Mips, + Mips64, } impl FromStr for InlineAsmArch { @@ -192,6 +193,7 @@ impl FromStr for InlineAsmArch { "nvptx64" => Ok(Self::Nvptx64), "hexagon" => Ok(Self::Hexagon), "mips" => Ok(Self::Mips), + "mips64" => Ok(Self::Mips64), _ => Err(()), } } @@ -259,7 +261,7 @@ impl InlineAsmReg { InlineAsmArch::Hexagon => { Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?) } - InlineAsmArch::Mips => { + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { Self::Mips(MipsInlineAsmReg::parse(arch, has_feature, target, &name)?) } }) @@ -409,7 +411,9 @@ impl InlineAsmRegClass { InlineAsmArch::Hexagon => { Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) } - InlineAsmArch::Mips => Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) + } }) }) } @@ -565,7 +569,7 @@ pub fn allocatable_registers( hexagon::fill_reg_map(arch, has_feature, target, &mut map); map } - InlineAsmArch::Mips => { + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { let mut map = mips::regclass_map(); mips::fill_reg_map(arch, has_feature, target, &mut map); map diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 8df08b2ab8f..47030a2ab47 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -27,7 +27,7 @@ Inline assembly is currently supported on the following architectures: - RISC-V - NVPTX - Hexagon -- MIPS32 +- MIPS32r2 and MIPS64r2 ## Basic usage @@ -513,8 +513,8 @@ Here is the list of currently supported register classes: | ARM | `qreg` | `q[0-15]` | `w` | | ARM | `qreg_low8` | `q[0-7]` | `t` | | ARM | `qreg_low4` | `q[0-3]` | `x` | -| MIPS32 | `reg` | `$[2-25]` | `r` | -| MIPS32 | `freg` | `$f[0-31]` | `f` | +| MIPS | `reg` | `$[2-25]` | `r` | +| MIPS | `freg` | `$f[0-31]` | `f` | | NVPTX | `reg16` | None\* | `h` | | NVPTX | `reg32` | None\* | `r` | | NVPTX | `reg64` | None\* | `l` | @@ -552,6 +552,8 @@ Each register class has constraints on which value types they can be used with. | ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | | MIPS32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | | MIPS32 | `freg` | None | `f32`, `f64` | +| MIPS64 | `reg` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` | +| MIPS64 | `freg` | None | `f32`, `f64` | | NVPTX | `reg16` | None | `i8`, `i16` | | NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` | | NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | @@ -637,11 +639,11 @@ Some registers cannot be used for input or output operands: | x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | | AArch64 | `xzr` | This is a constant zero register which can't be modified. | | ARM | `pc` | This is the program counter, not a real register. | -| MIPS32 | `$0` or `$zero` | This is a constant zero register which can't be modified. | -| MIPS32 | `$1` or `$at` | Reserved for assembler. | -| MIPS32 | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | -| MIPS32 | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | -| MIPS32 | `$ra` | Return address cannot be used as inputs or outputs. | +| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | +| MIPS | `$1` or `$at` | Reserved for assembler. | +| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | +| MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | +| MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | RISC-V | `x0` | This is a constant zero register which can't be modified. | | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | @@ -689,8 +691,8 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen | ARM | `dreg` | None | `d0` | `P` | | ARM | `qreg` | None | `q0` | `q` | | ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | -| MIPS32 | `reg` | None | `$2` | None | -| MIPS32 | `freg` | None | `$f0` | None | +| MIPS | `reg` | None | `$2` | None | +| MIPS | `freg` | None | `$f0` | None | | NVPTX | `reg16` | None | `rs0` | None | | NVPTX | `reg32` | None | `r0` | None | | NVPTX | `reg64` | None | `rd0` | None | diff --git a/src/test/assembly/asm/mips-types.rs b/src/test/assembly/asm/mips-types.rs index f37da849064..04e840dc166 100644 --- a/src/test/assembly/asm/mips-types.rs +++ b/src/test/assembly/asm/mips-types.rs @@ -1,6 +1,8 @@ // no-system-llvm +// revisions: mips32 mips64 // assembly-output: emit-asm -// compile-flags: --target mips-unknown-linux-gnu +//[mips32] compile-flags: --target mips-unknown-linux-gnu +//[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64 // needs-llvm-components: mips #![feature(no_core, lang_items, rustc_attrs, repr_simd)] @@ -32,7 +34,9 @@ impl Copy for i8 {} impl Copy for u8 {} impl Copy for i16 {} impl Copy for i32 {} +impl Copy for i64 {} impl Copy for f32 {} +impl Copy for f64 {} impl Copy for ptr {} extern "C" { fn extern_func(); @@ -44,168 +48,190 @@ extern "Rust" { fn dont_merge(s: &str); } -macro_rules! check { ($func:ident, $ty:ty, $class:ident) => { +macro_rules! check { ($func:ident, $ty:ty, $class:ident, $mov:literal) => { #[no_mangle] pub unsafe fn $func(x: $ty) -> $ty { dont_merge(stringify!($func)); let y; - asm!("move {}, {}", out($class) y, in($class) x); + asm!(concat!($mov," {}, {}"), out($class) y, in($class) x); y } };} -macro_rules! check_reg { ($func:ident, $ty:ty, $reg:tt) => { +macro_rules! check_reg { ($func:ident, $ty:ty, $reg:tt, $mov:literal) => { #[no_mangle] pub unsafe fn $func(x: $ty) -> $ty { dont_merge(stringify!($func)); let y; - asm!(concat!("move ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); y } };} -// CHECK-LABEL: sym_static: -// CHECK: #APP -// CHECK: lw $3, %got(extern_static) -// CHECK: #NO_APP +// mips32-LABEL: sym_static_32: +// mips32: #APP +// mips32: lw $3, %got(extern_static) +// mips32: #NO_APP +#[cfg(mips32)] #[no_mangle] -pub unsafe fn sym_static() { - asm!("la $v1, {}", sym extern_static); +pub unsafe fn sym_static_32() { + asm!("lw $v1, {}", sym extern_static); } -// CHECK-LABEL: sym_fn: -// CHECK: #APP -// CHECK: lw $3, %got(extern_func) -// CHECK: #NO_APP +// mips32-LABEL: sym_fn_32: +// mips32: #APP +// mips32: lw $3, %got(extern_func) +// mips32: #NO_APP +#[cfg(mips32)] #[no_mangle] -pub unsafe fn sym_fn() { - asm!("la $v1, {}", sym extern_func); +pub unsafe fn sym_fn_32() { + asm!("lw $v1, {}", sym extern_func); +} + +// mips64-LABEL: sym_static_64: +// mips64: #APP +// mips64: ld $3, %got_disp(extern_static) +// mips64: #NO_APP +#[cfg(mips64)] +#[no_mangle] +pub unsafe fn sym_static_64() { + asm!("ld $v1, {}", sym extern_static); +} + +// mips64-LABEL: sym_fn_64: +// mips64: #APP +// mips64: ld $3, %got_disp(extern_func) +// mips64: #NO_APP +#[cfg(mips64)] +#[no_mangle] +pub unsafe fn sym_fn_64() { + asm!("ld $v1, {}", sym extern_func); } // CHECK-LABEL: reg_f32: // CHECK: #APP // CHECK: mov.s $f{{[0-9]+}}, $f{{[0-9]+}} // CHECK: #NO_APP -#[no_mangle] -pub unsafe fn reg_f32(x: f32) -> f32 { - dont_merge("reg_f32"); - let y; - asm!("mov.s {}, {}", out(freg) y, in(freg) x); - y -} +check!(reg_f32, f32, freg, "mov.s"); // CHECK-LABEL: f0_f32: // CHECK: #APP // CHECK: mov.s $f0, $f0 // CHECK: #NO_APP #[no_mangle] -pub unsafe fn f0_f32(x: f32) -> f32 { - dont_merge("f0_f32"); - let y; - asm!("mov.s $f0, $f0", lateout("$f0") y, in("$f0") x); - y -} +check_reg!(f0_f32, f32, "$f0", "mov.s"); + +// CHECK-LABEL: reg_f32_64: +// CHECK: #APP +// CHECK: mov.d $f{{[0-9]+}}, $f{{[0-9]+}} +// CHECK: #NO_APP +check!(reg_f32_64, f32, freg, "mov.d"); + +// CHECK-LABEL: f0_f32_64: +// CHECK: #APP +// CHECK: mov.d $f0, $f0 +// CHECK: #NO_APP +#[no_mangle] +check_reg!(f0_f32_64, f32, "$f0", "mov.d"); // CHECK-LABEL: reg_f64: // CHECK: #APP // CHECK: mov.d $f{{[0-9]+}}, $f{{[0-9]+}} // CHECK: #NO_APP #[no_mangle] -pub unsafe fn reg_f64(x: f64) -> f64 { - dont_merge("reg_f64"); - let y; - asm!("mov.d {}, {}", out(freg) y, in(freg) x); - y -} +check!(reg_f64, f64, freg, "mov.d"); // CHECK-LABEL: f0_f64: // CHECK: #APP // CHECK: mov.d $f0, $f0 // CHECK: #NO_APP #[no_mangle] -pub unsafe fn f0_f64(x: f64) -> f64 { - dont_merge("f0_f64"); - let y; - asm!("mov.d $f0, $f0", lateout("$f0") y, in("$f0") x); - y -} +check_reg!(f0_f64, f64, "$f0", "mov.d"); // CHECK-LABEL: reg_ptr: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_ptr, ptr, reg); +check!(reg_ptr, ptr, reg, "move"); // CHECK-LABEL: reg_i32: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_i32, i32, reg); +check!(reg_i32, i32, reg, "move"); // CHECK-LABEL: reg_f32_soft: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_f32_soft, f32, reg); +check!(reg_f32_soft, f32, reg, "move"); + +// mips64-LABEL: reg_f64_soft: +// mips64: #APP +// mips64: move ${{[0-9]+}}, ${{[0-9]+}} +// mips64: #NO_APP +#[cfg(mips64)] +check!(reg_f64_soft, f64, reg, "move"); // CHECK-LABEL: reg_i8: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_i8, i8, reg); +check!(reg_i8, i8, reg, "move"); // CHECK-LABEL: reg_u8: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_u8, u8, reg); +check!(reg_u8, u8, reg, "move"); // CHECK-LABEL: reg_i16: // CHECK: #APP // CHECK: move ${{[0-9]+}}, ${{[0-9]+}} // CHECK: #NO_APP -check!(reg_i16, i16, reg); +check!(reg_i16, i16, reg, "move"); -// CHECK-LABEL: t0_ptr: +// mips64-LABEL: reg_i64: +// mips64: #APP +// mips64: move ${{[0-9]+}}, ${{[0-9]+}} +// mips64: #NO_APP +#[cfg(mips64)] +check!(reg_i64, i64, reg, "move"); + +// CHECK-LABEL: r8_ptr: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(t0_ptr, ptr, "$t0"); +check_reg!(r8_ptr, ptr, "$8", "move"); -// CHECK-LABEL: t0_i32: +// CHECK-LABEL: r8_i32: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(t0_i32, i32, "$t0"); +check_reg!(r8_i32, i32, "$8", "move"); -// CHECK-LABEL: t0_f32: +// CHECK-LABEL: r8_f32: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(t0_f32, f32, "$t0"); +check_reg!(r8_f32, f32, "$8", "move"); -// CHECK-LABEL: t0_i8: +// CHECK-LABEL: r8_i8: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(t0_i8, i8, "$t0"); +check_reg!(r8_i8, i8, "$8", "move"); -// CHECK-LABEL: t0_u8: +// CHECK-LABEL: r8_u8: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(t0_u8, u8, "$t0"); - -// CHECK-LABEL: t0_i16: -// CHECK: #APP -// CHECK: move $8, $8 -// CHECK: #NO_APP -check_reg!(t0_i16, i16, "$t0"); +check_reg!(r8_u8, u8, "$8", "move"); // CHECK-LABEL: r8_i16: // CHECK: #APP // CHECK: move $8, $8 // CHECK: #NO_APP -check_reg!(r8_i16, i16, "$8"); +check_reg!(r8_i16, i16, "$8", "move");