Calculate the TargetDataLayout correctly for the selected target

This commit is contained in:
Lukas Wirth 2022-12-21 15:11:24 +01:00
parent 9ed1829f1f
commit 33591cd3f4
15 changed files with 247 additions and 62 deletions

View file

@ -64,8 +64,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
#[salsa::invoke(crate::layout::current_target_data_layout_query)]
fn current_target_data_layout(&self) -> Arc<TargetDataLayout>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Arc<TargetDataLayout>;
#[salsa::invoke(crate::lower::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;

View file

@ -2,6 +2,7 @@
use std::sync::Arc;
use base_db::CrateId;
use chalk_ir::{AdtId, TyKind};
use hir_def::{
layout::{
@ -17,7 +18,7 @@ use crate::{db::HirDatabase, Interner, Substitution, Ty};
use self::adt::struct_variant_idx;
pub use self::{
adt::{layout_of_adt_query, layout_of_adt_recover},
target::current_target_data_layout_query,
target::target_data_layout_query,
};
macro_rules! user_error {
@ -31,6 +32,7 @@ mod target;
struct LayoutCx<'a> {
db: &'a dyn HirDatabase,
krate: CrateId,
}
impl LayoutCalculator for LayoutCx<'_> {
@ -41,7 +43,7 @@ impl LayoutCalculator for LayoutCx<'_> {
}
fn current_data_layout(&self) -> Arc<TargetDataLayout> {
self.db.current_target_data_layout()
self.db.target_data_layout(self.krate)
}
}
@ -53,9 +55,9 @@ fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
Layout::scalar(dl, scalar_unit(dl, value))
}
pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError> {
let dl = &*db.current_target_data_layout();
let cx = LayoutCx { db };
pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
let cx = LayoutCx { db, krate };
let dl = &*cx.current_data_layout();
Ok(match ty.kind(Interner) {
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
TyKind::Scalar(s) => match s {
@ -84,7 +86,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError
chalk_ir::IntTy::I64 => Integer::I64,
chalk_ir::IntTy::I128 => Integer::I128,
},
false,
true,
),
),
chalk_ir::Scalar::Uint(i) => scalar(
@ -98,7 +100,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError
chalk_ir::UintTy::U64 => Integer::I64,
chalk_ir::UintTy::U128 => Integer::I128,
},
true,
false,
),
),
chalk_ir::Scalar::Float(f) => scalar(
@ -114,7 +116,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError
let fields = tys
.iter(Interner)
.map(|k| layout_of_ty(db, k.assert_ty_ref(Interner)))
.map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
.collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>();
@ -132,7 +134,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError
},
_ => return Err(LayoutError::HasPlaceholder),
};
let element = layout_of_ty(db, element)?;
let element = layout_of_ty(db, element, krate)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
@ -153,7 +155,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError
}
}
TyKind::Slice(element) => {
let element = layout_of_ty(db, element)?;
let element = layout_of_ty(db, element, krate)?;
Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: element.size, count: 0 },

View file

