Skip to content
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

distinguish "no data" from "heterogeneous" in ABI #57645

Merged
merged 1 commit into from
Jan 26, 2019
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
5 changes: 4 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use rustc_incremental;
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::{self, CStore};
use rustc_mir as mir;
use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion};
use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test};
use rustc_plugin as plugin;
use rustc_plugin::registry::Registry;
use rustc_privacy;
Expand Down Expand Up @@ -1287,6 +1287,9 @@ where
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
}
});

time(sess, "layout testing", || layout_test::test_layout(tcx));

// Avoid overwhelming user with errors if type checking failed.
// I'm not sure how helpful this is, to be honest, but it avoids
// a
Expand Down
132 changes: 132 additions & 0 deletions src/librustc_passes/layout_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use rustc::hir;
nikomatsakis marked this conversation as resolved.
Show resolved Hide resolved
use rustc::hir::def_id::DefId;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::hir::ItemKind;
use rustc::ty::layout::HasDataLayout;
use rustc::ty::layout::HasTyCtxt;
use rustc::ty::layout::LayoutOf;
use rustc::ty::layout::TargetDataLayout;
use rustc::ty::layout::TyLayout;
use rustc::ty::ParamEnv;
use rustc::ty::Ty;
use rustc::ty::TyCtxt;
use syntax::ast::Attribute;

pub fn test_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
if tcx.features().rustc_attrs {
// if the `rustc_attrs` feature is not enabled, don't bother testing layout
tcx.hir()
.krate()
.visit_all_item_likes(&mut VarianceTest { tcx });
}
}

struct VarianceTest<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
}

impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
let item_def_id = self.tcx.hir().local_def_id(item.id);

if let ItemKind::Ty(..) = item.node {
for attr in self.tcx.get_attrs(item_def_id).iter() {
if attr.check_name("rustc_layout") {
self.dump_layout_of(item_def_id, item, attr);
}
}
}
}

fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
}

impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item, attr: &Attribute) {
let tcx = self.tcx;
let param_env = self.tcx.param_env(item_def_id);
let ty = self.tcx.type_of(item_def_id);
match self.tcx.layout_of(param_env.and(ty)) {
Ok(ty_layout) => {
// Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
// The `..` are the names of fields to dump.
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
let name = meta_item.word().map(|mi| mi.name().as_str());
let name = name.as_ref().map(|s| &s[..]).unwrap_or("");

match name {
"abi" => {
self.tcx
.sess
.span_err(item.span, &format!("abi: {:?}", ty_layout.abi));
}

"align" => {
self.tcx
.sess
.span_err(item.span, &format!("align: {:?}", ty_layout.align));
}

"size" => {
self.tcx
.sess
.span_err(item.span, &format!("size: {:?}", ty_layout.size));
}

"homogeneous_aggregate" => {
self.tcx.sess.span_err(
item.span,
&format!(
"homogeneous_aggregate: {:?}",
ty_layout
.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
),
);
}

_ => {
self.tcx.sess.span_err(
meta_item.span,
&format!("unrecognized field name `{}`", name),
);
}
}
}
}

Err(layout_error) => {
self.tcx
.sess
.span_err(item.span, &format!("layout error: {:?}", layout_error));
}
}
}
}

struct UnwrapLayoutCx<'me, 'tcx> {
tcx: TyCtxt<'me, 'tcx, 'tcx>,
param_env: ParamEnv<'tcx>,
}

impl<'me, 'tcx> LayoutOf for UnwrapLayoutCx<'me, 'tcx> {
type Ty = Ty<'tcx>;
type TyLayout = TyLayout<'tcx>;

fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
self.tcx.layout_of(self.param_env.and(ty)).unwrap()
}
}

impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
self.tcx
}
}

impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> {
fn data_layout(&self) -> &TargetDataLayout {
self.tcx.data_layout()
}
}
1 change: 1 addition & 0 deletions src/librustc_passes/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mod diagnostics;
pub mod ast_validation;
pub mod rvalue_promotion;
pub mod hir_stats;
pub mod layout_test;
pub mod loops;

