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

Enums MVP. #819

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
327 changes: 276 additions & 51 deletions crates/rustc_codegen_spirv/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{
self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyCtxt, TyKind,
TypeAndMut, UintTy,
self, layout::LayoutError, AdtDef, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig,
Ty, TyCtxt, TyKind, TypeAndMut, UintTy, VariantDef,
};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
Expand Down Expand Up @@ -150,21 +150,192 @@ pub(crate) fn provide(providers: &mut Providers) {
let TyAndLayout { ty, mut layout } =
(rustc_interface::DEFAULT_QUERY_PROVIDERS.layout_of)(tcx, key)?;

#[allow(clippy::match_like_matches_macro)]
let hide_niche = match ty.kind() {
ty::Bool => true,
_ => false,
match (&layout.variants, &layout.abi, &layout.fields, ty.kind()) {
(
Variants::Multiple { .. },
Abi::Aggregate { .. },
&FieldsShape::Arbitrary { .. },
TyKind::Adt(def, substs),
) => {
let mut our_layout = clone_layout(layout);
deoverlay_enum_fields(tcx, ty, key.param_env, def, substs, &mut our_layout)?;
layout = tcx.intern_layout(our_layout);
}
(_, _, _, ty::Bool) => {
layout = tcx.intern_layout(Layout {
largest_niche: None,
..clone_layout(layout)
});
}
_ => {}
}

Ok(TyAndLayout { ty, layout })
};
}

fn deoverlay_enum_fields<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
param_env: ParamEnv<'tcx>,
def: &AdtDef,
substs: SubstsRef<'tcx>,
layout: &mut Layout,
) -> Result<(), LayoutError<'tcx>> {
if let Variants::Multiple {
ref tag_encoding,
ref mut variants,
..
} = layout.variants
{
let mut niche = match tag_encoding {
TagEncoding::Niche {
dataful_variant, ..
} => Some((*dataful_variant, layout.fields.offset(0))),

TagEncoding::Direct => {
assert_eq!(Size::ZERO, layout.fields.offset(0));
None
}
};

if hide_niche {
layout = tcx.arena.alloc(Layout {
largest_niche: None,
..clone_layout(layout)
});
let mut field_cache = vec![];

for (index, variant_layout) in variants.iter_enumerated_mut() {
#[allow(clippy::map_err_ignore)]
deoverlay_variant(
tcx,
param_env,
&def.variants[index],
substs,
&mut field_cache,
index,
variant_layout,
&mut layout.size,
&mut niche,
def.repr.c(),
)
.map_err(|_| LayoutError::Unknown(ty))?;
}

Ok(TyAndLayout { ty, layout })
if let Some((_, offset)) = niche {
if let FieldsShape::Arbitrary {
ref mut offsets, ..
} = layout.fields
{
offsets[0] = offset;
} else {
bug!();
}
}
} else {
bug!();
}
Ok(())
}

#[allow(clippy::too_many_arguments)]
fn deoverlay_variant<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
def: &VariantDef,
substs: SubstsRef<'tcx>,
field_cache: &mut Vec<(Size, VariantIdx, TyAndLayout<'tcx>)>,
index: VariantIdx,
layout: &mut Layout,
size: &mut Size,
niche: &mut Option<(VariantIdx, Size)>,
is_c: bool,
) -> Result<(), ()> {
let find_field_by_offset_or_type =
|offset, ty, field_cache: &mut Vec<(Size, VariantIdx, TyAndLayout<'tcx>)>, dry: bool| {
match field_cache[..].binary_search_by(|&(foff, _, _)| foff.cmp(&offset)) {
Ok(idx) => {
if field_cache[idx].2 == ty && field_cache[idx].1 != index {
return Some(offset);
}
}
Err(idx) => {
let prev_fits =
idx == 0 || field_cache[idx - 1].0 + field_cache[idx - 1].2.size <= offset;
let next_fits =
idx == field_cache.len() || field_cache[idx].0 >= offset + ty.size;
if prev_fits && next_fits {
if !dry {
field_cache.insert(idx, (offset, index, ty));
}
return Some(offset);
}
}
}
if !is_c {
if let Some((offset, _, _)) = field_cache
.iter()
.find(|&(_, varidx, otherty)| *varidx != index && *otherty == ty)
{
return Some(*offset);
}
}
None
};
let mut add_at_end =
|i: usize,
ty: TyAndLayout<'tcx>,
field_cache: &mut Vec<(Size, VariantIdx, TyAndLayout<'tcx>)>| {
let offset = size.align_to(ty.align.abi);
if !is_c {
field_cache.push((offset, index, ty));
*size = offset + ty.size;
Ok(offset)
} else {
tcx.sess.span_err(
tcx.def_span(def.fields[i].did),
"Failed to make repr(C) layout: field overlaps a different data type",
);
Err(())
}
};
let mut find_or_add = |a, aofs, aty| {
find_field_by_offset_or_type(aofs, aty, field_cache, false)
.map_or_else(|| add_at_end(a, aty, field_cache), Ok)
};
let mut assign_and_check = |offset: &mut Size, newval| {
if Some((index, *offset)) == *niche {
*niche = Some((index, newval));
}
*offset = newval;
};
let (offsets, memory_index) = if let FieldsShape::Arbitrary {
ref mut offsets,
ref mut memory_index,
} = layout.fields
{
(offsets, memory_index)
} else {
bug!("univariant has FieldsShape: {:?}", layout.fields);
};
// ZST fields can happily live at whatever offset
let non_zst = (0..offsets.len())
.map(|i| (i, def.fields[i].ty(tcx, substs)))
.map(|(i, ty)| (i, tcx.layout_of(param_env.and(ty)).unwrap()))
.filter(|(_, ty)| !ty.is_zst());

match layout.abi {
// Apparently you're supposed to reserve space for non-ZST uninhabited fields
Abi::Aggregate { .. } => {
for (i, ty) in non_zst {
let newofs = find_or_add(i, offsets[i], ty)?;
assign_and_check(&mut offsets[i], newofs);
}
// rebuild memory_index
let mut new_memindex: Vec<_> = (0..offsets.len() as u32).collect();
new_memindex[..].sort_by_key(|i| offsets[*i as usize]);
*memory_index = new_memindex;
}
_ => bug!("non-Aggregate variant in Aggregate enum: {:?}", def),
}

Ok(())
}

pub(crate) fn provide_extern(providers: &mut ExternProviders) {
Expand Down Expand Up @@ -359,15 +530,11 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
// `ScalarPair`.
// There's a few layers that we go through here. First we inspect layout.abi, then if relevant, layout.fields, etc.
match self.abi {
Abi::Uninhabited => SpirvType::Adt {
def_id: def_id_for_spirv_type_adt(*self),
size: Some(Size::ZERO),
align: Align::from_bytes(0).unwrap(),
field_types: Vec::new(),
field_offsets: Vec::new(),
field_names: None,
}
.def_with_name(cx, span, TyLayoutNameKey::from(*self)),
Abi::Uninhabited => SpirvType::zst(def_id_for_spirv_type_adt(*self)).def_with_name(
cx,
span,
TyLayoutNameKey::from(*self),
),
Abi::Scalar(ref scalar) => trans_scalar(cx, span, *self, scalar, Size::ZERO),
Abi::ScalarPair(ref a, ref b) => {
// Note: We can't use auto_struct_layout here because the spirv types here might be undefined due to
Expand All @@ -391,6 +558,7 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
}
}
SpirvType::Adt {
is_enum: false,
def_id: def_id_for_spirv_type_adt(*self),
size,
align: self.align.abi,
Expand Down Expand Up @@ -559,15 +727,11 @@ fn dig_scalar_pointee<'tcx>(

fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
fn create_zst<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
SpirvType::Adt {
def_id: def_id_for_spirv_type_adt(ty),
size: Some(Size::ZERO),
align: Align::from_bytes(0).unwrap(),
field_types: Vec::new(),
field_offsets: Vec::new(),
field_names: None,
}
.def_with_name(cx, span, TyLayoutNameKey::from(ty))
SpirvType::zst(def_id_for_spirv_type_adt(ty)).def_with_name(
cx,
span,
TyLayoutNameKey::from(ty),
)
}
match ty.fields {
FieldsShape::Primitive => span_bug!(
Expand Down Expand Up @@ -617,11 +781,78 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
.def(span, cx)
}
}
FieldsShape::Arbitrary {
offsets: _,
memory_index: _,
} => trans_struct(cx, span, ty),
FieldsShape::Arbitrary { .. } => {
if let (Variants::Multiple { .. }, TyKind::Adt(_, _)) = (&ty.variants, ty.ty.kind()) {
trans_enum(cx, span, ty)
} else {
trans_struct(cx, span, ty)
}
}
}
}

fn trans_enum<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
let (variants, tag) = if let Variants::Multiple {
ref variants,
ref tag,
..
} = ty.variants
{
(variants, tag)
} else {
unreachable!();
};
let tag_offset = ty.fields.offset(0);
let mut field_cache = FxHashMap::default();
let mut field_offsets = vec![tag_offset];
field_cache.insert(
tag_offset,
(
"discriminant".to_string(),
trans_scalar(cx, span, ty, tag, tag_offset),
),
);
let def = if let TyKind::Adt(ref def, _) = ty.ty.kind() {
def
} else {
unreachable!()
};

for index in variants.indices() {
let variant_ty = ty.for_variant(cx, index);
for field in 0..variant_ty.fields.count() {
let offset = variant_ty.fields.offset(field);
match field_cache.entry(offset) {
Entry::Vacant(entry) => {
field_offsets.push(offset);
entry.insert((
def.variants[index].fields[field].ident.to_string(),
variant_ty.field(cx, field).spirv_type(span, cx),
));
}
Entry::Occupied(_) => {
// FIXME(mobius): record additional names?
}
}
}
}
field_offsets[1..].sort();

let (field_names, field_types) = field_offsets
.iter()
.map(|ofs| field_cache.remove(ofs).unwrap())
.unzip();

SpirvType::Adt {
is_enum: true,
def_id: def_id_for_spirv_type_adt(ty),
size: Some(ty.size),
align: ty.align.abi,
field_types,
field_offsets,
field_names: Some(field_names),
}
.def_with_name(cx, span, TyLayoutNameKey::from(ty))
}

// returns (field_offsets, size, align)
Expand Down Expand Up @@ -656,31 +887,25 @@ fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -
let mut field_types = Vec::new();
let mut field_offsets = Vec::new();
let mut field_names = Vec::new();
let index = if let Variants::Single { index } = ty.variants {
index
} else {
unreachable!()
};
for i in ty.fields.index_by_increasing_offset() {
let field_ty = ty.field(cx, i);
field_types.push(field_ty.spirv_type(span, cx));
let offset = ty.fields.offset(i);
field_offsets.push(offset);
if let Variants::Single { index } = ty.variants {
if let TyKind::Adt(adt, _) = ty.ty.kind() {
let field = &adt.variants[index].fields[i];
field_names.push(field.ident.name.to_ident_string());
} else {
field_names.push(format!("{}", i));
}
if let TyKind::Adt(adt, _) = ty.ty.kind() {
let field = &adt.variants[index].fields[i];
field_names.push(field.ident.name.to_ident_string());
} else {
if let TyKind::Adt(_, _) = ty.ty.kind() {
} else {
span_bug!(span, "Variants::Multiple not TyKind::Adt");
}
if i == 0 {
field_names.push("discriminant".to_string());
} else {
cx.tcx.sess.fatal("Variants::Multiple has multiple fields")
}
};
field_names.push(format!("{}", i));
}
}
SpirvType::Adt {
is_enum: false,
def_id: def_id_for_spirv_type_adt(ty),
size,
align,
Expand Down
Loading