Skip to content

Commit 3cbb932

Browse files
committed
Auto merge of rust-lang#121668 - erikdesjardins:commonprim, r=scottmcm,oli-obk
Represent `Result<usize, Box<T>>` as ScalarPair(i64, ptr) This allows types like `Result<usize, std::io::Error>` (and integers of differing sign, e.g. `Result<u64, i64>`) to be passed in a pair of registers instead of through memory, like `Result<u64, u64>` or `Result<Box<T>, Box<U>>` are today. Fixes rust-lang#97540. r? `@ghost`
2 parents 184c5ab + 9f55200 commit 3cbb932

File tree

7 files changed

+222
-19
lines changed

7 files changed

+222
-19
lines changed

compiler/rustc_abi/src/layout.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -816,15 +816,37 @@ where
816816
break;
817817
}
818818
};
819-
if let Some(pair) = common_prim {
820-
// This is pretty conservative. We could go fancier
821-
// by conflating things like i32 and u32, or even
822-
// realising that (u8, u8) could just cohabit with
823-
// u16 or even u32.
824-
if pair != (prim, offset) {
819+
if let Some((old_prim, common_offset)) = common_prim {
820+
// All variants must be at the same offset
821+
if offset != common_offset {
825822
common_prim = None;
826823
break;
827824
}
825+
// This is pretty conservative. We could go fancier
826+
// by realising that (u8, u8) could just cohabit with
827+
// u16 or even u32.
828+
let new_prim = match (old_prim, prim) {
829+
// Allow all identical primitives.
830+
(x, y) if x == y => x,
831+
// Allow integers of the same size with differing signedness.
832+
// We arbitrarily choose the signedness of the first variant.
833+
(p @ Primitive::Int(x, _), Primitive::Int(y, _)) if x == y => p,
834+
// Allow integers mixed with pointers of the same layout.
835+
// We must represent this using a pointer, to avoid
836+
// roundtripping pointers through ptrtoint/inttoptr.
837+
(p @ Primitive::Pointer(_), i @ Primitive::Int(..))
838+
| (i @ Primitive::Int(..), p @ Primitive::Pointer(_))
839+
if p.size(dl) == i.size(dl) && p.align(dl) == i.align(dl) =>
840+
{
841+
p
842+
}
843+
_ => {
844+
common_prim = None;
845+
break;
846+
}
847+
};
848+
// We may be updating the primitive here, for example from int->ptr.
849+
common_prim = Some((new_prim, common_offset));
828850
} else {
829851
common_prim = Some((prim, offset));
830852
}

tests/codegen/common_prim_int_ptr.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//@ compile-flags: -O
2+
3+
#![crate_type = "lib"]
4+
#![feature(core_intrinsics)]
5+
6+
// Tests that codegen works properly when enums like `Result<usize, Box<()>>`
7+
// are represented as `{ u64, ptr }`, i.e., for `Ok(123)`, `123` is stored
8+
// as a pointer.
9+
10+
// CHECK-LABEL: @insert_int
11+
#[no_mangle]
12+
pub fn insert_int(x: usize) -> Result<usize, Box<()>> {
13+
// CHECK: start:
14+
// CHECK-NEXT: inttoptr i{{[0-9]+}} %x to ptr
15+
// CHECK-NEXT: insertvalue
16+
// CHECK-NEXT: ret { i{{[0-9]+}}, ptr }
17+
Ok(x)
18+
}
19+
20+
// CHECK-LABEL: @insert_box
21+
#[no_mangle]
22+
pub fn insert_box(x: Box<()>) -> Result<usize, Box<()>> {
23+
// CHECK: start:
24+
// CHECK-NEXT: insertvalue { i{{[0-9]+}}, ptr }
25+
// CHECK-NEXT: ret
26+
Err(x)
27+
}
28+
29+
// CHECK-LABEL: @extract_int
30+
// CHECK-NOT: nonnull
31+
// CHECK-SAME: (i{{[0-9]+}} {{[^,]+}} [[DISCRIMINANT:%[0-9]+]], ptr {{[^,]+}} [[PAYLOAD:%[0-9]+]])
32+
#[no_mangle]
33+
pub unsafe fn extract_int(x: Result<usize, Box<()>>) -> usize {
34+
// CHECK: [[TEMP:%.+]] = ptrtoint ptr [[PAYLOAD]] to [[USIZE:i[0-9]+]]
35+
// CHECK: ret [[USIZE]] [[TEMP]]
36+
match x {
37+
Ok(v) => v,
38+
Err(_) => std::intrinsics::unreachable(),
39+
}
40+
}
41+
42+
// CHECK-LABEL: @extract_box
43+
// CHECK-SAME: (i{{[0-9]+}} {{[^,]+}} [[DISCRIMINANT:%[0-9]+]], ptr {{[^,]+}} [[PAYLOAD:%[0-9]+]])
44+
#[no_mangle]
45+
pub unsafe fn extract_box(x: Result<usize, Box<i32>>) -> Box<i32> {
46+
// CHECK: ret ptr [[PAYLOAD]]
47+
match x {
48+
Ok(_) => std::intrinsics::unreachable(),
49+
Err(e) => e,
50+
}
51+
}

tests/codegen/try_question_mark_nop.rs

+92-12
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,41 @@
44
#![crate_type = "lib"]
55
#![feature(try_blocks)]
66

7-
// These are now NOPs in LLVM 15, presumably thanks to nikic's change mentioned in
8-
// <https://github.com/rust-lang/rust/issues/85133#issuecomment-1072168354>.
9-
// Unfortunately, as of 2022-08-17 they're not yet nops for `u64`s nor `Option`.
10-
117
use std::ops::ControlFlow::{self, Continue, Break};
8+
use std::ptr::NonNull;
9+
10+
// CHECK-LABEL: @option_nop_match_32
11+
#[no_mangle]
12+
pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> {
13+
// CHECK: start:
14+
// CHECK-NEXT: insertvalue { i32, i32 }
15+
// CHECK-NEXT: insertvalue { i32, i32 }
16+
// CHECK-NEXT: ret { i32, i32 }
17+
match x {
18+
Some(x) => Some(x),
19+
None => None,
20+
}
21+
}
22+
23+
// CHECK-LABEL: @option_nop_traits_32
24+
#[no_mangle]
25+
pub fn option_nop_traits_32(x: Option<u32>) -> Option<u32> {
26+
// CHECK: start:
27+
// CHECK-NEXT: insertvalue { i32, i32 }
28+
// CHECK-NEXT: insertvalue { i32, i32 }
29+
// CHECK-NEXT: ret { i32, i32 }
30+
try {
31+
x?
32+
}
33+
}
1234

1335
// CHECK-LABEL: @result_nop_match_32
1436
#[no_mangle]
1537
pub fn result_nop_match_32(x: Result<i32, u32>) -> Result<i32, u32> {
16-
// CHECK: start
17-
// CHECK-NEXT: ret i64 %0
38+
// CHECK: start:
39+
// CHECK-NEXT: insertvalue { i32, i32 }
40+
// CHECK-NEXT: insertvalue { i32, i32 }
41+
// CHECK-NEXT: ret { i32, i32 }
1842
match x {
1943
Ok(x) => Ok(x),
2044
Err(x) => Err(x),
@@ -24,8 +48,60 @@ pub fn result_nop_match_32(x: Result<i32, u32>) -> Result<i32, u32> {
2448
// CHECK-LABEL: @result_nop_traits_32
2549
#[no_mangle]
2650
pub fn result_nop_traits_32(x: Result<i32, u32>) -> Result<i32, u32> {
27-
// CHECK: start
28-
// CHECK-NEXT: ret i64 %0
51+
// CHECK: start:
52+
// CHECK-NEXT: insertvalue { i32, i32 }
53+
// CHECK-NEXT: insertvalue { i32, i32 }
54+
// CHECK-NEXT: ret { i32, i32 }
55+
try {
56+
x?
57+
}
58+
}
59+
60+
// CHECK-LABEL: @result_nop_match_64
61+
#[no_mangle]
62+
pub fn result_nop_match_64(x: Result<i64, u64>) -> Result<i64, u64> {
63+
// CHECK: start:
64+
// CHECK-NEXT: insertvalue { i64, i64 }
65+
// CHECK-NEXT: insertvalue { i64, i64 }
66+
// CHECK-NEXT: ret { i64, i64 }
67+
match x {
68+
Ok(x) => Ok(x),
69+
Err(x) => Err(x),
70+
}
71+
}
72+
73+
// CHECK-LABEL: @result_nop_traits_64
74+
#[no_mangle]
75+
pub fn result_nop_traits_64(x: Result<i64, u64>) -> Result<i64, u64> {
76+
// CHECK: start:
77+
// CHECK-NEXT: insertvalue { i64, i64 }
78+
// CHECK-NEXT: insertvalue { i64, i64 }
79+
// CHECK-NEXT: ret { i64, i64 }
80+
try {
81+
x?
82+
}
83+
}
84+
85+
// CHECK-LABEL: @result_nop_match_ptr
86+
#[no_mangle]
87+
pub fn result_nop_match_ptr(x: Result<usize, Box<()>>) -> Result<usize, Box<()>> {
88+
// CHECK: start:
89+
// CHECK-NEXT: insertvalue { i{{[0-9]+}}, ptr }
90+
// CHECK-NEXT: insertvalue { i{{[0-9]+}}, ptr }
91+
// CHECK-NEXT: ret
92+
match x {
93+
Ok(x) => Ok(x),
94+
Err(x) => Err(x),
95+
}
96+
}
97+
98+
// CHECK-LABEL: @result_nop_traits_ptr
99+
#[no_mangle]
100+
pub fn result_nop_traits_ptr(x: Result<u64, NonNull<()>>) -> Result<u64, NonNull<()>> {
101+
// CHECK: start:
102+
// CHECK-NEXT: insertvalue { i{{[0-9]+}}, ptr }
103+
// CHECK-NEXT: insertvalue { i{{[0-9]+}}, ptr }
104+
// CHECK-NEXT: ret
29105
try {
30106
x?
31107
}
@@ -34,8 +110,10 @@ pub fn result_nop_traits_32(x: Result<i32, u32>) -> Result<i32, u32> {
34110
// CHECK-LABEL: @control_flow_nop_match_32
35111
#[no_mangle]
36112
pub fn control_flow_nop_match_32(x: ControlFlow<i32, u32>) -> ControlFlow<i32, u32> {
37-
// CHECK: start
38-
// CHECK-NEXT: ret i64 %0
113+
// CHECK: start:
114+
// CHECK-NEXT: insertvalue { i32, i32 }
115+
// CHECK-NEXT: insertvalue { i32, i32 }
116+
// CHECK-NEXT: ret { i32, i32 }
39117
match x {
40118
Continue(x) => Continue(x),
41119
Break(x) => Break(x),
@@ -45,8 +123,10 @@ pub fn control_flow_nop_match_32(x: ControlFlow<i32, u32>) -> ControlFlow<i32, u
45123
// CHECK-LABEL: @control_flow_nop_traits_32
46124
#[no_mangle]
47125
pub fn control_flow_nop_traits_32(x: ControlFlow<i32, u32>) -> ControlFlow<i32, u32> {
48-
// CHECK: start
49-
// CHECK-NEXT: ret i64 %0
126+
// CHECK: start:
127+
// CHECK-NEXT: insertvalue { i32, i32 }
128+
// CHECK-NEXT: insertvalue { i32, i32 }
129+
// CHECK-NEXT: ret { i32, i32 }
50130
try {
51131
x?
52132
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN"
2+
//@ normalize-stderr-test "Int\(I[0-9]+," -> "Int(I?,"
3+
//@ normalize-stderr-test "valid_range: 0..=[0-9]+" -> "valid_range: $$VALID_RANGE"
4+
5+
//! Enum layout tests related to scalar pairs with an int/ptr common primitive.
6+
7+
#![feature(rustc_attrs)]
8+
#![feature(never_type)]
9+
#![crate_type = "lib"]
10+
11+
#[rustc_layout(abi)]
12+
enum ScalarPairPointerWithInt { //~ERROR: abi: ScalarPair
13+
A(usize),
14+
B(Box<()>),
15+
}
16+
17+
// Negative test--ensure that pointers are not commoned with integers
18+
// of a different size. (Assumes that no target has 8 bit pointers, which
19+
// feels pretty safe.)
20+
#[rustc_layout(abi)]
21+
enum NotScalarPairPointerWithSmallerInt { //~ERROR: abi: Aggregate
22+
A(u8),
23+
B(Box<()>),
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: abi: ScalarPair(Initialized { value: Int(I?, false), valid_range: $VALID_RANGE }, Initialized { value: Pointer(AddressSpace(0)), valid_range: $VALID_RANGE })
2+
--> $DIR/enum-scalar-pair-int-ptr.rs:12:1
3+
|
4+
LL | enum ScalarPairPointerWithInt {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: abi: Aggregate { sized: true }
8+
--> $DIR/enum-scalar-pair-int-ptr.rs:21:1
9+
|
10+
LL | enum NotScalarPairPointerWithSmallerInt {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+

tests/ui/layout/enum.rs

+6
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ enum UninhabitedVariantSpace { //~ERROR: size: Size(16 bytes)
1616
A,
1717
B([u8; 15], !), // make sure there is space being reserved for this field.
1818
}
19+
20+
#[rustc_layout(abi)]
21+
enum ScalarPairDifferingSign { //~ERROR: abi: ScalarPair
22+
A(u8),
23+
B(i8),
24+
}

tests/ui/layout/enum.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,11 @@ error: size: Size(16 bytes)
1010
LL | enum UninhabitedVariantSpace {
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

13-
error: aborting due to 2 previous errors
13+
error: abi: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=1 }, Initialized { value: Int(I8, false), valid_range: 0..=255 })
14+
--> $DIR/enum.rs:21:1
15+
|
16+
LL | enum ScalarPairDifferingSign {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
1420

0 commit comments

Comments
 (0)