@ -5,7 +5,7 @@ use std::ops::Bound;
use hir_def::{
adt::VariantData,
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;
@ -23,12 +23,12 @@ pub fn layout_of_adt_query(
def: AdtId,
subst: Substitution,
) -> Result<Layout, LayoutError> {
let dl = db.current_target_data_layout();
let cx = LayoutCx { db };
let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() };
let dl = cx.current_data_layout();
let handle_variant = |def: VariantId, var: &VariantData| {
var.fields()
.iter()
.map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst)))
.map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
.collect::<Result<Vec<_>, _>>()
};
let (variants, is_enum, is_union, repr) = match def {

View file

@ -2,45 +2,130 @@
use std::sync::Arc;
use hir_def::layout::TargetDataLayout;
use base_db::CrateId;
use hir_def::layout::{TargetDataLayout, TargetDataLayoutErrors};
use crate::db::HirDatabase;
use hir_def::layout::{AbiAndPrefAlign, AddressSpace, Align, Endian, Integer, Size};
use hir_def::layout::{AbiAndPrefAlign, AddressSpace, Align, Endian, Size};
pub fn current_target_data_layout_query(db: &dyn HirDatabase) -> Arc<TargetDataLayout> {
pub fn target_data_layout_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<TargetDataLayout> {
let crate_graph = db.crate_graph();
let cfg_options = &crate_graph[crate_graph.iter().next().unwrap()].cfg_options;
let endian = match cfg_options.get_cfg_values("target_endian").next() {
Some(x) if x.as_str() == "big" => Endian::Big,
_ => Endian::Little,
};
let pointer_size =
Size::from_bytes(match cfg_options.get_cfg_values("target_pointer_width").next() {
Some(x) => match x.as_str() {
"16" => 2,
"32" => 4,
_ => 8,
},
_ => 8,
});
// FIXME: These values are incorrect for many architectures, at least for aarch64 and riscv64,
// use `rustc +nightly -Z unstable-options --print target-spec-json` or something similar instead.
Arc::new(TargetDataLayout {
endian,
i1_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
i8_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
i16_align: AbiAndPrefAlign::new(Align::from_bytes(2).unwrap()),
i32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
i64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
i128_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
f32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
f64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
pointer_size,
pointer_align: AbiAndPrefAlign::new(Align::from_bytes(pointer_size.bytes()).unwrap()),
aggregate_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
vector_align: vec![],
instruction_address_space: AddressSpace(0),
c_enum_min_size: Integer::I32,
})
let target_layout = &crate_graph[krate].target_layout;
let cfg_options = &crate_graph[krate].cfg_options;
Arc::new(
target_layout
.as_ref()
.and_then(|it| parse_from_llvm_datalayout_string(it).ok())
.unwrap_or_else(|| {
let endian = match cfg_options.get_cfg_values("target_endian").next() {
Some(x) if x.as_str() == "big" => Endian::Big,
_ => Endian::Little,
};
let pointer_size = Size::from_bytes(
match cfg_options.get_cfg_values("target_pointer_width").next() {
Some(x) => match x.as_str() {
"16" => 2,
"32" => 4,
_ => 8,
},
_ => 8,
},
);
TargetDataLayout { endian, pointer_size, ..TargetDataLayout::default() }
}),
)
}
/// copied from rustc as it is not exposed yet
fn parse_from_llvm_datalayout_string<'a>(
input: &'a str,
) -> Result<TargetDataLayout, TargetDataLayoutErrors<'a>> {
// Parse an address space index from a string.
let parse_address_space = |s: &'a str, cause: &'a str| {
s.parse::<u32>().map(AddressSpace).map_err(|err| {
TargetDataLayoutErrors::InvalidAddressSpace { addr_space: s, cause, err }
})
};
// Parse a bit count from a string.
let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| {
s.parse::<u64>().map_err(|err| TargetDataLayoutErrors::InvalidBits {
kind,
bit: s,
cause,
err,
})
};
// Parse a size string.
let size = |s: &'a str, cause: &'a str| parse_bits(s, "size", cause).map(Size::from_bits);
// Parse an alignment string.
let align = |s: &[&'a str], cause: &'a str| {
if s.is_empty() {
return Err(TargetDataLayoutErrors::MissingAlignment { cause });
}
let align_from_bits = |bits| {
Align::from_bits(bits)
.map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err })
};
let abi = parse_bits(s[0], "alignment", cause)?;
let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? })
};
let mut dl = TargetDataLayout::default();
let mut i128_align_src = 64;
for spec in input.split('-') {
let spec_parts = spec.split(':').collect::<Vec<_>>();
match &*spec_parts {
["e"] => dl.endian = Endian::Little,
["E"] => dl.endian = Endian::Big,
[p] if p.starts_with('P') => {
dl.instruction_address_space = parse_address_space(&p[1..], "P")?
}
["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
[p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
dl.pointer_size = size(s, p)?;
dl.pointer_align = align(a, p)?;
}
[s, ref a @ ..] if s.starts_with('i') => {
let Ok(bits) = s[1..].parse::<u64>() else {
size(&s[1..], "i")?; // For the user error.
continue;
};
let a = align(a, s)?;
match bits {
1 => dl.i1_align = a,
8 => dl.i8_align = a,
16 => dl.i16_align = a,
32 => dl.i32_align = a,
64 => dl.i64_align = a,
_ => {}
}
if bits >= i128_align_src && bits <= 128 {
// Default alignment for i128 is decided by taking the alignment of
// largest-sized i{64..=128}.
i128_align_src = bits;
dl.i128_align = a;
}
}
[s, ref a @ ..] if s.starts_with('v') => {
let v_size = size(&s[1..], "v")?;
let a = align(a, s)?;
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
v.1 = a;
continue;
}
// No existing entry, add a new one.
dl.vector_align.push((v_size, a));
}
_ => {} // Ignore everything else.
}
}
Ok(dl)
}

View file

@ -34,15 +34,17 @@ fn eval_goal(ra_fixture: &str) -> Result<Layout, LayoutError> {
})
.unwrap();
let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
layout_of_ty(&db, &goal_ty)
layout_of_ty(&db, &goal_ty, module_id.krate())
}
#[track_caller]
fn check_size_and_align(ra_fixture: &str, size: u64, align: u64) {
let l = eval_goal(ra_fixture).unwrap();
assert_eq!(l.size.bytes(), size);
assert_eq!(l.align.abi.bytes(), align);
}
#[track_caller]
fn check_fail(ra_fixture: &str, e: LayoutError) {
let r = eval_goal(ra_fixture);
assert_eq!(r, Err(e));