make CastTarget::size and CastTarget::llvm_type consistent, remove
special case that's not present in Clang Making the methods consistent doesn't require much justification. It's required for us to generate correct code. The special case was present near the end of `CastTarget::llvm_type`, and resulted in the final integer component of the ABI type being shrunk to the smallest integer that fits. You can see this in action here (https://godbolt.org/z/Pe73cr91d), where, for a struct with 5 u16 elements, rustc generates `{ i64, i16 }`, while Clang generates `[2 x i64]`. This special case was added a long time ago, when the function was originally written [1]. That commit consolidated logic from many backends, and in some of the code it deleted, sparc64 [2] and powerpc64 [3] had similar special cases. However, looking at Clang today, it doesn't have this special case for sparc64 (https://godbolt.org/z/YaafvYWdf) or powerpc64 (https://godbolt.org/z/5c3YePTje), so this change just removes it. [1]:f0636b61c7 (diff-183c4dadf10704bd1f521b71f71d89bf755c9603a93f894d66c03bb1effc6021R231)
[2]:f0636b61c7 (diff-2d8f87ea6db6d7f0a6fbeb1d5549adc07e93331278d951a1e051a40f92914436L163-L166)
[3]:f0636b61c7 (diff-88af4a9df9ead503a5c7774a0455d270dea3ba60e9b0ec1ce550b4c53d3bce3bL172-L175)
This commit is contained in:
parent
41c6fa812b
commit
74ef47e90c
2 changed files with 38 additions and 39 deletions
|
@ -16,13 +16,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_session::config;
|
use rustc_session::config;
|
||||||
pub use rustc_target::abi::call::*;
|
pub use rustc_target::abi::call::*;
|
||||||
use rustc_target::abi::{self, HasDataLayout, Int};
|
use rustc_target::abi::{self, HasDataLayout, Int, Size};
|
||||||
pub use rustc_target::spec::abi::Abi;
|
pub use rustc_target::spec::abi::Abi;
|
||||||
use rustc_target::spec::SanitizerSet;
|
use rustc_target::spec::SanitizerSet;
|
||||||
|
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
pub trait ArgAttributesExt {
|
pub trait ArgAttributesExt {
|
||||||
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
|
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
|
||||||
fn apply_attrs_to_callsite(
|
fn apply_attrs_to_callsite(
|
||||||
|
@ -130,42 +132,36 @@ impl LlvmType for Reg {
|
||||||
impl LlvmType for CastTarget {
|
impl LlvmType for CastTarget {
|
||||||
fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type {
|
fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type {
|
||||||
let rest_ll_unit = self.rest.unit.llvm_type(cx);
|
let rest_ll_unit = self.rest.unit.llvm_type(cx);
|
||||||
let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 {
|
let rest_count = if self.rest.total == Size::ZERO {
|
||||||
(0, 0)
|
0
|
||||||
} else {
|
} else {
|
||||||
(
|
assert_ne!(
|
||||||
self.rest.total.bytes() / self.rest.unit.size.bytes(),
|
self.rest.unit.size,
|
||||||
self.rest.total.bytes() % self.rest.unit.size.bytes(),
|
Size::ZERO,
|
||||||
)
|
"total size {:?} cannot be divided into units of zero size",
|
||||||
|
self.rest.total
|
||||||
|
);
|
||||||
|
if self.rest.total.bytes() % self.rest.unit.size.bytes() != 0 {
|
||||||
|
assert_eq!(self.rest.unit.kind, RegKind::Integer, "only int regs can be split");
|
||||||
|
}
|
||||||
|
self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Simplify to a single unit or an array if there's no prefix.
|
||||||
|
// This produces the same layout, but using a simpler type.
|
||||||
if self.prefix.iter().all(|x| x.is_none()) {
|
if self.prefix.iter().all(|x| x.is_none()) {
|
||||||
// Simplify to a single unit when there is no prefix and size <= unit size
|
if rest_count == 1 {
|
||||||
if self.rest.total <= self.rest.unit.size {
|
|
||||||
return rest_ll_unit;
|
return rest_ll_unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simplify to array when all chunks are the same size and type
|
return cx.type_array(rest_ll_unit, rest_count);
|
||||||
if rem_bytes == 0 {
|
|
||||||
return cx.type_array(rest_ll_unit, rest_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create list of fields in the main structure
|
|
||||||
let mut args: Vec<_> = self
|
|
||||||
.prefix
|
|
||||||
.iter()
|
|
||||||
.flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)))
|
|
||||||
.chain((0..rest_count).map(|_| rest_ll_unit))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Append final integer
|
|
||||||
if rem_bytes != 0 {
|
|
||||||
// Only integers can be really split further.
|
|
||||||
assert_eq!(self.rest.unit.kind, RegKind::Integer);
|
|
||||||
args.push(cx.type_ix(rem_bytes * 8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a struct type with the prefix and the "rest" arguments.
|
||||||
|
let prefix_args =
|
||||||
|
self.prefix.iter().flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)));
|
||||||
|
let rest_args = (0..rest_count).map(|_| rest_ll_unit);
|
||||||
|
let args: Vec<_> = prefix_args.chain(rest_args).collect();
|
||||||
cx.type_struct(&args, false)
|
cx.type_struct(&args, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,9 +251,9 @@ pub struct Uniform {
|
||||||
/// The total size of the argument, which can be:
|
/// The total size of the argument, which can be:
|
||||||
/// * equal to `unit.size` (one scalar/vector),
|
/// * equal to `unit.size` (one scalar/vector),
|
||||||
/// * a multiple of `unit.size` (an array of scalar/vectors),
|
/// * a multiple of `unit.size` (an array of scalar/vectors),
|
||||||
/// * if `unit.kind` is `Integer`, the last element
|
/// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }`
|
||||||
/// can be shorter, i.e., `{ i64, i64, i32 }` for
|
/// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed,
|
||||||
/// 64-bit integers with a total size of 20 bytes.
|
/// this size will be rounded up to the nearest multiple of `unit.size`.
|
||||||
pub total: Size,
|
pub total: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,14 +319,17 @@ impl CastTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
|
pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
|
||||||
let mut size = self.rest.total;
|
// Prefix arguments are passed in specific designated registers
|
||||||
for i in 0..self.prefix.iter().count() {
|
let prefix_size = self
|
||||||
match self.prefix[i] {
|
.prefix
|
||||||
Some(v) => size += v.size,
|
.iter()
|
||||||
None => {}
|
.filter_map(|x| x.map(|reg| reg.size))
|
||||||
}
|
.fold(Size::ZERO, |acc, size| acc + size);
|
||||||
}
|
// Remaining arguments are passed in chunks of the unit size
|
||||||
return size;
|
let rest_size =
|
||||||
|
self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes());
|
||||||
|
|
||||||
|
prefix_size + rest_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
|
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue