Skip to content

Store scalar pair bools as i8 in memory #51583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,13 +1020,8 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let mut abi = Abi::Aggregate { sized: true };
if tag.value.size(dl) == size {
abi = Abi::Scalar(tag.clone());
} else if !tag.is_bool() {
// HACK(nox): Blindly using ScalarPair for all tagged enums
// where applicable leads to Option<u8> being handled as {i1, i8},
// which later confuses SROA and some loop optimisations,
// ultimately leading to the repeat-trusted-len test
// failing. We make the trade-off of using ScalarPair only
// for types where the tag isn't a boolean.
} else {
// Try to use a ScalarPair for all tagged enums.
let mut common_prim = None;
for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) {
let offsets = match layout_variant.fields {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,8 +582,8 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
PassMode::Ignore => continue,
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
PassMode::Pair(..) => {
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0));
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1));
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
continue;
}
PassMode::Cast(cast) => cast.llvm_type(cx),
Expand Down
15 changes: 10 additions & 5 deletions src/librustc_codegen_llvm/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ pub fn unsize_thin_ptr<'a, 'tcx>(
}
let (lldata, llextra) = result.unwrap();
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
(bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)),
bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1)))
(bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0, true)),
bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1, true)))
}
_ => bug!("unsize_thin_ptr: called on bad types"),
}
Expand Down Expand Up @@ -396,9 +396,14 @@ pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef {

pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef {
if let layout::Abi::Scalar(ref scalar) = layout.abi {
if scalar.is_bool() {
return bx.trunc(val, Type::i1(bx.cx));
}
return to_immediate_scalar(bx, val, scalar);
}
val
}

pub fn to_immediate_scalar(bx: &Builder, val: ValueRef, scalar: &layout::Scalar) -> ValueRef {
if scalar.is_bool() {
return bx.trunc(val, Type::i1(bx.cx));
}
val
}
Expand Down
27 changes: 12 additions & 15 deletions src/librustc_codegen_llvm/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::sync::Lrc;

use base;
use common::{self, CodegenCx, C_null, C_undef, C_usize};
use common::{CodegenCx, C_null, C_undef, C_usize};
use builder::{Builder, MemFlags};
use value::Value;
use type_of::LayoutLlvmExt;
Expand Down Expand Up @@ -128,13 +128,13 @@ impl<'a, 'tcx> OperandRef<'tcx> {
bx.cx,
a,
a_scalar,
layout.scalar_pair_element_llvm_type(bx.cx, 0),
layout.scalar_pair_element_llvm_type(bx.cx, 0, true),
);
let b_llval = scalar_to_llvm(
bx.cx,
b,
b_scalar,
layout.scalar_pair_element_llvm_type(bx.cx, 1),
layout.scalar_pair_element_llvm_type(bx.cx, 1, true),
);
OperandValue::Pair(a_llval, b_llval)
},
Expand Down Expand Up @@ -193,8 +193,8 @@ impl<'a, 'tcx> OperandRef<'tcx> {
self, llty);
// Reconstruct the immediate aggregate.
let mut llpair = C_undef(llty);
llpair = bx.insert_value(llpair, a, 0);
llpair = bx.insert_value(llpair, b, 1);
llpair = bx.insert_value(llpair, base::from_immediate(bx, a), 0);
llpair = bx.insert_value(llpair, base::from_immediate(bx, b), 1);
llpair
} else {
self.immediate()
Expand All @@ -206,13 +206,14 @@ impl<'a, 'tcx> OperandRef<'tcx> {
llval: ValueRef,
layout: TyLayout<'tcx>)
-> OperandRef<'tcx> {
let val = if layout.is_llvm_scalar_pair() {
let val = if let layout::Abi::ScalarPair(ref a, ref b) = layout.abi {
debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}",
llval, layout);

// Deconstruct the immediate aggregate.
OperandValue::Pair(bx.extract_value(llval, 0),
bx.extract_value(llval, 1))
let a_llval = base::to_immediate_scalar(bx, bx.extract_value(llval, 0), a);
let b_llval = base::to_immediate_scalar(bx, bx.extract_value(llval, 1), b);
OperandValue::Pair(a_llval, b_llval)
} else {
OperandValue::Immediate(llval)
};
Expand Down Expand Up @@ -264,8 +265,8 @@ impl<'a, 'tcx> OperandRef<'tcx> {
*llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx));
}
OperandValue::Pair(ref mut a, ref mut b) => {
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0));
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1));
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0, true));
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1, true));
}
OperandValue::Ref(..) => bug!()
}
Expand Down Expand Up @@ -308,11 +309,7 @@ impl<'a, 'tcx> OperandValue {
}
OperandValue::Pair(a, b) => {
for (i, &x) in [a, b].iter().enumerate() {
let mut llptr = bx.struct_gep(dest.llval, i as u64);
// Make sure to always store i1 as i8.
if common::val_ty(x) == Type::i1(bx.cx) {
llptr = bx.pointercast(llptr, Type::i8p(bx.cx));
}
let llptr = bx.struct_gep(dest.llval, i as u64);
let val = base::from_immediate(bx, x);
bx.store_with_flags(val, llptr, dest.align, flags);
}
Expand Down
6 changes: 1 addition & 5 deletions src/librustc_codegen_llvm/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,7 @@ impl<'a, 'tcx> PlaceRef<'tcx> {
OperandValue::Immediate(base::to_immediate(bx, llval, self.layout))
} else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi {
let load = |i, scalar: &layout::Scalar| {
let mut llptr = bx.struct_gep(self.llval, i as u64);
// Make sure to always load i1 as i8.
if scalar.is_bool() {
llptr = bx.pointercast(llptr, Type::i8p(bx.cx));
}
let llptr = bx.struct_gep(self.llval, i as u64);
let load = bx.load(llptr, self.align);
scalar_load_metadata(load, scalar);
if scalar.is_bool() {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
// HACK(eddyb) have to bitcast pointers
// until LLVM removes pointee types.
let lldata = bx.pointercast(lldata,
cast.scalar_pair_element_llvm_type(bx.cx, 0));
cast.scalar_pair_element_llvm_type(bx.cx, 0, true));
OperandValue::Pair(lldata, llextra)
}
OperandValue::Immediate(lldata) => {
Expand All @@ -251,7 +251,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
if let OperandValue::Pair(data_ptr, meta) = operand.val {
if cast.is_llvm_scalar_pair() {
let data_cast = bx.pointercast(data_ptr,
cast.scalar_pair_element_llvm_type(bx.cx, 0));
cast.scalar_pair_element_llvm_type(bx.cx, 0, true));
OperandValue::Pair(data_cast, meta)
} else { // cast to thin-ptr
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
Expand Down
25 changes: 12 additions & 13 deletions src/librustc_codegen_llvm/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
}
layout::Abi::ScalarPair(..) => {
return Type::struct_(cx, &[
layout.scalar_pair_element_llvm_type(cx, 0),
layout.scalar_pair_element_llvm_type(cx, 1),
layout.scalar_pair_element_llvm_type(cx, 0, false),
layout.scalar_pair_element_llvm_type(cx, 1, false),
], false);
}
layout::Abi::Uninhabited |
Expand Down Expand Up @@ -206,7 +206,7 @@ pub trait LayoutLlvmExt<'tcx> {
fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>,
scalar: &layout::Scalar, offset: Size) -> Type;
fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>,
index: usize) -> Type;
index: usize, immediate: bool) -> Type;
fn llvm_field_index(&self, index: usize) -> u64;
fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size)
-> Option<PointeeInfo>;
Expand Down Expand Up @@ -340,7 +340,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
}

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

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

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/function-arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub fn enum_id_1(x: Option<Result<u16, u16>>) -> Option<Result<u16, u16>> {
x
}

