Skip to content

Commit b3e7d70

Browse files
committed
Auto merge of #51583 - cuviper:packed_pair-bool, r=Mark-Simulacrum
Store scalar pair bools as i8 in memory We represent `bool` as `i1` in a `ScalarPair`, unlike other aggregates, to optimize IR for checked operators and the like. With this patch, we still do so when the pair is an immediate value, but we use the `i8` memory type when the value is loaded or stored as an LLVM aggregate. So `(bool, bool)` looks like an `{ i1, i1 }` immediate, but `{ i8, i8 }` in memory. When a pair is a direct function argument, `PassMode::Pair`, it is still passed using the immediate `i1` type, but as a return value it will use the `i8` memory type. Also, `bool`-like` enum tags will now use scalar pairs when possible, where they were previously excluded due to optimization issues. Fixes #51516. Closes #51566. r? @eddyb cc @nox
2 parents 295858e + 557736b commit b3e7d70

File tree

9 files changed

+96
-50
lines changed

9 files changed

+96
-50
lines changed

src/librustc/ty/layout.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -1020,13 +1020,8 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
10201020
let mut abi = Abi::Aggregate { sized: true };
10211021
if tag.value.size(dl) == size {
10221022
abi = Abi::Scalar(tag.clone());
1023-
} else if !tag.is_bool() {
1024-
// HACK(nox): Blindly using ScalarPair for all tagged enums
1025-
// where applicable leads to Option<u8> being handled as {i1, i8},
1026-
// which later confuses SROA and some loop optimisations,
1027-
// ultimately leading to the repeat-trusted-len test
1028-
// failing. We make the trade-off of using ScalarPair only
1029-
// for types where the tag isn't a boolean.
1023+
} else {
1024+
// Try to use a ScalarPair for all tagged enums.
10301025
let mut common_prim = None;
10311026
for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) {
10321027
let offsets = match layout_variant.fields {

src/librustc_codegen_llvm/abi.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,8 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
582582
PassMode::Ignore => continue,
583583
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
584584
PassMode::Pair(..) => {
585-
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0));
586-
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1));
585+
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
586+
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
587587
continue;
588588
}
589589
PassMode::Cast(cast) => cast.llvm_type(cx),

src/librustc_codegen_llvm/base.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ pub fn unsize_thin_ptr<'a, 'tcx>(
266266
}
267267
let (lldata, llextra) = result.unwrap();
268268
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
269-
(bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)),
270-
bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1)))
269+
(bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0, true)),
270+
bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1, true)))
271271
}
272272
_ => bug!("unsize_thin_ptr: called on bad types"),
273273
}
@@ -397,9 +397,14 @@ pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef {
397397

398398
pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef {
399399
if let layout::Abi::Scalar(ref scalar) = layout.abi {
400-
if scalar.is_bool() {
401-
return bx.trunc(val, Type::i1(bx.cx));
402-
}
400+
return to_immediate_scalar(bx, val, scalar);
401+
}
402+
val
403+
}
404+
405+
pub fn to_immediate_scalar(bx: &Builder, val: ValueRef, scalar: &layout::Scalar) -> ValueRef {
406+
if scalar.is_bool() {
407+
return bx.trunc(val, Type::i1(bx.cx));
403408
}
404409
val
405410
}

src/librustc_codegen_llvm/mir/operand.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_data_structures::indexed_vec::Idx;
1818
use rustc_data_structures::sync::Lrc;
1919

2020
use base;
21-
use common::{self, CodegenCx, C_null, C_undef, C_usize};
21+
use common::{CodegenCx, C_null, C_undef, C_usize};
2222
use builder::{Builder, MemFlags};
2323
use value::Value;
2424
use type_of::LayoutLlvmExt;
@@ -128,13 +128,13 @@ impl<'a, 'tcx> OperandRef<'tcx> {
128128
bx.cx,
129129
a,
130130
a_scalar,
131-
layout.scalar_pair_element_llvm_type(bx.cx, 0),
131+
layout.scalar_pair_element_llvm_type(bx.cx, 0, true),
132132
);
133133
let b_llval = scalar_to_llvm(
134134
bx.cx,
135135
b,
136136
b_scalar,
137-
layout.scalar_pair_element_llvm_type(bx.cx, 1),
137+
layout.scalar_pair_element_llvm_type(bx.cx, 1, true),
138138
);
139139
OperandValue::Pair(a_llval, b_llval)
140140
},
@@ -193,8 +193,8 @@ impl<'a, 'tcx> OperandRef<'tcx> {
193193
self, llty);
194194
// Reconstruct the immediate aggregate.
195195
let mut llpair = C_undef(llty);
196-
llpair = bx.insert_value(llpair, a, 0);
197-
llpair = bx.insert_value(llpair, b, 1);
196+
llpair = bx.insert_value(llpair, base::from_immediate(bx, a), 0);
197+
llpair = bx.insert_value(llpair, base::from_immediate(bx, b), 1);
198198
llpair
199199
} else {
200200
self.immediate()
@@ -206,13 +206,14 @@ impl<'a, 'tcx> OperandRef<'tcx> {
206206
llval: ValueRef,
207207
layout: TyLayout<'tcx>)
208208
-> OperandRef<'tcx> {
209-
let val = if layout.is_llvm_scalar_pair() {
209+
let val = if let layout::Abi::ScalarPair(ref a, ref b) = layout.abi {
210210
debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}",
211211
llval, layout);
212212

213213
// Deconstruct the immediate aggregate.
214-
OperandValue::Pair(bx.extract_value(llval, 0),
215-
bx.extract_value(llval, 1))
214+
let a_llval = base::to_immediate_scalar(bx, bx.extract_value(llval, 0), a);
215+
let b_llval = base::to_immediate_scalar(bx, bx.extract_value(llval, 1), b);
216+
OperandValue::Pair(a_llval, b_llval)
216217
} else {
217218
OperandValue::Immediate(llval)
218219
};
@@ -264,8 +265,8 @@ impl<'a, 'tcx> OperandRef<'tcx> {
264265
*llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx));
265266
}
266267
OperandValue::Pair(ref mut a, ref mut b) => {
267-
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0));
268-
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1));
268+
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0, true));
269+
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1, true));
269270
}
270271
OperandValue::Ref(..) => bug!()
271272
}
@@ -308,11 +309,7 @@ impl<'a, 'tcx> OperandValue {
308309
}
309310
OperandValue::Pair(a, b) => {
310311
for (i, &x) in [a, b].iter().enumerate() {
311-
let mut llptr = bx.struct_gep(dest.llval, i as u64);
312-
// Make sure to always store i1 as i8.
313-
if common::val_ty(x) == Type::i1(bx.cx) {
314-
llptr = bx.pointercast(llptr, Type::i8p(bx.cx));
315-
}
312+
let llptr = bx.struct_gep(dest.llval, i as u64);
316313
let val = base::from_immediate(bx, x);
317314
bx.store_with_flags(val, llptr, dest.align, flags);
318315
}

src/librustc_codegen_llvm/mir/place.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,7 @@ impl<'a, 'tcx> PlaceRef<'tcx> {
127127
OperandValue::Immediate(base::to_immediate(bx, llval, self.layout))
128128
} else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi {
129129
let load = |i, scalar: &layout::Scalar| {
130-
let mut llptr = bx.struct_gep(self.llval, i as u64);
131-
// Make sure to always load i1 as i8.
132-
if scalar.is_bool() {
133-
llptr = bx.pointercast(llptr, Type::i8p(bx.cx));
134-
}
130+
let llptr = bx.struct_gep(self.llval, i as u64);
135131
let load = bx.load(llptr, self.align);
136132
scalar_load_metadata(load, scalar);
137133
if scalar.is_bool() {

src/librustc_codegen_llvm/mir/rvalue.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
232232
// HACK(eddyb) have to bitcast pointers
233233
// until LLVM removes pointee types.
234234
let lldata = bx.pointercast(lldata,
235-
cast.scalar_pair_element_llvm_type(bx.cx, 0));
235+
cast.scalar_pair_element_llvm_type(bx.cx, 0, true));
236236
OperandValue::Pair(lldata, llextra)
237237
}
238238
OperandValue::Immediate(lldata) => {
@@ -251,7 +251,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
251251
if let OperandValue::Pair(data_ptr, meta) = operand.val {
252252
if cast.is_llvm_scalar_pair() {
253253
let data_cast = bx.pointercast(data_ptr,
254-
cast.scalar_pair_element_llvm_type(bx.cx, 0));
254+
cast.scalar_pair_element_llvm_type(bx.cx, 0, true));
255255
OperandValue::Pair(data_cast, meta)
256256
} else { // cast to thin-ptr
257257
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and

src/librustc_codegen_llvm/type_of.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
4747
}
4848
layout::Abi::ScalarPair(..) => {
4949
return Type::struct_(cx, &[
50-
layout.scalar_pair_element_llvm_type(cx, 0),
51-
layout.scalar_pair_element_llvm_type(cx, 1),
50+
layout.scalar_pair_element_llvm_type(cx, 0, false),
51+
layout.scalar_pair_element_llvm_type(cx, 1, false),
5252
], false);
5353
}
5454
layout::Abi::Uninhabited |
@@ -206,7 +206,7 @@ pub trait LayoutLlvmExt<'tcx> {
206206
fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>,
207207
scalar: &layout::Scalar, offset: Size) -> Type;
208208
fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>,
209-
index: usize) -> Type;
209+
index: usize, immediate: bool) -> Type;
210210
fn llvm_field_index(&self, index: usize) -> u64;
211211
fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size)
212212
-> Option<PointeeInfo>;
@@ -340,7 +340,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
340340
}
341341

342342
fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>,
343-
index: usize) -> Type {
343+
index: usize, immediate: bool) -> Type {
344344
// HACK(eddyb) special-case fat pointers until LLVM removes
345345
// pointee types, to avoid bitcasting every `OperandRef::deref`.
346346
match self.ty.sty {
@@ -350,7 +350,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
350350
}
351351
ty::TyAdt(def, _) if def.is_box() => {
352352
let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
353-
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index);
353+
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
354354
}
355355
_ => {}
356356
}
@@ -361,14 +361,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
361361
};
362362
let scalar = [a, b][index];
363363

364-
// Make sure to return the same type `immediate_llvm_type` would,
365-
// to avoid dealing with two types and the associated conversions.
366-
// This means that `(bool, bool)` is represented as `{i1, i1}`,
367-
// both in memory and as an immediate, while `bool` is typically
368-
// `i8` in memory and only `i1` when immediate. While we need to
369-
// load/store `bool` as `i8` to avoid crippling LLVM optimizations,
370-
// `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`.
371-
if scalar.is_bool() {
364+
// Make sure to return the same type `immediate_llvm_type` would when
365+
// dealing with an immediate pair. This means that `(bool, bool)` is
366+
// effectively represented as `{i8, i8}` in memory and two `i1`s as an
367+
// immediate, just like `bool` is typically `i8` in memory and only `i1`
368+
// when immediate. We need to load/store `bool` as `i8` to avoid
369+
// crippling LLVM optimizations or triggering other LLVM bugs with `i1`.
370+
if immediate && scalar.is_bool() {
372371
return Type::i1(cx);
373372
}
374373

src/test/codegen/function-arguments.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub fn enum_id_1(x: Option<Result<u16, u16>>) -> Option<Result<u16, u16>> {
149149
x
150150
}
151151

152-
// CHECK: i16 @enum_id_2(i16)
152+
// CHECK: { i8, i8 } @enum_id_2(i1 zeroext %x.0, i8 %x.1)
153153
#[no_mangle]
154154
pub fn enum_id_2(x: Option<u8>) -> Option<u8> {
155155
x

src/test/codegen/scalar-pair-bool.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
13+
#![crate_type = "lib"]
14+
15+
// CHECK: define { i8, i8 } @pair_bool_bool(i1 zeroext %pair.0, i1 zeroext %pair.1)
16+
#[no_mangle]
17+
pub fn pair_bool_bool(pair: (bool, bool)) -> (bool, bool) {
18+
pair
19+
}
20+
21+
// CHECK: define { i8, i32 } @pair_bool_i32(i1 zeroext %pair.0, i32 %pair.1)
22+
#[no_mangle]
23+
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
24+
pair
25+
}
26+
27+
// CHECK: define { i32, i8 } @pair_i32_bool(i32 %pair.0, i1 zeroext %pair.1)
28+
#[no_mangle]
29+
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
30+
pair
31+
}
32+
33+
// CHECK: define { i8, i8 } @pair_and_or(i1 zeroext %arg0.0, i1 zeroext %arg0.1)
34+
#[no_mangle]
35+
pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) {
36+
// Make sure it can operate directly on the unpacked args
37+
// CHECK: and i1 %arg0.0, %arg0.1
38+
// CHECK: or i1 %arg0.0, %arg0.1
39+
(a && b, a || b)
40+
}
41+
42+
// CHECK: define void @pair_branches(i1 zeroext %arg0.0, i1 zeroext %arg0.1)
43+
#[no_mangle]
44+
pub fn pair_branches((a, b): (bool, bool)) {
45+
// Make sure it can branch directly on the unpacked bool args
46+
// CHECK: br i1 %arg0.0
47+
if a {
48+
println!("Hello!");
49+
}
50+
// CHECK: br i1 %arg0.1
51+
if b {
52+
println!("Goodbye!");
53+
}
54+
}

0 commit comments

Comments
 (0)