Skip to content

Commit 0ebbea6

Browse files
committed
Set signext or zeroext for integer arguments on RISC-V
1 parent 85f787e commit 0ebbea6

14 files changed

+247
-38
lines changed

compiler/rustc_target/src/callconv/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
728728
let spec = cx.target_spec();
729729
match &spec.arch[..] {
730730
"x86" => x86::compute_rust_abi_info(cx, self, abi),
731+
"riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi),
731732
_ => {}
732733
};
733734

compiler/rustc_target/src/callconv/riscv.rs

+27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
88
use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
99
use crate::spec::HasTargetSpec;
10+
use crate::spec::abi::Abi as SpecAbi;
1011

1112
#[derive(Copy, Clone)]
1213
enum RegPassKind {
@@ -365,3 +366,29 @@ where
365366
);
366367
}
367368
}
369+
370+
pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi)
371+
where
372+
Ty: TyAbiInterface<'a, C> + Copy,
373+
C: HasDataLayout + HasTargetSpec,
374+
{
375+
if abi == SpecAbi::RustIntrinsic {
376+
return;
377+
}
378+
379+
let xlen = cx.data_layout().pointer_size.bits();
380+
381+
for arg in fn_abi.args.iter_mut() {
382+
if arg.is_ignore() {
383+
continue;
384+
}
385+
386+
// LLVM integers types do not differentiate between signed or unsigned integers.
387+
// Some RISC-V instructions do not have a `.w` suffix version, they use all the
388+
// XLEN bits. By explicitly setting the `signext` or `zeroext` attribute
389+
// according to signedness to avoid unnecessary integer extending instructions.
390+
//
391+
// See https://github.com/rust-lang/rust/issues/114508 for details.
392+
extend_integer_width(arg, xlen);
393+
}
394+
}

tests/assembly/riscv64gc-zbb.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ assembly-output: emit-asm
2+
//@ compile-flags: --target riscv64gc-unknown-linux-gnu
3+
//@ compile-flags: -C target-feature=+zbb
4+
//@ compile-flags: -C opt-level=3
5+
//@ needs-llvm-components: riscv
6+
7+
#![crate_type = "lib"]
8+
9+
#[no_mangle]
10+
// CHECK-LABEL: issue_114508_u32:
11+
pub fn issue_114508_u32(a: u32, b: u32) -> u32 {
12+
// CHECK: maxu a0, a0, a1
13+
// CHECK-NEXT: ret
14+
u32::max(a, b)
15+
}
16+
17+
#[no_mangle]
18+
// CHECK-LABEL: issue_114508_i32:
19+
pub fn issue_114508_i32(a: i32, b: i32) -> i32 {
20+
// CHECK: max a0, a0, a1
21+
// CHECK-NEXT: ret
22+
i32::max(a, b)
23+
}

tests/codegen/checked_ilog.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
//@ compile-flags: -O
2+
//@ revisions: others riscv64
3+
4+
//@[others] ignore-riscv64
5+
//@[riscv64] only-riscv64
6+
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
7+
//@[riscv64] needs-llvm-components: riscv
28

39
#![crate_type = "lib"]
410

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

713
// CHECK-LABEL: @checked_ilog
8-
// CHECK-SAME: (i16 noundef %val, i16 noundef %base)
14+
// others-SAME: (i16 noundef %val, i16 noundef %base)
15+
// riscv64-SAME: (i16 noundef zeroext %val, i16 noundef zeroext %base)
916
#[no_mangle]
1017
pub fn checked_ilog(val: u16, base: u16) -> Option<u32> {
1118
// CHECK-NOT: udiv

tests/codegen/checked_math.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
//@ compile-flags: -O -Z merge-functions=disabled
2+
//@ revisions: others riscv64
3+
4+
//@[others] ignore-riscv64
5+
//@[riscv64] only-riscv64
6+
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
7+
//@[riscv64] needs-llvm-components: riscv
28

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

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

2835
// CHECK-LABEL: @checked_shl_unsigned
29-
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
36+
// others-SAME: (i32 noundef %a, i32 noundef %b)
37+
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
3038
#[no_mangle]
3139
pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
3240
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -41,7 +49,8 @@ pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
4149
}
4250