// CHECK: i16 @enum_id_2(i16)
// CHECK: { i8, i8 } @enum_id_2(i1 zeroext %x.0, i8 %x.1)
#[no_mangle]
pub fn enum_id_2(x: Option<u8>) -> Option<u8> {
x
Expand Down
54 changes: 54 additions & 0 deletions src/test/codegen/scalar-pair-bool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -O

#![crate_type = "lib"]

// CHECK: define { i8, i8 } @pair_bool_bool(i1 zeroext %pair.0, i1 zeroext %pair.1)
#[no_mangle]
pub fn pair_bool_bool(pair: (bool, bool)) -> (bool, bool) {
pair
}

// CHECK: define { i8, i32 } @pair_bool_i32(i1 zeroext %pair.0, i32 %pair.1)
#[no_mangle]
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
pair
}

// CHECK: define { i32, i8 } @pair_i32_bool(i32 %pair.0, i1 zeroext %pair.1)
#[no_mangle]
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
pair
}

// CHECK: define { i8, i8 } @pair_and_or(i1 zeroext %arg0.0, i1 zeroext %arg0.1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is another unfortunate consequence, I forgot about return values. I wonder if they should be using the pair "passing mode" that arguments do, and create a LLVM aggregate type on the fly, using the immediate types for the pair components. That way we'd get {i1, i1} for returns, but everything else would see {i8, i8}.

Not sure it's worth the complexity though. When inlining, LLVM should collapse the zext followed by trunc, just like it gets rid of packing into a pair and unpacking it.

#[no_mangle]
pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) {
// Make sure it can operate directly on the unpacked args
// CHECK: and i1 %arg0.0, %arg0.1
// CHECK: or i1 %arg0.0, %arg0.1
(a && b, a || b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cool to see this with branches, since they only ever take i1.

}

// CHECK: define void @pair_branches(i1 zeroext %arg0.0, i1 zeroext %arg0.1)
#[no_mangle]
pub fn pair_branches((a, b): (bool, bool)) {
// Make sure it can branch directly on the unpacked bool args
// CHECK: br i1 %arg0.0
if a {
println!("Hello!");
}
// CHECK: br i1 %arg0.1
if b {
println!("Goodbye!");
}
}