Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/layout/target.rs')
-rw-r--r--crates/hir-ty/src/layout/target.rs155
1 files changed, 120 insertions, 35 deletions
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
index b248031f15..54fa0fd9ea 100644
--- a/crates/hir-ty/src/layout/target.rs
+++ b/crates/hir-ty/src/layout/target.rs
@@ -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 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 }
+ })
};
- 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,
- })
+
+ // 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)
}