4351
// CHECK-LABEL: @checked_shr_unsigned
44-
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
52+
// others-SAME: (i32 noundef %a, i32 noundef %b)
53+
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
4554
#[no_mangle]
4655
pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
4756
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -56,7 +65,8 @@ pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
5665
}
5766

5867
// CHECK-LABEL: @checked_shl_signed
59-
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
68+
// others-SAME: (i32 noundef %a, i32 noundef %b)
69+
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
6070
#[no_mangle]
6171
pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
6272
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -71,7 +81,8 @@ pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
7181
}
7282

7383
// CHECK-LABEL: @checked_shr_signed
74-
// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
84+
// others-SAME: (i32 noundef %a, i32 noundef %b)
85+
// riscv64-SAME: (i32 noundef signext %a, i32 noundef signext %b)
7586
#[no_mangle]
7687
pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
7788
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -86,7 +97,8 @@ pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
8697
}
8798

8899
// CHECK-LABEL: @checked_add_one_unwrap_unsigned
89-
// CHECK-SAME: (i32 noundef %x)
100+
// others-SAME: (i32 noundef %x)
101+
// riscv64-SAME: (i32 noundef signext %x)
90102
#[no_mangle]
91103
pub fn checked_add_one_unwrap_unsigned(x: u32) -> u32 {
92104
// CHECK: %[[IS_MAX:.+]] = icmp eq i32 %x, -1

tests/codegen/comparison-operators-newtype.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
// in the operators for such a type all optimize away.
44

55
//@ compile-flags: -C opt-level=1
6+
//@ revisions: others riscv64
7+
8+
//@[others] ignore-riscv64
9+
//@[riscv64] only-riscv64
10+
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
11+
//@[riscv64] needs-llvm-components: riscv
612

713
#![crate_type = "lib"]
814

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

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

2330
// CHECK-LABEL: @check_le
24-
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
31+
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
32+
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
2533
#[no_mangle]
2634
pub fn check_le(a: Foo, b: Foo) -> bool {
2735
// CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
@@ -30,7 +38,8 @@ pub fn check_le(a: Foo, b: Foo) -> bool {
3038
}
3139

3240
// CHECK-LABEL: @check_gt
33-
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
41+
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
42+
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
3443
#[no_mangle]
3544
pub fn check_gt(a: Foo, b: Foo) -> bool {
3645
// CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
@@ -39,7 +48,8 @@ pub fn check_gt(a: Foo, b: Foo) -> bool {
3948
}
4049

4150
// CHECK-LABEL: @check_ge
42-
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
51+
// others-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
52+
// riscv64-SAME: (i16 noundef zeroext %[[A:.+]], i16 noundef zeroext %[[B:.+]])
4353
#[no_mangle]
4454
pub fn check_ge(a: Foo, b: Foo) -> bool {
4555
// CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]

tests/codegen/fewer-names.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
//@ compile-flags: -Coverflow-checks=no -O
2-
//@ revisions: YES NO
3-
//@ [YES]compile-flags: -Zfewer-names=yes
4-
//@ [NO] compile-flags: -Zfewer-names=no
2+
//@ revisions: YES NO riscv64-YES riscv64-NO
3+
//@ [YES] ignore-riscv64
4+
//@ [YES] compile-flags: -Zfewer-names=yes
5+
//@ [NO] ignore-riscv64
6+
//@ [NO] compile-flags: -Zfewer-names=no
7+
//@ [riscv64-YES] only-riscv64
8+
//@ [riscv64-YES] compile-flags: --target riscv64gc-unknown-linux-gnu
9+
//@ [riscv64-YES] compile-flags: -Zfewer-names=yes
10+
//@ [riscv64-YES] needs-llvm-components: riscv
11+
//@ [riscv64-NO] only-riscv64
12+
//@ [riscv64-NO] compile-flags: --target riscv64gc-unknown-linux-gnu
13+
//@ [riscv64-NO] compile-flags: -Zfewer-names=no
14+
//@ [riscv64-NO] needs-llvm-components: riscv
515
#![crate_type = "lib"]
616

717
#[no_mangle]
@@ -14,6 +24,15 @@ pub fn sum(x: u32, y: u32) -> u32 {
1424
// NO-NEXT: start:
1525
// NO-NEXT: %z = add i32 %y, %x
1626
// NO-NEXT: ret i32 %z
27+
28+
// riscv64-YES-LABEL: define{{.*}}i32 @sum(i32 noundef signext %0, i32 noundef signext %1)
29+
// riscv64-YES-NEXT: %3 = add i32 %1, %0
30+
// riscv64-YES-NEXT: ret i32 %3
31+
32+
// riscv64-NO-LABEL: define{{.*}}i32 @sum(i32 noundef signext %x, i32 noundef signext %y)
33+
// riscv64-NO-NEXT: start:
34+
// riscv64-NO-NEXT: %z = add i32 %y, %x
35+
// riscv64-NO-NEXT: ret i32 %z
1736
let z = x + y;
1837
z
1938
}

tests/codegen/function-arguments.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
//@ compile-flags: -O -C no-prepopulate-passes
2+
//@ revisions: others riscv64
3+
4+
//@[others] ignore-riscv64
5+
//@[riscv64] only-riscv64
6+
//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
7+
//@[riscv64] needs-llvm-components: riscv
8+
29
#![crate_type = "lib"]
310
#![feature(dyn_star)]
411
#![feature(allocator_api)]
@@ -32,7 +39,8 @@ pub fn boolean(x: bool) -> bool {
3239
x
3340
}
3441

35-
// CHECK: i8 @maybeuninit_boolean(i8 %x)
42+
// others: i8 @maybeuninit_boolean(i8 %x)
43+
// riscv64: i8 @maybeuninit_boolean(i8 zeroext %x)
3644
#[no_mangle]
3745
pub fn maybeuninit_boolean(x: MaybeUninit<bool>) -> MaybeUninit<bool> {
3846
x
@@ -44,19 +52,23 @@ pub fn enum_bool(x: MyBool) -> MyBool {
4452
x
4553
}
4654

47-
// CHECK: i8 @maybeuninit_enum_bool(i8 %x)
55+
// others: i8 @maybeuninit_enum_bool(i8 %x)
56+
// riscv64: i8 @maybeuninit_enum_bool(i8 zeroext %x)
4857
#[no_mangle]
4958
pub fn maybeuninit_enum_bool(x: MaybeUninit<MyBool>) -> MaybeUninit<MyBool> {
5059
x
5160
}
5261

53-
// CHECK: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef{{( range\(i32 0, 1114112\))?}} %x)
62+
// ignore-tidy-linelength
63+
// others: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef{{( range\(i32 0, 1114112\))?}} %x)
64+
// riscv64: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef signext{{( range\(i32 0, 1114112\))?}} %x)
5465
#[no_mangle]
5566
pub fn char(x: char) -> char {
5667
x
5768
}
5869

59-
// CHECK: i32 @maybeuninit_char(i32 %x)
70+
// others: i32 @maybeuninit_char(i32 %x)
71+
// riscv64: i32 @maybeuninit_char(i32 signext %x)
6072
#[no_mangle]
6173
pub fn maybeuninit_char(x: MaybeUninit<char>) -> MaybeUninit<char> {
6274
x

tests/codegen/intrinsics/three_way_compare.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
//@ revisions: DEBUG OPTIM
1+
//@ revisions: DEBUG OPTIM riscv64-DEBUG riscv64-OPTIM
2+
//@ [DEBUG] ignore-riscv64
23
//@ [DEBUG] compile-flags: -C opt-level=0
4+
//@ [OPTIM] ignore-riscv64
35
//@ [OPTIM] compile-flags: -C opt-level=3
6+
//@ [riscv64-DEBUG] only-riscv64
7+
//@ [riscv64-DEBUG] compile-flags: --target riscv64gc-unknown-linux-gnu
8+
//@ [riscv64-DEBUG] compile-flags: -C opt-level=0
9+
//@ [riscv64-DEBUG] needs-llvm-components: riscv
10+
//@ [riscv64-OPTIM] only-riscv64
11+
//@ [riscv64-OPTIM] compile-flags: --target riscv64gc-unknown-linux-gnu
12+
//@ [riscv64-OPTIM] compile-flags: -C opt-level=3
13+
//@ [riscv64-OPTIM] needs-llvm-components: riscv
414
//@ compile-flags: -C no-prepopulate-passes
515

616
#![crate_type = "lib"]
@@ -12,6 +22,8 @@ use std::intrinsics::three_way_compare;
1222
// CHECK-LABEL: @signed_cmp
1323
// DEBUG-SAME: (i16 %a, i16 %b)
1424
// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
25+
// riscv64-DEBUG-SAME: (i16 signext %a, i16 signext %b)
26+
// riscv64-OPTIM-SAME: (i16 noundef signext %a, i16 noundef signext %b)
1527
pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
1628
// DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
1729
// DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
@@ -24,13 +36,27 @@ pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
2436
// OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
2537
// OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
2638
// OPTIM: ret i8 %[[CGEL]]
39+
40+
// riscv64-DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
41+
// riscv64-DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
42+
// riscv64-DEBUG: %[[LT:.+]] = icmp slt i16 %a, %b
43+
// riscv64-DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
44+
// riscv64-DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]
45+
46+
// riscv64-OPTIM: %[[LT:.+]] = icmp slt i16 %a, %b
47+
// riscv64-OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
48+
// riscv64-OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
49+
// riscv64-OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
50+
// riscv64-OPTIM: ret i8 %[[CGEL]]
2751
three_way_compare(a, b)
2852
}
2953

3054
#[no_mangle]
3155
// CHECK-LABEL: @unsigned_cmp
3256
// DEBUG-SAME: (i16 %a, i16 %b)
3357
// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
58+
// riscv64-DEBUG-SAME: (i16 zeroext %a, i16 zeroext %b)
59+
// riscv64-OPTIM-SAME: (i16 noundef zeroext %a, i16 noundef zeroext %b)
3460
pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
3561
// DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
3662
// DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
@@ -43,5 +69,17 @@ pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
4369
// OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
4470
// OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
4571
// OPTIM: ret i8 %[[CGEL]]
72+
73+
// riscv64-DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
74+
// riscv64-DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
75+
// riscv64-DEBUG: %[[LT:.+]] = icmp ult i16 %a, %b
76+
// riscv64-DEBUG: %[[ZLT:.+]] = zext i1 %[[LT]] to i8
77+
// riscv64-DEBUG: %[[R:.+]] = sub nsw i8 %[[ZGT]], %[[ZLT]]
78+
79+
// riscv64-OPTIM: %[[LT:.+]] = icmp ult i16 %a, %b
80+
// riscv64-OPTIM: %[[NE:.+]] = icmp ne i16 %a, %b
81+
// riscv64-OPTIM: %[[CGE:.+]] = select i1 %[[NE]], i8 1, i8 0
82+
// riscv64-OPTIM: %[[CGEL:.+]] = select i1 %[[LT]], i8 -1, i8 %[[CGE]]
83+
// riscv64-OPTIM: ret i8 %[[CGEL]]
4684
three_way_compare(a, b)
4785
}

0 commit comments

Comments
 (0)