Skip to content

Commit

Permalink
distinguish "no data" from "heterogeneous" for ABI purposes
Browse files Browse the repository at this point in the history
Also, add a testing infrastructure and tests that lets us dump layout.
  • Loading branch information
nikomatsakis committed Jan 25, 2019
1 parent 01f8e25 commit 8e4c57f
Show file tree
Hide file tree
Showing 17 changed files with 492 additions and 20 deletions.
5 changes: 4 additions & 1 deletion src/librustc_driver/driver.rs
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
@@ -0,0 +1,132 @@
use rustc::hir;
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
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
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
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
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
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
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
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
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
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

0 comments on commit 8e4c57f

Please sign in to comment.