Skip to content

Commit

Permalink
Set signext or zeroext for integer arguments on RISC-V
Browse files Browse the repository at this point in the history
  • Loading branch information
SpriteOvO committed Oct 20, 2024
1 parent 85f787e commit 0ebbea6
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 38 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
let spec = cx.target_spec();
match &spec.arch[..] {
"x86" => x86::compute_rust_abi_info(cx, self, abi),
"riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi),
_ => {}
};

Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_target/src/callconv/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
use crate::spec::HasTargetSpec;
use crate::spec::abi::Abi as SpecAbi;

#[derive(Copy, Clone)]
enum RegPassKind {
Expand Down Expand Up @@ -365,3 +366,29 @@ where
);
}
}

pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout + HasTargetSpec,
{
if abi == SpecAbi::RustIntrinsic {
return;
}

let xlen = cx.data_layout().pointer_size.bits();

for arg in fn_abi.args.iter_mut() {
if arg.is_ignore() {
continue;
}

// LLVM integers types do not differentiate between signed or unsigned integers.
// Some RISC-V instructions do not have a `.w` suffix version, they use all the
// XLEN bits. By explicitly setting the `signext` or `zeroext` attribute
// according to signedness to avoid unnecessary integer extending instructions.
//
// See https://github.com/rust-lang/rust/issues/114508 for details.
extend_integer_width(arg, xlen);
}
}
23 changes: 23 additions & 0 deletions tests/assembly/riscv64gc-zbb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ assembly-output: emit-asm
//@ compile-flags: --target riscv64gc-unknown-linux-gnu
//@ compile-flags: -C target-feature=+zbb
//@ compile-flags: -C opt-level=3
//@ needs-llvm-components: riscv

#![crate_type = "lib"]

#[no_mangle]
// CHECK-LABEL: issue_114508_u32:
pub fn issue_114508_u32(a: u32, b: u32) -> u32 {
// CHECK: maxu a0, a0, a1
// CHECK-NEXT: ret
u32::max(a, b)
}

#[no_mangle]
// CHECK-LABEL: issue_114508_i32:
pub fn issue_114508_i32(a: i32, b: i32) -> i32 {
// CHECK: max a0, a0, a1
// CHECK-NEXT: ret
i32::max(a, b)
}
9 changes: 8 additions & 1 deletion tests/codegen/checked_ilog.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
//@ compile-flags: -O
//@ revisions: others riscv64

//@[others] ignore-riscv64
//@[riscv64] only-riscv64
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
//@[riscv64] needs-llvm-components: riscv

#![crate_type = "lib"]

// Ensure that when val < base, we do not divide or multiply.

// CHECK-LABEL: @checked_ilog
// CHECK-SAME: (i16 noundef %val, i16 noundef %base)
// others-SAME: (i16 noundef %val, i16 noundef %base)
// riscv64-SAME: (i16 noundef zeroext %val, i16 noundef zeroext %base)
#[no_mangle]
pub fn checked_ilog(val: u16, base: u16) -> Option<u32> {
// CHECK-NOT: udiv
Expand Down
24 changes: 18 additions & 6 deletions tests/codegen/checked_math.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
//@ compile-flags: -O -Z merge-functions=disabled
//@ revisions: others riscv64

//@[others] ignore-riscv64
//@[riscv64] only-riscv64
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
//@[riscv64] needs-llvm-components: riscv

#![crate_type = "lib"]
#![feature(unchecked_shifts)]
Expand All @@ -8,7 +14,8 @@
// Thanks to poison semantics, this doesn't even need branches.

// CHECK-LABEL: @checked_sub_unsigned
// CHECK-SAME: (i16 noundef %a, i16 noundef %b)
// others-SAME: (i16 noundef %a, i16 noundef %b)
// riscv64-SAME: (i16 noundef zeroext %a, i16 noundef zeroext %b)
#[no_mangle]
pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
// CHECK-DAG: %[[IS_SOME:.+]] = icmp uge i16 %a, %b
Expand All @@ -26,7 +33,8 @@ pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
// looking for no-wrap flags, we just need there to not be any masking.

// CHECK-LABEL: @checked_shl_unsigned
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
// others-SAME: (i32 noundef %a, i32 noundef %b)
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
#[no_mangle]
pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
Expand All @@ -41,7 +49,8 @@ pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
}

// CHECK-LABEL: @checked_shr_unsigned
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
// others-SAME: (i32 noundef %a, i32 noundef %b)
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
#[no_mangle]
pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
Expand All @@ -56,7 +65,8 @@ pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
}

