Skip to content

Commit 7a36141

Browse files
committed
rustc: unpack scalar pair newtype layout ABIs.
1 parent 37a7521 commit 7a36141

File tree

9 files changed

+185
-156
lines changed

9 files changed

+185
-156
lines changed

src/librustc/ty/layout.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,10 +1087,11 @@ impl<'a, 'tcx> CachedLayout {
10871087
// We have exactly one non-ZST field.
10881088
match (non_zst_fields.next(), non_zst_fields.next()) {
10891089
(Some(field), None) => {
1090-
// Field size match and it has a scalar ABI.
1090+
// Field size matches and it has a scalar or scalar pair ABI.
10911091
if size == field.size {
10921092
match field.abi {
1093-
Abi::Scalar(_) => {
1093+
Abi::Scalar(_) |
1094+
Abi::ScalarPair(..) => {
10941095
abi = field.abi.clone();
10951096
}
10961097
_ => {}
@@ -2228,17 +2229,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
22282229
ty::TyAdt(def, substs) => {
22292230
match self.variants {
22302231
Variants::Single { index } => {
2231-
let mut field_ty = def.variants[index].fields[i].ty(tcx, substs);
2232-
2233-
// Treat NonZero<*T> as containing &T.
2234-
// This is especially useful for fat pointers.
2235-
if Some(def.did) == tcx.lang_items().non_zero() {
2236-
if let ty::TyRawPtr(mt) = field_ty.sty {
2237-
field_ty = tcx.mk_ref(tcx.types.re_erased, mt);
2238-
}
2239-
}
2240-
2241-
field_ty
2232+
def.variants[index].fields[i].ty(tcx, substs)
22422233
}
22432234

22442235
// Discriminant field for enums (where applicable).
@@ -2294,21 +2285,22 @@ impl<'a, 'tcx> TyLayout<'tcx> {
22942285
where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
22952286
HasTyCtxt<'tcx>
22962287
{
2297-
if let Abi::Scalar(Scalar { value, ref valid_range }) = self.abi {
2288+
let scalar_component = |scalar: &Scalar, offset| {
22982289
// FIXME(eddyb) support negative/wrap-around discriminant ranges.
2299-
return if valid_range.start < valid_range.end {
2290+
let Scalar { value, ref valid_range } = *scalar;
2291+
if valid_range.start < valid_range.end {
23002292
let bits = value.size(cx).bits();
23012293
assert!(bits <= 128);
23022294
let max_value = !0u128 >> (128 - bits);
23032295
if valid_range.start > 0 {
23042296
let niche = valid_range.start - 1;
2305-
Ok(Some((self.fields.offset(0), Scalar {
2297+
Ok(Some((offset, Scalar {
23062298
value,
23072299
valid_range: niche..=valid_range.end
23082300
}, niche)))
23092301
} else if valid_range.end < max_value {
23102302
let niche = valid_range.end + 1;
2311-
Ok(Some((self.fields.offset(0), Scalar {
2303+
Ok(Some((offset, Scalar {
23122304
value,
23132305
valid_range: valid_range.start..=niche
23142306
}, niche)))
@@ -2317,7 +2309,20 @@ impl<'a, 'tcx> TyLayout<'tcx> {
23172309
}
23182310
} else {
23192311
Ok(None)
2320-
};
2312+
}
2313+
};
2314+
2315+
match self.abi {
2316+
Abi::Scalar(ref scalar) => {
2317+
return scalar_component(scalar, Size::from_bytes(0));
2318+
}
2319+
Abi::ScalarPair(ref a, ref b) => {
2320+
if let Some(result) = scalar_component(a, Size::from_bytes(0))? {
2321+
return Ok(Some(result));
2322+
}
2323+
return scalar_component(b, a.value.size(cx).abi_align(b.value.align(cx)));
2324+
}
2325+
_ => {}
23212326
}
23222327

23232328
// Perhaps one of the fields is non-zero, let's recurse and find out.

src/librustc_trans/base.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,31 @@ pub fn unsize_thin_ptr<'a, 'tcx>(
240240
let ptr_ty = bcx.ccx.layout_of(b).llvm_type(bcx.ccx).ptr_to();
241241
(bcx.pointercast(src, ptr_ty), unsized_info(bcx.ccx, a, b, None))
242242
}
243+
(&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => {
244+
assert_eq!(def_a, def_b);
245+
246+
let src_layout = bcx.ccx.layout_of(src_ty);
247+
let dst_layout = bcx.ccx.layout_of(dst_ty);
248+
let mut result = None;
249+
for i in 0..src_layout.fields.count() {
250+
let src_f = src_layout.field(bcx.ccx, i);
251+
assert_eq!(src_layout.fields.offset(i).bytes(), 0);
252+
assert_eq!(dst_layout.fields.offset(i).bytes(), 0);
253+
if src_f.is_zst() {
254+
continue;
255+
}
256+
assert_eq!(src_layout.size, src_f.size);
257+
258+
let dst_f = dst_layout.field(bcx.ccx, i);
259+
assert_ne!(src_f.ty, dst_f.ty);
260+
assert_eq!(result, None);
261+
result = Some(unsize_thin_ptr(bcx, src, src_f.ty, dst_f.ty));
262+
}
263+
let (lldata, llextra) = result.unwrap();
264+
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
265+
(bcx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bcx.ccx, 0)),
266+
bcx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bcx.ccx, 1)))
267+
}
243268
_ => bug!("unsize_thin_ptr: called on bad types"),
244269
}
245270
}

src/librustc_trans/mir/block.rs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -685,46 +685,19 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
685685
let tuple = self.trans_operand(bcx, operand);
686686

687687
// Handle both by-ref and immediate tuples.
688-
match tuple.val {
689-
Ref(llval, align) => {
690-
let tuple_ptr = LvalueRef::new_sized(llval, tuple.layout, align);
691-
for i in 0..tuple.layout.fields.count() {
692-
let field_ptr = tuple_ptr.project_field(bcx, i);
693-
self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[i]);
694-
}
695-
688+
if let Ref(llval, align) = tuple.val {
689+
let tuple_ptr = LvalueRef::new_sized(llval, tuple.layout, align);
690+
for i in 0..tuple.layout.fields.count() {
691+
let field_ptr = tuple_ptr.project_field(bcx, i);
692+
self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[i]);
696693
}
697-
Immediate(llval) => {
698-
for i in 0..tuple.layout.fields.count() {
699-
let field = tuple.layout.field(bcx.ccx, i);
700-
let elem = if field.is_zst() {
701-
C_undef(field.llvm_type(bcx.ccx))
702-
} else {
703-
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
704-
bcx.bitcast(llval, field.immediate_llvm_type(bcx.ccx))
705-
};
706-
// If the tuple is immediate, the elements are as well
707-
let op = OperandRef {
708-
val: Immediate(elem),
709-
layout: field,
710-
};
711-
self.trans_argument(bcx, op, llargs, &args[i]);
712-
}
713-
}
714-
Pair(a, b) => {
715-
let elems = [a, b];
716-
assert_eq!(tuple.layout.fields.count(), 2);
717-
for i in 0..2 {
718-
// Pair is always made up of immediates
719-
let op = OperandRef {
720-
val: Immediate(elems[i]),
721-
layout: tuple.layout.field(bcx.ccx, i),
722-
};
723-
self.trans_argument(bcx, op, llargs, &args[i]);
724-
}
694+
} else {
695+
// If the tuple is immediate, the elements are as well.
696+
for i in 0..tuple.layout.fields.count() {
697+
let op = tuple.extract_field(bcx, i);
698+
self.trans_argument(bcx, op, llargs, &args[i]);
725699
}
726700
}
727-
728701
}
729702

730703
fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> LvalueRef<'tcx> {

src/librustc_trans/mir/constant.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,12 @@ impl<'a, 'tcx> Const<'tcx> {
127127
layout::Abi::ScalarPair(ref a, ref b) => {
128128
let offset = layout.fields.offset(i);
129129
if offset.bytes() == 0 {
130-
assert_eq!(field.size, a.value.size(ccx));
131-
const_get_elt(self.llval, 0)
130+
if field.size == layout.size {
131+
self.llval
132+
} else {
133+
assert_eq!(field.size, a.value.size(ccx));
134+
const_get_elt(self.llval, 0)
135+
}
132136
} else {
133137
assert_eq!(offset, a.value.size(ccx)
134138
.abi_align(b.value.align(ccx)));
@@ -166,8 +170,9 @@ impl<'a, 'tcx> Const<'tcx> {
166170
let llvalty = val_ty(self.llval);
167171

168172
let val = if llty == llvalty && layout.is_llvm_scalar_pair() {
169-
let (a, b) = self.get_pair(ccx);
170-
OperandValue::Pair(a, b)
173+
OperandValue::Pair(
174+
const_get_elt(self.llval, 0),
175+
const_get_elt(self.llval, 1))
171176
} else if llty == llvalty && layout.is_llvm_immediate() {
172177
// If the types match, we can use the value directly.
173178
OperandValue::Immediate(self.llval)

src/librustc_trans/mir/lvalue.rs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,31 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
135135
return OperandRef::new_zst(bcx.ccx, self.layout);
136136
}
137137

138+
let scalar_load_metadata = |load, scalar: &layout::Scalar| {
139+
let (min, max) = (scalar.valid_range.start, scalar.valid_range.end);
140+
let max_next = max.wrapping_add(1);
141+
let bits = scalar.value.size(bcx.ccx).bits();
142+
assert!(bits <= 128);
143+
let mask = !0u128 >> (128 - bits);
144+
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
145+
// However, that is fine here (it would still represent the full range),
146+
// i.e., if the range is everything. The lo==hi case would be
147+
// rejected by the LLVM verifier (it would mean either an
148+
// empty set, which is impossible, or the entire range of the
149+
// type, which is pointless).
150+
match scalar.value {
151+
layout::Int(..) if max_next & mask != min & mask => {
152+
// llvm::ConstantRange can deal with ranges that wrap around,
153+
// so an overflow on (max + 1) is fine.
154+
bcx.range_metadata(load, min..max_next);
155+
}
156+
layout::Pointer if 0 < min && min < max => {
157+
bcx.nonnull_metadata(load);
158+
}
159+
_ => {}
160+
}
161+
};
162+
138163
let val = if self.layout.is_llvm_immediate() {
139164
let mut const_llval = ptr::null_mut();
140165
unsafe {
@@ -149,39 +174,27 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
149174
} else {
150175
let load = bcx.load(self.llval, self.alignment.non_abi());
151176
if let layout::Abi::Scalar(ref scalar) = self.layout.abi {
152-
let (min, max) = (scalar.valid_range.start, scalar.valid_range.end);
153-
let max_next = max.wrapping_add(1);
154-
let bits = scalar.value.size(bcx.ccx).bits();
155-
assert!(bits <= 128);
156-
let mask = !0u128 >> (128 - bits);
157-
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
158-
// However, that is fine here (it would still represent the full range),
159-
// i.e., if the range is everything. The lo==hi case would be
160-
// rejected by the LLVM verifier (it would mean either an
161-
// empty set, which is impossible, or the entire range of the
162-
// type, which is pointless).
163-
match scalar.value {
164-
layout::Int(..) if max_next & mask != min & mask => {
165-
// llvm::ConstantRange can deal with ranges that wrap around,
166-
// so an overflow on (max + 1) is fine.
167-
bcx.range_metadata(load, min..max_next);
168-
}
169-
layout::Pointer if 0 < min && min < max => {
170-
bcx.nonnull_metadata(load);
171-
}
172-
_ => {}
173-
}
177+
scalar_load_metadata(load, scalar);
174178
}
175179
load
176180
};
177181
OperandValue::Immediate(base::to_immediate(bcx, llval, self.layout))
178-
} else if self.layout.is_llvm_scalar_pair() {
179-
let load = |i| {
180-
let x = self.project_field(bcx, i).load(bcx).immediate();
181-
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
182-
bcx.bitcast(x, self.layout.scalar_pair_element_llvm_type(bcx.ccx, i))
182+
} else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi {
183+
let load = |i, scalar: &layout::Scalar| {
184+
let mut llptr = bcx.struct_gep(self.llval, i as u64);
185+
// Make sure to always load i1 as i8.
186+
if scalar.is_bool() {
187+
llptr = bcx.pointercast(llptr, Type::i8p(bcx.ccx));
188+
}
189+
let load = bcx.load(llptr, self.alignment.non_abi());
190+
scalar_load_metadata(load, scalar);
191+
if scalar.is_bool() {
192+
bcx.trunc(load, Type::i1(bcx.ccx))
193+
} else {
194+
load
195+
}
183196
};
184-
OperandValue::Pair(load(0), load(1))
197+
OperandValue::Pair(load(0, a), load(1, b))
185198
} else {
186199
OperandValue::Ref(self.llval, self.alignment)
187200
};

0 commit comments

Comments
 (0)