__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
where Ty: TyLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
{
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
let size = arg.layout.size;

// Ensure we have at most four uniquely addressable members.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
where Ty: TyLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
{
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
let size = arg.layout.size;

// Ensure we have at most four uniquely addressable members.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/asmjs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
{
if ret.layout.is_aggregate() {
if let Some(unit) = ret.layout.homogeneous_aggregate(cx) {
if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() {
let size = ret.layout.size;
if unit.size == size {
ret.cast_to(Uniform {
Expand Down
79 changes: 66 additions & 13 deletions src/librustc_target/abi/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,33 @@ impl CastTarget {
}
}

/// Return value from the `homogeneous_aggregate` test function.
#[derive(Copy, Clone, Debug)]
pub enum HomogeneousAggregate {
/// Yes, all the "leaf fields" of this struct are passed in the
/// same way (specified in the `Reg` value).
Homogeneous(Reg),

/// There are distinct leaf fields passed in different ways,
/// or this is uninhabited.
Heterogeneous,

/// There are no leaf fields at all.
NoData,
}

impl HomogeneousAggregate {
/// If this is a homogeneous aggregate, returns the homogeneous
/// unit, else `None`.
pub fn unit(self) -> Option<Reg> {
if let HomogeneousAggregate::Homogeneous(r) = self {
Some(r)
} else {
None
}
}
}

impl<'a, Ty> TyLayout<'a, Ty> {
fn is_aggregate(&self) -> bool {
match self.abi {
Expand All @@ -239,11 +266,21 @@ impl<'a, Ty> TyLayout<'a, Ty> {
}
}

fn homogeneous_aggregate<C>(&self, cx: &C) -> Option<Reg>
/// True if this layout is an aggregate containing fields of only
/// a single type (e.g., `(u32, u32)`). Such aggregates are often
/// special-cased in ABIs.
///
/// Note: We generally ignore fields of zero-sized type when computing
/// this value (cc #56877).
///
/// This is public so that it can be used in unit tests, but
/// should generally only be relevant to the ABI details of
/// specific targets.
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> HomogeneousAggregate
where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf<Ty = Ty, TyLayout = Self>
{
match self.abi {
Abi::Uninhabited => None,
Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,

// The primitive for this algorithm.
Abi::Scalar(ref scalar) => {
Expand All @@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> {
abi::Pointer => RegKind::Integer,
abi::Float(_) => RegKind::Float,
};
Some(Reg {
HomogeneousAggregate::Homogeneous(Reg {
kind,
size: self.size
})
}

Abi::Vector { .. } => {
Some(Reg {
assert!(!self.is_zst());
HomogeneousAggregate::Homogeneous(Reg {
kind: RegKind::Vector,
size: self.size
})
Expand All @@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
if count > 0 {
return self.field(cx, 0).homogeneous_aggregate(cx);
} else {
return None;
return HomogeneousAggregate::NoData;
}
}
FieldPlacement::Union(_) => true,
Expand All @@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> {

for i in 0..self.fields.count() {
if !is_union && total != self.fields.offset(i) {
return None;
return HomogeneousAggregate::Heterogeneous;
}

let field = self.field(cx, i);

match (result, field.homogeneous_aggregate(cx)) {
// The field itself must be a homogeneous aggregate.
(_, None) => return None,
(_, HomogeneousAggregate::NoData) => {
// Ignore fields that have no data
}
(_, HomogeneousAggregate::Heterogeneous) => {
// The field itself must be a homogeneous aggregate.
return HomogeneousAggregate::Heterogeneous;
}
// If this is the first field, record the unit.
(None, Some(unit)) => {
(None, HomogeneousAggregate::Homogeneous(unit)) => {
result = Some(unit);
}
// For all following fields, the unit must be the same.
(Some(prev_unit), Some(unit)) => {
(Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => {
if prev_unit != unit {
return None;
return HomogeneousAggregate::Heterogeneous;
}
}
}
Expand All @@ -314,9 +358,18 @@ impl<'a, Ty> TyLayout<'a, Ty> {

// There needs to be no padding.
if total != self.size {
None
HomogeneousAggregate::Heterogeneous
} else {
result
match result {
Some(reg) => {
assert_ne!(total, Size::ZERO);
HomogeneousAggregate::Homogeneous(reg)
}
None => {
assert_eq!(total, Size::ZERO);
HomogeneousAggregate::NoData
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/powerpc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>, abi: A
where Ty: TyLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
{
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
// ELFv1 only passes one-member aggregates transparently.
// ELFv2 passes up to eight uniquely addressable members.
if (abi == ELFv1 && arg.layout.size > unit.size)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/sparc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
where Ty: TyLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
{
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
// Ensure we have at most eight uniquely addressable members.
if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
return None;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
};

// At this point we know this must be a primitive of sorts.
let unit = arg.layout.homogeneous_aggregate(cx).unwrap();
let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
assert_eq!(unit.size, arg.layout.size);
if unit.kind == RegKind::Float {
continue;
Expand Down
7 changes: 7 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
is just used for rustc unit tests \
and will never be stable",
cfg_fn!(rustc_attrs))),
("rustc_layout", Normal, template!(List: "field1, field2, ..."),
Gated(Stability::Unstable,
"rustc_attrs",
"the `#[rustc_layout]` attribute \
is just used for rustc unit tests \
and will never be stable",
cfg_fn!(rustc_attrs))),
("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable,
"rustc_attrs",
"the `#[rustc_regions]` attribute \
Expand Down
Loading