// CHECK-LABEL: @checked_shl_signed
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
// others-SAME: (i32 noundef %a, i32 noundef %b)
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
#[no_mangle]
pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
Expand All @@ -71,7 +81,8 @@ pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
}

// CHECK-LABEL: @checked_shr_signed
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
// others-SAME: (i32 noundef %a, i32 noundef %b)
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
#[no_mangle]
pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
Expand All @@ -86,7 +97,8 @@ pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
}

// CHECK-LABEL: @checked_add_one_unwrap_unsigned
// CHECK-SAME: (i32 noundef %x)
// others-SAME: (i32 noundef %x)
// riscv64-SAME: (i32 noundef signext %x)
#[no_mangle]
pub fn checked_add_one_unwrap_unsigned(x: u32) -> u32 {
// CHECK: %[[IS_MAX:.+]] = icmp eq i32 %x, -1
Expand Down
18 changes: 14 additions & 4 deletions tests/codegen/comparison-operators-newtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
// in the operators for such a type all optimize away.

//@ compile-flags: -C opt-level=1
//@ revisions: others riscv64

//@[others] ignore-riscv64
//@[riscv64] only-riscv64
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
//@[riscv64] needs-llvm-components: riscv

#![crate_type = "lib"]

Expand All @@ -12,7 +18,8 @@ use std::cmp::Ordering;
pub struct Foo(u16);

// CHECK-LABEL: @check_lt
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
#[no_mangle]
pub fn check_lt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ult i16 %[[A]], %[[B]]
Expand All @@ -21,7 +28,8 @@ pub fn check_lt(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_le
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
#[no_mangle]
pub fn check_le(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
Expand All @@ -30,7 +38,8 @@ pub fn check_le(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_gt
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
#[no_mangle]
pub fn check_gt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
Expand All @@ -39,7 +48,8 @@ pub fn check_gt(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_ge
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
#[no_mangle]
pub fn check_ge(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]
Expand Down
25 changes: 22 additions & 3 deletions tests/codegen/fewer-names.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
//@ compile-flags: -Coverflow-checks=no -O
//@ revisions: YES NO
//@ [YES]compile-flags: -Zfewer-names=yes
//@ [NO] compile-flags: -Zfewer-names=no
//@ revisions: YES NO riscv64-YES riscv64-NO
//@ [YES] ignore-riscv64
//@ [YES] compile-flags: -Zfewer-names=yes
//@ [NO] ignore-riscv64
//@ [NO] compile-flags: -Zfewer-names=no
//@ [riscv64-YES] only-riscv64
//@ [riscv64-YES] compile-flags: --target riscv64gc-unknown-linux-gnu
//@ [riscv64-YES] compile-flags: -Zfewer-names=yes
//@ [riscv64-YES] needs-llvm-components: riscv
//@ [riscv64-NO] only-riscv64
//@ [riscv64-NO] compile-flags: --target riscv64gc-unknown-linux-gnu
//@ [riscv64-NO] compile-flags: -Zfewer-names=no
//@ [riscv64-NO] needs-llvm-components: riscv
#![crate_type = "lib"]

#[no_mangle]
Expand All @@ -14,6 +24,15 @@ pub fn sum(x: u32, y: u32) -> u32 {
// NO-NEXT: start:
// NO-NEXT: %z = add i32 %y, %x
// NO-NEXT: ret i32 %z

// riscv64-YES-LABEL: define{{.*}}i32 @sum(i32 noundef signext %0, i32 noundef signext %1)
// riscv64-YES-NEXT: %3 = add i32 %1, %0
// riscv64-YES-NEXT: ret i32 %3

// riscv64-NO-LABEL: define{{.*}}i32 @sum(i32 noundef signext %x, i32 noundef signext %y)
// riscv64-NO-NEXT: start:
// riscv64-NO-NEXT: %z = add i32 %y, %x
// riscv64-NO-NEXT: ret i32 %z
let z = x + y;
z
}
20 changes: 16 additions & 4 deletions tests/codegen/function-arguments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
//@ compile-flags: -O -C no-prepopulate-passes
//@ revisions: others riscv64

//@[others] ignore-riscv64
//@[riscv64] only-riscv64
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
//@[riscv64] needs-llvm-components: riscv

#![crate_type = "lib"]
#![feature(dyn_star)]
#![feature(allocator_api)]
Expand Down Expand Up @@ -32,7 +39,8 @@ pub fn boolean(x: bool) -> bool {
x
}

// CHECK: i8 @maybeuninit_boolean(i8 %x)
// others: i8 @maybeuninit_boolean(i8 %x)
// riscv64: i8 @maybeuninit_boolean(i8 zeroext %x)
#[no_mangle]
pub fn maybeuninit_boolean(x: MaybeUninit<bool>) -> MaybeUninit<bool> {
x
Expand All @@ -44,19 +52,23 @@ pub fn enum_bool(x: MyBool) -> MyBool {
x
}

// CHECK: i8 @maybeuninit_enum_bool(i8 %x)
// others: i8 @maybeuninit_enum_bool(i8 %x)
// riscv64: i8 @maybeuninit_enum_bool(i8 zeroext %x)
#[no_mangle]
pub fn maybeuninit_enum_bool(x: MaybeUninit<MyBool>) -> MaybeUninit<MyBool> {
x
}

// CHECK: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef{{( range\(i32 0, 1114112\))?}} %x)
// ignore-tidy-linelength
// others: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef{{( range\(i32 0, 1114112\))?}} %x)
// riscv64: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef signext{{( range\(i32 0, 1114112\))?}} %x)
#[no_mangle]
pub fn char(x: char) -> char {
x
}

// CHECK: i32 @maybeuninit_char(i32 %x)
// others: i32 @maybeuninit_char(i32 %x)
// riscv64: i32 @maybeuninit_char(i32 signext %x)
#[no_mangle]
pub fn maybeuninit_char(x: MaybeUninit<char>) -> MaybeUninit<char> {
x
Expand Down
40 changes: 39 additions & 1 deletion tests/codegen/intrinsics/three_way_compare.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
//@ revisions: DEBUG OPTIM
//@ revisions: DEBUG OPTIM riscv64-DEBUG riscv64-OPTIM
//@ [DEBUG] ignore-riscv64
//@ [DEBUG] compile-flags: -C opt-level=0
//@ [OPTIM] ignore-riscv64
//@ [OPTIM] compile-flags: -C opt-level=3
//@ [riscv64-DEBUG] only-riscv64
//@ [riscv64-DEBUG] compile-flags: --target riscv64gc-unknown-linux-gnu
//@ [riscv64-DEBUG] compile-flags: -C opt-level=0
//@ [riscv64-DEBUG] needs-llvm-components: riscv
//@ [riscv64-OPTIM] only-riscv64
//@ [riscv64-OPTIM] compile-flags: --target riscv64gc-unknown-linux-gnu
//@ [riscv64-OPTIM] compile-flags: -C opt-level=3
//@ [riscv64-OPTIM] needs-llvm-components: riscv
//@ compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]
Expand All @@ -12,6 +22,8 @@ use std::intrinsics::three_way_compare;
// CHECK-LABEL: @signed_cmp
// DEBUG-SAME: (i16 %a, i16 %b)
// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
// riscv64-DEBUG-SAME: (i16 signext %a, i16 signext %b)
// riscv64-OPTIM-SAME: (i16 noundef signext %a, i16 noundef signext %b)
pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
// DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
// DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
Expand All @@ -24,13 +36,27 @@ pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
// OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
// OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
// OPTIM: ret i8 %[[CGEL]]

// riscv64-DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
// riscv64-DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
// riscv64-DEBUG: %[[LT:.+]] = icmp slt i16 %a, %b
// riscv64-DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
// riscv64-DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]

// riscv64-OPTIM: %[[LT:.+]] = icmp slt i16 %a, %b
// riscv64-OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
// riscv64-OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
// riscv64-OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
// riscv64-OPTIM: ret i8 %[[CGEL]]
three_way_compare(a, b)
}

#[no_mangle]
// CHECK-LABEL: @unsigned_cmp
// DEBUG-SAME: (i16 %a, i16 %b)
// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
// riscv64-DEBUG-SAME: (i16 zeroext %a, i16 zeroext %b)
// riscv64-OPTIM-SAME: (i16 noundef zeroext %a, i16 noundef zeroext %b)
pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
// DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
// DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
Expand All @@ -43,5 +69,17 @@ pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
// OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
// OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
// OPTIM: ret i8 %[[CGEL]]

// riscv64-DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
// riscv64-DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
// riscv64-DEBUG: %[[LT:.+]] = icmp ult i16 %a, %b
// riscv64-DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
// riscv64-DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]

// riscv64-OPTIM: %[[LT:.+]] = icmp ult i16 %a, %b
// riscv64-OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
// riscv64-OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
// riscv64-OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
// riscv64-OPTIM: ret i8 %[[CGEL]]
three_way_compare(a, b)
}
Loading

0 comments on commit 0ebbea6

Please sign in to comment.