Skip to content

Commit 37a7521

Browse files
committed
rustc: unpack scalar newtype layout ABIs.
1 parent 0b86972 commit 37a7521

File tree

7 files changed

+129
-50
lines changed

7 files changed

+129
-50
lines changed

src/librustc/ty/layout.rs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,30 @@ impl<'a, 'tcx> CachedLayout {
10781078
packed
10791079
};
10801080

1081+
// Unpack newtype ABIs.
1082+
if sized && optimize && size.bytes() > 0 {
1083+
// All but one field must be ZSTs, and so they all start at 0.
1084+
if offsets.iter().all(|o| o.bytes() == 0) {
1085+
let mut non_zst_fields = fields.iter().filter(|f| !f.is_zst());
1086+
1087+
// We have exactly one non-ZST field.
1088+
match (non_zst_fields.next(), non_zst_fields.next()) {
1089+
(Some(field), None) => {
1090+
// Field size match and it has a scalar ABI.
1091+
if size == field.size {
1092+
match field.abi {
1093+
Abi::Scalar(_) => {
1094+
abi = field.abi.clone();
1095+
}
1096+
_ => {}
1097+
}
1098+
}
1099+
}
1100+
_ => {}
1101+
}
1102+
}
1103+
}
1104+
10811105
// Look for a scalar pair, as an ABI optimization.
10821106
// FIXME(eddyb) ignore extra ZST fields and field ordering.
10831107
if sized && !packed && fields.len() == 2 {
@@ -1424,6 +1448,18 @@ impl<'a, 'tcx> CachedLayout {
14241448

14251449
let mut st = univariant_uninterned(&variants[v], &def.repr, kind)?;
14261450
st.variants = Variants::Single { index: v };
1451+
// Exclude 0 from the range of a newtype ABI NonZero<T>.
1452+
if Some(def.did) == cx.tcx().lang_items().non_zero() {
1453+
match st.abi {
1454+
Abi::Scalar(ref mut scalar) |
1455+
Abi::ScalarPair(ref mut scalar, _) => {
1456+
if scalar.valid_range.start == 0 {
1457+
scalar.valid_range.start = 1;
1458+
}
1459+
}
1460+
_ => {}
1461+
}
1462+
}
14271463
return Ok(tcx.intern_layout(st));
14281464
}
14291465

@@ -2284,20 +2320,6 @@ impl<'a, 'tcx> TyLayout<'tcx> {
22842320
};
22852321
}
22862322

2287-
// Is this the NonZero lang item wrapping a pointer or integer type?
2288-
if let ty::TyAdt(def, _) = self.ty.sty {
2289-
if Some(def.did) == cx.tcx().lang_items().non_zero() {
2290-
let field = self.field(cx, 0)?;
2291-
let offset = self.fields.offset(0);
2292-
if let Abi::Scalar(Scalar { value, ref valid_range }) = field.abi {
2293-
return Ok(Some((offset, Scalar {
2294-
value,
2295-
valid_range: 0..=valid_range.end
2296-
}, 0)));
2297-
}
2298-
}
2299-
}
2300-
23012323
// Perhaps one of the fields is non-zero, let's recurse and find out.
23022324
if let FieldPlacement::Union(_) = self.fields {
23032325
// Only Rust enums have safe-to-inspect fields

src/librustc_trans/mir/analyze.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
139139
let ccx = self.cx.ccx;
140140

141141
if let mir::Lvalue::Projection(ref proj) = *lvalue {
142-
// Allow uses of projections that are ZSTs or from immediate scalar fields.
142+
// Allow uses of projections that are ZSTs or from scalar fields.
143143
if let LvalueContext::Consume = context {
144144
let base_ty = proj.base.ty(self.cx.mir, ccx.tcx());
145145
let base_ty = self.cx.monomorphize(&base_ty);
@@ -153,7 +153,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
153153

154154
if let mir::ProjectionElem::Field(..) = proj.elem {
155155
let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx()));
156-
if layout.is_llvm_scalar_pair() {
156+
if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() {
157157
// Recurse as a `Consume` instead of `Projection`,
158158
// potentially stopping at non-operand projections,
159159
// which would trigger `mark_as_lvalue` on locals.

src/librustc_trans/mir/block.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -700,19 +700,21 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
700700
let elem = if field.is_zst() {
701701
C_undef(field.llvm_type(bcx.ccx))
702702
} else {
703-
bcx.extract_value(llval, tuple.layout.llvm_field_index(i))
703+
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
704+
bcx.bitcast(llval, field.immediate_llvm_type(bcx.ccx))
704705
};
705706
// If the tuple is immediate, the elements are as well
706707
let op = OperandRef {
707-
val: Immediate(base::to_immediate(bcx, elem, field)),
708+
val: Immediate(elem),
708709
layout: field,
709710
};
710711
self.trans_argument(bcx, op, llargs, &args[i]);
711712
}
712713
}
713714
Pair(a, b) => {
714715
let elems = [a, b];
715-
for i in 0..tuple.layout.fields.count() {
716+
assert_eq!(tuple.layout.fields.count(), 2);
717+
for i in 0..2 {
716718
// Pair is always made up of immediates
717719
let op = OperandRef {
718720
val: Immediate(elems[i]),

src/librustc_trans/mir/constant.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,27 @@ impl<'a, 'tcx> Const<'tcx> {
118118

119119
fn get_field(&self, ccx: &CrateContext<'a, 'tcx>, i: usize) -> ValueRef {
120120
let layout = ccx.layout_of(self.ty);
121-
if let layout::Abi::ScalarPair(..) = layout.abi {
122-
const_get_elt(self.llval, i as u64)
123-
} else {
124-
const_get_elt(self.llval, layout.llvm_field_index(i))
121+
let field = layout.field(ccx, i);
122+
if field.is_zst() {
123+
return C_undef(field.immediate_llvm_type(ccx));
124+
}
125+
match layout.abi {
126+
layout::Abi::Scalar(_) => self.llval,
127+
layout::Abi::ScalarPair(ref a, ref b) => {
128+
let offset = layout.fields.offset(i);
129+
if offset.bytes() == 0 {
130+
assert_eq!(field.size, a.value.size(ccx));
131+
const_get_elt(self.llval, 0)
132+
} else {
133+
assert_eq!(offset, a.value.size(ccx)
134+
.abi_align(b.value.align(ccx)));
135+
assert_eq!(field.size, b.value.size(ccx));
136+
const_get_elt(self.llval, 1)
137+
}
138+
}
139+
_ => {
140+
const_get_elt(self.llval, layout.llvm_field_index(i))
141+
}
125142
}
126143
}
127144

@@ -159,7 +176,8 @@ impl<'a, 'tcx> Const<'tcx> {
159176
// a constant LLVM global and cast its address if necessary.
160177
let align = ccx.align_of(self.ty);
161178
let ptr = consts::addr_of(ccx, self.llval, align, "const");
162-
OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()), Alignment::AbiAligned)
179+
OperandValue::Ref(consts::ptrcast(ptr, layout.llvm_type(ccx).ptr_to()),
180+
Alignment::AbiAligned)
163181
};
164182

165183
OperandRef {
@@ -1179,12 +1197,26 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
11791197
-> Const<'tcx> {
11801198
assert_eq!(vals.len(), layout.fields.count());
11811199

1182-
if let layout::Abi::ScalarPair(..) = layout.abi {
1183-
assert_eq!(vals.len(), 2);
1184-
return Const::new(C_struct(ccx, &[
1185-
vals[0].llval,
1186-
vals[1].llval,
1187-
], false), layout.ty);
1200+
match layout.abi {
1201+
layout::Abi::Scalar(_) |
1202+
layout::Abi::ScalarPair(..) if discr.is_none() => {
1203+
let mut non_zst_fields = vals.iter().enumerate().map(|(i, f)| {
1204+
(f, layout.fields.offset(i))
1205+
}).filter(|&(f, _)| !ccx.layout_of(f.ty).is_zst());
1206+
match (non_zst_fields.next(), non_zst_fields.next()) {
1207+
(Some((x, offset)), None) if offset.bytes() == 0 => {
1208+
return Const::new(x.llval, layout.ty);
1209+
}
1210+
(Some((a, a_offset)), Some((b, _))) if a_offset.bytes() == 0 => {
1211+
return Const::new(C_struct(ccx, &[a.llval, b.llval], false), layout.ty);
1212+
}
1213+
(Some((a, _)), Some((b, b_offset))) if b_offset.bytes() == 0 => {
1214+
return Const::new(C_struct(ccx, &[b.llval, a.llval], false), layout.ty);
1215+
}
1216+
_ => {}
1217+
}
1218+
}
1219+
_ => {}
11881220
}
11891221

11901222
// offset of current value

src/librustc_trans/mir/operand.rs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
use llvm::ValueRef;
1212
use rustc::ty;
13-
use rustc::ty::layout::{LayoutOf, TyLayout};
13+
use rustc::ty::layout::{self, LayoutOf, TyLayout};
1414
use rustc::mir;
1515
use rustc_data_structures::indexed_vec::Idx;
1616

1717
use base;
18-
use common::{CrateContext, C_undef};
18+
use common::{CrateContext, C_undef, C_usize};
1919
use builder::Builder;
2020
use value::Value;
2121
use type_of::LayoutLlvmExt;
@@ -207,24 +207,47 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
207207
if let mir::ProjectionElem::Field(ref f, _) = proj.elem {
208208
if let Some(o) = self.maybe_trans_consume_direct(bcx, &proj.base) {
209209
let layout = o.layout.field(bcx.ccx, f.index());
210+
let offset = o.layout.fields.offset(f.index());
210211

211212
// Handled in `trans_consume`.
212213
assert!(!layout.is_zst());
213214

214-
match o.val {
215-
OperandValue::Pair(a, b) => {
216-
let llval = [a, b][f.index()];
217-
// HACK(eddyb) have to bitcast pointers
218-
// until LLVM removes pointee types.
219-
let llval = bcx.bitcast(llval,
220-
layout.immediate_llvm_type(bcx.ccx));
221-
return Some(OperandRef {
222-
val: OperandValue::Immediate(llval),
223-
layout
224-
});
215+
// Offset has to match a scalar component.
216+
let llval = match (o.val, &o.layout.abi) {
217+
(OperandValue::Immediate(llval),
218+
&layout::Abi::Scalar(ref scalar)) => {
219+
assert_eq!(offset.bytes(), 0);
220+
assert_eq!(layout.size, scalar.value.size(bcx.ccx));
221+
llval
225222
}
226-
_ => {}
227-
}
223+
(OperandValue::Pair(a_llval, b_llval),
224+
&layout::Abi::ScalarPair(ref a, ref b)) => {
225+
if offset.bytes() == 0 {
226+
assert_eq!(layout.size, a.value.size(bcx.ccx));
227+
a_llval
228+
} else {
229+
assert_eq!(offset, a.value.size(bcx.ccx)
230+
.abi_align(b.value.align(bcx.ccx)));
231+
assert_eq!(layout.size, b.value.size(bcx.ccx));
232+
b_llval
233+
}
234+
}
235+
236+
// `#[repr(simd)]` types are also immediate.
237+
(OperandValue::Immediate(llval),
238+
&layout::Abi::Vector) => {
239+
bcx.extract_element(llval, C_usize(bcx.ccx, f.index() as u64))
240+
}
241+
242+
_ => return None
243+
};
244+
245+
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
246+
let llval = bcx.bitcast(llval, layout.immediate_llvm_type(bcx.ccx));
247+
return Some(OperandRef {
248+
val: OperandValue::Immediate(llval),
249+
layout
250+
});
228251
}
229252
}
230253
}

src/test/codegen/function-arguments.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ pub fn static_borrow(_: &'static i32) {
4646
pub fn named_borrow<'r>(_: &'r i32) {
4747
}
4848

49-
// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0)
49+
// CHECK: @unsafe_borrow(i16* dereferenceable(2) %arg0)
5050
// unsafe interior means this isn't actually readonly and there may be aliases ...
5151
#[no_mangle]
5252
pub fn unsafe_borrow(_: &UnsafeInner) {
5353
}
5454

55-
// CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0)
55+
// CHECK: @mutable_unsafe_borrow(i16* dereferenceable(2) %arg0)
5656
// ... unless this is a mutable borrow, those never alias
5757
// ... except that there's this LLVM bug that forces us to not use noalias, see #29485
5858
#[no_mangle]
@@ -110,7 +110,7 @@ pub fn slice(_: &[u8]) {
110110
pub fn mutable_slice(_: &mut [u8]) {
111111
}
112112

113-
// CHECK: @unsafe_slice([0 x %UnsafeInner]* nonnull %arg0.0, [[USIZE]] %arg0.1)
113+
// CHECK: @unsafe_slice([0 x i16]* nonnull %arg0.0, [[USIZE]] %arg0.1)
114114
// unsafe interior means this isn't actually readonly and there may be aliases ...
115115
#[no_mangle]
116116
pub fn unsafe_slice(_: &[UnsafeInner]) {

src/test/codegen/issue-32031.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#[no_mangle]
1616
pub struct F32(f32);
1717

18-
// CHECK: define float @add_newtype_f32(float, float)
18+
// CHECK: define float @add_newtype_f32(float %a, float %b)
1919
#[inline(never)]
2020
#[no_mangle]
2121
pub fn add_newtype_f32(a: F32, b: F32) -> F32 {
@@ -25,7 +25,7 @@ pub fn add_newtype_f32(a: F32, b: F32) -> F32 {
2525
#[no_mangle]
2626
pub struct F64(f64);
2727

28-
// CHECK: define double @add_newtype_f64(double, double)
28+
// CHECK: define double @add_newtype_f64(double %a, double %b)
2929
#[inline(never)]
3030
#[no_mangle]
3131
pub fn add_newtype_f64(a: F64, b: F64) -> F64 {

0 commit comments

Comments
 (0)