Merge from rustc
This commit is contained in:
commit
18e19b9f04
1037 changed files with 12634 additions and 8021 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
|
@ -70,6 +70,7 @@ jobs:
|
|||
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
DOCKER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SCCACHE_BUCKET: rust-lang-ci-sccache2
|
||||
SCCACHE_REGION: us-west-1
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
continue-on-error: ${{ matrix.continue_on_error || false }}
|
||||
strategy:
|
||||
|
|
@ -173,6 +174,11 @@ jobs:
|
|||
- name: ensure the stable version number is correct
|
||||
run: src/ci/scripts/verify-stable-version-number.sh
|
||||
|
||||
# Show the environment just before we run the build
|
||||
# This makes it easier to diagnose problems with the above install scripts.
|
||||
- name: show the current environment
|
||||
run: src/ci/scripts/dump-environment.sh
|
||||
|
||||
- name: run the build
|
||||
# Redirect stderr to stdout to avoid reordering the two streams in the GHA logs.
|
||||
run: src/ci/scripts/run-build-from-ci.sh 2>&1
|
||||
|
|
|
|||
16
Cargo.lock
16
Cargo.lock
|
|
@ -2751,9 +2751,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.24"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
|
||||
checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
|
@ -3748,7 +3748,6 @@ dependencies = [
|
|||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -3780,6 +3779,7 @@ dependencies = [
|
|||
"rustc_fluent_macro",
|
||||
"rustc_fs_util",
|
||||
"rustc_graphviz",
|
||||
"rustc_hashes",
|
||||
"rustc_hir",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
|
|
@ -3813,7 +3813,6 @@ dependencies = [
|
|||
name = "rustc_infer"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -4004,7 +4003,6 @@ dependencies = [
|
|||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
|
|
@ -4134,7 +4132,6 @@ name = "rustc_next_trait_solver"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"derive-where",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
|
|
@ -4454,7 +4451,6 @@ dependencies = [
|
|||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -4493,7 +4489,6 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_infer",
|
||||
|
|
@ -4509,7 +4504,6 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -4954,9 +4948,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
|
||||
checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc-main"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ fn main() {
|
|||
// linking, so we need to explicitly depend on the function.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn _rjem_je_zone_register();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_abi"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -65,8 +65,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
match self.backend_repr {
|
||||
BackendRepr::Uninhabited => Err(Heterogeneous),
|
||||
|
||||
// The primitive for this algorithm.
|
||||
BackendRepr::Scalar(scalar) => {
|
||||
let kind = match scalar.primitive() {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl Reg {
|
|||
128 => dl.f128_align.abi,
|
||||
_ => panic!("unsupported float: {self:?}"),
|
||||
},
|
||||
RegKind::Vector => dl.vector_align(self.size).abi,
|
||||
RegKind::Vector => dl.llvmlike_vector_align(self.size).abi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
},
|
||||
backend_repr: BackendRepr::ScalarPair(a, b),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
align,
|
||||
size,
|
||||
max_repr_align: None,
|
||||
|
|
@ -221,8 +222,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
LayoutData {
|
||||
variants: Variants::Empty,
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Uninhabited,
|
||||
backend_repr: BackendRepr::Memory { sized: true },
|
||||
largest_niche: None,
|
||||
uninhabited: true,
|
||||
align: dl.i8_align,
|
||||
size: Size::ZERO,
|
||||
max_repr_align: None,
|
||||
|
|
@ -308,10 +310,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
|
||||
let mut max_repr_align = repr.align;
|
||||
|
||||
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't
|
||||
// disabled, we can use that common ABI for the union as a whole.
|
||||
// If all the non-ZST fields have the same repr and union repr optimizations aren't
|
||||
// disabled, we can use that common repr for the union as a whole.
|
||||
struct AbiMismatch;
|
||||
let mut common_non_zst_abi_and_align = if repr.inhibits_union_abi_opt() {
|
||||
let mut common_non_zst_repr_and_align = if repr.inhibits_union_abi_opt() {
|
||||
// Can't optimize
|
||||
Err(AbiMismatch)
|
||||
} else {
|
||||
|
|
@ -335,14 +337,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Ok(common) = common_non_zst_abi_and_align {
|
||||
if let Ok(common) = common_non_zst_repr_and_align {
|
||||
// Discard valid range information and allow undef
|
||||
let field_abi = field.backend_repr.to_union();
|
||||
|
||||
if let Some((common_abi, common_align)) = common {
|
||||
if common_abi != field_abi {
|
||||
// Different fields have different ABI: disable opt
|
||||
common_non_zst_abi_and_align = Err(AbiMismatch);
|
||||
common_non_zst_repr_and_align = Err(AbiMismatch);
|
||||
} else {
|
||||
// Fields with the same non-Aggregate ABI should also
|
||||
// have the same alignment
|
||||
|
|
@ -355,7 +357,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
}
|
||||
} else {
|
||||
// First non-ZST field: record its ABI and alignment
|
||||
common_non_zst_abi_and_align = Ok(Some((field_abi, field.align.abi)));
|
||||
common_non_zst_repr_and_align = Ok(Some((field_abi, field.align.abi)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -374,16 +376,25 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
|
||||
// If all non-ZST fields have the same ABI, we may forward that ABI
|
||||
// for the union as a whole, unless otherwise inhibited.
|
||||
let abi = match common_non_zst_abi_and_align {
|
||||
let backend_repr = match common_non_zst_repr_and_align {
|
||||
Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true },
|
||||
Ok(Some((abi, _))) => {
|
||||
if abi.inherent_align(dl).map(|a| a.abi) != Some(align.abi) {
|
||||
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
|
||||
Ok(Some((repr, _))) => match repr {
|
||||
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
|
||||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _)
|
||||
if repr.scalar_align(dl).unwrap() != align.abi =>
|
||||
{
|
||||
BackendRepr::Memory { sized: true }
|
||||
} else {
|
||||
abi
|
||||
}
|
||||
}
|
||||
// Vectors require at least element alignment, else disable the opt
|
||||
BackendRepr::Vector { element, count: _ } if element.align(dl).abi > align.abi => {
|
||||
BackendRepr::Memory { sized: true }
|
||||
}
|
||||
// the alignment tests passed and we can use this
|
||||
BackendRepr::Scalar(..)
|
||||
| BackendRepr::ScalarPair(..)
|
||||
| BackendRepr::Vector { .. }
|
||||
| BackendRepr::Memory { .. } => repr,
|
||||
},
|
||||
};
|
||||
|
||||
let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else {
|
||||
|
|
@ -398,8 +409,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
Ok(LayoutData {
|
||||
variants: Variants::Single { index: only_variant_idx },
|
||||
fields: FieldsShape::Union(union_field_count),
|
||||
backend_repr: abi,
|
||||
backend_repr,
|
||||
largest_niche: None,
|
||||
uninhabited: false,
|
||||
align,
|
||||
size: size.align_to(align.abi),
|
||||
max_repr_align,
|
||||
|
|
@ -447,7 +459,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
Scalar::Union { .. } => {}
|
||||
};
|
||||
match &mut st.backend_repr {
|
||||
BackendRepr::Uninhabited => {}
|
||||
BackendRepr::Scalar(scalar) => hide_niches(scalar),
|
||||
BackendRepr::ScalarPair(a, b) => {
|
||||
hide_niches(a);
|
||||
|
|
@ -639,9 +650,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
let same_size = size == variant_layouts[largest_variant_index].size;
|
||||
let same_align = align == variant_layouts[largest_variant_index].align;
|
||||
|
||||
let abi = if variant_layouts.iter().all(|v| v.is_uninhabited()) {
|
||||
BackendRepr::Uninhabited
|
||||
} else if same_size && same_align && others_zst {
|
||||
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
|
||||
let abi = if same_size && same_align && others_zst {
|
||||
match variant_layouts[largest_variant_index].backend_repr {
|
||||
// When the total alignment and size match, we can use the
|
||||
// same ABI as the scalar variant with the reserved niche.
|
||||
|
|
@ -683,6 +693,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
},
|
||||
backend_repr: abi,
|
||||
largest_niche,
|
||||
uninhabited,
|
||||
size,
|
||||
align,
|
||||
max_repr_align,
|
||||
|
|
@ -853,9 +864,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
};
|
||||
let mut abi = BackendRepr::Memory { sized: true };
|
||||
|
||||
if layout_variants.iter().all(|v| v.is_uninhabited()) {
|
||||
abi = BackendRepr::Uninhabited;
|
||||
} else if tag.size(dl) == size {
|
||||
let uninhabited = layout_variants.iter().all(|v| v.is_uninhabited());
|
||||
if tag.size(dl) == size {
|
||||
// Make sure we only use scalar layout when the enum is entirely its
|
||||
// own tag (i.e. it has no padding nor any non-ZST variant fields).
|
||||
abi = BackendRepr::Scalar(tag);
|
||||
|
|
@ -995,6 +1005,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
memory_index: [0].into(),
|
||||
},
|
||||
largest_niche,
|
||||
uninhabited,
|
||||
backend_repr: abi,
|
||||
align,
|
||||
size,
|
||||
|
|
@ -1355,9 +1366,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
if fields.iter().any(|f| f.is_uninhabited()) {
|
||||
abi = BackendRepr::Uninhabited;
|
||||
}
|
||||
let uninhabited = fields.iter().any(|f| f.is_uninhabited());
|
||||
|
||||
let unadjusted_abi_align = if repr.transparent() {
|
||||
match layout_of_single_non_zst_field {
|
||||
|
|
@ -1378,6 +1387,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
fields: FieldsShape::Arbitrary { offsets, memory_index },
|
||||
backend_repr: abi,
|
||||
largest_niche,
|
||||
uninhabited,
|
||||
align,
|
||||
size,
|
||||
max_repr_align,
|
||||
|
|
|
|||
|
|
@ -329,19 +329,19 @@ impl TargetDataLayout {
|
|||
[p] if p.starts_with('P') => {
|
||||
dl.instruction_address_space = parse_address_space(&p[1..], "P")?
|
||||
}
|
||||
["a", ref a @ ..] => dl.aggregate_align = parse_align(a, "a")?,
|
||||
["f16", ref a @ ..] => dl.f16_align = parse_align(a, "f16")?,
|
||||
["f32", ref a @ ..] => dl.f32_align = parse_align(a, "f32")?,
|
||||
["f64", ref a @ ..] => dl.f64_align = parse_align(a, "f64")?,
|
||||
["f128", ref a @ ..] => dl.f128_align = parse_align(a, "f128")?,
|
||||
["a", a @ ..] => dl.aggregate_align = parse_align(a, "a")?,
|
||||
["f16", a @ ..] => dl.f16_align = parse_align(a, "f16")?,
|
||||
["f32", a @ ..] => dl.f32_align = parse_align(a, "f32")?,
|
||||
["f64", a @ ..] => dl.f64_align = parse_align(a, "f64")?,
|
||||
["f128", a @ ..] => dl.f128_align = parse_align(a, "f128")?,
|
||||
// FIXME(erikdesjardins): we should be parsing nonzero address spaces
|
||||
// this will require replacing TargetDataLayout::{pointer_size,pointer_align}
|
||||
// with e.g. `fn pointer_size_in(AddressSpace)`
|
||||
[p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
|
||||
[p @ "p", s, a @ ..] | [p @ "p0", s, a @ ..] => {
|
||||
dl.pointer_size = parse_size(s, p)?;
|
||||
dl.pointer_align = parse_align(a, p)?;
|
||||
}
|
||||
[s, ref a @ ..] if s.starts_with('i') => {
|
||||
[s, a @ ..] if s.starts_with('i') => {
|
||||
let Ok(bits) = s[1..].parse::<u64>() else {
|
||||
parse_size(&s[1..], "i")?; // For the user error.
|
||||
continue;
|
||||
|
|
@ -362,7 +362,7 @@ impl TargetDataLayout {
|
|||
dl.i128_align = a;
|
||||
}
|
||||
}
|
||||
[s, ref a @ ..] if s.starts_with('v') => {
|
||||
[s, a @ ..] if s.starts_with('v') => {
|
||||
let v_size = parse_size(&s[1..], "v")?;
|
||||
let a = parse_align(a, s)?;
|
||||
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
|
||||
|
|
@ -408,16 +408,21 @@ impl TargetDataLayout {
|
|||
}
|
||||
}
|
||||
|
||||
/// psABI-mandated alignment for a vector type, if any
|
||||
#[inline]
|
||||
pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
|
||||
for &(size, align) in &self.vector_align {
|
||||
if size == vec_size {
|
||||
return align;
|
||||
}
|
||||
}
|
||||
// Default to natural alignment, which is what LLVM does.
|
||||
// That is, use the size, rounded up to a power of 2.
|
||||
AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
|
||||
fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAndPrefAlign> {
|
||||
self.vector_align
|
||||
.iter()
|
||||
.find(|(size, _align)| *size == vec_size)
|
||||
.map(|(_size, align)| *align)
|
||||
}
|
||||
|
||||
/// an alignment resembling the one LLVM would pick for a vector
|
||||
#[inline]
|
||||
pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
|
||||
self.cabi_vector_align(vec_size).unwrap_or(AbiAndPrefAlign::new(
|
||||
Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -810,20 +815,19 @@ impl Align {
|
|||
self.bits().try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Computes the best alignment possible for the given offset
|
||||
/// (the largest power of two that the offset is a multiple of).
|
||||
/// Obtain the greatest factor of `size` that is an alignment
|
||||
/// (the largest power of two the Size is a multiple of).
|
||||
///
|
||||
/// N.B., for an offset of `0`, this happens to return `2^64`.
|
||||
/// Note that all numbers are factors of 0
|
||||
#[inline]
|
||||
pub fn max_for_offset(offset: Size) -> Align {
|
||||
Align { pow2: offset.bytes().trailing_zeros() as u8 }
|
||||
pub fn max_aligned_factor(size: Size) -> Align {
|
||||
Align { pow2: size.bytes().trailing_zeros() as u8 }
|
||||
}
|
||||
|
||||
/// Lower the alignment, if necessary, such that the given offset
|
||||
/// is aligned to it (the offset is a multiple of the alignment).
|
||||
/// Reduces Align to an aligned factor of `size`.
|
||||
#[inline]
|
||||
pub fn restrict_for_offset(self, offset: Size) -> Align {
|
||||
self.min(Align::max_for_offset(offset))
|
||||
pub fn restrict_for_offset(self, size: Size) -> Align {
|
||||
self.min(Align::max_aligned_factor(size))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1345,7 +1349,7 @@ impl<FieldIdx: Idx> FieldsShape<FieldIdx> {
|
|||
|
||||
/// Gets source indices of the fields by increasing offsets.
|
||||
#[inline]
|
||||
pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> + '_ {
|
||||
pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> {
|
||||
let mut inverse_small = [0u8; 64];
|
||||
let mut inverse_big = IndexVec::new();
|
||||
let use_small = self.count() <= inverse_small.len();
|
||||
|
|
@ -1404,7 +1408,6 @@ impl AddressSpace {
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum BackendRepr {
|
||||
Uninhabited,
|
||||
Scalar(Scalar),
|
||||
ScalarPair(Scalar, Scalar),
|
||||
Vector {
|
||||
|
|
@ -1423,10 +1426,9 @@ impl BackendRepr {
|
|||
#[inline]
|
||||
pub fn is_unsized(&self) -> bool {
|
||||
match *self {
|
||||
BackendRepr::Uninhabited
|
||||
| BackendRepr::Scalar(_)
|
||||
| BackendRepr::ScalarPair(..)
|
||||
| BackendRepr::Vector { .. } => false,
|
||||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::Memory { sized } => !sized,
|
||||
}
|
||||
}
|
||||
|
|
@ -1445,12 +1447,6 @@ impl BackendRepr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is an uninhabited type
|
||||
#[inline]
|
||||
pub fn is_uninhabited(&self) -> bool {
|
||||
matches!(*self, BackendRepr::Uninhabited)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a scalar type
|
||||
#[inline]
|
||||
pub fn is_scalar(&self) -> bool {
|
||||
|
|
@ -1463,37 +1459,38 @@ impl BackendRepr {
|
|||
matches!(*self, BackendRepr::Scalar(s) if s.is_bool())
|
||||
}
|
||||
|
||||
/// Returns the fixed alignment of this ABI, if any is mandated.
|
||||
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
|
||||
Some(match *self {
|
||||
BackendRepr::Scalar(s) => s.align(cx),
|
||||
BackendRepr::ScalarPair(s1, s2) => s1.align(cx).max(s2.align(cx)),
|
||||
BackendRepr::Vector { element, count } => {
|
||||
cx.data_layout().vector_align(element.size(cx) * count)
|
||||
}
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { .. } => return None,
|
||||
})
|
||||
/// The psABI alignment for a `Scalar` or `ScalarPair`
|
||||
///
|
||||
/// `None` for other variants.
|
||||
pub fn scalar_align<C: HasDataLayout>(&self, cx: &C) -> Option<Align> {
|
||||
match *self {
|
||||
BackendRepr::Scalar(s) => Some(s.align(cx).abi),
|
||||
BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
|
||||
// The align of a Vector can vary in surprising ways
|
||||
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fixed size of this ABI, if any is mandated.
|
||||
pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
|
||||
Some(match *self {
|
||||
BackendRepr::Scalar(s) => {
|
||||
// No padding in scalars.
|
||||
s.size(cx)
|
||||
}
|
||||
/// The psABI size for a `Scalar` or `ScalarPair`
|
||||
///
|
||||
/// `None` for other variants
|
||||
pub fn scalar_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
|
||||
match *self {
|
||||
// No padding in scalars.
|
||||
BackendRepr::Scalar(s) => Some(s.size(cx)),
|
||||
// May have some padding between the pair.
|
||||
BackendRepr::ScalarPair(s1, s2) => {
|
||||
// May have some padding between the pair.
|
||||
let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
|
||||
(field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi)
|
||||
let size = (field2_offset + s2.size(cx)).align_to(
|
||||
self.scalar_align(cx)
|
||||
// We absolutely must have an answer here or everything is FUBAR.
|
||||
.unwrap(),
|
||||
);
|
||||
Some(size)
|
||||
}
|
||||
BackendRepr::Vector { element, count } => {
|
||||
// No padding in vectors, except possibly for trailing padding
|
||||
// to make the size a multiple of align (e.g. for vectors of size 3).
|
||||
(element.size(cx) * count).align_to(self.inherent_align(cx)?.abi)
|
||||
}
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { .. } => return None,
|
||||
})
|
||||
// The size of a Vector can vary in surprising ways
|
||||
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Discard validity range information and allow undef.
|
||||
|
|
@ -1506,9 +1503,7 @@ impl BackendRepr {
|
|||
BackendRepr::Vector { element, count } => {
|
||||
BackendRepr::Vector { element: element.to_union(), count }
|
||||
}
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {
|
||||
BackendRepr::Memory { sized: true }
|
||||
}
|
||||
BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1704,6 +1699,11 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
|
|||
/// The leaf scalar with the largest number of invalid values
|
||||
/// (i.e. outside of its `valid_range`), if it exists.
|
||||
pub largest_niche: Option<Niche>,
|
||||
/// Is this type known to be uninhabted?
|
||||
///
|
||||
/// This is separate from BackendRepr because uninhabited return types can affect ABI,
|
||||
/// especially in the case of by-pointer struct returns, which allocate stack even when unused.
|
||||
pub uninhabited: bool,
|
||||
|
||||
pub align: AbiAndPrefAlign,
|
||||
pub size: Size,
|
||||
|
|
@ -1735,14 +1735,14 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::Uninhabited | BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => false,
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => false,
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is an uninhabited type
|
||||
pub fn is_uninhabited(&self) -> bool {
|
||||
self.backend_repr.is_uninhabited()
|
||||
self.uninhabited
|
||||
}
|
||||
|
||||
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
|
||||
|
|
@ -1778,6 +1778,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Scalar(scalar),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
|
|
@ -1802,10 +1803,11 @@ where
|
|||
backend_repr,
|
||||
fields,
|
||||
largest_niche,
|
||||
uninhabited,
|
||||
variants,
|
||||
max_repr_align,
|
||||
unadjusted_abi_align,
|
||||
ref randomization_seed,
|
||||
randomization_seed,
|
||||
} = self;
|
||||
f.debug_struct("Layout")
|
||||
.field("size", size)
|
||||
|
|
@ -1813,6 +1815,7 @@ where
|
|||
.field("abi", backend_repr)
|
||||
.field("fields", fields)
|
||||
.field("largest_niche", largest_niche)
|
||||
.field("uninhabited", uninhabited)
|
||||
.field("variants", variants)
|
||||
.field("max_repr_align", max_repr_align)
|
||||
.field("unadjusted_abi_align", unadjusted_abi_align)
|
||||
|
|
@ -1877,7 +1880,6 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::Uninhabited => self.size.bytes() == 0,
|
||||
BackendRepr::Memory { sized } => sized && self.size.bytes() == 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_arena"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_ast"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use crate::{Ty, TyKind};
|
|||
/// functions. The proper solution is to recognize and resolve this DAG of autodiff invocations,
|
||||
/// as it's already done in the C++ and Julia frontend of Enzyme.
|
||||
///
|
||||
/// (FIXME) remove *First variants.
|
||||
/// Documentation for using [reverse](https://enzyme.mit.edu/rust/rev.html) and
|
||||
/// [forward](https://enzyme.mit.edu/rust/fwd.html) mode is available online.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>(
|
|||
visit_opt!(visitor, visit_ident, rename);
|
||||
}
|
||||
UseTreeKind::Glob => {}
|
||||
UseTreeKind::Nested { ref items, span: _ } => {
|
||||
UseTreeKind::Nested { items, span: _ } => {
|
||||
for &(ref nested_tree, nested_id) in items {
|
||||
try_visit!(visitor.visit_use_tree(nested_tree, nested_id, true));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_ast_ir"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
//! Common utilities shared by both `rustc_ast` and `rustc_type_ir`.
|
||||
//!
|
||||
//! Don't depend on this crate directly; both of those crates should re-export
|
||||
//! the functionality. Additionally, if you're in scope of `rustc_middle`, then
|
||||
//! prefer imports via that too, to avoid needing to directly depend on (e.g.)
|
||||
//! `rustc_type_ir` for a single import.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(never_type))]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_ast_lowering"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::fmt::Write;
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, kw, sym};
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_target::asm;
|
||||
|
||||
use super::LoweringContext;
|
||||
|
|
@ -230,20 +229,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
tokens: None,
|
||||
};
|
||||
|
||||
// Wrap the expression in an AnonConst.
|
||||
let parent_def_id = self.current_hir_id_owner.def_id;
|
||||
let node_id = self.next_node_id();
|
||||
self.create_def(
|
||||
parent_def_id,
|
||||
node_id,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
*op_sp,
|
||||
);
|
||||
let anon_const = AnonConst { id: node_id, value: P(expr) };
|
||||
hir::InlineAsmOperand::SymFn {
|
||||
anon_const: self.lower_anon_const_to_anon_const(&anon_const),
|
||||
}
|
||||
hir::InlineAsmOperand::SymFn { expr: self.lower_expr(&expr) }
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::Label { block } => {
|
||||
|
|
|
|||
|
|
@ -251,7 +251,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
.arena
|
||||
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
|
||||
},
|
||||
ItemKind::GlobalAsm(asm) => hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm)),
|
||||
ItemKind::GlobalAsm(asm) => {
|
||||
let asm = self.lower_inline_asm(span, asm);
|
||||
let fake_body =
|
||||
self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
|
||||
hir::ItemKind::GlobalAsm { asm, fake_body }
|
||||
}
|
||||
ItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => {
|
||||
// We lower
|
||||
//
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
|
@ -1821,11 +1820,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.new_named_lifetime_with_res(new_id, ident, res)
|
||||
}
|
||||
|
||||
fn lower_generic_params_mut<'s>(
|
||||
&'s mut self,
|
||||
params: &'s [GenericParam],
|
||||
fn lower_generic_params_mut(
|
||||
&mut self,
|
||||
params: &[GenericParam],
|
||||
source: hir::GenericParamSource,
|
||||
) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
|
||||
) -> impl Iterator<Item = hir::GenericParam<'hir>> {
|
||||
params.iter().map(move |param| self.lower_generic_param(param, source))
|
||||
}
|
||||
|
||||
|
|
@ -1986,11 +1985,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.arena.alloc_from_iter(self.lower_param_bounds_mut(bounds, itctx))
|
||||
}
|
||||
|
||||
fn lower_param_bounds_mut<'s>(
|
||||
&'s mut self,
|
||||
bounds: &'s [GenericBound],
|
||||
fn lower_param_bounds_mut(
|
||||
&mut self,
|
||||
bounds: &[GenericBound],
|
||||
itctx: ImplTraitContext,
|
||||
) -> impl Iterator<Item = hir::GenericBound<'hir>> + Captures<'s> + Captures<'a> {
|
||||
) -> impl Iterator<Item = hir::GenericBound<'hir>> {
|
||||
bounds.iter().map(move |bound| self.lower_param_bound(bound, itctx))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_ast_passes"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -804,7 +804,14 @@ pub(crate) struct NegativeBoundWithParentheticalNotation {
|
|||
pub(crate) struct MatchArmWithNoBody {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
|
||||
// We include the braces around `todo!()` so that a comma is optional, and we don't have to have
|
||||
// any logic looking at the arm being replaced if there was a comma already or not for the
|
||||
// resulting code to be correct.
|
||||
#[suggestion(
|
||||
code = " => {{ todo!() }}",
|
||||
applicability = "has-placeholders",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_ast_pretty"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_attr_data_structures"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_attr_parsing"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -4,25 +4,25 @@ use rustc_span::{Symbol, sym};
|
|||
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub fn allow_internal_unstable<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> + 'a {
|
||||
pub fn allow_internal_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::allow_internal_unstable)
|
||||
}
|
||||
|
||||
pub fn rustc_allow_const_fn_unstable<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> + 'a {
|
||||
pub fn rustc_allow_const_fn_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
|
||||
}
|
||||
|
||||
fn allow_unstable<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [impl AttributeExt],
|
||||
fn allow_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
symbol: Symbol,
|
||||
) -> impl Iterator<Item = Symbol> + 'a {
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
let attrs = filter_by_name(attrs, symbol);
|
||||
let list = attrs
|
||||
.filter_map(move |attr| {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_baked_icu_data"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_borrowck"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use std::ops::ControlFlow;
|
|||
|
||||
use either::Either;
|
||||
use hir::{ClosureKind, Path};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
|
||||
|
|
@ -386,7 +385,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = self.infcx.tcx.hir();
|
||||
if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) {
|
||||
let expr = body.value;
|
||||
let place = &self.move_data.move_paths[mpi].place;
|
||||
|
|
@ -402,7 +400,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if let Some(span) = span
|
||||
&& let Some(expr) = finder.expr
|
||||
{
|
||||
for (_, expr) in hir.parent_iter(expr.hir_id) {
|
||||
for (_, expr) in tcx.hir_parent_iter(expr.hir_id) {
|
||||
if let hir::Node::Expr(expr) = expr {
|
||||
if expr.span.contains(span) {
|
||||
// If the let binding occurs within the same loop, then that
|
||||
|
|
@ -969,7 +967,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let mut parent = None;
|
||||
// The top-most loop where the moved expression could be moved to a new binding.
|
||||
let mut outer_most_loop: Option<&hir::Expr<'_>> = None;
|
||||
for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
|
||||
for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
|
||||
let e = match node {
|
||||
hir::Node::Expr(e) => e,
|
||||
hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => {
|
||||
|
|
@ -1021,8 +1019,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
let loop_count: usize = tcx
|
||||
.hir()
|
||||
.parent_iter(expr.hir_id)
|
||||
.hir_parent_iter(expr.hir_id)
|
||||
.map(|(_, node)| match node {
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Loop(..), .. }) => 1,
|
||||
_ => 0,
|
||||
|
|
@ -1048,8 +1045,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.collect::<Vec<Span>>();
|
||||
// All of the spans for the loops above the expression with the move error.
|
||||
let loop_spans: Vec<_> = tcx
|
||||
.hir()
|
||||
.parent_iter(expr.hir_id)
|
||||
.hir_parent_iter(expr.hir_id)
|
||||
.filter_map(|(_, node)| match node {
|
||||
hir::Node::Expr(hir::Expr { span, kind: hir::ExprKind::Loop(..), .. }) => {
|
||||
Some(*span)
|
||||
|
|
@ -1334,7 +1330,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {
|
||||
for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
for (_, node) in self.infcx.tcx.hir_parent_iter(expr.hir_id) {
|
||||
if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node
|
||||
&& let hir::CaptureBy::Value { .. } = closure.capture_clause
|
||||
{
|
||||
|
|
@ -2118,7 +2114,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
issued_span: Span,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
|
||||
let has_split_at_mut = |ty: Ty<'tcx>| {
|
||||
let ty = ty.peel_refs();
|
||||
|
|
@ -2171,7 +2166,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
return;
|
||||
};
|
||||
|
||||
let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
|
||||
let Some(object) = tcx.hir_parent_id_iter(index1.hir_id).find_map(|id| {
|
||||
if let hir::Node::Expr(expr) = tcx.hir_node(id)
|
||||
&& let hir::ExprKind::Index(obj, ..) = expr.kind
|
||||
{
|
||||
|
|
@ -2189,7 +2184,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
return;
|
||||
};
|
||||
|
||||
let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
|
||||
let Some(swap_call) = tcx.hir_parent_id_iter(object.hir_id).find_map(|id| {
|
||||
if let hir::Node::Expr(call) = tcx.hir_node(id)
|
||||
&& let hir::ExprKind::Call(callee, ..) = call.kind
|
||||
&& let hir::ExprKind::Path(qpath) = callee.kind
|
||||
|
|
@ -2621,7 +2616,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } =
|
||||
local.pat
|
||||
&& let Some(init) = local.init
|
||||
&& let hir::Expr {
|
||||
&& let &hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::Closure(&hir::Closure {
|
||||
kind: hir::ClosureKind::Closure,
|
||||
|
|
@ -3534,10 +3529,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
location: Location,
|
||||
mpi: MovePathIndex,
|
||||
) -> (Vec<MoveSite>, Vec<Location>) {
|
||||
fn predecessor_locations<'a, 'tcx>(
|
||||
body: &'a mir::Body<'tcx>,
|
||||
fn predecessor_locations<'tcx>(
|
||||
body: &mir::Body<'tcx>,
|
||||
location: Location,
|
||||
) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
|
||||
) -> impl Iterator<Item = Location> {
|
||||
if location.statement_index == 0 {
|
||||
let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
|
||||
Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
let local_decl = &body.local_decls[dropped_local];
|
||||
|
||||
if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
|
||||
&& let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(if_then).next()
|
||||
&& let Some((_, hir::Node::Expr(expr))) = tcx.hir_parent_iter(if_then).next()
|
||||
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
|
||||
&& let hir::ExprKind::Let(&hir::LetExpr {
|
||||
span: _,
|
||||
|
|
@ -262,7 +262,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
|
||||
if let hir::ExprKind::If(cond, _conseq, _alt)
|
||||
| hir::ExprKind::Loop(
|
||||
hir::Block {
|
||||
&hir::Block {
|
||||
expr:
|
||||
Some(&hir::Expr {
|
||||
kind: hir::ExprKind::If(cond, _conseq, _alt),
|
||||
|
|
@ -522,7 +522,7 @@ fn suggest_rewrite_if_let<G: EmissionGuarantee>(
|
|||
);
|
||||
if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
|
||||
let needs_block = if let Some(hir::Node::Expr(expr)) =
|
||||
alt.and_then(|alt| tcx.hir().parent_iter(alt.hir_id).next()).map(|(_, node)| node)
|
||||
alt.and_then(|alt| tcx.hir_parent_iter(alt.hir_id).next()).map(|(_, node)| node)
|
||||
{
|
||||
matches!(expr.kind, hir::ExprKind::If(..))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{self, Body, Local, Location};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
|
||||
|
|
@ -45,7 +45,22 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
|
|||
|
||||
let block_data = &self.body[p.block];
|
||||
|
||||
match self.def_use(p, block_data.visitable(p.statement_index)) {
|
||||
let mut visitor = DefUseVisitor {
|
||||
body: self.body,
|
||||
tcx: self.tcx,
|
||||
region_vid: self.region_vid,
|
||||
def_use_result: None,
|
||||
};
|
||||
|
||||
let is_statement = p.statement_index < block_data.statements.len();
|
||||
|
||||
if is_statement {
|
||||
visitor.visit_statement(&block_data.statements[p.statement_index], p);
|
||||
} else {
|
||||
visitor.visit_terminator(block_data.terminator.as_ref().unwrap(), p);
|
||||
}
|
||||
|
||||
match visitor.def_use_result {
|
||||
Some(DefUseResult::Def) => {}
|
||||
|
||||
Some(DefUseResult::UseLive { local }) => {
|
||||
|
|
@ -57,7 +72,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
None => {
|
||||
if p.statement_index < block_data.statements.len() {
|
||||
if is_statement {
|
||||
queue.push_back(p.successor_within_block());
|
||||
} else {
|
||||
queue.extend(
|
||||
|
|
@ -77,19 +92,6 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<DefUseResult> {
|
||||
let mut visitor = DefUseVisitor {
|
||||
body: self.body,
|
||||
tcx: self.tcx,
|
||||
region_vid: self.region_vid,
|
||||
def_use_result: None,
|
||||
};
|
||||
|
||||
thing.apply(location, &mut visitor);
|
||||
|
||||
visitor.def_use_result
|
||||
}
|
||||
}
|
||||
|
||||
struct DefUseVisitor<'a, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -1126,7 +1126,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
|
||||
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
|
||||
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
|
||||
if let hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
|
||||
if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
|
||||
for (captured_place, place) in
|
||||
self.infcx.tcx.closure_captures(def_id).iter().zip(places)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
// Search for an appropriate place for the structured `.clone()` suggestion to be applied.
|
||||
// If we encounter a statement before the borrow error, we insert a statement there.
|
||||
for (_, node) in tcx.hir().parent_iter(closure_expr.hir_id) {
|
||||
for (_, node) in tcx.hir_parent_iter(closure_expr.hir_id) {
|
||||
if let Node::Stmt(stmt) = node {
|
||||
let padding = tcx
|
||||
.sess
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
pat.kind
|
||||
{
|
||||
if upvar_ident.name == kw::SelfLower {
|
||||
for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
|
||||
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
|
||||
if let Some(fn_decl) = node.fn_decl() {
|
||||
if !matches!(
|
||||
fn_decl.implicit_self,
|
||||
|
|
@ -934,7 +934,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
err.span_label(sp, format!("cannot {act}"));
|
||||
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
let closure_id = self.mir_hir_id();
|
||||
let closure_span = tcx.def_span(self.mir_def_id());
|
||||
let fn_call_id = tcx.parent_hir_id(closure_id);
|
||||
|
|
@ -1017,10 +1016,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if look_at_return && hir.get_fn_id_for_return_block(closure_id).is_some() {
|
||||
if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
|
||||
// ...otherwise we are probably in the tail expression of the function, point at the
|
||||
// return type.
|
||||
match tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
|
||||
match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
|
||||
hir::Node::Item(hir::Item {
|
||||
ident, kind: hir::ItemKind::Fn { sig, .. }, ..
|
||||
})
|
||||
|
|
|
|||
|
|
@ -671,7 +671,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
#[instrument(level = "trace", skip(self))]
|
||||
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
|
||||
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
|
||||
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
|
||||
|
|
@ -682,7 +681,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
let mir_hir_id = self.mir_hir_id();
|
||||
|
||||
let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, kind, fn_decl_span, .. }),
|
||||
..
|
||||
}) => {
|
||||
|
|
@ -711,7 +710,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
hir::CoroutineSource::Fn,
|
||||
)) => {
|
||||
let parent_item =
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from async fn should be in fn")
|
||||
|
|
@ -741,7 +740,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
hir::CoroutineSource::Fn,
|
||||
)) => {
|
||||
let parent_item =
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from gen fn should be in fn")
|
||||
|
|
@ -768,7 +767,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
hir::CoroutineSource::Fn,
|
||||
)) => {
|
||||
let parent_item =
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from async gen fn should be in fn")
|
||||
|
|
@ -874,7 +873,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
.name;
|
||||
|
||||
let yield_span = match tcx.hir_node(self.mir_hir_id()) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
|
||||
..
|
||||
}) => tcx.sess.source_map().end_point(fn_decl_span),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::hash::Hash;
|
||||
use std::ops::Index;
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
|
@ -147,9 +146,7 @@ impl<'tcx, R> MemberConstraintSet<'tcx, R>
|
|||
where
|
||||
R: Copy + Hash + Eq,
|
||||
{
|
||||
pub(crate) fn all_indices(
|
||||
&self,
|
||||
) -> impl Iterator<Item = NllMemberConstraintIndex> + Captures<'tcx> + '_ {
|
||||
pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> {
|
||||
self.constraints.indices()
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +156,7 @@ where
|
|||
pub(crate) fn indices(
|
||||
&self,
|
||||
member_region_vid: R,
|
||||
) -> impl Iterator<Item = NllMemberConstraintIndex> + Captures<'tcx> + '_ {
|
||||
) -> impl Iterator<Item = NllMemberConstraintIndex> {
|
||||
let mut next = self.first_constraints.get(&member_region_vid).cloned();
|
||||
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
|
||||
if let Some(current) = next {
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ fn emit_polonius_mir<'tcx>(
|
|||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
pass_where.clone(),
|
||||
pass_where,
|
||||
out,
|
||||
)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ impl LocalizedConstraintGraph {
|
|||
}
|
||||
|
||||
/// Returns the outgoing edges of a given node, not its transitive closure.
|
||||
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
|
||||
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> {
|
||||
// The outgoing edges are:
|
||||
// - the physical edges present at this node,
|
||||
// - the materialized logical edges that exist virtually at all points for this node's
|
||||
|
|
|
|||
|
|
@ -576,9 +576,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
|
||||
/// Returns an iterator over all the outlives constraints.
|
||||
pub(crate) fn outlives_constraints(
|
||||
&self,
|
||||
) -> impl Iterator<Item = OutlivesConstraint<'tcx>> + '_ {
|
||||
pub(crate) fn outlives_constraints(&self) -> impl Iterator<Item = OutlivesConstraint<'tcx>> {
|
||||
self.constraints.outlives().iter().copied()
|
||||
}
|
||||
|
||||
|
|
@ -615,10 +613,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_values.region_value_str(scc)
|
||||
}
|
||||
|
||||
pub(crate) fn placeholders_contained_in<'a>(
|
||||
&'a self,
|
||||
pub(crate) fn placeholders_contained_in(
|
||||
&self,
|
||||
r: RegionVid,
|
||||
) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
|
||||
) -> impl Iterator<Item = ty::PlaceholderRegion> {
|
||||
let scc = self.constraint_sccs.scc(r);
|
||||
self.scc_values.placeholders_contained_in(scc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ pub(crate) struct ReverseSccGraph {
|
|||
|
||||
impl ReverseSccGraph {
|
||||
/// Find all universal regions that are required to outlive the given SCC.
|
||||
pub(super) fn upper_bounds<'a>(
|
||||
&'a self,
|
||||
scc0: ConstraintSccIndex,
|
||||
) -> impl Iterator<Item = RegionVid> + 'a {
|
||||
pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator<Item = RegionVid> {
|
||||
let mut duplicates = FxIndexSet::default();
|
||||
graph::depth_first_search(&self.graph, scc0)
|
||||
.flat_map(move |scc1| {
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ impl LivenessValues {
|
|||
}
|
||||
|
||||
/// Iterate through each region that has a value in this set.
|
||||
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
|
||||
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> {
|
||||
self.points.as_ref().expect("use with_specific_points").rows()
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ impl LivenessValues {
|
|||
// We are passing query instability implications to the caller.
|
||||
#[rustc_lint_query_instability]
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> + '_ {
|
||||
pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> {
|
||||
self.live_regions.as_ref().unwrap().iter().copied()
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ impl LivenessValues {
|
|||
}
|
||||
|
||||
/// Returns an iterator of all the points where `region` is live.
|
||||
fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ {
|
||||
fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> {
|
||||
let Some(points) = &self.points else {
|
||||
unreachable!(
|
||||
"Should be using LivenessValues::with_specific_points to ask whether live at a location"
|
||||
|
|
@ -340,7 +340,7 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
/// Returns the locations contained within a given region `r`.
|
||||
pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
|
||||
pub(crate) fn locations_outlived_by(&self, r: N) -> impl Iterator<Item = Location> {
|
||||
self.points.row(r).into_iter().flat_map(move |set| {
|
||||
set.iter()
|
||||
.take_while(move |&p| self.location_map.point_in_range(p))
|
||||
|
|
@ -349,18 +349,15 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
/// Returns just the universal regions that are contained in a given region's value.
|
||||
pub(crate) fn universal_regions_outlived_by<'a>(
|
||||
&'a self,
|
||||
r: N,
|
||||
) -> impl Iterator<Item = RegionVid> + 'a {
|
||||
pub(crate) fn universal_regions_outlived_by(&self, r: N) -> impl Iterator<Item = RegionVid> {
|
||||
self.free_regions.row(r).into_iter().flat_map(|set| set.iter())
|
||||
}
|
||||
|
||||
/// Returns all the elements contained in a given region's value.
|
||||
pub(crate) fn placeholders_contained_in<'a>(
|
||||
&'a self,
|
||||
pub(crate) fn placeholders_contained_in(
|
||||
&self,
|
||||
r: N,
|
||||
) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
|
||||
) -> impl Iterator<Item = ty::PlaceholderRegion> {
|
||||
self.placeholders
|
||||
.row(r)
|
||||
.into_iter()
|
||||
|
|
@ -369,10 +366,7 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
/// Returns all the elements contained in a given region's value.
|
||||
pub(crate) fn elements_contained_in<'a>(
|
||||
&'a self,
|
||||
r: N,
|
||||
) -> impl Iterator<Item = RegionElement> + 'a {
|
||||
pub(crate) fn elements_contained_in(&self, r: N) -> impl Iterator<Item = RegionElement> {
|
||||
let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
|
||||
|
||||
let free_regions_iter =
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ impl UniversalRegionRelations<'_> {
|
|||
}
|
||||
|
||||
/// Returns the _non-transitive_ set of known `outlives` constraints between free regions.
|
||||
pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> + '_ {
|
||||
pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> {
|
||||
self.outlives.base_edges()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ rustc_index::newtype_index! {
|
|||
fn appearances_iter(
|
||||
first: Option<AppearanceIndex>,
|
||||
appearances: &Appearances,
|
||||
) -> impl Iterator<Item = AppearanceIndex> + '_ {
|
||||
) -> impl Iterator<Item = AppearanceIndex> {
|
||||
AppearancesIter { appearances, current: first }
|
||||
}
|
||||
|
||||
|
|
@ -107,17 +107,17 @@ impl LocalUseMap {
|
|||
local_use_map
|
||||
}
|
||||
|
||||
pub(crate) fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
|
||||
pub(crate) fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> {
|
||||
appearances_iter(self.first_def_at[local], &self.appearances)
|
||||
.map(move |aa| self.appearances[aa].point_index)
|
||||
}
|
||||
|
||||
pub(crate) fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
|
||||
pub(crate) fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> {
|
||||
appearances_iter(self.first_use_at[local], &self.appearances)
|
||||
.map(move |aa| self.appearances[aa].point_index)
|
||||
}
|
||||
|
||||
pub(crate) fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
|
||||
pub(crate) fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> {
|
||||
appearances_iter(self.first_drop_at[local], &self.appearances)
|
||||
.map(move |aa| self.appearances[aa].point_index)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rustc_index::bit_set::DenseBitSet;
|
|||
use rustc_index::interval::IntervalSet;
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::for_liveness;
|
||||
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
|
||||
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location};
|
||||
use rustc_middle::traits::query::DropckOutlivesResult;
|
||||
use rustc_middle::ty::relate::Relate;
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
|
|
@ -11,7 +11,10 @@ use rustc_mir_dataflow::ResultsCursor;
|
|||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
|
||||
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use rustc_trait_selection::traits::query::dropck_outlives;
|
||||
use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -162,9 +165,10 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
|
||||
for local in boring_locals {
|
||||
let local_ty = self.cx.body.local_decls[local].ty;
|
||||
let local_span = self.cx.body.local_decls[local].source_info.span;
|
||||
let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
|
||||
let typeck = &self.cx.typeck;
|
||||
move || LivenessContext::compute_drop_data(typeck, local_ty)
|
||||
move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
|
||||
});
|
||||
|
||||
drop_data.dropck_result.report_overflows(
|
||||
|
|
@ -522,9 +526,10 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
values::pretty_print_points(self.location_map, live_at.iter()),
|
||||
);
|
||||
|
||||
let local_span = self.body.local_decls()[dropped_local].source_info.span;
|
||||
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
|
||||
let typeck = &self.typeck;
|
||||
move || Self::compute_drop_data(typeck, dropped_ty)
|
||||
move || Self::compute_drop_data(typeck, dropped_ty, local_span)
|
||||
});
|
||||
|
||||
if let Some(data) = &drop_data.region_constraint_data {
|
||||
|
|
@ -589,19 +594,50 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> {
|
||||
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
|
||||
fn compute_drop_data(
|
||||
typeck: &TypeChecker<'_, 'tcx>,
|
||||
dropped_ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
) -> DropData<'tcx> {
|
||||
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty);
|
||||
|
||||
match typeck
|
||||
.infcx
|
||||
.param_env
|
||||
.and(DropckOutlives { dropped_ty })
|
||||
.fully_perform(typeck.infcx, DUMMY_SP)
|
||||
{
|
||||
let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty });
|
||||
|
||||
match op.fully_perform(typeck.infcx, DUMMY_SP) {
|
||||
Ok(TypeOpOutput { output, constraints, .. }) => {
|
||||
DropData { dropck_result: output, region_constraint_data: constraints }
|
||||
}
|
||||
Err(_) => DropData { dropck_result: Default::default(), region_constraint_data: None },
|
||||
Err(ErrorGuaranteed { .. }) => {
|
||||
// We don't run dropck on HIR, and dropck looks inside fields of
|
||||
// types, so there's no guarantee that it succeeds. We also
|
||||
// can't rely on the the `ErrorGuaranteed` from `fully_perform` here
|
||||
// because it comes from delay_span_bug.
|
||||
//
|
||||
// Do this inside of a probe because we don't particularly care (or want)
|
||||
// any region side-effects of this operation in our infcx.
|
||||
typeck.infcx.probe(|_| {
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx);
|
||||
let errors = match dropck_outlives::compute_dropck_outlives_with_errors(
|
||||
&ocx, op, span,
|
||||
) {
|
||||
Ok(_) => ocx.select_all_or_error(),
|
||||
Err(e) => {
|
||||
if e.is_empty() {
|
||||
ocx.select_all_or_error()
|
||||
} else {
|
||||
e
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Could have no errors if a type lowering error, say, caused the query
|
||||
// to fail.
|
||||
if !errors.is_empty() {
|
||||
typeck.infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
}
|
||||
});
|
||||
DropData { dropck_result: Default::default(), region_constraint_data: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -456,38 +456,38 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
|
||||
self.super_local_decl(local, local_decl);
|
||||
|
||||
if let Some(user_ty) = &local_decl.user_ty {
|
||||
for (user_ty, span) in user_ty.projections_and_spans() {
|
||||
let ty = if !local_decl.is_nonref_binding() {
|
||||
// If we have a binding of the form `let ref x: T = ..`
|
||||
// then remove the outermost reference so we can check the
|
||||
// type annotation for the remaining type.
|
||||
if let ty::Ref(_, rty, _) = local_decl.ty.kind() {
|
||||
*rty
|
||||
} else {
|
||||
bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
|
||||
}
|
||||
} else {
|
||||
local_decl.ty
|
||||
};
|
||||
for user_ty in
|
||||
local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections)
|
||||
{
|
||||
let span = self.typeck.user_type_annotations[user_ty.base].span;
|
||||
|
||||
if let Err(terr) = self.typeck.relate_type_and_user_type(
|
||||
ty,
|
||||
ty::Invariant,
|
||||
user_ty,
|
||||
Locations::All(*span),
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration),
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
local,
|
||||
"bad user type on variable {:?}: {:?} != {:?} ({:?})",
|
||||
local,
|
||||
local_decl.ty,
|
||||
local_decl.user_ty,
|
||||
terr,
|
||||
);
|
||||
}
|
||||
let ty = if local_decl.is_nonref_binding() {
|
||||
local_decl.ty
|
||||
} else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() {
|
||||
// If we have a binding of the form `let ref x: T = ..`
|
||||
// then remove the outermost reference so we can check the
|
||||
// type annotation for the remaining type.
|
||||
rty
|
||||
} else {
|
||||
bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
|
||||
};
|
||||
|
||||
if let Err(terr) = self.typeck.relate_type_and_user_type(
|
||||
ty,
|
||||
ty::Invariant,
|
||||
user_ty,
|
||||
Locations::All(span),
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration),
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
local,
|
||||
"bad user type on variable {:?}: {:?} != {:?} ({:?})",
|
||||
local,
|
||||
local_decl.ty,
|
||||
local_decl.user_ty,
|
||||
terr,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ pub(crate) enum DefiningTy<'tcx> {
|
|||
/// The MIR represents an inline const. The signature has no inputs and a
|
||||
/// single return value found via `InlineConstArgs::ty`.
|
||||
InlineConst(DefId, GenericArgsRef<'tcx>),
|
||||
|
||||
// Fake body for a global asm. Not particularly useful or interesting,
|
||||
// but we need it so we can properly store the typeck results of the asm
|
||||
// operands, which aren't associated with a body otherwise.
|
||||
GlobalAsm(DefId),
|
||||
}
|
||||
|
||||
impl<'tcx> DefiningTy<'tcx> {
|
||||
|
|
@ -138,9 +143,10 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
DefiningTy::Closure(_, args) => args.as_closure().upvar_tys(),
|
||||
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().upvar_tys(),
|
||||
DefiningTy::Coroutine(_, args) => args.as_coroutine().upvar_tys(),
|
||||
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
|
||||
ty::List::empty()
|
||||
}
|
||||
DefiningTy::FnDef(..)
|
||||
| DefiningTy::Const(..)
|
||||
| DefiningTy::InlineConst(..)
|
||||
| DefiningTy::GlobalAsm(_) => ty::List::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +158,10 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
DefiningTy::Closure(..)
|
||||
| DefiningTy::CoroutineClosure(..)
|
||||
| DefiningTy::Coroutine(..) => 1,
|
||||
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
|
||||
DefiningTy::FnDef(..)
|
||||
| DefiningTy::Const(..)
|
||||
| DefiningTy::InlineConst(..)
|
||||
| DefiningTy::GlobalAsm(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +180,8 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
| DefiningTy::Coroutine(def_id, ..)
|
||||
| DefiningTy::FnDef(def_id, ..)
|
||||
| DefiningTy::Const(def_id, ..)
|
||||
| DefiningTy::InlineConst(def_id, ..) => def_id,
|
||||
| DefiningTy::InlineConst(def_id, ..)
|
||||
| DefiningTy::GlobalAsm(def_id) => def_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -308,7 +318,7 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
|
||||
/// Returns an iterator over all the RegionVids corresponding to
|
||||
/// universally quantified free regions.
|
||||
pub(crate) fn universal_regions_iter(&self) -> impl Iterator<Item = RegionVid> {
|
||||
pub(crate) fn universal_regions_iter(&self) -> impl Iterator<Item = RegionVid> + 'static {
|
||||
(FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::from_usize)
|
||||
}
|
||||
|
||||
|
|
@ -332,9 +342,9 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
}
|
||||
|
||||
/// Gets an iterator over all the early-bound regions that have names.
|
||||
pub(crate) fn named_universal_regions_iter<'s>(
|
||||
&'s self,
|
||||
) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
|
||||
pub(crate) fn named_universal_regions_iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> {
|
||||
self.indices.indices.iter().map(|(&r, &v)| (r, v))
|
||||
}
|
||||
|
||||
|
|
@ -411,6 +421,7 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
tcx.def_path_str_with_args(def_id, args),
|
||||
));
|
||||
}
|
||||
DefiningTy::GlobalAsm(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,6 +644,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
DefiningTy::InlineConst(self.mir_def.to_def_id(), args)
|
||||
}
|
||||
}
|
||||
|
||||
BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(self.mir_def.to_def_id()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -666,6 +679,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
DefiningTy::FnDef(_, args) | DefiningTy::Const(_, args) => args,
|
||||
|
||||
DefiningTy::GlobalAsm(_) => ty::List::empty(),
|
||||
};
|
||||
|
||||
let global_mapping = iter::once((tcx.lifetimes.re_static, fr_static));
|
||||
|
|
@ -802,6 +817,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
let ty = args.as_inline_const().ty();
|
||||
ty::Binder::dummy(tcx.mk_type_list(&[ty]))
|
||||
}
|
||||
|
||||
DefiningTy::GlobalAsm(def_id) => {
|
||||
ty::Binder::dummy(tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity()]))
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME(#129952): We probably want a more principled approach here.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_builtin_macros"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
[lints.rust]
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ mod llvm_enzyme {
|
|||
defaultness: ast::Defaultness::Final,
|
||||
sig: d_sig,
|
||||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body: Some(d_body),
|
||||
});
|
||||
let mut rustc_ad_attr =
|
||||
|
|
|
|||
|
|
@ -19,16 +19,12 @@ pub fn inject(
|
|||
let edition = sess.psess.edition;
|
||||
|
||||
// the first name in this list is the crate name of the crate with the prelude
|
||||
let names: &[Symbol] = if attr::contains_name(pre_configured_attrs, sym::no_core) {
|
||||
let name: Symbol = if attr::contains_name(pre_configured_attrs, sym::no_core) {
|
||||
return 0;
|
||||
} else if attr::contains_name(pre_configured_attrs, sym::no_std) {
|
||||
if attr::contains_name(pre_configured_attrs, sym::compiler_builtins) {
|
||||
&[sym::core]
|
||||
} else {
|
||||
&[sym::core, sym::compiler_builtins]
|
||||
}
|
||||
sym::core
|
||||
} else {
|
||||
&[sym::std]
|
||||
sym::std
|
||||
};
|
||||
|
||||
let expn_id = resolver.expansion_for_ast_pass(
|
||||
|
|
@ -43,36 +39,16 @@ pub fn inject(
|
|||
let ecfg = ExpansionConfig::default("std_lib_injection".to_string(), features);
|
||||
let cx = ExtCtxt::new(sess, ecfg, resolver, None);
|
||||
|
||||
// .rev() to preserve ordering above in combination with insert(0, ...)
|
||||
for &name in names.iter().rev() {
|
||||
let ident_span = if edition >= Edition2018 { span } else { call_site };
|
||||
let item = if name == sym::compiler_builtins {
|
||||
// compiler_builtins is a private implementation detail. We only
|
||||
// need to insert it into the crate graph for linking and should not
|
||||
// expose any of its public API.
|
||||
//
|
||||
// FIXME(#113634) We should inject this during post-processing like
|
||||
// we do for the panic runtime, profiler runtime, etc.
|
||||
cx.item(
|
||||
span,
|
||||
Ident::new(kw::Underscore, ident_span),
|
||||
thin_vec![],
|
||||
ast::ItemKind::ExternCrate(Some(name)),
|
||||
)
|
||||
} else {
|
||||
cx.item(
|
||||
span,
|
||||
Ident::new(name, ident_span),
|
||||
thin_vec![cx.attr_word(sym::macro_use, span)],
|
||||
ast::ItemKind::ExternCrate(None),
|
||||
)
|
||||
};
|
||||
krate.items.insert(0, item);
|
||||
}
|
||||
let ident_span = if edition >= Edition2018 { span } else { call_site };
|
||||
|
||||
// The crates have been injected, the assumption is that the first one is
|
||||
// the one with the prelude.
|
||||
let name = names[0];
|
||||
let item = cx.item(
|
||||
span,
|
||||
Ident::new(name, ident_span),
|
||||
thin_vec![cx.attr_word(sym::macro_use, span)],
|
||||
ast::ItemKind::ExternCrate(None),
|
||||
);
|
||||
|
||||
krate.items.insert(0, item);
|
||||
|
||||
let root = (edition == Edition2015).then_some(kw::PathRoot);
|
||||
|
||||
|
|
@ -88,6 +64,7 @@ pub fn inject(
|
|||
.map(|&symbol| Ident::new(symbol, span))
|
||||
.collect();
|
||||
|
||||
// Inject the relevant crate's prelude.
|
||||
let use_item = cx.item(
|
||||
span,
|
||||
Ident::empty(),
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ index 7165c3e48af..968552ad435 100644
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
-compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
core = { path = "../core", public = true }
|
||||
-compiler_builtins = { version = "=0.1.147", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.147", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::prelude::*;
|
|||
|
||||
pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) {
|
||||
let item = tcx.hir_item(item_id);
|
||||
if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
|
||||
if let rustc_hir::ItemKind::GlobalAsm { asm, .. } = item.kind {
|
||||
let is_x86 =
|
||||
matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64);
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String,
|
|||
}
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::SymFn { anon_const } => {
|
||||
InlineAsmOperand::SymFn { expr } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx().span_err(
|
||||
item.span,
|
||||
|
|
@ -63,7 +63,7 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String,
|
|||
);
|
||||
}
|
||||
|
||||
let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
|
||||
let ty = tcx.typeck(item_id.owner_id).expr_ty(expr);
|
||||
let instance = match ty.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => span_bug!(op_sp, "asm sym is not a function"),
|
||||
|
|
|
|||
|
|
@ -340,14 +340,10 @@ fn codegen_float_intrinsic_call<'tcx>(
|
|||
sym::ceilf64 => ("ceil", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::truncf32 => ("truncf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::truncf64 => ("trunc", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::rintf32 => ("rintf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::rintf64 => ("rint", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::round_ties_even_f32 => ("rintf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::round_ties_even_f64 => ("rint", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::roundf32 => ("roundf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::roundf64 => ("round", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::roundevenf32 => ("roundevenf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::roundevenf64 => ("roundeven", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::nearbyintf32 => ("nearbyintf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::nearbyintf64 => ("nearbyint", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::sinf32 => ("sinf", 1, fx.tcx.types.f32, types::F32),
|
||||
sym::sinf64 => ("sin", 1, fx.tcx.types.f64, types::F64),
|
||||
sym::cosf32 => ("cosf", 1, fx.tcx.types.f32, types::F32),
|
||||
|
|
@ -399,8 +395,8 @@ fn codegen_float_intrinsic_call<'tcx>(
|
|||
| sym::ceilf64
|
||||
| sym::truncf32
|
||||
| sym::truncf64
|
||||
| sym::nearbyintf32
|
||||
| sym::nearbyintf64
|
||||
| sym::round_ties_even_f32
|
||||
| sym::round_ties_even_f64
|
||||
| sym::sqrtf32
|
||||
| sym::sqrtf64 => {
|
||||
let val = match intrinsic {
|
||||
|
|
@ -408,7 +404,9 @@ fn codegen_float_intrinsic_call<'tcx>(
|
|||
sym::floorf32 | sym::floorf64 => fx.bcx.ins().floor(args[0]),
|
||||
sym::ceilf32 | sym::ceilf64 => fx.bcx.ins().ceil(args[0]),
|
||||
sym::truncf32 | sym::truncf64 => fx.bcx.ins().trunc(args[0]),
|
||||
sym::nearbyintf32 | sym::nearbyintf64 => fx.bcx.ins().nearest(args[0]),
|
||||
sym::round_ties_even_f32 | sym::round_ties_even_f64 => {
|
||||
fx.bcx.ins().nearest(args[0])
|
||||
}
|
||||
sym::sqrtf32 | sym::sqrtf64 => fx.bcx.ins().sqrt(args[0]),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -638,9 +638,7 @@ impl<'tcx> CPlace<'tcx> {
|
|||
}
|
||||
CPlaceInner::Addr(_, Some(_)) => bug!("Can't write value to unsized place {:?}", self),
|
||||
CPlaceInner::Addr(to_ptr, None) => {
|
||||
if dst_layout.size == Size::ZERO
|
||||
|| dst_layout.backend_repr == BackendRepr::Uninhabited
|
||||
{
|
||||
if dst_layout.size == Size::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -989,10 +989,14 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
OperandValue::Ref(place.val)
|
||||
} else if place.layout.is_gcc_immediate() {
|
||||
let load = self.load(place.layout.gcc_type(self), place.val.llval, place.val.align);
|
||||
if let abi::BackendRepr::Scalar(ref scalar) = place.layout.backend_repr {
|
||||
scalar_load_metadata(self, load, scalar);
|
||||
}
|
||||
OperandValue::Immediate(self.to_immediate(load, place.layout))
|
||||
OperandValue::Immediate(
|
||||
if let abi::BackendRepr::Scalar(ref scalar) = place.layout.backend_repr {
|
||||
scalar_load_metadata(self, load, scalar);
|
||||
self.to_immediate_scalar(load, *scalar)
|
||||
} else {
|
||||
load
|
||||
},
|
||||
)
|
||||
} else if let abi::BackendRepr::ScalarPair(ref a, ref b) = place.layout.backend_repr {
|
||||
let b_offset = a.size(self).align_to(b.align(self).abi);
|
||||
|
||||
|
|
@ -1694,7 +1698,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
|
||||
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value {
|
||||
if scalar.is_bool() {
|
||||
return self.trunc(val, self.cx().type_i1());
|
||||
return self.unchecked_utrunc(val, self.cx().type_i1());
|
||||
}
|
||||
val
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use gccjit::FunctionType;
|
|||
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_abi::HasDataLayout;
|
||||
use rustc_abi::{BackendRepr, HasDataLayout};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
use rustc_codegen_ssa::common::IntPredicate;
|
||||
|
|
@ -84,14 +84,11 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
|||
sym::ceilf64 => "ceil",
|
||||
sym::truncf32 => "truncf",
|
||||
sym::truncf64 => "trunc",
|
||||
sym::rintf32 => "rintf",
|
||||
sym::rintf64 => "rint",
|
||||
sym::nearbyintf32 => "nearbyintf",
|
||||
sym::nearbyintf64 => "nearbyint",
|
||||
// We match the LLVM backend and lower this to `rint`.
|
||||
sym::round_ties_even_f32 => "rintf",
|
||||
sym::round_ties_even_f64 => "rint",
|
||||
sym::roundf32 => "roundf",
|
||||
sym::roundf64 => "round",
|
||||
sym::roundevenf32 => "roundevenf",
|
||||
sym::roundevenf64 => "roundeven",
|
||||
sym::abort => "abort",
|
||||
_ => return None,
|
||||
};
|
||||
|
|
@ -181,14 +178,19 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
sym::volatile_load | sym::unaligned_volatile_load => {
|
||||
let tp_ty = fn_args.type_at(0);
|
||||
let ptr = args[0].immediate();
|
||||
let layout = self.layout_of(tp_ty);
|
||||
let load = if let PassMode::Cast { cast: ref ty, pad_i32: _ } = fn_abi.ret.mode {
|
||||
let gcc_ty = ty.gcc_type(self);
|
||||
self.volatile_load(gcc_ty, ptr)
|
||||
} else {
|
||||
self.volatile_load(self.layout_of(tp_ty).gcc_type(self), ptr)
|
||||
self.volatile_load(layout.gcc_type(self), ptr)
|
||||
};
|
||||
// TODO(antoyo): set alignment.
|
||||
self.to_immediate(load, self.layout_of(tp_ty))
|
||||
if let BackendRepr::Scalar(scalar) = layout.backend_repr {
|
||||
self.to_immediate_scalar(load, scalar)
|
||||
} else {
|
||||
load
|
||||
}
|
||||
}
|
||||
sym::volatile_store => {
|
||||
let dst = args[0].deref(self.cx());
|
||||
|
|
@ -310,7 +312,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
let layout = self.layout_of(tp_ty).layout;
|
||||
let _use_integer_compare = match layout.backend_repr() {
|
||||
Scalar(_) | ScalarPair(_, _) => true,
|
||||
Uninhabited | Vector { .. } => false,
|
||||
Vector { .. } => false,
|
||||
Memory { .. } => {
|
||||
// For rusty ABIs, small aggregates are actually passed
|
||||
// as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(
|
|||
false,
|
||||
);
|
||||
}
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {}
|
||||
BackendRepr::Memory { .. } => {}
|
||||
}
|
||||
|
||||
let name = match *layout.ty.kind() {
|
||||
|
|
@ -179,19 +179,16 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
|||
fn is_gcc_immediate(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => true,
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_gcc_scalar_pair(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::ScalarPair(..) => true,
|
||||
BackendRepr::Uninhabited
|
||||
| BackendRepr::Scalar(_)
|
||||
| BackendRepr::Vector { .. }
|
||||
| BackendRepr::Memory { .. } => false,
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_codegen_llvm"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable
|
||||
codegen_llvm_autodiff_without_lto = using the autodiff feature requires using fat-lto
|
||||
|
||||
codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
|
||||
|
|
|
|||
|
|
@ -362,8 +362,8 @@ fn fat_lto(
|
|||
ptr as *const *const libc::c_char,
|
||||
symbols_below_threshold.len() as libc::size_t,
|
||||
);
|
||||
save_temp_bitcode(cgcx, &module, "lto.after-restriction");
|
||||
}
|
||||
save_temp_bitcode(cgcx, &module, "lto.after-restriction");
|
||||
}
|
||||
|
||||
Ok(LtoModuleCodegen::Fat(module))
|
||||
|
|
@ -586,6 +586,42 @@ fn thin_lto(
|
|||
}
|
||||
}
|
||||
|
||||
fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen<ModuleLlvm>) {
|
||||
for &val in ad {
|
||||
match val {
|
||||
config::AutoDiff::PrintModBefore => {
|
||||
unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
|
||||
}
|
||||
config::AutoDiff::PrintPerf => {
|
||||
llvm::set_print_perf(true);
|
||||
}
|
||||
config::AutoDiff::PrintAA => {
|
||||
llvm::set_print_activity(true);
|
||||
}
|
||||
config::AutoDiff::PrintTA => {
|
||||
llvm::set_print_type(true);
|
||||
}
|
||||
config::AutoDiff::Inline => {
|
||||
llvm::set_inline(true);
|
||||
}
|
||||
config::AutoDiff::LooseTypes => {
|
||||
llvm::set_loose_types(false);
|
||||
}
|
||||
config::AutoDiff::PrintSteps => {
|
||||
llvm::set_print(true);
|
||||
}
|
||||
// We handle this below
|
||||
config::AutoDiff::PrintModAfter => {}
|
||||
// This is required and already checked
|
||||
config::AutoDiff::Enable => {}
|
||||
}
|
||||
}
|
||||
// This helps with handling enums for now.
|
||||
llvm::set_strict_aliasing(false);
|
||||
// FIXME(ZuseZ4): Test this, since it was added a long time ago.
|
||||
llvm::set_rust_rules(true);
|
||||
}
|
||||
|
||||
pub(crate) fn run_pass_manager(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
|
|
@ -604,34 +640,37 @@ pub(crate) fn run_pass_manager(
|
|||
let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
|
||||
let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
|
||||
|
||||
// If this rustc version was build with enzyme/autodiff enabled, and if users applied the
|
||||
// `#[autodiff]` macro at least once, then we will later call llvm_optimize a second time.
|
||||
debug!("running llvm pm opt pipeline");
|
||||
unsafe {
|
||||
write::llvm_optimize(
|
||||
cgcx,
|
||||
dcx,
|
||||
module,
|
||||
config,
|
||||
opt_level,
|
||||
opt_stage,
|
||||
write::AutodiffStage::DuringAD,
|
||||
)?;
|
||||
// The PostAD behavior is the same that we would have if no autodiff was used.
|
||||
// It will run the default optimization pipeline. If AD is enabled we select
|
||||
// the DuringAD stage, which will disable vectorization and loop unrolling, and
|
||||
// schedule two autodiff optimization + differentiation passes.
|
||||
// We then run the llvm_optimize function a second time, to optimize the code which we generated
|
||||
// in the enzyme differentiation pass.
|
||||
let enable_ad = config.autodiff.contains(&config::AutoDiff::Enable);
|
||||
let stage =
|
||||
if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD };
|
||||
|
||||
if enable_ad {
|
||||
enable_autodiff_settings(&config.autodiff, module);
|
||||
}
|
||||
// FIXME(ZuseZ4): Make this more granular
|
||||
if cfg!(llvm_enzyme) && !thin {
|
||||
|
||||
unsafe {
|
||||
write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage, stage)?;
|
||||
}
|
||||
|
||||
if cfg!(llvm_enzyme) && enable_ad {
|
||||
let opt_stage = llvm::OptStage::FatLTO;
|
||||
let stage = write::AutodiffStage::PostAD;
|
||||
unsafe {
|
||||
write::llvm_optimize(
|
||||
cgcx,
|
||||
dcx,
|
||||
module,
|
||||
config,
|
||||
opt_level,
|
||||
llvm::OptStage::FatLTO,
|
||||
write::AutodiffStage::PostAD,
|
||||
)?;
|
||||
write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage, stage)?;
|
||||
}
|
||||
|
||||
// This is the final IR, so people should be able to inspect the optimized autodiff output.
|
||||
if config.autodiff.contains(&config::AutoDiff::PrintModAfter) {
|
||||
unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
|
||||
}
|
||||
}
|
||||
|
||||
debug!("lto done");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -564,19 +564,16 @@ pub(crate) unsafe fn llvm_optimize(
|
|||
// FIXME(ZuseZ4): In a future update we could figure out how to only optimize individual functions getting
|
||||
// differentiated.
|
||||
|
||||
let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable);
|
||||
let run_enzyme = autodiff_stage == AutodiffStage::DuringAD;
|
||||
let unroll_loops;
|
||||
let vectorize_slp;
|
||||
let vectorize_loop;
|
||||
let run_enzyme = cfg!(llvm_enzyme) && autodiff_stage == AutodiffStage::DuringAD;
|
||||
|
||||
// When we build rustc with enzyme/autodiff support, we want to postpone size-increasing
|
||||
// optimizations until after differentiation. Our pipeline is thus: (opt + enzyme), (full opt).
|
||||
// We therefore have two calls to llvm_optimize, if autodiff is used.
|
||||
//
|
||||
// FIXME(ZuseZ4): Before shipping on nightly,
|
||||
// we should make this more granular, or at least check that the user has at least one autodiff
|
||||
// call in their code, to justify altering the compilation pipeline.
|
||||
if cfg!(llvm_enzyme) && autodiff_stage != AutodiffStage::PostAD {
|
||||
if consider_ad && autodiff_stage != AutodiffStage::PostAD {
|
||||
unroll_loops = false;
|
||||
vectorize_slp = false;
|
||||
vectorize_loop = false;
|
||||
|
|
@ -706,10 +703,8 @@ pub(crate) unsafe fn optimize(
|
|||
|
||||
// If we know that we will later run AD, then we disable vectorization and loop unrolling.
|
||||
// Otherwise we pretend AD is already done and run the normal opt pipeline (=PostAD).
|
||||
// FIXME(ZuseZ4): Make this more granular, only set PreAD if we actually have autodiff
|
||||
// usages, not just if we build rustc with autodiff support.
|
||||
let autodiff_stage =
|
||||
if cfg!(llvm_enzyme) { AutodiffStage::PreAD } else { AutodiffStage::PostAD };
|
||||
let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable);
|
||||
let autodiff_stage = if consider_ad { AutodiffStage::PreAD } else { AutodiffStage::PostAD };
|
||||
return unsafe {
|
||||
llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage, autodiff_stage)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,13 +29,15 @@ use smallvec::SmallVec;
|
|||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::abi::FnAbiLlvmExt;
|
||||
use crate::attributes;
|
||||
use crate::common::Funclet;
|
||||
use crate::context::{CodegenCx, SimpleCx};
|
||||
use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, Metadata, True};
|
||||
use crate::llvm::{
|
||||
self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, GEPNoWrapFlags, Metadata, True,
|
||||
};
|
||||
use crate::type_::Type;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use crate::value::Value;
|
||||
use crate::{attributes, llvm_util};
|
||||
|
||||
#[must_use]
|
||||
pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SimpleCx<'ll>>> {
|
||||
|
|
@ -606,7 +608,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
|
||||
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value {
|
||||
if scalar.is_bool() {
|
||||
return self.trunc(val, self.cx().type_i1());
|
||||
return self.unchecked_utrunc(val, self.cx().type_i1());
|
||||
}
|
||||
val
|
||||
}
|
||||
|
|
@ -746,10 +748,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
let load = self.load(llty, place.val.llval, place.val.align);
|
||||
if let abi::BackendRepr::Scalar(scalar) = place.layout.backend_repr {
|
||||
scalar_load_metadata(self, load, scalar, place.layout, Size::ZERO);
|
||||
self.to_immediate_scalar(load, scalar)
|
||||
} else {
|
||||
load
|
||||
}
|
||||
load
|
||||
});
|
||||
OperandValue::Immediate(self.to_immediate(llval, place.layout))
|
||||
OperandValue::Immediate(llval)
|
||||
} else if let abi::BackendRepr::ScalarPair(a, b) = place.layout.backend_repr {
|
||||
let b_offset = a.size(self).align_to(b.align(self).abi);
|
||||
|
||||
|
|
@ -908,13 +912,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
|
||||
fn gep(&mut self, ty: &'ll Type, ptr: &'ll Value, indices: &[&'ll Value]) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMBuildGEP2(
|
||||
llvm::LLVMBuildGEPWithNoWrapFlags(
|
||||
self.llbuilder,
|
||||
ty,
|
||||
ptr,
|
||||
indices.as_ptr(),
|
||||
indices.len() as c_uint,
|
||||
UNNAMED,
|
||||
GEPNoWrapFlags::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -926,13 +931,33 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
indices: &[&'ll Value],
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMBuildInBoundsGEP2(
|
||||
llvm::LLVMBuildGEPWithNoWrapFlags(
|
||||
self.llbuilder,
|
||||
ty,
|
||||
ptr,
|
||||
indices.as_ptr(),
|
||||
indices.len() as c_uint,
|
||||
UNNAMED,
|
||||
GEPNoWrapFlags::InBounds,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn inbounds_nuw_gep(
|
||||
&mut self,
|
||||
ty: &'ll Type,
|
||||
ptr: &'ll Value,
|
||||
indices: &[&'ll Value],
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMBuildGEPWithNoWrapFlags(
|
||||
self.llbuilder,
|
||||
ty,
|
||||
ptr,
|
||||
indices.as_ptr(),
|
||||
indices.len() as c_uint,
|
||||
UNNAMED,
|
||||
GEPNoWrapFlags::InBounds | GEPNoWrapFlags::NUW,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -942,6 +967,34 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
unsafe { llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
}
|
||||
|
||||
fn unchecked_utrunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
debug_assert_ne!(self.val_ty(val), dest_ty);
|
||||
|
||||
let trunc = self.trunc(val, dest_ty);
|
||||
if llvm_util::get_version() >= (19, 0, 0) {
|
||||
unsafe {
|
||||
if llvm::LLVMIsAInstruction(trunc).is_some() {
|
||||
llvm::LLVMSetNUW(trunc, True);
|
||||
}
|
||||
}
|
||||
}
|
||||
trunc
|
||||
}
|
||||
|
||||
fn unchecked_strunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
debug_assert_ne!(self.val_ty(val), dest_ty);
|
||||
|
||||
let trunc = self.trunc(val, dest_ty);
|
||||
if llvm_util::get_version() >= (19, 0, 0) {
|
||||
unsafe {
|
||||
if llvm::LLVMIsAInstruction(trunc).is_some() {
|
||||
llvm::LLVMSetNSW(trunc, True);
|
||||
}
|
||||
}
|
||||
}
|
||||
trunc
|
||||
}
|
||||
|
||||
fn sext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::back::write::llvm_err;
|
|||
use crate::builder::SBuilder;
|
||||
use crate::context::SimpleCx;
|
||||
use crate::declare::declare_simple_fn;
|
||||
use crate::errors::LlvmError;
|
||||
use crate::errors::{AutoDiffWithoutEnable, LlvmError};
|
||||
use crate::llvm::AttributePlace::Function;
|
||||
use crate::llvm::{Metadata, True};
|
||||
use crate::value::Value;
|
||||
|
|
@ -46,9 +46,6 @@ fn generate_enzyme_call<'ll>(
|
|||
let output = attrs.ret_activity;
|
||||
|
||||
// We have to pick the name depending on whether we want forward or reverse mode autodiff.
|
||||
// FIXME(ZuseZ4): The new pass based approach should not need the {Forward/Reverse}First method anymore, since
|
||||
// it will handle higher-order derivatives correctly automatically (in theory). Currently
|
||||
// higher-order derivatives fail, so we should debug that before adjusting this code.
|
||||
let mut ad_name: String = match attrs.mode {
|
||||
DiffMode::Forward => "__enzyme_fwddiff",
|
||||
DiffMode::Reverse => "__enzyme_autodiff",
|
||||
|
|
@ -291,6 +288,14 @@ pub(crate) fn differentiate<'ll>(
|
|||
let diag_handler = cgcx.create_dcx();
|
||||
let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx };
|
||||
|
||||
// First of all, did the user try to use autodiff without using the -Zautodiff=Enable flag?
|
||||
if !diff_items.is_empty()
|
||||
&& !cgcx.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable)
|
||||
{
|
||||
let dcx = cgcx.create_dcx();
|
||||
return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutEnable));
|
||||
}
|
||||
|
||||
// Before dumping the module, we want all the TypeTrees to become part of the module.
|
||||
for item in diff_items.iter() {
|
||||
let name = item.source.clone();
|
||||
|
|
|
|||
|
|
@ -66,9 +66,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
|||
// LLVM will prefix the name with `__imp_`. Ideally, we'd like the
|
||||
// existing logic below to set the Storage Class, but it has an
|
||||
// exemption for MinGW for backwards compatibility.
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
llvm::set_dllimport_storage_class(llfn);
|
||||
llfn
|
||||
} else {
|
||||
cx.declare_fn(sym, fn_abi, Some(instance))
|
||||
|
|
@ -99,65 +97,61 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
|||
// has been applied to the definition (wherever that definition may be).
|
||||
|
||||
llvm::set_linkage(llfn, llvm::Linkage::ExternalLinkage);
|
||||
unsafe {
|
||||
let is_generic = instance.args.non_erasable_generics().next().is_some();
|
||||
let is_generic = instance.args.non_erasable_generics().next().is_some();
|
||||
|
||||
let is_hidden = if is_generic {
|
||||
// This is a monomorphization of a generic function.
|
||||
if !(cx.tcx.sess.opts.share_generics()
|
||||
|| tcx.codegen_fn_attrs(instance_def_id).inline
|
||||
== rustc_attr_parsing::InlineAttr::Never)
|
||||
{
|
||||
// When not sharing generics, all instances are in the same
|
||||
// crate and have hidden visibility.
|
||||
true
|
||||
} else {
|
||||
if let Some(instance_def_id) = instance_def_id.as_local() {
|
||||
// This is a monomorphization of a generic function
|
||||
// defined in the current crate. It is hidden if:
|
||||
// - the definition is unreachable for downstream
|
||||
// crates, or
|
||||
// - the current crate does not re-export generics
|
||||
// (because the crate is a C library or executable)
|
||||
cx.tcx.is_unreachable_local_definition(instance_def_id)
|
||||
|| !cx.tcx.local_crate_exports_generics()
|
||||
} else {
|
||||
// This is a monomorphization of a generic function
|
||||
// defined in an upstream crate. It is hidden if:
|
||||
// - it is instantiated in this crate, and
|
||||
// - the current crate does not re-export generics
|
||||
instance.upstream_monomorphization(tcx).is_none()
|
||||
&& !cx.tcx.local_crate_exports_generics()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is a non-generic function. It is hidden if:
|
||||
// - it is instantiated in the local crate, and
|
||||
// - it is defined an upstream crate (non-local), or
|
||||
// - it is not reachable
|
||||
cx.tcx.is_codegened_item(instance_def_id)
|
||||
&& (!instance_def_id.is_local()
|
||||
|| !cx.tcx.is_reachable_non_generic(instance_def_id))
|
||||
};
|
||||
if is_hidden {
|
||||
llvm::set_visibility(llfn, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
// MinGW: For backward compatibility we rely on the linker to decide whether it
|
||||
// should use dllimport for functions.
|
||||
if cx.use_dll_storage_attrs
|
||||
&& let Some(library) = tcx.native_library(instance_def_id)
|
||||
&& library.kind.is_dllimport()
|
||||
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
|
||||
let is_hidden = if is_generic {
|
||||
// This is a monomorphization of a generic function.
|
||||
if !(cx.tcx.sess.opts.share_generics()
|
||||
|| tcx.codegen_fn_attrs(instance_def_id).inline
|
||||
== rustc_attr_parsing::InlineAttr::Never)
|
||||
{
|
||||
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
|
||||
if cx.should_assume_dso_local(llfn, true) {
|
||||
llvm::LLVMRustSetDSOLocal(llfn, true);
|
||||
// When not sharing generics, all instances are in the same
|
||||
// crate and have hidden visibility.
|
||||
true
|
||||
} else {
|
||||
if let Some(instance_def_id) = instance_def_id.as_local() {
|
||||
// This is a monomorphization of a generic function
|
||||
// defined in the current crate. It is hidden if:
|
||||
// - the definition is unreachable for downstream
|
||||
// crates, or
|
||||
// - the current crate does not re-export generics
|
||||
// (because the crate is a C library or executable)
|
||||
cx.tcx.is_unreachable_local_definition(instance_def_id)
|
||||
|| !cx.tcx.local_crate_exports_generics()
|
||||
} else {
|
||||
// This is a monomorphization of a generic function
|
||||
// defined in an upstream crate. It is hidden if:
|
||||
// - it is instantiated in this crate, and
|
||||
// - the current crate does not re-export generics
|
||||
instance.upstream_monomorphization(tcx).is_none()
|
||||
&& !cx.tcx.local_crate_exports_generics()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is a non-generic function. It is hidden if:
|
||||
// - it is instantiated in the local crate, and
|
||||
// - it is defined an upstream crate (non-local), or
|
||||
// - it is not reachable
|
||||
cx.tcx.is_codegened_item(instance_def_id)
|
||||
&& (!instance_def_id.is_local()
|
||||
|| !cx.tcx.is_reachable_non_generic(instance_def_id))
|
||||
};
|
||||
if is_hidden {
|
||||
llvm::set_visibility(llfn, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
// MinGW: For backward compatibility we rely on the linker to decide whether it
|
||||
// should use dllimport for functions.
|
||||
if cx.use_dll_storage_attrs
|
||||
&& let Some(library) = tcx.native_library(instance_def_id)
|
||||
&& library.kind.is_dllimport()
|
||||
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
|
||||
{
|
||||
llvm::set_dllimport_storage_class(llfn);
|
||||
}
|
||||
|
||||
cx.assume_dso_local(llfn, true);
|
||||
|
||||
llfn
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -336,12 +336,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
}
|
||||
|
||||
let dso_local = self.should_assume_dso_local(g, true);
|
||||
if dso_local {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
}
|
||||
}
|
||||
let dso_local = self.assume_dso_local(g, true);
|
||||
|
||||
if !def_id.is_local() {
|
||||
let needs_dll_storage_attr = self.use_dll_storage_attrs
|
||||
|
|
@ -375,9 +370,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
// is actually present in the current crate. We can find out via the
|
||||
// is_codegened_item query.
|
||||
if !self.tcx.is_codegened_item(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
llvm::set_dllimport_storage_class(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -387,9 +380,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
&& library.kind.is_dllimport()
|
||||
{
|
||||
// For foreign (native) libs we know the exact storage type to use.
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
llvm::set_dllimport_storage_class(g);
|
||||
}
|
||||
|
||||
self.instances.borrow_mut().insert(instance, g);
|
||||
|
|
@ -460,9 +451,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
set_global_alignment(self, g, alloc.align);
|
||||
llvm::set_initializer(g, v);
|
||||
|
||||
if self.should_assume_dso_local(g, true) {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
}
|
||||
self.assume_dso_local(g, true);
|
||||
|
||||
// Forward the allocation's mutability (picked by the const interner) to LLVM.
|
||||
if alloc.mutability.is_not() {
|
||||
|
|
|
|||
|
|
@ -92,9 +92,12 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_autodiff_without_lto)]
|
||||
#[note]
|
||||
pub(crate) struct AutoDiffWithoutLTO;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_autodiff_without_enable)]
|
||||
pub(crate) struct AutoDiffWithoutEnable;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_disallowed)]
|
||||
pub(crate) struct LtoDisallowed;
|
||||
|
|
|
|||
|
|
@ -127,15 +127,14 @@ fn get_simple_intrinsic<'ll>(
|
|||
sym::truncf64 => "llvm.trunc.f64",
|
||||
sym::truncf128 => "llvm.trunc.f128",
|
||||
|
||||
sym::rintf16 => "llvm.rint.f16",
|
||||
sym::rintf32 => "llvm.rint.f32",
|
||||
sym::rintf64 => "llvm.rint.f64",
|
||||
sym::rintf128 => "llvm.rint.f128",
|
||||
|
||||
sym::nearbyintf16 => "llvm.nearbyint.f16",
|
||||
sym::nearbyintf32 => "llvm.nearbyint.f32",
|
||||
sym::nearbyintf64 => "llvm.nearbyint.f64",
|
||||
sym::nearbyintf128 => "llvm.nearbyint.f128",
|
||||
// We could use any of `rint`, `nearbyint`, or `roundeven`
|
||||
// for this -- they are all identical in semantics when
|
||||
// assuming the default FP environment.
|
||||
// `rint` is what we used for $forever.
|
||||
sym::round_ties_even_f16 => "llvm.rint.f16",
|
||||
sym::round_ties_even_f32 => "llvm.rint.f32",
|
||||
sym::round_ties_even_f64 => "llvm.rint.f64",
|
||||
sym::round_ties_even_f128 => "llvm.rint.f128",
|
||||
|
||||
sym::roundf16 => "llvm.round.f16",
|
||||
sym::roundf32 => "llvm.round.f32",
|
||||
|
|
@ -144,11 +143,6 @@ fn get_simple_intrinsic<'ll>(
|
|||
|
||||
sym::ptr_mask => "llvm.ptrmask",
|
||||
|
||||
sym::roundevenf16 => "llvm.roundeven.f16",
|
||||
sym::roundevenf32 => "llvm.roundeven.f32",
|
||||
sym::roundevenf64 => "llvm.roundeven.f64",
|
||||
sym::roundevenf128 => "llvm.roundeven.f128",
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
Some(cx.get_intrinsic(llvm_name))
|
||||
|
|
@ -476,7 +470,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
let layout = self.layout_of(tp_ty).layout;
|
||||
let use_integer_compare = match layout.backend_repr() {
|
||||
Scalar(_) | ScalarPair(_, _) => true,
|
||||
Uninhabited | Vector { .. } => false,
|
||||
Vector { .. } => false,
|
||||
Memory { .. } => {
|
||||
// For rusty ABIs, small aggregates are actually passed
|
||||
// as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use super::ffi::{BasicBlock, Metadata, Module, Type, Value};
|
|||
use crate::llvm::Bool;
|
||||
|
||||
#[link(name = "llvm-wrapper", kind = "static")]
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
// Enzyme
|
||||
pub(crate) fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool;
|
||||
pub(crate) fn LLVMRustEraseInstUntilInclusive(BB: &BasicBlock, I: &Value);
|
||||
|
|
@ -18,7 +18,7 @@ extern "C" {
|
|||
pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
// Enzyme
|
||||
pub(crate) fn LLVMDumpModule(M: &Module);
|
||||
pub(crate) fn LLVMDumpValue(V: &Value);
|
||||
|
|
@ -35,3 +35,97 @@ pub enum LLVMRustVerifierFailureAction {
|
|||
LLVMPrintMessageAction = 1,
|
||||
LLVMReturnStatusAction = 2,
|
||||
}
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub use self::Enzyme_AD::*;
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub mod Enzyme_AD {
|
||||
use libc::c_void;
|
||||
extern "C" {
|
||||
pub fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8);
|
||||
}
|
||||
extern "C" {
|
||||
static mut EnzymePrintPerf: c_void;
|
||||
static mut EnzymePrintActivity: c_void;
|
||||
static mut EnzymePrintType: c_void;
|
||||
static mut EnzymePrint: c_void;
|
||||
static mut EnzymeStrictAliasing: c_void;
|
||||
static mut looseTypeAnalysis: c_void;
|
||||
static mut EnzymeInline: c_void;
|
||||
static mut RustTypeRules: c_void;
|
||||
}
|
||||
pub fn set_print_perf(print: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintPerf), print as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_print_activity(print: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintActivity), print as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_print_type(print: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintType), print as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_print(print: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrint), print as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_strict_aliasing(strict: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeStrictAliasing), strict as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_loose_types(loose: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(looseTypeAnalysis), loose as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_inline(val: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeInline), val as u8);
|
||||
}
|
||||
}
|
||||
pub fn set_rust_rules(val: bool) {
|
||||
unsafe {
|
||||
EnzymeSetCLBool(std::ptr::addr_of_mut!(RustTypeRules), val as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub use self::Fallback_AD::*;
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub mod Fallback_AD {
|
||||
#![allow(unused_variables)]
|
||||
|
||||
pub fn set_inline(val: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_print_perf(print: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_print_activity(print: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_print_type(print: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_print(print: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_strict_aliasing(strict: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_loose_types(loose: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn set_rust_rules(val: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -954,6 +954,17 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
// These values **must** match with LLVMGEPNoWrapFlags
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Default)]
|
||||
pub struct GEPNoWrapFlags : c_uint {
|
||||
const InBounds = 1 << 0;
|
||||
const NUSW = 1 << 1;
|
||||
const NUW = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
pub type ModuleBuffer;
|
||||
}
|
||||
|
|
@ -1454,21 +1465,14 @@ unsafe extern "C" {
|
|||
|
||||
pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value;
|
||||
|
||||
pub(crate) fn LLVMBuildGEP2<'a>(
|
||||
B: &Builder<'a>,
|
||||
Ty: &'a Type,
|
||||
Pointer: &'a Value,
|
||||
Indices: *const &'a Value,
|
||||
NumIndices: c_uint,
|
||||
Name: *const c_char,
|
||||
) -> &'a Value;
|
||||
pub(crate) fn LLVMBuildInBoundsGEP2<'a>(
|
||||
pub(crate) fn LLVMBuildGEPWithNoWrapFlags<'a>(
|
||||
B: &Builder<'a>,
|
||||
Ty: &'a Type,
|
||||
Pointer: &'a Value,
|
||||
Indices: *const &'a Value,
|
||||
NumIndices: c_uint,
|
||||
Name: *const c_char,
|
||||
Flags: GEPNoWrapFlags,
|
||||
) -> &'a Value;
|
||||
|
||||
// Casts
|
||||
|
|
|
|||
|
|
@ -403,3 +403,15 @@ pub(crate) fn add_module_flag_str(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_dllimport_storage_class<'ll>(v: &'ll Value) {
|
||||
unsafe {
|
||||
LLVMSetDLLStorageClass(v, DLLStorageClass::DllImport);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_dso_local<'ll>(v: &'ll Value) {
|
||||
unsafe {
|
||||
LLVMRustSetDSOLocal(v, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,11 +38,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
|
||||
llvm::set_linkage(g, base::linkage_to_llvm(linkage));
|
||||
llvm::set_visibility(g, base::visibility_to_llvm(visibility));
|
||||
unsafe {
|
||||
if self.should_assume_dso_local(g, false) {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
}
|
||||
}
|
||||
self.assume_dso_local(g, false);
|
||||
|
||||
self.instances.borrow_mut().insert(instance, g);
|
||||
}
|
||||
|
|
@ -79,9 +75,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
|
||||
debug!("predefine_fn: instance = {:?}", instance);
|
||||
|
||||
if self.should_assume_dso_local(lldecl, false) {
|
||||
unsafe { llvm::LLVMRustSetDSOLocal(lldecl, true) };
|
||||
}
|
||||
self.assume_dso_local(lldecl, false);
|
||||
|
||||
self.instances.borrow_mut().insert(instance, lldecl);
|
||||
}
|
||||
|
|
@ -90,11 +84,16 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
impl CodegenCx<'_, '_> {
|
||||
/// Whether a definition or declaration can be assumed to be local to a group of
|
||||
/// libraries that form a single DSO or executable.
|
||||
pub(crate) fn should_assume_dso_local(
|
||||
&self,
|
||||
llval: &llvm::Value,
|
||||
is_declaration: bool,
|
||||
) -> bool {
|
||||
/// Marks the local as DSO if so.
|
||||
pub(crate) fn assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
|
||||
let assume = self.should_assume_dso_local(llval, is_declaration);
|
||||
if assume {
|
||||
llvm::set_dso_local(llval);
|
||||
}
|
||||
assume
|
||||
}
|
||||
|
||||
fn should_assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool {
|
||||
let linkage = llvm::get_linkage(llval);
|
||||
let visibility = llvm::get_visibility(llval);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ fn uncached_llvm_type<'a, 'tcx>(
|
|||
let element = layout.scalar_llvm_type_at(cx, element);
|
||||
return cx.type_vector(element, count);
|
||||
}
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {}
|
||||
BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {}
|
||||
}
|
||||
|
||||
let name = match layout.ty.kind() {
|
||||
|
|
@ -172,19 +172,16 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
|||
fn is_llvm_immediate(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => true,
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_llvm_scalar_pair(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::ScalarPair(..) => true,
|
||||
BackendRepr::Uninhabited
|
||||
| BackendRepr::Scalar(_)
|
||||
| BackendRepr::Vector { .. }
|
||||
| BackendRepr::Memory { .. } => false,
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_codegen_ssa"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -244,22 +244,17 @@ pub fn each_linked_rlib(
|
|||
|
||||
fmts
|
||||
} else {
|
||||
for combination in info.dependency_formats.iter().combinations(2) {
|
||||
let (ty1, list1) = &combination[0];
|
||||
let (ty2, list2) = &combination[1];
|
||||
if list1 != list2 {
|
||||
return Err(errors::LinkRlibError::IncompatibleDependencyFormats {
|
||||
ty1: format!("{ty1:?}"),
|
||||
ty2: format!("{ty2:?}"),
|
||||
list1: format!("{list1:?}"),
|
||||
list2: format!("{list2:?}"),
|
||||
});
|
||||
}
|
||||
let mut dep_formats = info.dependency_formats.iter();
|
||||
let (ty1, list1) = dep_formats.next().ok_or(errors::LinkRlibError::MissingFormat)?;
|
||||
if let Some((ty2, list2)) = dep_formats.find(|(_, list2)| list1 != *list2) {
|
||||
return Err(errors::LinkRlibError::IncompatibleDependencyFormats {
|
||||
ty1: format!("{ty1:?}"),
|
||||
ty2: format!("{ty2:?}"),
|
||||
list1: format!("{list1:?}"),
|
||||
list2: format!("{list2:?}"),
|
||||
});
|
||||
}
|
||||
if info.dependency_formats.is_empty() {
|
||||
return Err(errors::LinkRlibError::MissingFormat);
|
||||
}
|
||||
info.dependency_formats.first().unwrap().1
|
||||
list1
|
||||
};
|
||||
|
||||
let used_dep_crates = info.used_crates.iter();
|
||||
|
|
@ -626,10 +621,9 @@ fn link_staticlib(
|
|||
|
||||
let mut all_rust_dylibs = vec![];
|
||||
for &cnum in crates {
|
||||
match fmts.get(cnum) {
|
||||
Some(&Linkage::Dynamic) => {}
|
||||
_ => continue,
|
||||
}
|
||||
let Some(Linkage::Dynamic) = fmts.get(cnum) else {
|
||||
continue;
|
||||
};
|
||||
let crate_name = codegen_results.crate_info.crate_name[&cnum];
|
||||
let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum];
|
||||
if let Some((path, _)) = &used_crate_source.dylib {
|
||||
|
|
@ -1990,6 +1984,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor)
|
|||
if let Some(args) = sess.target.pre_link_args.get(&flavor) {
|
||||
cmd.verbatim_args(args.iter().map(Deref::deref));
|
||||
}
|
||||
|
||||
cmd.verbatim_args(&sess.opts.unstable_opts.pre_link_args);
|
||||
}
|
||||
|
||||
|
|
@ -2518,6 +2513,12 @@ fn add_order_independent_options(
|
|||
"--target-cpu",
|
||||
&codegen_results.crate_info.target_cpu,
|
||||
]);
|
||||
if codegen_results.crate_info.target_features.len() > 0 {
|
||||
cmd.link_arg(&format!(
|
||||
"--target-feature={}",
|
||||
&codegen_results.crate_info.target_features.join(",")
|
||||
));
|
||||
}
|
||||
} else if flavor == LinkerFlavor::Ptx {
|
||||
cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]);
|
||||
} else if flavor == LinkerFlavor::Bpf {
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ pub(crate) fn get_linker<'a>(
|
|||
hinted_static: None,
|
||||
is_ld: cc == Cc::No,
|
||||
is_gnu: flavor.is_gnu(),
|
||||
uses_lld: flavor.uses_lld(),
|
||||
}) as Box<dyn Linker>,
|
||||
LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>,
|
||||
LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>,
|
||||
|
|
@ -361,6 +362,7 @@ struct GccLinker<'a> {
|
|||
// Link as ld
|
||||
is_ld: bool,
|
||||
is_gnu: bool,
|
||||
uses_lld: bool,
|
||||
}
|
||||
|
||||
impl<'a> GccLinker<'a> {
|
||||
|
|
@ -552,6 +554,7 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
self.link_args(&["--entry", "_initialize"]);
|
||||
}
|
||||
}
|
||||
|
||||
// VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,
|
||||
// it switches linking for libc and similar system libraries to static without using
|
||||
// any `#[link]` attributes in the `libc` crate, see #72782 for details.
|
||||
|
|
@ -567,6 +570,15 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
{
|
||||
self.cc_arg("--static-crt");
|
||||
}
|
||||
|
||||
// avr-none doesn't have default ISA, users must specify which specific
|
||||
// CPU (well, microcontroller) they are targetting using `-Ctarget-cpu`.
|
||||
//
|
||||
// Currently this makes sense only when using avr-gcc as a linker, since
|
||||
// it brings a couple of hand-written important intrinsics from libgcc.
|
||||
if self.sess.target.arch == "avr" && !self.uses_lld {
|
||||
self.verbatim_arg(format!("-mmcu={}", self.target_cpu));
|
||||
}
|
||||
}
|
||||
|
||||
fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool) {
|
||||
|
|
|
|||
|
|
@ -252,15 +252,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
|||
// Unsupported architecture.
|
||||
_ => return None,
|
||||
};
|
||||
let binary_format = if sess.target.is_like_osx {
|
||||
BinaryFormat::MachO
|
||||
} else if sess.target.is_like_windows {
|
||||
BinaryFormat::Coff
|
||||
} else if sess.target.is_like_aix {
|
||||
BinaryFormat::Xcoff
|
||||
} else {
|
||||
BinaryFormat::Elf
|
||||
};
|
||||
let binary_format = sess.target.binary_format.to_object();
|
||||
|
||||
let mut file = write::Object::new(binary_format, architecture, endianness);
|
||||
file.set_sub_architecture(sub_architecture);
|
||||
|
|
|
|||
|
|
@ -183,11 +183,11 @@ fn exported_symbols_provider_local(
|
|||
});
|
||||
|
||||
let mut symbols: Vec<_> =
|
||||
sorted.iter().map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)).collect();
|
||||
sorted.iter().map(|&(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)).collect();
|
||||
|
||||
// Export TLS shims
|
||||
if !tcx.sess.target.dll_tls_export {
|
||||
symbols.extend(sorted.iter().filter_map(|(&def_id, &info)| {
|
||||
symbols.extend(sorted.iter().filter_map(|&(&def_id, &info)| {
|
||||
tcx.needs_thread_local_shim(def_id).then(|| {
|
||||
(
|
||||
ExportedSymbol::ThreadLocalShim(def_id),
|
||||
|
|
|
|||
|
|
@ -405,7 +405,8 @@ fn generate_lto_work<B: ExtraBackendMethods>(
|
|||
B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise());
|
||||
if cgcx.lto == Lto::Fat && !autodiff.is_empty() {
|
||||
let config = cgcx.config(ModuleKind::Regular);
|
||||
module = unsafe { module.autodiff(cgcx, autodiff, config).unwrap() };
|
||||
module =
|
||||
unsafe { module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise()) };
|
||||
}
|
||||
// We are adding a single work item, so the cost doesn't matter.
|
||||
vec![(WorkItem::LTO(module), 0)]
|
||||
|
|
@ -572,10 +573,10 @@ fn produce_final_output_artifacts(
|
|||
};
|
||||
|
||||
let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| {
|
||||
if compiled_modules.modules.len() == 1 {
|
||||
if let [module] = &compiled_modules.modules[..] {
|
||||
// 1) Only one codegen unit. In this case it's no difficulty
|
||||
// to copy `foo.0.x` to `foo.x`.
|
||||
let module_name = Some(&compiled_modules.modules[0].name[..]);
|
||||
let module_name = Some(&module.name[..]);
|
||||
let path = crate_output.temp_path(output_type, module_name);
|
||||
let output = crate_output.path(output_type);
|
||||
if !output_type.is_text_output() && output.is_tty() {
|
||||
|
|
@ -707,8 +708,8 @@ fn produce_final_output_artifacts(
|
|||
}
|
||||
|
||||
if sess.opts.json_artifact_notifications {
|
||||
if compiled_modules.modules.len() == 1 {
|
||||
compiled_modules.modules[0].for_each_output(|_path, ty| {
|
||||
if let [module] = &compiled_modules.modules[..] {
|
||||
module.for_each_output(|_path, ty| {
|
||||
if sess.opts.output_types.contains_key(&ty) {
|
||||
let descr = ty.shorthand();
|
||||
// for single cgu file is renamed to drop cgu specific suffix
|
||||
|
|
@ -864,7 +865,7 @@ pub(crate) fn compute_per_cgu_lto_type(
|
|||
// require LTO so the request for LTO is always unconditionally
|
||||
// passed down to the backend, but we don't actually want to do
|
||||
// anything about it yet until we've got a final product.
|
||||
let is_rlib = sess_crate_types.len() == 1 && sess_crate_types[0] == CrateType::Rlib;
|
||||
let is_rlib = matches!(sess_crate_types, [CrateType::Rlib]);
|
||||
|
||||
match sess_lto {
|
||||
Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin,
|
||||
|
|
@ -1537,8 +1538,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// Spin up what work we can, only doing this while we've got available
|
||||
// parallelism slots and work left to spawn.
|
||||
if codegen_state != Aborted {
|
||||
while !work_items.is_empty() && running_with_own_token < tokens.len() {
|
||||
let (item, _) = work_items.pop().unwrap();
|
||||
while running_with_own_token < tokens.len()
|
||||
&& let Some((item, _)) = work_items.pop()
|
||||
{
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&mut llvm_start_time,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use rustc_middle::query::Providers;
|
|||
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
|
||||
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
|
||||
use rustc_span::{DUMMY_SP, Symbol, sym};
|
||||
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
|
||||
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
|
||||
|
|
@ -364,13 +364,7 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
let rhs_sz = bx.cx().int_width(rhs_llty);
|
||||
let lhs_sz = bx.cx().int_width(lhs_llty);
|
||||
if lhs_sz < rhs_sz {
|
||||
if is_unchecked && bx.sess().opts.optimize != OptLevel::No {
|
||||
// FIXME: Use `trunc nuw` once that's available
|
||||
let inrange = bx.icmp(IntPredicate::IntULE, rhs, mask);
|
||||
bx.assume(inrange);
|
||||
}
|
||||
|
||||
bx.trunc(rhs, lhs_llty)
|
||||
if is_unchecked { bx.unchecked_utrunc(rhs, lhs_llty) } else { bx.trunc(rhs, lhs_llty) }
|
||||
} else if lhs_sz > rhs_sz {
|
||||
// We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the
|
||||
// RHS to `255i32`. But then we mask the shift amount to be within the size of the LHS
|
||||
|
|
@ -921,6 +915,7 @@ impl CrateInfo {
|
|||
let n_crates = crates.len();
|
||||
let mut info = CrateInfo {
|
||||
target_cpu,
|
||||
target_features: tcx.global_backend_features(()).clone(),
|
||||
crate_types,
|
||||
exported_symbols,
|
||||
linked_symbols,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::attr::list_contains_name;
|
||||
use rustc_ast::expand::autodiff_attrs::{
|
||||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
|
||||
};
|
||||
|
|
@ -377,24 +376,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let segments =
|
||||
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
[sym::arm, sym::a32] | [sym::arm, sym::t32] => {
|
||||
if !tcx.sess.target.has_thumb_interworking {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
} else if segments[1] == sym::a32 {
|
||||
Some(InstructionSetAttr::ArmA32)
|
||||
} else if segments[1] == sym::t32 {
|
||||
Some(InstructionSetAttr::ArmT32)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
[sym::arm, sym::a32 | sym::t32]
|
||||
if !tcx.sess.target.has_thumb_interworking =>
|
||||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
|
||||
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
|
||||
_ => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
|
|
@ -435,7 +430,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
&& let Some((sym::align, literal)) = item.singleton_lit_list()
|
||||
{
|
||||
rustc_attr_parsing::parse_alignment(&literal.kind)
|
||||
.map_err(|msg| {
|
||||
.inspect_err(|msg| {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
literal.span,
|
||||
|
|
@ -536,25 +531,27 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
|
||||
if attr.is_word() {
|
||||
InlineAttr::Hint
|
||||
} else if let Some(ref items) = attr.meta_item_list() {
|
||||
inline_span = Some(attr.span);
|
||||
if items.len() != 1 {
|
||||
struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
|
||||
InlineAttr::None
|
||||
} else if list_contains_name(items, sym::always) {
|
||||
InlineAttr::Always
|
||||
} else if list_contains_name(items, sym::never) {
|
||||
InlineAttr::Never
|
||||
} else {
|
||||
struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
|
||||
.with_help("valid inline arguments are `always` and `never`")
|
||||
.emit();
|
||||
return InlineAttr::Hint;
|
||||
}
|
||||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return ia;
|
||||
};
|
||||
|
||||
InlineAttr::None
|
||||
}
|
||||
inline_span = Some(attr.span);
|
||||
let [item] = &items[..] else {
|
||||
struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
|
||||
return InlineAttr::None;
|
||||
};
|
||||
if item.has_name(sym::always) {
|
||||
InlineAttr::Always
|
||||
} else if item.has_name(sym::never) {
|
||||
InlineAttr::Never
|
||||
} else {
|
||||
ia
|
||||
struct_span_code_err!(tcx.dcx(), item.span(), E0535, "invalid argument")
|
||||
.with_help("valid inline arguments are `always` and `never`")
|
||||
.emit();
|
||||
|
||||
InlineAttr::None
|
||||
}
|
||||
});
|
||||
codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
|
||||
|
|
@ -586,23 +583,25 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
|
||||
if attr.is_word() {
|
||||
err(attr.span, "expected one argument");
|
||||
ia
|
||||
} else if let Some(ref items) = attr.meta_item_list() {
|
||||
inline_span = Some(attr.span);
|
||||
if items.len() != 1 {
|
||||
err(attr.span, "expected one argument");
|
||||
OptimizeAttr::Default
|
||||
} else if list_contains_name(items, sym::size) {
|
||||
OptimizeAttr::Size
|
||||
} else if list_contains_name(items, sym::speed) {
|
||||
OptimizeAttr::Speed
|
||||
} else if list_contains_name(items, sym::none) {
|
||||
OptimizeAttr::DoNotOptimize
|
||||
} else {
|
||||
err(items[0].span(), "invalid argument");
|
||||
OptimizeAttr::Default
|
||||
}
|
||||
return ia;
|
||||
}
|
||||
let Some(ref items) = attr.meta_item_list() else {
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
|
||||
inline_span = Some(attr.span);
|
||||
let [item] = &items[..] else {
|
||||
err(attr.span, "expected one argument");
|
||||
return OptimizeAttr::Default;
|
||||
};
|
||||
if item.has_name(sym::size) {
|
||||
OptimizeAttr::Size
|
||||
} else if item.has_name(sym::speed) {
|
||||
OptimizeAttr::Speed
|
||||
} else if item.has_name(sym::none) {
|
||||
OptimizeAttr::DoNotOptimize
|
||||
} else {
|
||||
err(item.span(), "invalid argument");
|
||||
OptimizeAttr::Default
|
||||
}
|
||||
});
|
||||
|
|
@ -644,25 +643,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// llvm/llvm-project#70563).
|
||||
if !codegen_fn_attrs.target_features.is_empty()
|
||||
&& matches!(codegen_fn_attrs.inline, InlineAttr::Always)
|
||||
&& let Some(span) = inline_span
|
||||
{
|
||||
if let Some(span) = inline_span {
|
||||
tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
|
||||
}
|
||||
tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
|
||||
}
|
||||
|
||||
if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() {
|
||||
if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(did);
|
||||
tcx.node_span_lint(
|
||||
lint::builtin::INLINE_NO_SANITIZE,
|
||||
hir_id,
|
||||
no_sanitize_span,
|
||||
|lint| {
|
||||
lint.primary_message("`no_sanitize` will have no effect after inlining");
|
||||
lint.span_note(inline_span, "inlining requested here");
|
||||
},
|
||||
)
|
||||
}
|
||||
if !codegen_fn_attrs.no_sanitize.is_empty()
|
||||
&& codegen_fn_attrs.inline.always()
|
||||
&& let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
|
||||
{
|
||||
let hir_id = tcx.local_def_id_to_hir_id(did);
|
||||
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
|
||||
lint.primary_message("`no_sanitize` will have no effect after inlining");
|
||||
lint.span_note(inline_span, "inlining requested here");
|
||||
})
|
||||
}
|
||||
|
||||
// Weak lang items have the same semantics as "std internal" symbols in the
|
||||
|
|
@ -692,10 +686,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// Any linkage to LLVM intrinsics for now forcibly marks them all as never
|
||||
// unwinds since LLVM sometimes can't handle codegen which `invoke`s
|
||||
// intrinsic functions.
|
||||
if let Some(name) = &codegen_fn_attrs.link_name {
|
||||
if name.as_str().starts_with("llvm.") {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||
}
|
||||
if let Some(name) = &codegen_fn_attrs.link_name
|
||||
&& name.as_str().starts_with("llvm.")
|
||||
{
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||
}
|
||||
|
||||
if let Some(features) = check_tied_features(
|
||||
|
|
@ -756,18 +750,13 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||
|
||||
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
|
||||
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
|
||||
let meta_item_list = attr.meta_item_list();
|
||||
let meta_item_list = meta_item_list.as_deref();
|
||||
let sole_meta_list = match meta_item_list {
|
||||
Some([item]) => item.lit(),
|
||||
Some(_) => {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
_ => None,
|
||||
let meta_item_list = attr.meta_item_list()?;
|
||||
let [sole_meta_list] = &meta_item_list[..] else {
|
||||
tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
|
||||
return None;
|
||||
};
|
||||
if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
|
||||
sole_meta_list
|
||||
sole_meta_list.lit()
|
||||
{
|
||||
// According to the table at
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
|
||||
|
|
@ -894,7 +883,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
let [mode, input_activities @ .., ret_activity] = &list[..] else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
|
||||
};
|
||||
let mode = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = mode {
|
||||
let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
|
||||
|
|
@ -910,7 +899,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
};
|
||||
|
||||
// First read the ret symbol from the attribute
|
||||
let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = ret_activity {
|
||||
let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
|
||||
p1.segments.first().unwrap().ident
|
||||
} else {
|
||||
span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
|
||||
|
|
@ -924,7 +913,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
// Now parse all the intermediate (input) activities
|
||||
let mut arg_activities: Vec<DiffActivity> = vec![];
|
||||
for arg in input_activities {
|
||||
let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p2, .. }) = arg {
|
||||
let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
|
||||
match p2.segments.first() {
|
||||
Some(x) => x.ident,
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ impl From<&cstore::NativeLib> for NativeLib {
|
|||
#[derive(Debug, Encodable, Decodable)]
|
||||
pub struct CrateInfo {
|
||||
pub target_cpu: String,
|
||||
pub target_features: Vec<String>,
|
||||
pub crate_types: Vec<CrateType>,
|
||||
pub exported_symbols: UnordMap<CrateType, Vec<String>>,
|
||||
pub linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>>,
|
||||
|
|
@ -230,6 +231,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
crate::base::provide(providers);
|
||||
crate::target_features::provide(providers);
|
||||
crate::codegen_attrs::provide(providers);
|
||||
providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![];
|
||||
}
|
||||
|
||||
/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc`
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, WrappingRange};
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::mir::{
|
||||
self, AssertKind, BasicBlock, InlineAsmMacro, SwitchTargets, UnwindTerminateReason,
|
||||
};
|
||||
use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
|
|
@ -722,14 +720,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
// Put together the arguments to the panic entry point.
|
||||
let (lang_item, args) = match msg {
|
||||
AssertKind::BoundsCheck { ref len, ref index } => {
|
||||
AssertKind::BoundsCheck { len, index } => {
|
||||
let len = self.codegen_operand(bx, len).immediate();
|
||||
let index = self.codegen_operand(bx, index).immediate();
|
||||
// It's `fn panic_bounds_check(index: usize, len: usize)`,
|
||||
// and `#[track_caller]` adds an implicit third argument.
|
||||
(LangItem::PanicBoundsCheck, vec![index, len, location])
|
||||
}
|
||||
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
|
||||
AssertKind::MisalignedPointerDereference { required, found } => {
|
||||
let required = self.codegen_operand(bx, required).immediate();
|
||||
let found = self.codegen_operand(bx, found).immediate();
|
||||
// It's `fn panic_misaligned_pointer_dereference(required: usize, found: usize)`,
|
||||
|
|
@ -942,7 +940,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
&fn_abi.ret,
|
||||
&mut llargs,
|
||||
Some(intrinsic),
|
||||
target,
|
||||
);
|
||||
let dest = match ret_dest {
|
||||
_ if fn_abi.ret.is_indirect() => llargs[0],
|
||||
|
|
@ -998,23 +995,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
};
|
||||
|
||||
let mut llargs = Vec::with_capacity(arg_count);
|
||||
let destination = target.as_ref().map(|&target| {
|
||||
(
|
||||
self.make_return_dest(
|
||||
bx,
|
||||
destination,
|
||||
&fn_abi.ret,
|
||||
&mut llargs,
|
||||
None,
|
||||
Some(target),
|
||||
),
|
||||
target,
|
||||
)
|
||||
});
|
||||
|
||||
// We still need to call `make_return_dest` even if there's no `target`, since
|
||||
// `fn_abi.ret` could be `PassMode::Indirect`, even if it is uninhabited,
|
||||
// and `make_return_dest` adds the return-place indirect pointer to `llargs`.
|
||||
let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, None);
|
||||
let destination = target.map(|target| (return_dest, target));
|
||||
|
||||
// Split the rust-call tupled arguments off.
|
||||
let (first_args, untuple) = if abi == ExternAbi::RustCall && !args.is_empty() {
|
||||
let (tup, args) = args.split_last().unwrap();
|
||||
let (first_args, untuple) = if abi == ExternAbi::RustCall
|
||||
&& let Some((tup, args)) = args.split_last()
|
||||
{
|
||||
(args, Some(tup))
|
||||
} else {
|
||||
(args, None)
|
||||
|
|
@ -1040,7 +1031,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let (idx, _) = op.layout.non_1zst_field(bx).expect(
|
||||
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
|
||||
);
|
||||
op = op.extract_field(bx, idx);
|
||||
op = op.extract_field(self, bx, idx);
|
||||
}
|
||||
|
||||
// Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
|
||||
|
|
@ -1072,7 +1063,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let (idx, _) = op.layout.non_1zst_field(bx).expect(
|
||||
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
|
||||
);
|
||||
op = op.extract_field(bx, idx);
|
||||
op = op.extract_field(self, bx, idx);
|
||||
}
|
||||
|
||||
// Make sure that we've actually unwrapped the rcvr down
|
||||
|
|
@ -1572,9 +1563,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
if scalar.is_bool() {
|
||||
bx.range_metadata(llval, WrappingRange { start: 0, end: 1 });
|
||||
}
|
||||
// We store bools as `i8` so we need to truncate to `i1`.
|
||||
llval = bx.to_immediate_scalar(llval, scalar);
|
||||
}
|
||||
// We store bools as `i8` so we need to truncate to `i1`.
|
||||
llval = bx.to_immediate(llval, arg.layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1604,7 +1595,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
} else {
|
||||
// If the tuple is immediate, the elements are as well.
|
||||
for i in 0..tuple.layout.fields.count() {
|
||||
let op = tuple.extract_field(bx, i);
|
||||
let op = tuple.extract_field(self, bx, i);
|
||||
self.codegen_argument(bx, op, llargs, &args[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1813,11 +1804,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
fn_ret: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
llargs: &mut Vec<Bx::Value>,
|
||||
intrinsic: Option<ty::IntrinsicDef>,
|
||||
target: Option<BasicBlock>,
|
||||
) -> ReturnDest<'tcx, Bx::Value> {
|
||||
if target.is_none() {
|
||||
return ReturnDest::Nothing;
|
||||
}
|
||||
// If the return is ignored, we can just return a do-nothing `ReturnDest`.
|
||||
if fn_ret.is_ignore() {
|
||||
return ReturnDest::Nothing;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_middle::ty::{Instance, Ty, TyCtxt};
|
|||
use rustc_middle::{bug, span_bug, ty};
|
||||
use rustc_span::sym;
|
||||
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
|
||||
use rustc_target::spec::WasmCAbi;
|
||||
use rustc_target::spec::{BinaryFormat, WasmCAbi};
|
||||
|
||||
use crate::common;
|
||||
use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};
|
||||
|
|
@ -104,27 +104,6 @@ fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
}
|
||||
}
|
||||
|
||||
enum AsmBinaryFormat {
|
||||
Elf,
|
||||
Macho,
|
||||
Coff,
|
||||
Wasm,
|
||||
}
|
||||
|
||||
impl AsmBinaryFormat {
|
||||
fn from_target(target: &rustc_target::spec::Target) -> Self {
|
||||
if target.is_like_windows {
|
||||
Self::Coff
|
||||
} else if target.is_like_osx {
|
||||
Self::Macho
|
||||
} else if target.is_like_wasm {
|
||||
Self::Wasm
|
||||
} else {
|
||||
Self::Elf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix_and_suffix<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
|
|
@ -134,7 +113,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
) -> (String, String) {
|
||||
use std::fmt::Write;
|
||||
|
||||
let asm_binary_format = AsmBinaryFormat::from_target(&tcx.sess.target);
|
||||
let asm_binary_format = &tcx.sess.target.binary_format;
|
||||
|
||||
let is_arm = tcx.sess.target.arch == "arm";
|
||||
let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
|
||||
|
|
@ -178,10 +157,13 @@ fn prefix_and_suffix<'tcx>(
|
|||
}
|
||||
Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
|
||||
match asm_binary_format {
|
||||
AsmBinaryFormat::Elf | AsmBinaryFormat::Coff | AsmBinaryFormat::Wasm => {
|
||||
BinaryFormat::Elf
|
||||
| BinaryFormat::Coff
|
||||
| BinaryFormat::Wasm
|
||||
| BinaryFormat::Xcoff => {
|
||||
writeln!(w, ".weak {asm_name}")?;
|
||||
}
|
||||
AsmBinaryFormat::Macho => {
|
||||
BinaryFormat::MachO => {
|
||||
writeln!(w, ".globl {asm_name}")?;
|
||||
writeln!(w, ".weak_definition {asm_name}")?;
|
||||
}
|
||||
|
|
@ -207,7 +189,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
let mut begin = String::new();
|
||||
let mut end = String::new();
|
||||
match asm_binary_format {
|
||||
AsmBinaryFormat::Elf => {
|
||||
BinaryFormat::Elf | BinaryFormat::Xcoff => {
|
||||
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
|
||||
|
||||
let progbits = match is_arm {
|
||||
|
|
@ -239,7 +221,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
AsmBinaryFormat::Macho => {
|
||||
BinaryFormat::MachO => {
|
||||
let section = link_section.unwrap_or("__TEXT,__text".to_string());
|
||||
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
|
||||
writeln!(begin, ".balign {align}").unwrap();
|
||||
|
|
@ -255,7 +237,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
AsmBinaryFormat::Coff => {
|
||||
BinaryFormat::Coff => {
|
||||
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
|
||||
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
|
||||
writeln!(begin, ".balign {align}").unwrap();
|
||||
|
|
@ -272,7 +254,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
AsmBinaryFormat::Wasm => {
|
||||
BinaryFormat::Wasm => {
|
||||
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
|
||||
|
||||
writeln!(begin, ".section {section},\"\",@").unwrap();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::fmt;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use either::Either;
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Align, BackendRepr, Size};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
|
||||
use rustc_middle::mir::{self, ConstValue};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use tracing::debug;
|
||||
|
||||
use super::place::{PlaceRef, PlaceValue};
|
||||
|
|
@ -352,79 +351,83 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
|
||||
pub(crate) fn extract_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
&self,
|
||||
fx: &mut FunctionCx<'a, 'tcx, Bx>,
|
||||
bx: &mut Bx,
|
||||
i: usize,
|
||||
) -> Self {
|
||||
let field = self.layout.field(bx.cx(), i);
|
||||
let offset = self.layout.fields.offset(i);
|
||||
|
||||
let mut val = match (self.val, self.layout.backend_repr) {
|
||||
// If the field is ZST, it has no data.
|
||||
_ if field.is_zst() => OperandValue::ZeroSized,
|
||||
|
||||
// Newtype of a scalar, scalar pair or vector.
|
||||
(OperandValue::Immediate(_) | OperandValue::Pair(..), _)
|
||||
if field.size == self.layout.size =>
|
||||
if !bx.is_backend_ref(self.layout) && bx.is_backend_ref(field) {
|
||||
if let BackendRepr::Vector { count, .. } = self.layout.backend_repr
|
||||
&& let BackendRepr::Memory { sized: true } = field.backend_repr
|
||||
&& count.is_power_of_two()
|
||||
{
|
||||
assert_eq!(offset.bytes(), 0);
|
||||
self.val
|
||||
assert_eq!(field.size, self.layout.size);
|
||||
// This is being deprecated, but for now stdarch still needs it for
|
||||
// Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]);
|
||||
let place = PlaceRef::alloca(bx, field);
|
||||
self.val.store(bx, place.val.with_type(self.layout));
|
||||
return bx.load_operand(place);
|
||||
} else {
|
||||
// Part of https://github.com/rust-lang/compiler-team/issues/838
|
||||
bug!("Non-ref type {self:?} cannot project to ref field type {field:?}");
|
||||
}
|
||||
|
||||
// Extract a scalar component from a pair.
|
||||
(OperandValue::Pair(a_llval, b_llval), BackendRepr::ScalarPair(a, b)) => {
|
||||
if offset.bytes() == 0 {
|
||||
assert_eq!(field.size, a.size(bx.cx()));
|
||||
OperandValue::Immediate(a_llval)
|
||||
} else {
|
||||
assert_eq!(offset, a.size(bx.cx()).align_to(b.align(bx.cx()).abi));
|
||||
assert_eq!(field.size, b.size(bx.cx()));
|
||||
OperandValue::Immediate(b_llval)
|
||||
}
|
||||
}
|
||||
|
||||
// `#[repr(simd)]` types are also immediate.
|
||||
(OperandValue::Immediate(llval), BackendRepr::Vector { .. }) => {
|
||||
OperandValue::Immediate(bx.extract_element(llval, bx.cx().const_usize(i as u64)))
|
||||
}
|
||||
|
||||
_ => bug!("OperandRef::extract_field({:?}): not applicable", self),
|
||||
};
|
||||
|
||||
match (&mut val, field.backend_repr) {
|
||||
(OperandValue::ZeroSized, _) => {}
|
||||
(
|
||||
OperandValue::Immediate(llval),
|
||||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. },
|
||||
) => {
|
||||
// Bools in union fields needs to be truncated.
|
||||
*llval = bx.to_immediate(*llval, field);
|
||||
}
|
||||
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(a_abi, b_abi)) => {
|
||||
// Bools in union fields needs to be truncated.
|
||||
*a = bx.to_immediate_scalar(*a, a_abi);
|
||||
*b = bx.to_immediate_scalar(*b, b_abi);
|
||||
}
|
||||
// Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]);
|
||||
(OperandValue::Immediate(llval), BackendRepr::Memory { sized: true }) => {
|
||||
assert_matches!(self.layout.backend_repr, BackendRepr::Vector { .. });
|
||||
|
||||
let llfield_ty = bx.cx().backend_type(field);
|
||||
|
||||
// Can't bitcast an aggregate, so round trip through memory.
|
||||
let llptr = bx.alloca(field.size, field.align.abi);
|
||||
bx.store(*llval, llptr, field.align.abi);
|
||||
*llval = bx.load(llfield_ty, llptr, field.align.abi);
|
||||
}
|
||||
(
|
||||
OperandValue::Immediate(_),
|
||||
BackendRepr::Uninhabited | BackendRepr::Memory { sized: false },
|
||||
) => {
|
||||
bug!()
|
||||
}
|
||||
(OperandValue::Pair(..), _) => bug!(),
|
||||
(OperandValue::Ref(..), _) => bug!(),
|
||||
}
|
||||
|
||||
let val = if field.is_zst() {
|
||||
OperandValue::ZeroSized
|
||||
} else if field.size == self.layout.size {
|
||||
assert_eq!(offset.bytes(), 0);
|
||||
fx.codegen_transmute_operand(bx, *self, field).unwrap_or_else(|| {
|
||||
bug!(
|
||||
"Expected `codegen_transmute_operand` to handle equal-size \
|
||||
field {i:?} projection from {self:?} to {field:?}"
|
||||
)
|
||||
})
|
||||
} else {
|
||||
let (in_scalar, imm) = match (self.val, self.layout.backend_repr) {
|
||||
// Extract a scalar component from a pair.
|
||||
(OperandValue::Pair(a_llval, b_llval), BackendRepr::ScalarPair(a, b)) => {
|
||||
if offset.bytes() == 0 {
|
||||
assert_eq!(field.size, a.size(bx.cx()));
|
||||
(Some(a), a_llval)
|
||||
} else {
|
||||
assert_eq!(offset, a.size(bx.cx()).align_to(b.align(bx.cx()).abi));
|
||||
assert_eq!(field.size, b.size(bx.cx()));
|
||||
(Some(b), b_llval)
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(fx.mir.span, "OperandRef::extract_field({:?}): not applicable", self)
|
||||
}
|
||||
};
|
||||
OperandValue::Immediate(match field.backend_repr {
|
||||
BackendRepr::Vector { .. } => imm,
|
||||
BackendRepr::Scalar(out_scalar) => {
|
||||
let Some(in_scalar) = in_scalar else {
|
||||
span_bug!(
|
||||
fx.mir.span,
|
||||
"OperandRef::extract_field({:?}): missing input scalar for output scalar",
|
||||
self
|
||||
)
|
||||
};
|
||||
if in_scalar != out_scalar {
|
||||
// If the backend and backend_immediate types might differ,
|
||||
// flip back to the backend type then to the new immediate.
|
||||
// This avoids nop truncations, but still handles things like
|
||||
// Bools in union fields needs to be truncated.
|
||||
let backend = bx.from_immediate(imm);
|
||||
bx.to_immediate_scalar(backend, out_scalar)
|
||||
} else {
|
||||
imm
|
||||
}
|
||||
}
|
||||
BackendRepr::ScalarPair(_, _) | BackendRepr::Memory { .. } => bug!(),
|
||||
})
|
||||
};
|
||||
|
||||
OperandRef { val, layout: field }
|
||||
}
|
||||
}
|
||||
|
|
@ -581,13 +584,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// Moves out of scalar and scalar pair fields are trivial.
|
||||
for elem in place_ref.projection.iter() {
|
||||
match elem {
|
||||
mir::ProjectionElem::Field(ref f, _) => {
|
||||
mir::ProjectionElem::Field(f, _) => {
|
||||
assert!(
|
||||
!o.layout.ty.is_any_ptr(),
|
||||
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
|
||||
but tried to access field {f:?} of pointer {o:?}",
|
||||
);
|
||||
o = o.extract_field(bx, f.index());
|
||||
o = o.extract_field(self, bx, f.index());
|
||||
}
|
||||
mir::ProjectionElem::Index(_)
|
||||
| mir::ProjectionElem::ConstantIndex { .. } => {
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
layout.size
|
||||
};
|
||||
|
||||
let llval = bx.inbounds_gep(bx.cx().backend_type(layout), self.val.llval, &[llindex]);
|
||||
let llval = bx.inbounds_nuw_gep(bx.cx().backend_type(layout), self.val.llval, &[llindex]);
|
||||
let align = self.val.align.restrict_for_offset(offset);
|
||||
PlaceValue::new_sized(llval, align).with_type(layout)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
///
|
||||
/// Returns `None` for cases that can't work in that framework, such as for
|
||||
/// `Immediate`->`Ref` that needs an `alloc` to get the location.
|
||||
fn codegen_transmute_operand(
|
||||
pub(crate) fn codegen_transmute_operand(
|
||||
&mut self,
|
||||
bx: &mut Bx,
|
||||
operand: OperandRef<'tcx, Bx::Value>,
|
||||
|
|
@ -260,6 +260,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
OperandValue::Ref(source_place_val) => {
|
||||
assert_eq!(source_place_val.llextra, None);
|
||||
assert_matches!(operand_kind, OperandValueKind::Ref);
|
||||
// The existing alignment is part of `source_place_val`,
|
||||
// so that alignment will be used, not `cast`'s.
|
||||
Some(bx.load_operand(source_place_val.with_type(cast)).val)
|
||||
}
|
||||
OperandValue::ZeroSized => {
|
||||
|
|
@ -664,9 +666,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
lhs.layout.ty,
|
||||
),
|
||||
|
||||
(OperandValue::Immediate(lhs_val), OperandValue::Immediate(rhs_val)) => {
|
||||
self.codegen_scalar_binop(bx, op, lhs_val, rhs_val, lhs.layout.ty)
|
||||
}
|
||||
(OperandValue::Immediate(lhs_val), OperandValue::Immediate(rhs_val)) => self
|
||||
.codegen_scalar_binop(
|
||||
bx,
|
||||
op,
|
||||
lhs_val,
|
||||
rhs_val,
|
||||
lhs.layout.ty,
|
||||
rhs.layout.ty,
|
||||
),
|
||||
|
||||
_ => bug!(),
|
||||
};
|
||||
|
|
@ -887,10 +895,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
op: mir::BinOp,
|
||||
lhs: Bx::Value,
|
||||
rhs: Bx::Value,
|
||||
input_ty: Ty<'tcx>,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
) -> Bx::Value {
|
||||
let is_float = input_ty.is_floating_point();
|
||||
let is_signed = input_ty.is_signed();
|
||||
let is_float = lhs_ty.is_floating_point();
|
||||
let is_signed = lhs_ty.is_signed();
|
||||
match op {
|
||||
mir::BinOp::Add => {
|
||||
if is_float {
|
||||
|
|
@ -956,9 +965,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
mir::BinOp::BitAnd => bx.and(lhs, rhs),
|
||||
mir::BinOp::BitXor => bx.xor(lhs, rhs),
|
||||
mir::BinOp::Offset => {
|
||||
let pointee_type = input_ty
|
||||
let pointee_type = lhs_ty
|
||||
.builtin_deref(true)
|
||||
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", input_ty));
|
||||
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", lhs_ty));
|
||||
let pointee_layout = bx.cx().layout_of(pointee_type);
|
||||
if pointee_layout.is_zst() {
|
||||
// `Offset` works in terms of the size of pointee,
|
||||
|
|
@ -966,7 +975,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
lhs
|
||||
} else {
|
||||
let llty = bx.cx().backend_type(pointee_layout);
|
||||
bx.inbounds_gep(llty, lhs, &[rhs])
|
||||
if !rhs_ty.is_signed() {
|
||||
bx.inbounds_nuw_gep(llty, lhs, &[rhs])
|
||||
} else {
|
||||
bx.inbounds_gep(llty, lhs, &[rhs])
|
||||
}
|
||||
}
|
||||
}
|
||||
mir::BinOp::Shl | mir::BinOp::ShlUnchecked => {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
|
|||
}
|
||||
MonoItem::GlobalAsm(item_id) => {
|
||||
let item = cx.tcx().hir_item(item_id);
|
||||
if let hir::ItemKind::GlobalAsm(asm) = item.kind {
|
||||
if let hir::ItemKind::GlobalAsm { asm, .. } = item.kind {
|
||||
let operands: Vec<_> = asm
|
||||
.operands
|
||||
.iter()
|
||||
|
|
@ -71,11 +71,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::SymFn { ref anon_const } => {
|
||||
let ty = cx
|
||||
.tcx()
|
||||
.typeck_body(anon_const.body)
|
||||
.node_type(anon_const.hir_id);
|
||||
hir::InlineAsmOperand::SymFn { expr } => {
|
||||
let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr);
|
||||
let instance = match ty.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => span_bug!(*op_sp, "asm sym is not a function"),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_abi::{Align, BackendRepr, Scalar, Size, WrappingRange};
|
||||
use rustc_abi::{Align, Scalar, Size, WrappingRange};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{Instance, Ty};
|
||||
|
|
@ -223,13 +223,6 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
) -> (Self::Value, Self::Value);
|
||||
|
||||
fn from_immediate(&mut self, val: Self::Value) -> Self::Value;
|
||||
fn to_immediate(&mut self, val: Self::Value, layout: TyAndLayout<'_>) -> Self::Value {
|
||||
if let BackendRepr::Scalar(scalar) = layout.backend_repr {
|
||||
self.to_immediate_scalar(val, scalar)
|
||||
} else {
|
||||
val
|
||||
}
|
||||
}
|
||||
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value;
|
||||
|
||||
fn alloca(&mut self, size: Size, align: Align) -> Self::Value;
|
||||
|
|
@ -332,6 +325,14 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
ptr: Self::Value,
|
||||
indices: &[Self::Value],
|
||||
) -> Self::Value;
|
||||
fn inbounds_nuw_gep(
|
||||
&mut self,
|
||||
ty: Self::Type,
|
||||
ptr: Self::Value,
|
||||
indices: &[Self::Value],
|
||||
) -> Self::Value {
|
||||
self.inbounds_gep(ty, ptr, indices)
|
||||
}
|
||||
fn ptradd(&mut self, ptr: Self::Value, offset: Self::Value) -> Self::Value {
|
||||
self.gep(self.cx().type_i8(), ptr, &[offset])
|
||||
}
|
||||
|
|
@ -340,6 +341,17 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
}
|
||||
|
||||
fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
/// Produces the same value as [`Self::trunc`] (and defaults to that),
|
||||
/// but is UB unless the *zero*-extending the result can reproduce `val`.
|
||||
fn unchecked_utrunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value {
|
||||
self.trunc(val, dest_ty)
|
||||
}
|
||||
/// Produces the same value as [`Self::trunc`] (and defaults to that),
|
||||
/// but is UB unless the *sign*-extending the result can reproduce `val`.
|
||||
fn unchecked_strunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value {
|
||||
self.trunc(val, dest_ty)
|
||||
}
|
||||
|
||||
fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_const_eval"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -502,12 +502,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
RemainderByZero(op) => RemainderByZero(eval_to_int(op)?),
|
||||
ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind),
|
||||
ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind),
|
||||
MisalignedPointerDereference { ref required, ref found } => {
|
||||
MisalignedPointerDereference {
|
||||
required: eval_to_int(required)?,
|
||||
found: eval_to_int(found)?,
|
||||
}
|
||||
}
|
||||
MisalignedPointerDereference { required, found } => MisalignedPointerDereference {
|
||||
required: eval_to_int(required)?,
|
||||
found: eval_to_int(found)?,
|
||||
},
|
||||
NullPointerDereference => NullPointerDereference,
|
||||
};
|
||||
Err(ConstEvalErrKind::AssertFailure(err)).into()
|
||||
|
|
|
|||
|
|
@ -430,10 +430,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
let erased_trait_ref =
|
||||
ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
|
||||
assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
|
||||
erased_trait_ref,
|
||||
self.tcx.instantiate_bound_regions_with_erased(b)
|
||||
)));
|
||||
assert_eq!(
|
||||
data_b.principal().map(|b| {
|
||||
self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b)
|
||||
}),
|
||||
Some(erased_trait_ref),
|
||||
);
|
||||
} else {
|
||||
// In this case codegen would keep using the old vtable. We don't want to do
|
||||
// that as it has the wrong trait. The reason codegen can do this is that
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@ use either::{Left, Right};
|
|||
use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{
|
||||
|
|
@ -17,8 +14,7 @@ use rustc_middle::{mir, span_bug};
|
|||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use tracing::{debug, instrument, trace};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::{
|
||||
Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine,
|
||||
|
|
@ -320,40 +316,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the two things are equal in the current param_env, using an infcx to get proper
|
||||
/// equality checks.
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
pub(super) fn eq_in_param_env<T>(&self, a: T, b: T) -> bool
|
||||
where
|
||||
T: PartialEq + TypeFoldable<TyCtxt<'tcx>> + ToTrace<'tcx>,
|
||||
{
|
||||
// Fast path: compare directly.
|
||||
if a == b {
|
||||
return true;
|
||||
}
|
||||
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
|
||||
let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let cause = ObligationCause::dummy_with_span(self.cur_span());
|
||||
// equate the two trait refs after normalization
|
||||
let a = ocx.normalize(&cause, param_env, a);
|
||||
let b = ocx.normalize(&cause, param_env, b);
|
||||
|
||||
if let Err(terr) = ocx.eq(&cause, param_env, a, b) {
|
||||
trace!(?terr);
|
||||
return false;
|
||||
}
|
||||
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
trace!(?errors);
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good.
|
||||
true
|
||||
}
|
||||
|
||||
/// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
|
||||
/// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic,
|
||||
/// and is primarily intended for the panic machinery.
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
|
|||
/// already mutable (as a sanity check).
|
||||
///
|
||||
/// Returns an iterator over all relocations referred to by this allocation.
|
||||
fn intern_shallow<'rt, 'tcx, T, M: CompileTimeMachine<'tcx, T>>(
|
||||
ecx: &'rt mut InterpCx<'tcx, M>,
|
||||
fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>(
|
||||
ecx: &mut InterpCx<'tcx, M>,
|
||||
alloc_id: AllocId,
|
||||
mutability: Mutability,
|
||||
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
(Immediate::Uninit, _) => Immediate::Uninit,
|
||||
// If the field is uninhabited, we can forget the data (can happen in ConstProp).
|
||||
// `enum S { A(!), B, C }` is an example of an enum with Scalar layout that
|
||||
// has an `Uninhabited` variant, which means this case is possible.
|
||||
// has an uninhabited variant, which means this case is possible.
|
||||
_ if layout.is_uninhabited() => Immediate::Uninit,
|
||||
// the field contains no information, can be left uninit
|
||||
// (Scalar/ScalarPair can contain even aligned ZST, not just 1-ZST)
|
||||
|
|
|
|||
|
|
@ -86,21 +86,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type });
|
||||
}
|
||||
|
||||
// This checks whether there is a subtyping relation between the predicates in either direction.
|
||||
// For example:
|
||||
// - casting between `dyn for<'a> Trait<fn(&'a u8)>` and `dyn Trait<fn(&'static u8)>` is OK
|
||||
// - casting between `dyn Trait<for<'a> fn(&'a u8)>` and either of the above is UB
|
||||
for (a_pred, b_pred) in std::iter::zip(sorted_vtable, sorted_expected) {
|
||||
let is_eq = match (a_pred.skip_binder(), b_pred.skip_binder()) {
|
||||
(
|
||||
ty::ExistentialPredicate::Trait(a_data),
|
||||
ty::ExistentialPredicate::Trait(b_data),
|
||||
) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)),
|
||||
let a_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, a_pred);
|
||||
let b_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b_pred);
|
||||
|
||||
(
|
||||
ty::ExistentialPredicate::Projection(a_data),
|
||||
ty::ExistentialPredicate::Projection(b_data),
|
||||
) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)),
|
||||
|
||||
_ => false,
|
||||
};
|
||||
if !is_eq {
|
||||
if a_pred != b_pred {
|
||||
throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1264,21 +1264,20 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
}
|
||||
}
|
||||
|
||||
// *After* all of this, check the ABI. We need to check the ABI to handle
|
||||
// types like `NonNull` where the `Scalar` info is more restrictive than what
|
||||
// the fields say (`rustc_layout_scalar_valid_range_start`).
|
||||
// But in most cases, this will just propagate what the fields say,
|
||||
// and then we want the error to point at the field -- so, first recurse,
|
||||
// then check ABI.
|
||||
// *After* all of this, check further information stored in the layout. We need to check
|
||||
// this to handle types like `NonNull` where the `Scalar` info is more restrictive than what
|
||||
// the fields say (`rustc_layout_scalar_valid_range_start`). But in most cases, this will
|
||||
// just propagate what the fields say, and then we want the error to point at the field --
|
||||
// so, we first recurse, then we do this check.
|
||||
//
|
||||
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
|
||||
// scalars, we do the same check on every "level" (e.g., first we check
|
||||
// MyNewtype and then the scalar in there).
|
||||
if val.layout.is_uninhabited() {
|
||||
let ty = val.layout.ty;
|
||||
throw_validation_failure!(self.path, UninhabitedVal { ty });
|
||||
}
|
||||
match val.layout.backend_repr {
|
||||
BackendRepr::Uninhabited => {
|
||||
let ty = val.layout.ty;
|
||||
throw_validation_failure!(self.path, UninhabitedVal { ty });
|
||||
}
|
||||
BackendRepr::Scalar(scalar_layout) => {
|
||||
if !scalar_layout.is_uninit_valid() {
|
||||
// There is something to check here.
|
||||
|
|
|
|||
|
|
@ -111,13 +111,15 @@ fn check_validity_requirement_lax<'tcx>(
|
|||
};
|
||||
|
||||
// Check the ABI.
|
||||
let valid = match this.backend_repr {
|
||||
BackendRepr::Uninhabited => false, // definitely UB
|
||||
BackendRepr::Scalar(s) => scalar_allows_raw_init(s),
|
||||
BackendRepr::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2),
|
||||
BackendRepr::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
|
||||
BackendRepr::Memory { .. } => true, // Fields are checked below.
|
||||
};
|
||||
let valid = !this.is_uninhabited() // definitely UB if uninhabited
|
||||
&& match this.backend_repr {
|
||||
BackendRepr::Scalar(s) => scalar_allows_raw_init(s),
|
||||
BackendRepr::ScalarPair(s1, s2) => {
|
||||
scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2)
|
||||
}
|
||||
BackendRepr::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s),
|
||||
BackendRepr::Memory { .. } => true, // Fields are checked below.
|
||||
};
|
||||
if !valid {
|
||||
// This is definitely not okay.
|
||||
return Ok(false);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "rustc_data_structures"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -4,31 +4,6 @@
|
|||
//! green/native threading. This is just a bare-bones enough solution for
|
||||
//! librustdoc, it is not production quality at all.
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
cfg_match! {
|
||||
cfg(target_os = "linux") => {
|
||||
mod linux;
|
||||
use linux as imp;
|
||||
}
|
||||
cfg(target_os = "redox") => {
|
||||
mod linux;
|
||||
use linux as imp;
|
||||
}
|
||||
cfg(unix) => {
|
||||
mod unix;
|
||||
use unix as imp;
|
||||
}
|
||||
cfg(windows) => {
|
||||
mod windows;
|
||||
use self::windows as imp;
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
use unsupported as imp;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
cfg_match! {
|
||||
target_os = "linux" => {
|
||||
mod linux;
|
||||
|
|
|
|||
|
|
@ -193,11 +193,11 @@ impl<N: Debug, E: Debug> Graph<N, E> {
|
|||
AdjacentEdges { graph: self, direction, next: first_edge }
|
||||
}
|
||||
|
||||
pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
|
||||
pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator<Item = NodeIndex> {
|
||||
self.outgoing_edges(source).targets()
|
||||
}
|
||||
|
||||
pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
|
||||
pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator<Item = NodeIndex> {
|
||||
self.incoming_edges(target).sources()
|
||||
}
|
||||
|
||||
|
|
@ -255,11 +255,11 @@ pub struct AdjacentEdges<'g, N, E> {
|
|||
}
|
||||
|
||||
impl<'g, N: Debug, E: Debug> AdjacentEdges<'g, N, E> {
|
||||
fn targets(self) -> impl Iterator<Item = NodeIndex> + 'g {
|
||||
fn targets(self) -> impl Iterator<Item = NodeIndex> {
|
||||
self.map(|(_, edge)| edge.target)
|
||||
}
|
||||
|
||||
fn sources(self) -> impl Iterator<Item = NodeIndex> + 'g {
|
||||
fn sources(self) -> impl Iterator<Item = NodeIndex> {
|
||||
self.map(|(_, edge)| edge.source)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
|
|||
/// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after.
|
||||
/// This is convenient when the edges represent dependencies: when you visit
|
||||
/// `S1`, the value for `S2` will already have been computed.
|
||||
pub fn all_sccs(&self) -> impl Iterator<Item = S> {
|
||||
pub fn all_sccs(&self) -> impl Iterator<Item = S> + 'static {
|
||||
(0..self.scc_data.len()).map(S::new)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -860,69 +860,6 @@ fn get_thread_id() -> u32 {
|
|||
}
|
||||
|
||||
// Memory reporting
|
||||
#[cfg(bootstrap)]
|
||||
cfg_match! {
|
||||
cfg(windows) => {
|
||||
pub fn get_resident_set_size() -> Option<usize> {
|
||||
use std::mem;
|
||||
|
||||
use windows::{
|
||||
Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
|
||||
Win32::System::Threading::GetCurrentProcess,
|
||||
};
|
||||
|
||||
let mut pmc = PROCESS_MEMORY_COUNTERS::default();
|
||||
let pmc_size = mem::size_of_val(&pmc);
|
||||
unsafe {
|
||||
K32GetProcessMemoryInfo(
|
||||
GetCurrentProcess(),
|
||||
&mut pmc,
|
||||
pmc_size as u32,
|
||||
)
|
||||
}
|
||||
.ok()
|
||||
.ok()?;
|
||||
|
||||
Some(pmc.WorkingSetSize)
|
||||
}
|
||||
}
|
||||
cfg(target_os = "macos") => {
|
||||
pub fn get_resident_set_size() -> Option<usize> {
|
||||
use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO};
|
||||
use std::mem;
|
||||
const PROC_TASKINFO_SIZE: c_int = mem::size_of::<proc_taskinfo>() as c_int;
|
||||
|
||||
unsafe {
|
||||
let mut info: proc_taskinfo = mem::zeroed();
|
||||
let info_ptr = &mut info as *mut proc_taskinfo as *mut c_void;
|
||||
let pid = getpid() as c_int;
|
||||
let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info_ptr, PROC_TASKINFO_SIZE);
|
||||
if ret == PROC_TASKINFO_SIZE {
|
||||
Some(info.pti_resident_size as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg(unix) => {
|
||||
pub fn get_resident_set_size() -> Option<usize> {
|
||||
let field = 1;
|
||||
let contents = fs::read("/proc/self/statm").ok()?;
|
||||
let contents = String::from_utf8(contents).ok()?;
|
||||
let s = contents.split_whitespace().nth(field)?;
|
||||
let npages = s.parse::<usize>().ok()?;
|
||||
Some(npages * 4096)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
pub fn get_resident_set_size() -> Option<usize> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
cfg_match! {
|
||||
windows => {
|
||||
pub fn get_resident_set_size() -> Option<usize> {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue