feat(ast_codegen): add alignment and size data to the schema. (#4615)

This PR generates the layouts in the schema but doesn't use it to reorder.
This commit is contained in:
rzvxa 2024-08-03 12:43:10 +00:00
parent 3f53b6f586
commit 0c52c0db02
11 changed files with 3038 additions and 9 deletions

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,8 @@ pub mod syntax_directed_operations;
mod trivia;
mod generated {
#[cfg(test)]
pub mod assert_layouts;
pub mod ast_builder;
pub mod ast_kind;
pub mod span;

View file

@ -1,5 +1,5 @@
use super::{REnum, RStruct, RType};
use crate::{schema::Inherit, util::TypeExt, TypeName};
use crate::{layout::KnownLayout, schema::Inherit, util::TypeExt, TypeName};
use quote::ToTokens;
use serde::Serialize;
@ -9,11 +9,26 @@ pub enum TypeDef {
Enum(EnumDef),
}
impl TypeDef {
pub fn name(&self) -> &String {
match self {
Self::Struct(it) => &it.name,
Self::Enum(it) => &it.name,
}
}
}
#[derive(Debug, Serialize)]
pub struct StructDef {
pub name: TypeName,
pub fields: Vec<FieldDef>,
pub has_lifetime: bool,
pub size_64: usize,
pub align_64: usize,
pub offsets_64: Option<Vec<usize>>,
pub size_32: usize,
pub align_32: usize,
pub offsets_32: Option<Vec<usize>>,
}
#[derive(Debug, Serialize)]
@ -23,6 +38,12 @@ pub struct EnumDef {
/// For `@inherits` inherited enum variants
pub inherits: Vec<EnumInheritDef>,
pub has_lifetime: bool,
pub size_64: usize,
pub align_64: usize,
pub offsets_64: Option<Vec<usize>>,
pub size_32: usize,
pub align_32: usize,
pub offsets_32: Option<Vec<usize>>,
}
#[derive(Debug, Serialize)]
@ -57,21 +78,55 @@ impl From<&RType> for Option<TypeDef> {
impl From<&REnum> for EnumDef {
fn from(it @ REnum { item, meta }: &REnum) -> Self {
let (size_64, align_64, offsets_64) = meta
.layout_64
.clone()
.layout()
.map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack);
let (size_32, align_32, offsets_32) = meta
.layout_32
.clone()
.layout()
.map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack);
Self {
name: it.ident().to_string(),
variants: item.variants.iter().map(Into::into).collect(),
has_lifetime: item.generics.lifetimes().count() > 0,
inherits: meta.inherits.iter().map(Into::into).collect(),
has_lifetime: item.generics.lifetimes().count() > 0,
size_64,
align_64,
offsets_64,
size_32,
align_32,
offsets_32,
}
}
}
impl From<&RStruct> for StructDef {
fn from(it @ RStruct { item, .. }: &RStruct) -> Self {
fn from(it @ RStruct { item, meta }: &RStruct) -> Self {
let (size_64, align_64, offsets_64) = meta
.layout_64
.clone()
.layout()
.map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack);
let (size_32, align_32, offsets_32) = meta
.layout_32
.clone()
.layout()
.map_or_else(|| panic!("Uncalculated layout on {}!", item.ident), KnownLayout::unpack);
Self {
name: it.ident().to_string(),
fields: item.fields.iter().map(Into::into).collect(),
has_lifetime: item.generics.lifetimes().count() > 0,
size_64,
align_64,
offsets_64,
size_32,
align_32,
offsets_32,
}
}
}

View file

@ -0,0 +1,118 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{PathArguments, Type};
use crate::{
defs::{FieldDef, TypeDef},
output, CodegenCtx, Generator, GeneratorOutput,
};
use super::{define_generator, generated_header};
define_generator! {
pub struct AssertLayouts;
}
impl Generator for AssertLayouts {
fn name(&self) -> &'static str {
stringify!(AssertLayouts)
}
fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput {
let (assertions_64, assertions_32) = ctx
.schema
.borrow()
.definitions
.iter()
.map(|def| {
let typ =
ctx.find(def.name()).and_then(|ty| ty.borrow().as_type()).map(|mut ty| {
if let Type::Path(ty) = &mut ty {
if let Some(seg) = ty.path.segments.first_mut() {
seg.arguments = PathArguments::None;
}
}
ty
});
let typ = typ.unwrap();
assert_type(&typ, def)
})
.collect::<(Vec<TokenStream>, Vec<TokenStream>)>();
let header = generated_header!();
GeneratorOutput::Stream((
output(crate::AST_CRATE, "assert_layouts.rs"),
quote! {
#header
use std::mem::{align_of, offset_of, size_of};
endl!();
use crate::ast::*;
endl!();
#[cfg(target_pointer_width = "64")]
const _: () = { #(#assertions_64)* };
endl!();
#[cfg(target_pointer_width = "32")]
const _: () = { #(#assertions_32)* };
endl!();
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
const _: () = panic!("Platforms with pointer width other than 64 or 32 bit are not supported");
},
))
}
}
fn assert_type(ty: &Type, def: &TypeDef) -> (TokenStream, TokenStream) {
match def {
TypeDef::Struct(def) => (
with_offsets_assertion(
assert_size_align(ty, def.size_64, def.align_64),
ty,
&def.fields,
def.offsets_64.as_deref(),
),
with_offsets_assertion(
assert_size_align(ty, def.size_32, def.align_32),
ty,
&def.fields,
def.offsets_64.as_deref(),
),
),
TypeDef::Enum(def) => (
assert_size_align(ty, def.size_64, def.align_64),
assert_size_align(ty, def.size_32, def.align_32),
),
}
}
fn assert_size_align(ty: &Type, size: usize, align: usize) -> TokenStream {
quote! {
assert!(size_of::<#ty>() == #size);
assert!(align_of::<#ty>() == #align);
}
}
fn with_offsets_assertion(
mut tk: TokenStream,
ty: &Type,
fields: &[FieldDef],
offsets: Option<&[usize]>,
) -> TokenStream {
let Some(offsets) = offsets else { return tk };
let assertions = fields.iter().zip(offsets).map(|(field, offset)| {
let field = field.name.as_ref().map(|it| format_ident!("{it}"));
quote! {
assert!(offset_of!(#ty, #field) == #offset);
}
});
tk.extend(assertions);
tk
}

View file

@ -1,3 +1,4 @@
mod assert_layouts;
mod ast_builder;
mod ast_kind;
mod impl_get_span;
@ -39,6 +40,7 @@ macro_rules! generated_header {
pub(crate) use generated_header;
pub(crate) use insert;
pub use assert_layouts::AssertLayouts;
pub use ast_builder::AstBuilderGenerator;
pub use ast_kind::AstKindGenerator;
pub use impl_get_span::ImplGetSpanGenerator;

View file

@ -0,0 +1,181 @@
#[derive(Debug, Default, Clone)]
pub enum Layout {
#[default]
Unknown,
Layout(KnownLayout),
}
impl Layout {
pub const fn known(size: usize, align: usize, niches: u128) -> Self {
Self::Layout(KnownLayout { size, align, niches, offsets: None })
}
pub fn layout(self) -> Option<KnownLayout> {
if let Self::Layout(layout) = self {
Some(layout)
} else {
None
}
}
}
impl From<KnownLayout> for Layout {
fn from(layout: KnownLayout) -> Self {
Self::Layout(layout)
}
}
#[derive(Debug, Default, Clone)]
pub struct KnownLayout {
size: usize,
align: usize,
/// number of available niches
niches: u128,
offsets: Option<Vec<usize>>,
}
impl KnownLayout {
pub const fn new(size: usize, align: usize, niches: u128) -> Self {
Self { size, align, niches, offsets: None }
}
#[inline]
pub fn size(&self) -> usize {
self.size
}
#[inline]
pub fn align(&self) -> usize {
self.align
}
/// number of available niches
#[inline]
pub fn niches(&self) -> u128 {
self.niches
}
#[inline]
pub fn offsets(&self) -> Option<&Vec<usize>> {
self.offsets.as_ref()
}
pub unsafe fn set_size_unchecked(&mut self, size: usize) {
self.size = size;
}
pub unsafe fn set_align_unchecked(&mut self, align: usize) {
self.align = align;
}
pub unsafe fn set_niches_unchecked(&mut self, niches: u128) {
self.niches = niches;
}
pub fn with_offsets(mut self, offsets: Vec<usize>) -> Self {
self.offsets = Some(offsets);
self
}
/// Panics
/// if doesn't have enough viable space and `can_resize` is false
pub fn consume_niches(&mut self, n: u128, can_resize: bool) {
if self.niches() >= n {
self.niches -= n;
} else if can_resize {
let align = self.align();
self.size += align;
self.niches += max_val_of_bytes(align);
self.consume_niches(n, can_resize);
} else {
panic!("`{}` called on a layout without enough space.", stringify!(consume_niches));
}
}
pub fn unpack(self) -> (/* size */ usize, /* align */ usize, /* offsets */ Option<Vec<usize>>) {
let Self { size, align, offsets, .. } = self;
(size, align, offsets)
}
}
impl Layout {
/// # Panics
/// If `T` has more than 8 niches.
pub const fn of<T>() -> Self {
// TODO: find a better way of calculating this.
struct N1<T>(Option<T>);
struct N2<T>(N1<N1<T>>);
struct N3<T>(N1<N2<T>>);
struct N4<T>(N1<N3<T>>);
struct N5<T>(N1<N4<T>>);
struct N6<T>(N1<N5<T>>);
struct N7<T>(N1<N6<T>>);
struct N8<T>(N1<N7<T>>);
let size = size_of::<T>();
let align = align_of::<T>();
let niches = if size_of::<N1<T>>() > size {
0
} else if size_of::<N2<T>>() > size {
1
} else if size_of::<N3<T>>() > size {
2
} else if size_of::<N4<T>>() > size {
3
} else if size_of::<N5<T>>() > size {
4
} else if size_of::<N6<T>>() > size {
5
} else if size_of::<N7<T>>() > size {
6
} else if size_of::<N8<T>>() > size {
7
} else if size_of::<N8<T>>() == size {
8
} else {
panic!("`T` has more niches than what we can infer automatically");
};
Self::known(size, align, niches)
}
pub const fn zero() -> Self {
#[repr(C)]
struct Empty;
Self::of::<Empty>()
}
pub const fn ptr_32() -> Self {
Layout::known(4, 4, 0)
}
pub const fn ptr_64() -> Self {
Layout::known(8, 8, 0)
}
pub const fn wide_ptr_32() -> Self {
Layout::known(8, 4, 1)
}
pub const fn wide_ptr_64() -> Self {
Layout::of::<&str>()
}
pub fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
}
/// Returns the max valid number in a primitive with the size of `n` bytes.
/// Panics
/// For `n` bigger than `16`, Or if it's not a power of 2 number
fn max_val_of_bytes(n: usize) -> u128 {
match n {
1 => u8::MAX as u128,
2 => u16::MAX as u128,
4 => u32::MAX as u128,
8 => u64::MAX as u128,
16 => u128::MAX,
_ => panic!("We do not support `n` bigger than 16 bytes."),
}
}

View file

@ -1,8 +1,11 @@
const AST_CRATE: &str = "crates/oxc_ast";
#[allow(dead_code)]
const AST_MACROS_CRATE: &str = "crates/oxc_ast_macros";
mod defs;
mod fmt;
mod generators;
mod layout;
mod markers;
mod passes;
mod schema;
@ -13,13 +16,14 @@ use std::{cell::RefCell, collections::HashMap, io::Read, path::PathBuf, rc::Rc};
use bpaf::{Bpaf, Parser};
use fmt::{cargo_fmt, pprint};
use itertools::Itertools;
use passes::{BuildSchema, Linker, Pass};
use passes::{BuildSchema, CalcLayout, Linker, Pass};
use proc_macro2::TokenStream;
use syn::parse_file;
use defs::TypeDef;
use generators::{
AstBuilderGenerator, AstKindGenerator, Generator, VisitGenerator, VisitMutGenerator,
AssertLayouts, AstBuilderGenerator, AstKindGenerator, Generator, VisitGenerator,
VisitMutGenerator,
};
use schema::{Module, REnum, RStruct, RType, Schema};
use util::{write_all_to, NormalizeError};
@ -258,7 +262,9 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let CodegenResult { outputs, schema } = files()
.fold(AstCodegen::default(), AstCodegen::add_file)
.pass(Linker)
.pass(CalcLayout)
.pass(BuildSchema)
.gen(AssertLayouts)
.gen(AstKindGenerator)
.gen(AstBuilderGenerator)
.gen(ImplGetSpanGenerator)

View file

@ -0,0 +1,370 @@
/// We use compiler to infer 64bit type layouts.
#[cfg(not(target_pointer_width = "64"))]
compile_error!("`oxc_ast_codegen::layout` only supports 64 architectures.");
use std::collections::HashMap;
use itertools::Itertools;
use lazy_static::lazy_static;
use quote::ToTokens;
use syn::Type;
use crate::{
layout::{KnownLayout, Layout},
schema::{REnum, RStruct, RType},
util::{NormalizeError, TypeAnalyzeResult, TypeExt, TypeWrapper},
CodegenCtx, Result, TypeRef,
};
use super::{define_pass, Pass};
type WellKnown = HashMap<&'static str, PlatformLayout>;
define_pass! {
pub struct CalcLayout;
}
impl Pass for CalcLayout {
fn name(&self) -> &'static str {
stringify!(CalcLayout)
}
fn each(&mut self, ty: &mut RType, ctx: &CodegenCtx) -> crate::Result<bool> {
calc_layout(ty, ctx)
}
}
#[derive(Debug, Clone)]
struct PlatformLayout(/* 64 */ Layout, /* 32 */ Layout);
impl PlatformLayout {
const UNKNOWN: Self = Self(Layout::Unknown, Layout::Unknown);
const fn zero() -> Self {
Self(Layout::zero(), Layout::zero())
}
const fn ptr() -> Self {
Self(Layout::ptr_64(), Layout::ptr_32())
}
const fn wide_ptr() -> Self {
Self(Layout::wide_ptr_64(), Layout::wide_ptr_32())
}
/// Return `true` if either of platform layouts is unknown.
fn is_unknown(&self) -> bool {
self.0.is_unknown() || self.1.is_unknown()
}
}
impl From<(Layout, Layout)> for PlatformLayout {
fn from((x64, x32): (Layout, Layout)) -> Self {
Self(x64, x32)
}
}
/// Calculates the layout of `ty` by mutating it.
/// Returns `false` if the layout is unknown at this point.
pub fn calc_layout(ty: &mut RType, ctx: &CodegenCtx) -> Result<bool> {
let unknown_layout = ty
.layout_32()
.and_then(|x32| ty.layout_64().map(|x64| PlatformLayout(x64, x32)))
.is_ok_and(|pl| pl.is_unknown());
let layout = match ty {
RType::Enum(enum_) if unknown_layout => calc_enum_layout(enum_, ctx),
RType::Struct(struct_) if unknown_layout => calc_struct_layout(struct_, ctx),
_ => return Ok(true),
}?;
if layout.is_unknown() {
Ok(false)
} else {
let PlatformLayout(x64, x32) = layout;
ty.set_layout(x64, x32)?;
Ok(true)
}
}
fn calc_enum_layout(ty: &mut REnum, ctx: &CodegenCtx) -> Result<PlatformLayout> {
fn collect_variant_layouts(ty: &REnum, ctx: &CodegenCtx) -> Result<Vec<PlatformLayout>> {
// all unit variants?
if ty.item.variants.iter().all(|var| var.fields.is_empty()) {
// all AST enums are `repr(u8)` so it would have a 1 byte layout/alignment,
// if it holds no data
let layout = KnownLayout::new(0, 1, 0);
let layout = Layout::Layout(layout);
Ok(vec![PlatformLayout(layout.clone(), layout)])
} else {
ty.item
.variants
.iter()
.map(|var| {
let typ =
var.fields.iter().exactly_one().map(|f| f.ty.analyze(ctx)).normalize()?;
calc_type_layout(&typ, ctx)
})
.collect()
}
}
#[allow(clippy::needless_pass_by_value)]
fn fold_layout(mut acc: KnownLayout, layout: KnownLayout) -> KnownLayout {
// SAFETY: we are folding valid layouts so it is safe.
unsafe {
// max alignment
if layout.align() > acc.align() {
acc.set_align_unchecked(layout.align());
}
// max size
if layout.size() > acc.size() {
acc.set_size_unchecked(layout.size());
}
// min niches
if layout.niches() < acc.niches() {
acc.set_niches_unchecked(layout.niches());
}
}
acc
}
let with_tag = |mut acc: KnownLayout| -> KnownLayout {
acc.consume_niches(ty.item.variants.len() as u128, true);
acc
};
let layouts = collect_variant_layouts(ty, ctx)?;
let (layouts_x64, layouts_x32): (Vec<KnownLayout>, Vec<KnownLayout>) = layouts
.into_iter()
.map(|PlatformLayout(x64, x32)| {
x64.layout().and_then(|x64| x32.layout().map(|x32| (x64, x32)))
})
.collect::<Option<_>>()
.expect("already checked.");
let x32 = with_tag(layouts_x32.into_iter().fold(KnownLayout::default(), fold_layout));
let x64 = with_tag(layouts_x64.into_iter().fold(KnownLayout::default(), fold_layout));
Ok(PlatformLayout(Layout::from(x64), Layout::from(x32)))
}
fn calc_struct_layout(ty: &mut RStruct, ctx: &CodegenCtx) -> Result<PlatformLayout> {
fn collect_field_layouts(ty: &RStruct, ctx: &CodegenCtx) -> Result<Vec<PlatformLayout>> {
if ty.item.fields.is_empty() {
Ok(vec![PlatformLayout::zero()])
} else {
ty.item
.fields
.iter()
.map(|field| {
let typ = field.ty.analyze(ctx);
calc_type_layout(&typ, ctx)
})
.collect()
}
}
fn with_padding(
layouts: &[KnownLayout],
) -> std::result::Result<KnownLayout, std::alloc::LayoutError> {
let layouts = layouts.iter().enumerate();
let mut offsets = vec![0; layouts.len()];
let mut output = std::alloc::Layout::from_size_align(0, 1)?;
let mut niches = 0;
for (ix, layout) in layouts {
let (new_layout, offset) = output
.extend(std::alloc::Layout::from_size_align(layout.size(), layout.align())?)?;
output = new_layout;
niches += layout.niches();
offsets[ix] = offset;
}
let output = output.pad_to_align();
Ok(KnownLayout::new(output.size(), output.align(), niches).with_offsets(offsets))
}
let layouts = collect_field_layouts(ty, ctx)?;
if layouts.iter().any(PlatformLayout::is_unknown) {
return Ok(PlatformLayout::UNKNOWN);
}
let (layouts_x64, layouts_x32): (Vec<KnownLayout>, Vec<KnownLayout>) = layouts
.into_iter()
.map(|PlatformLayout(x64, x32)| {
x64.layout().and_then(|x64| x32.layout().map(|x32| (x64, x32)))
})
.collect::<Option<_>>()
.expect("already checked.");
let x32 = with_padding(&layouts_x32).normalize()?;
let x64 = with_padding(&layouts_x64).normalize()?;
Ok(PlatformLayout(Layout::from(x64), Layout::from(x32)))
}
fn calc_type_layout(ty: &TypeAnalyzeResult, ctx: &CodegenCtx) -> Result<PlatformLayout> {
fn is_slice(ty: &TypeAnalyzeResult) -> bool {
if let Type::Reference(typ) = &ty.typ {
// TODO: support for &[T] slices.
typ.elem.get_ident().as_ident().is_some_and(|id| id == "str")
} else {
false
}
}
fn try_fold_option(layout: Layout) -> Layout {
let Layout::Layout(mut known) = layout else { return layout };
// option needs only one niche, We allow resizing in case there isn't enough space.
known.consume_niches(1, true);
Layout::Layout(known)
}
let get_layout = |type_ref: Option<&TypeRef>| -> Result<PlatformLayout> {
let result = if let Some(type_ref) = &type_ref {
if calc_layout(&mut type_ref.borrow_mut(), ctx)? {
type_ref.borrow().layouts().map(PlatformLayout::from)?
} else {
PlatformLayout::UNKNOWN
}
} else if let Some(well_known) =
WELL_KNOWN.get(ty.typ.get_ident().inner_ident().to_string().as_str())
{
well_known.clone()
} else {
let Type::Path(typ) = &ty.typ else {
panic!();
};
let typ = typ
.path
.segments
.first()
.map(|it| it.to_token_stream().to_string().replace(' ', ""))
.expect("We only accept single segment types.");
if let Some(typ) = WELL_KNOWN.get(typ.as_str()) {
typ.clone()
} else {
panic!("Unsupported type: {:#?}", ty.typ.to_token_stream().to_string())
}
};
Ok(result)
};
let layout = match ty.wrapper {
TypeWrapper::Vec | TypeWrapper::VecBox | TypeWrapper::VecOpt => {
WELL_KNOWN[stringify!(Vec)].clone()
}
TypeWrapper::OptVec => {
let mut pl = WELL_KNOWN[stringify!(Vec)].clone();
// preconsume one niche for option
if let Layout::Layout(layout) = &mut pl.0 {
layout.consume_niches(1, true);
}
if let Layout::Layout(layout) = &mut pl.1 {
layout.consume_niches(1, true);
}
pl
}
TypeWrapper::Ref if is_slice(ty) => PlatformLayout::wide_ptr(),
TypeWrapper::Ref | TypeWrapper::Box | TypeWrapper::OptBox => PlatformLayout::ptr(),
TypeWrapper::None => get_layout(ty.type_ref.as_ref())?,
TypeWrapper::Opt => {
let PlatformLayout(x64, x32) = get_layout(ty.type_ref.as_ref())?;
PlatformLayout(try_fold_option(x64), try_fold_option(x32))
}
TypeWrapper::Complex => {
let PlatformLayout(x64, x32) = get_layout(ty.type_ref.as_ref())?;
PlatformLayout(x64, x32)
}
};
Ok(layout)
}
macro_rules! well_known {
($($typ:ty: { $($platform:tt => $layout:expr,)*},)*) => {
WellKnown::from([
$((
stringify!($typ),
well_known!(@ $( $platform => $layout,)*)
)),*
])
};
// entries
(@ _ => $layout:expr,) => {
PlatformLayout($layout, $layout)
};
(@ 64 => $layout_64:expr, 32 => $layout_32:expr,) => {
PlatformLayout($layout_64, $layout_32)
};
(@ 32 => $layout_32:expr, 64 => $layout_64:expr,) => {
well_known!(@ 64 => $layout_64, 32 => $layout_32)
};
// compile errors
(@ 32 => $layout:expr,) => {
::std::compile_error!("non_exhaustive well known type, `64` target isn't covered.")
};
(@ 64 => $layout:expr,) => {
::std::compile_error!("non_exhaustive well known type, `32` target isn't covered.")
};
}
lazy_static! {
static ref WELL_KNOWN: WellKnown = well_known! {
// builtins
// types smaller or equal to 4bytes have the same layout on most platforms.
char: { _ => Layout::of::<char>(), },
bool: { _ => Layout::of::<bool>(), },
u8: { _ => Layout::of::<u8>(), },
i8: { _ => Layout::of::<i8>(), },
u16: { _ => Layout::of::<u16>(), },
i16: { _ => Layout::of::<i16>(), },
u32: { _ => Layout::of::<u32>(), },
i32: { _ => Layout::of::<i32>(), },
f32: { _ => Layout::of::<f32>(), },
// 32bit layouts are based on WASM
u64: {
64 => Layout::of::<u64>(),
32 => Layout::known(8, 8, 0),
},
i64: {
64 => Layout::of::<i64>(),
32 => Layout::known(8, 8, 0),
},
f64: {
64 => Layout::of::<f64>(),
32 => Layout::known(8, 8, 0),
},
usize: {
64 => Layout::of::<usize>(),
32 => Layout::known(4, 4, 0),
},
isize: {
64 => Layout::of::<isize>(),
32 => Layout::known(4, 4, 0),
},
// well known types
// TODO: generate const assertions for these in the ast crate
Span: { _ => Layout::known(8, 4, 0), },
Atom: {
64 => Layout::wide_ptr_64(),
32 => Layout::wide_ptr_32(),
},
Vec: {
64 => Layout::known(32, 8, 1),
32 => Layout::known(16, 4, 1),
},
Cell<Option<ScopeId>>: { _ => Layout::known(4, 4, 0), },
Cell<Option<SymbolId>>: { _ => Layout::known(4, 4, 0), },
Cell<Option<ReferenceId>>: { _ => Layout::known(4, 4, 0), },
ReferenceFlag: { _ => Layout::known(1, 1, 0), },
AssignmentOperator: { _ => Layout::known(1, 1, 1), },
LogicalOperator: { _ => Layout::known(1, 1, 1), },
UnaryOperator: { _ => Layout::known(1, 1, 1), },
BinaryOperator: { _ => Layout::known(1, 1, 1), },
UpdateOperator: { _ => Layout::known(1, 1, 1), },
SourceType: { _ => Layout::known(4, 1, 1), },
RegExpFlags: { _ => Layout::known(1, 1, 0), },
BigintBase: { _ => Layout::known(1, 1, 1), },
NumberBase: { _ => Layout::known(1, 1, 1), },
};
}

View file

@ -1,4 +1,5 @@
mod build_schema;
mod calc_layout;
mod linker;
use std::collections::VecDeque;
@ -8,6 +9,7 @@ use itertools::Itertools;
use crate::{schema::RType, CodegenCtx, Result};
pub use build_schema::BuildSchema;
pub use calc_layout::CalcLayout;
pub use linker::Linker;
pub trait Pass {

View file

@ -9,13 +9,13 @@ use syn::{
Path, Token, Type, Variant, Visibility,
};
use crate::{util::NormalizeError, TypeName};
use crate::{layout::Layout, util::NormalizeError, TypeName};
use super::{parse_file, Itertools, PathBuf, Rc, Read, RefCell, Result, TypeDef, TypeRef};
#[derive(Debug, Default, serde::Serialize)]
pub struct Schema {
definitions: Vec<TypeDef>,
pub definitions: Vec<TypeDef>,
}
#[derive(Debug, Clone)]
@ -33,6 +33,8 @@ impl From<Ident> for Inherit {
#[derive(Debug, Default, Clone)]
pub struct EnumMeta {
pub inherits: Vec<Inherit>,
pub layout_32: Layout,
pub layout_64: Layout,
pub visitable: bool,
pub ast: bool,
}
@ -68,6 +70,8 @@ impl From<ItemEnum> for REnum {
/// Placeholder for now!
#[derive(Debug, Default, Clone)]
pub struct StructMeta {
pub layout_32: Layout,
pub layout_64: Layout,
pub visitable: bool,
pub ast: bool,
}
@ -170,6 +174,41 @@ impl RType {
}
Ok(())
}
pub fn layout_32(&self) -> Result<Layout> {
match self {
RType::Enum(it) => Ok(it.meta.layout_32.clone()),
RType::Struct(it) => Ok(it.meta.layout_32.clone()),
_ => Err("Unsupported type!".to_string()),
}
}
pub fn layout_64(&self) -> Result<Layout> {
match self {
RType::Enum(it) => Ok(it.meta.layout_64.clone()),
RType::Struct(it) => Ok(it.meta.layout_64.clone()),
_ => Err("Unsupported type!".to_string()),
}
}
pub fn layouts(&self) -> Result<(/* 64 */ Layout, /* 32 */ Layout)> {
self.layout_64().and_then(|x64| self.layout_32().map(|x32| (x64, x32)))
}
pub fn set_layout(&mut self, layout_64: Layout, layout_32: Layout) -> Result<()> {
macro_rules! assign {
($it:ident) => {{
$it.meta.layout_32 = layout_32;
$it.meta.layout_64 = layout_64;
}};
}
match self {
RType::Enum(it) => assign!(it),
RType::Struct(it) => assign!(it),
_ => return Err("Unsupported type!".to_string()),
}
Ok(())
}
}
impl TryFrom<Item> for RType {

View file

@ -132,6 +132,7 @@ pub enum TypeWrapper {
pub struct TypeAnalyzeResult {
pub type_ref: Option<TypeRef>,
pub wrapper: TypeWrapper,
pub typ: Type,
}
impl TypeExt for Type {
@ -225,11 +226,15 @@ impl TypeExt for Type {
}
let type_ident = self.get_ident();
let Some((type_ident, wrapper)) = analyze(&type_ident) else {
return TypeAnalyzeResult { type_ref: None, wrapper: TypeWrapper::Ref };
return TypeAnalyzeResult {
type_ref: None,
wrapper: TypeWrapper::Ref,
typ: self.clone(),
};
};
let type_ref = ctx.find(&type_ident.to_string());
TypeAnalyzeResult { type_ref, wrapper }
TypeAnalyzeResult { type_ref, wrapper, typ: self.clone() }
}
}