Merge pull request #4619 from rust-lang/rustup-2025-10-08

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-10-08 06:22:54 +00:00 committed by GitHub
commit 14212c59b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
712 changed files with 15220 additions and 7736 deletions

View file

@ -216,7 +216,7 @@ dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.12",
"winnow 0.7.13",
]
[[package]]
@ -590,7 +590,7 @@ dependencies = [
"serde_json",
"tempfile",
"termize",
"toml 0.7.8",
"toml 0.9.7",
"ui_test",
"walkdir",
]
@ -632,7 +632,7 @@ dependencies = [
"regex-syntax 0.8.5",
"semver",
"serde",
"toml 0.7.8",
"toml 0.9.7",
"unicode-normalization",
"unicode-script",
"url",
@ -686,6 +686,7 @@ dependencies = [
"anyhow",
"serde",
"serde_json",
"similar",
"spdx-rs",
]
@ -1866,13 +1867,14 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
[[package]]
name = "indexmap"
version = "2.10.0"
version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown",
"serde",
"serde_core",
]
[[package]]
@ -5003,10 +5005,11 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
@ -5032,10 +5035,19 @@ dependencies = [
]
[[package]]
name = "serde_derive"
version = "1.0.219"
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@ -5084,6 +5096,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
dependencies = [
"serde_core",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -5545,8 +5566,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"toml_edit 0.19.15",
]
@ -5558,11 +5579,26 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"toml_edit 0.22.27",
]
[[package]]
name = "toml"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned 1.0.2",
"toml_datetime 0.7.2",
"toml_parser",
"toml_writer",
"winnow 0.7.13",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
@ -5572,6 +5608,15 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
@ -5580,8 +5625,8 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
@ -5593,10 +5638,19 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"toml_write",
"winnow 0.7.12",
"winnow 0.7.13",
]
[[package]]
name = "toml_parser"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [
"winnow 0.7.13",
]
[[package]]
@ -5605,6 +5659,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "toml_writer"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
[[package]]
name = "tracing"
version = "0.1.41"
@ -6569,9 +6629,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.12"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]

View file

@ -622,11 +622,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
}
fn check_incompatible_features(sess: &Session, features: &Features) {
let enabled_lang_features =
features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
let enabled_lib_features =
features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
let enabled_features = enabled_lang_features.chain(enabled_lib_features);
let enabled_features = features.enabled_features_iter_stable_order();
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
.iter()

View file

@ -1,3 +1,9 @@
attr_parsing_as_needed_compatibility =
linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
attr_parsing_cfg_predicate_identifier =
`cfg` predicate key must be an identifier
@ -18,16 +24,12 @@ attr_parsing_empty_attribute =
}
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_empty_link_name =
link name must not be empty
.label = empty link name
attr_parsing_expected_one_cfg_pattern =
expected 1 cfg-pattern
@ -48,6 +50,15 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
*[other] valid forms for the attribute are {$suggestions}
}
attr_parsing_import_name_type_raw =
import name type can only be used with link kind `raw-dylib`
attr_parsing_import_name_type_x86 =
import name type is only supported on x86
attr_parsing_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes
attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@ -67,6 +78,11 @@ attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
attr_parsing_invalid_alignment_value =
invalid alignment value: {$error_part}
attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
.label = this is not an unsafe attribute
.suggestion = remove the `unsafe(...)`
.note = extraneous unsafe is not allowed in attributes
attr_parsing_invalid_issue_string =
`issue` must be a non-zero numeric string or "none"
.must_not_be_zero = `issue` must not be "0", use "none" instead
@ -75,6 +91,13 @@ attr_parsing_invalid_issue_string =
.pos_overflow = number too large to fit in target type
.neg_overflow = number too small to fit in target type
attr_parsing_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
attr_parsing_invalid_predicate =
invalid predicate `{$predicate}`
@ -100,9 +123,36 @@ attr_parsing_invalid_style = {$is_used_as_inner ->
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}
attr_parsing_link_arg_unstable =
link kind `link-arg` is unstable
attr_parsing_link_cfg_unstable =
link cfg is unstable
attr_parsing_link_framework_apple =
link kind `framework` is only supported on Apple targets
attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
.note = the value may not exceed `u16::MAX`
attr_parsing_link_requires_name =
`#[link]` attribute requires a `name = "string"` argument
.label = missing `name` argument
attr_parsing_meta_bad_delim = wrong meta list delimiters
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
attr_parsing_missing_feature =
missing 'feature'
@ -115,6 +165,9 @@ attr_parsing_missing_note =
attr_parsing_missing_since =
missing 'since'
attr_parsing_multiple_modifiers =
multiple `{$modifier}` modifiers in a single `modifiers` argument
attr_parsing_multiple_stability_levels =
multiple stability levels
@ -138,6 +191,15 @@ attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a stri
attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal
attr_parsing_raw_dylib_elf_unstable =
link kind `raw-dylib` is unstable on ELF platforms
attr_parsing_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
attr_parsing_raw_dylib_only_windows =
link kind `raw-dylib` is only supported on Windows targets
attr_parsing_repr_ident =
meta item in `repr` must be an identifier
@ -152,6 +214,9 @@ attr_parsing_soft_no_args =
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
attr_parsing_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}
@ -164,6 +229,10 @@ attr_parsing_unrecognized_repr_hint =
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
.note = for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
attr_parsing_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change
@ -193,77 +262,5 @@ attr_parsing_unused_multiple =
-attr_parsing_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
attr_parsing_meta_bad_delim = wrong meta list delimiters
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
.label = this is not an unsafe attribute
.suggestion = remove the `unsafe(...)`
.note = extraneous unsafe is not allowed in attributes
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
attr_parsing_as_needed_compatibility =
linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
attr_parsing_empty_link_name =
link name must not be empty
.label = empty link name
attr_parsing_import_name_type_raw =
import name type can only be used with link kind `raw-dylib`
attr_parsing_import_name_type_x86 =
import name type is only supported on x86
attr_parsing_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes
attr_parsing_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
attr_parsing_link_arg_unstable =
link kind `link-arg` is unstable
attr_parsing_link_cfg_unstable =
link cfg is unstable
attr_parsing_link_framework_apple =
link kind `framework` is only supported on Apple targets
attr_parsing_link_requires_name =
`#[link]` attribute requires a `name = "string"` argument
.label = missing `name` argument
attr_parsing_multiple_modifiers =
multiple `{$modifier}` modifiers in a single `modifiers` argument
attr_parsing_multiple_renamings =
multiple renamings were specified for library `{$lib_name}`
attr_parsing_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
attr_parsing_raw_dylib_elf_unstable =
link kind `raw-dylib` is unstable on ELF platforms
attr_parsing_raw_dylib_only_windows =
link kind `raw-dylib` is only supported on Windows targets
attr_parsing_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}

View file

@ -370,6 +370,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
pub(crate) struct UsedParser {
first_compiler: Option<Span>,
first_linker: Option<Span>,
first_default: Option<Span>,
}
// A custom `AttributeParser` is used rather than a Simple attribute parser because
@ -382,7 +383,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
template!(Word, List: &["compiler", "linker"]),
|group: &mut Self, cx, args| {
let used_by = match args {
ArgParser::NoArgs => UsedBy::Linker,
ArgParser::NoArgs => UsedBy::Default,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.expected_single_argument(list.span);
@ -423,12 +424,29 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
ArgParser::NameValue(_) => return,
};
let attr_span = cx.attr_span;
// `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the
// circumstances are more complicated). While we're checking `used_by`, also report
// these cross-`UsedBy` duplicates to warn.
let target = match used_by {
UsedBy::Compiler => &mut group.first_compiler,
UsedBy::Linker => &mut group.first_linker,
UsedBy::Linker => {
if let Some(prev) = group.first_default {
cx.warn_unused_duplicate(prev, attr_span);
return;
}
&mut group.first_linker
}
UsedBy::Default => {
if let Some(prev) = group.first_linker {
cx.warn_unused_duplicate(prev, attr_span);
return;
}
&mut group.first_default
}
};
let attr_span = cx.attr_span;
if let Some(prev) = *target {
cx.warn_unused_duplicate(prev, attr_span);
} else {
@ -440,11 +458,13 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
// Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
Some(match (self.first_compiler, self.first_linker) {
(_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
(Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
(None, None) => return None,
// If a specific form of `used` is specified, it takes precedence over generic `#[used]`.
// If both `linker` and `compiler` are specified, use `linker`.
Some(match (self.first_compiler, self.first_linker, self.first_default) {
(_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
(Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
(_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
(None, None, None) => return None,
})
}
}

View file

@ -65,10 +65,22 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let mut result = None;
let Some(items) = args.list() else {
cx.expected_list(cx.attr_span);
return result;
let items = match args {
ArgParser::List(list) => list,
// This is an edgecase added because making this a hard error would break too many crates
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "link");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
_ => {
cx.expected_list(cx.attr_span);
return None;
}
};
let sess = cx.sess();
@ -113,7 +125,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
};
if !cont {
return result;
return None;
}
}
@ -168,7 +180,8 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
(sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed }))
| (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => {
| (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed }))
| (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => {
report_unstable_modifier!(native_link_modifiers_as_needed);
assign_modifier(as_needed)
}
@ -202,31 +215,30 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
let Some((name, name_span)) = name else {
cx.emit_err(LinkRequiresName { span: cx.attr_span });
return result;
return None;
};
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
if kind != Some(NativeLibKind::RawDylib) {
if !matches!(kind, Some(NativeLibKind::RawDylib { .. })) {
cx.emit_err(ImportNameTypeRaw { span });
}
}
if let Some(NativeLibKind::RawDylib) = kind
if let Some(NativeLibKind::RawDylib { .. }) = kind
&& name.as_str().contains('\0')
{
cx.emit_err(RawDylibNoNul { span: name_span });
}
result = Some(LinkEntry {
Some(LinkEntry {
span: cx.attr_span,
kind: kind.unwrap_or(NativeLibKind::Unspecified),
name,
cfg,
verbatim,
import_name_type,
});
result
})
}
}
@ -304,7 +316,7 @@ impl LinkParser {
cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
}
NativeLibKind::RawDylib
NativeLibKind::RawDylib { as_needed: None }
}
sym::link_dash_arg => {
if !features.link_arg_attribute() {
@ -455,8 +467,14 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
const PATH: &[Symbol] = &[sym::link_section];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::Static),
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
const TEMPLATE: AttributeTemplate = template!(
NameValueStr: "name",
"https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"

View file

@ -135,6 +135,15 @@ builtin_macros_concat_missing_literal = expected a literal
builtin_macros_default_arg = `#[default]` attribute does not accept a value
.suggestion = try using `#[default]`
builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field
builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields ->
[true] multiple fields
*[false] no fields
}
builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind}
builtin_macros_derive_macro_call = `derive` cannot be used on items with type macros
builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept arguments
@ -229,15 +238,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
builtin_macros_format_use_positional = consider using a positional formatting argument instead
builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind}
builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields ->
[true] multiple fields
*[false] no fields
}
builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field
builtin_macros_incomplete_include = include macro expected single expression in source
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes

View file

@ -29,13 +29,24 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
_variable_alloca: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: Option<Range<Size>>,
_fragment: &Option<Range<Size>>,
) {
// FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
#[cfg(feature = "master")]
_variable_alloca.set_location(_dbg_loc);
}
fn dbg_var_value(
&mut self,
_dbg_var: Self::DIVariable,
_dbg_loc: Self::DILocation,
_value: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: &Option<Range<Size>>,
) {
}
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
// TODO(antoyo): insert reference to gdb debug scripts section global.
}

View file

@ -1,4 +1,3 @@
use std::borrow::Borrow;
use std::cmp;
use libc::c_uint;
@ -13,7 +12,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::{bug, ty};
use rustc_session::config;
use rustc_session::{Session, config};
use rustc_target::callconv::{
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode,
};
@ -23,11 +22,9 @@ use smallvec::SmallVec;
use crate::attributes::{self, llfn_attrs_from_instance};
use crate::builder::Builder;
use crate::context::CodegenCx;
use crate::llvm::{self, Attribute, AttributePlace};
use crate::llvm::{self, Attribute, AttributePlace, Type, Value};
use crate::llvm_util;
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
trait ArgAttributesExt {
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
@ -400,7 +397,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
}
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv {
llvm::CallConv::from_conv(self.conv, cx.tcx.sess.target.arch.borrow())
to_llvm_calling_convention(cx.tcx.sess, self.conv)
}
fn apply_attrs_llfn(
@ -663,43 +660,44 @@ impl AbiBuilderMethods for Builder<'_, '_, '_> {
}
}
impl llvm::CallConv {
pub(crate) fn from_conv(conv: CanonAbi, arch: &str) -> Self {
match conv {
CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
CanonAbi::RustCold => llvm::PreserveMost,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => llvm::CCallConv,
CanonAbi::GpuKernel => {
if arch == "amdgpu" {
llvm::AmdgpuKernel
} else if arch == "nvptx64" {
llvm::PtxKernel
} else {
panic!("Architecture {arch} does not support GpuKernel calling convention");
}
/// Determines the appropriate [`llvm::CallConv`] to use for a given function
/// ABI, for the current target.
pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm::CallConv {
match abi {
CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
CanonAbi::RustCold => llvm::PreserveMost,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => llvm::CCallConv,
CanonAbi::GpuKernel => {
let arch = sess.target.arch.as_ref();
if arch == "amdgpu" {
llvm::AmdgpuKernel
} else if arch == "nvptx64" {
llvm::PtxKernel
} else {
panic!("Architecture {arch} does not support GpuKernel calling convention");
}
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
InterruptKind::Avr => llvm::AvrInterrupt,
InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt,
InterruptKind::Msp430 => llvm::Msp430Intr,
InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv,
InterruptKind::X86 => llvm::X86_Intr,
},
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => llvm::ArmAapcsCallConv,
ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv,
},
CanonAbi::X86(x86_call) => match x86_call {
X86Call::Fastcall => llvm::X86FastcallCallConv,
X86Call::Stdcall => llvm::X86StdcallCallConv,
X86Call::SysV64 => llvm::X86_64_SysV,
X86Call::Thiscall => llvm::X86_ThisCall,
X86Call::Vectorcall => llvm::X86_VectorCall,
X86Call::Win64 => llvm::X86_64_Win64,
},
}
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
InterruptKind::Avr => llvm::AvrInterrupt,
InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt,
InterruptKind::Msp430 => llvm::Msp430Intr,
InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv,
InterruptKind::X86 => llvm::X86_Intr,
},
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => llvm::ArmAapcsCallConv,
ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv,
},
CanonAbi::X86(x86_call) => match x86_call {
X86Call::Fastcall => llvm::X86FastcallCallConv,
X86Call::Stdcall => llvm::X86StdcallCallConv,
X86Call::SysV64 => llvm::X86_64_SysV,
X86Call::Thiscall => llvm::X86_ThisCall,
X86Call::Vectorcall => llvm::X86_VectorCall,
X86Call::Win64 => llvm::X86_64_Win64,
},
}
}

View file

@ -14,7 +14,7 @@ use rustc_symbol_mangling::mangle_internal_symbol;
use crate::attributes::llfn_attrs_from_instance;
use crate::builder::SBuilder;
use crate::declare::declare_simple_fn;
use crate::llvm::{self, FALSE, TRUE, Type, Value};
use crate::llvm::{self, FALSE, FromGeneric, TRUE, Type, Value};
use crate::{SimpleCx, attributes, debuginfo};
pub(crate) unsafe fn codegen(

View file

@ -13,14 +13,12 @@ use rustc_target::asm::*;
use smallvec::SmallVec;
use tracing::debug;
use crate::attributes;
use crate::builder::Builder;
use crate::common::Funclet;
use crate::context::CodegenCx;
use crate::llvm::ToLlvmBool;
use crate::type_::Type;
use crate::llvm::{self, ToLlvmBool, Type, Value};
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use crate::{attributes, llvm};
impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
fn codegen_inline_asm(

View file

@ -13,8 +13,9 @@ use smallvec::SmallVec;
use crate::context::SimpleCx;
use crate::errors::SanitizerMemtagRequiresMte;
use crate::llvm::AttributePlace::Function;
use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
use crate::value::Value;
use crate::llvm::{
self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value,
};
use crate::{Session, attributes, llvm_util};
pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {

View file

@ -44,7 +44,7 @@ use crate::errors::{
};
use crate::llvm::diagnostic::OptimizationDiagnosticKind::*;
use crate::llvm::{self, DiagnosticInfo};
use crate::type_::Type;
use crate::type_::llvm_type_ptr;
use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util};
pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! {
@ -1160,7 +1160,7 @@ fn create_msvc_imps(
// underscores added in front).
let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" };
let ptr_ty = Type::ptr_llcx(llcx);
let ptr_ty = llvm_type_ptr(llcx);
let globals = base::iter_globals(llmod)
.filter(|&val| {
llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage && !llvm::is_declaration(val)

View file

@ -28,10 +28,10 @@ use rustc_span::Symbol;
use rustc_target::spec::SanitizerSet;
use super::ModuleLlvm;
use crate::attributes;
use crate::builder::Builder;
use crate::context::CodegenCx;
use crate::value::Value;
use crate::{attributes, llvm};
use crate::llvm::{self, Value};
pub(crate) struct ValueIter<'ll> {
cur: Option<&'ll Value>,

View file

@ -36,11 +36,10 @@ use crate::attributes;
use crate::common::Funclet;
use crate::context::{CodegenCx, FullCx, GenericCx, SCx};
use crate::llvm::{
self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, GEPNoWrapFlags, Metadata, TRUE, ToLlvmBool,
self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE,
ToLlvmBool, Type, Value,
};
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
#[must_use]
pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SCx<'ll>>> {

View file

@ -12,9 +12,7 @@ use tracing::debug;
use crate::builder::{Builder, PlaceRef, UNNAMED};
use crate::context::SimpleCx;
use crate::declare::declare_simple_fn;
use crate::llvm;
use crate::llvm::{Metadata, TRUE, Type};
use crate::value::Value;
use crate::llvm::{self, Metadata, TRUE, Type, Value};
pub(crate) fn adjust_activity_to_abi<'tcx>(
tcx: TyCtxt<'tcx>,

View file

@ -10,8 +10,7 @@ use rustc_middle::ty::{self, Instance, TypeVisitableExt};
use tracing::debug;
use crate::context::CodegenCx;
use crate::llvm;
use crate::value::Value;
use crate::llvm::{self, Value};
/// Codegens a reference to a fn/method item, monomorphizing and
/// inlining as it goes.

View file

@ -20,9 +20,7 @@ use tracing::debug;
use crate::consts::const_alloc_to_llvm;
pub(crate) use crate::context::CodegenCx;
use crate::context::{GenericCx, SCx};
use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool};
use crate::type_::Type;
use crate::value::Value;
use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value};
/*
* A note on nomenclature of linking: "extern", "foreign", and "upcall".

View file

@ -21,10 +21,9 @@ use tracing::{debug, instrument, trace};
use crate::common::CodegenCx;
use crate::errors::SymbolAlreadyDefined;
use crate::type_::Type;
use crate::llvm::{self, Type, Value};
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use crate::{base, debuginfo, llvm};
use crate::{base, debuginfo};
pub(crate) fn const_alloc_to_llvm<'ll>(
cx: &CodegenCx<'ll, '_>,

View file

@ -31,13 +31,12 @@ use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
use smallvec::SmallVec;
use crate::abi::to_llvm_calling_convention;
use crate::back::write::to_llvm_code_model;
use crate::callee::get_fn;
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
use crate::llvm::{Metadata, MetadataKindId, Module};
use crate::type_::Type;
use crate::value::Value;
use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
use crate::llvm::{self, Metadata, MetadataKindId, Module, Type, Value};
use crate::{attributes, common, coverageinfo, debuginfo, llvm_util};
/// `TyCtxt` (and related cache datastructures) can't be move between threads.
/// However, there are various cx related functions which we want to be available to the builder and
@ -740,7 +739,7 @@ impl<'ll> SimpleCx<'ll> {
llcx: &'ll llvm::Context,
pointer_size: Size,
) -> Self {
let isize_ty = llvm::Type::ix_llcx(llcx, pointer_size.bits());
let isize_ty = llvm::LLVMIntTypeInContext(llcx, pointer_size.bits() as c_uint);
Self(SCx { llmod, llcx, isize_ty }, PhantomData)
}
}
@ -901,10 +900,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
if self.get_declared_value(entry_name).is_none() {
let llfn = self.declare_entry_fn(
entry_name,
llvm::CallConv::from_conv(
self.sess().target.entry_abi,
self.sess().target.arch.borrow(),
),
to_llvm_calling_convention(self.sess(), self.sess().target.entry_abi),
llvm::UnnamedAddr::Global,
fn_type,
);

View file

@ -1,5 +1,3 @@
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
/// Must match the layout of `LLVMRustCounterKind`.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
@ -26,30 +24,12 @@ pub(crate) enum CounterKind {
pub(crate) struct Counter {
// Important: The layout (order and types of fields) must match its C++ counterpart.
pub(crate) kind: CounterKind,
id: u32,
pub(crate) id: u32,
}
impl Counter {
/// A `Counter` of kind `Zero`. For this counter kind, the `id` is not used.
pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 };
/// Constructs a new `Counter` of kind `CounterValueReference`.
pub(crate) fn counter_value_reference(counter_id: CounterId) -> Self {
Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() }
}
/// Constructs a new `Counter` of kind `Expression`.
pub(crate) fn expression(expression_id: ExpressionId) -> Self {
Self { kind: CounterKind::Expression, id: expression_id.as_u32() }
}
pub(crate) fn from_term(term: CovTerm) -> Self {
match term {
CovTerm::Zero => Self::ZERO,
CovTerm::Counter(id) => Self::counter_value_reference(id),
CovTerm::Expression(id) => Self::expression(id),
}
}
}
/// Corresponds to enum `llvm::coverage::CounterExpression::ExprKind`.
@ -94,29 +74,6 @@ pub(crate) struct CoverageSpan {
pub(crate) end_col: u32,
}
/// Holds tables of the various region types in one struct.
///
/// Don't pass this struct across FFI; pass the individual region tables as
/// pointer/length pairs instead.
///
/// Each field name has a `_regions` suffix for improved readability after
/// exhaustive destructing, which ensures that all region types are handled.
#[derive(Clone, Debug, Default)]
pub(crate) struct Regions {
pub(crate) code_regions: Vec<CodeRegion>,
pub(crate) expansion_regions: Vec<ExpansionRegion>,
pub(crate) branch_regions: Vec<BranchRegion>,
}
impl Regions {
/// Returns true if none of this structure's tables contain any regions.
pub(crate) fn has_no_regions(&self) -> bool {
let Self { code_regions, expansion_regions, branch_regions } = self;
code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty()
}
}
/// Must match the layout of `LLVMRustCoverageCodeRegion`.
#[derive(Clone, Debug)]
#[repr(C)]

View file

@ -57,12 +57,35 @@ pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef<str>]) -> Vec<u8
})
}
/// Holds tables of the various region types in one struct.
///
/// Don't pass this struct across FFI; pass the individual region tables as
/// pointer/length pairs instead.
///
/// Each field name has a `_regions` suffix for improved readability after
/// exhaustive destructing, which ensures that all region types are handled.
#[derive(Clone, Debug, Default)]
pub(crate) struct Regions {
pub(crate) code_regions: Vec<ffi::CodeRegion>,
pub(crate) expansion_regions: Vec<ffi::ExpansionRegion>,
pub(crate) branch_regions: Vec<ffi::BranchRegion>,
}
impl Regions {
/// Returns true if none of this structure's tables contain any regions.
pub(crate) fn has_no_regions(&self) -> bool {
let Self { code_regions, expansion_regions, branch_regions } = self;
code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty()
}
}
pub(crate) fn write_function_mappings_to_buffer(
virtual_file_mapping: &[u32],
expressions: &[ffi::CounterExpression],
regions: &ffi::Regions,
regions: &Regions,
) -> Vec<u8> {
let ffi::Regions { code_regions, expansion_regions, branch_regions } = regions;
let Regions { code_regions, expansion_regions, branch_regions } = regions;
// SAFETY:
// - All types are FFI-compatible and have matching representations in Rust/C++.

View file

@ -6,7 +6,6 @@ use rustc_abi::Align;
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::IndexVec;
use rustc_macros::TryFromU32;
use rustc_middle::ty::TyCtxt;
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
@ -16,7 +15,7 @@ use tracing::debug;
use crate::common::CodegenCx;
use crate::coverageinfo::llvm_cov;
use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
use crate::llvm;
use crate::{TryFromU32, llvm};
mod covfun;
mod spans;

View file

@ -10,8 +10,8 @@ use std::sync::Arc;
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods};
use rustc_middle::mir::coverage::{
BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping,
MappingKind, Op,
BasicCoverageBlock, CounterId, CovTerm, CoverageIdsInfo, Expression, ExpressionId,
FunctionCoverageInfo, Mapping, MappingKind, Op,
};
use rustc_middle::ty::{Instance, TyCtxt};
use rustc_span::{SourceFile, Span};
@ -36,7 +36,7 @@ pub(crate) struct CovfunRecord<'tcx> {
virtual_file_mapping: VirtualFileMapping,
expressions: Vec<ffi::CounterExpression>,
regions: ffi::Regions,
regions: llvm_cov::Regions,
}
impl<'tcx> CovfunRecord<'tcx> {
@ -64,7 +64,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
is_used,
virtual_file_mapping: VirtualFileMapping::default(),
expressions,
regions: ffi::Regions::default(),
regions: llvm_cov::Regions::default(),
};
fill_region_tables(tcx, fn_cov_info, ids_info, &mut covfun);
@ -77,10 +77,21 @@ pub(crate) fn prepare_covfun_record<'tcx>(
Some(covfun)
}
pub(crate) fn counter_for_term(term: CovTerm) -> ffi::Counter {
use ffi::Counter;
match term {
CovTerm::Zero => Counter::ZERO,
CovTerm::Counter(id) => {
Counter { kind: ffi::CounterKind::CounterValueReference, id: CounterId::as_u32(id) }
}
CovTerm::Expression(id) => {
Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) }
}
}
}
/// Convert the function's coverage-counter expressions into a form suitable for FFI.
fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec<ffi::CounterExpression> {
let counter_for_term = ffi::Counter::from_term;
// We know that LLVM will optimize out any unused expressions before
// producing the final coverage map, so there's no need to do the same
// thing on the Rust side unless we're confident we can do much better.
@ -113,7 +124,7 @@ fn fill_region_tables<'tcx>(
} else {
CovTerm::Zero
};
ffi::Counter::from_term(term)
counter_for_term(term)
};
// Currently a function's mappings must all be in the same file, so use the
@ -136,7 +147,7 @@ fn fill_region_tables<'tcx>(
if discard_all { None } else { spans::make_coords(source_map, &source_file, span) }
};
let ffi::Regions {
let llvm_cov::Regions {
code_regions,
expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions
branch_regions,

View file

@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64);
/// Double-checked by a static assertion in `RustWrapper.cpp`.
#[allow(non_upper_case_globals)]
pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000;
// It describes the actual value of a source variable which might not exist in registers or in memory.
#[allow(non_upper_case_globals)]
pub(crate) const DW_OP_stack_value: u64 = 0x9f;

View file

@ -9,8 +9,7 @@ use rustc_session::config::{CrateType, DebugInfo};
use crate::builder::Builder;
use crate::common::CodegenCx;
use crate::llvm;
use crate::value::Value;
use crate::llvm::{self, Value};
/// Inserts a side-effect free instruction sequence that makes sure that the
/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker.

View file

@ -37,12 +37,11 @@ use crate::common::{AsCCharPtr, CodegenCx};
use crate::debuginfo::dwarf_const;
use crate::debuginfo::metadata::type_map::build_type_with_children;
use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind};
use crate::llvm;
use crate::llvm::debuginfo::{
DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock,
DIScope, DIType, DebugEmissionKind, DebugNameTableKind,
};
use crate::value::Value;
use crate::llvm::{self, FromGeneric, Value};
impl PartialEq for llvm::Metadata {
fn eq(&self, other: &Self) -> bool {

View file

@ -35,12 +35,11 @@ use self::namespace::mangled_name_of_instance;
use self::utils::{DIB, create_DIArray, is_node_local_to_unit};
use crate::builder::Builder;
use crate::common::{AsCCharPtr, CodegenCx};
use crate::llvm;
use crate::llvm::debuginfo::{
DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope,
DITemplateTypeParameter, DIType, DIVariable,
};
use crate::value::Value;
use crate::llvm::{self, Value};
mod create_scope_map;
mod dwarf_const;
@ -156,7 +155,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
variable_alloca: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
fragment: Option<Range<Size>>,
fragment: &Option<Range<Size>>,
) {
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
@ -187,7 +186,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
};
unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
llvm::LLVMDIBuilderInsertDeclareRecordAtEnd(
di_builder,
variable_alloca,
@ -199,6 +197,56 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
};
}
fn dbg_var_value(
&mut self,
dbg_var: &'ll DIVariable,
dbg_loc: &'ll DILocation,
value: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
fragment: &Option<Range<Size>>,
) {
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value};
// Convert the direct and indirect offsets and fragment byte range to address ops.
let mut addr_ops = SmallVec::<[u64; 8]>::new();
if direct_offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(direct_offset.bytes() as u64);
addr_ops.push(DW_OP_stack_value);
}
for &offset in indirect_offsets {
addr_ops.push(DW_OP_deref);
if offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(offset.bytes() as u64);
}
}
if let Some(fragment) = fragment {
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
addr_ops.push(DW_OP_LLVM_fragment);
addr_ops.push(fragment.start.bits() as u64);
addr_ops.push((fragment.end - fragment.start).bits() as u64);
}
let di_builder = DIB(self.cx());
let addr_expr = unsafe {
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
};
unsafe {
llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd(
di_builder,
value,
dbg_var,
addr_expr,
dbg_loc,
self.llbb(),
);
}
}
fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
unsafe {
llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc);

View file

@ -23,13 +23,11 @@ use smallvec::SmallVec;
use tracing::debug;
use crate::abi::FnAbiLlvmExt;
use crate::attributes;
use crate::common::AsCCharPtr;
use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
use crate::llvm::AttributePlace::Function;
use crate::llvm::Visibility;
use crate::type_::Type;
use crate::value::Value;
use crate::{attributes, llvm};
use crate::llvm::{self, FromGeneric, Type, Value, Visibility};
/// Declare a function with a SimpleCx.
///

View file

@ -25,11 +25,9 @@ use crate::builder::Builder;
use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
use crate::context::CodegenCx;
use crate::errors::AutoDiffWithoutEnable;
use crate::llvm::{self, Metadata};
use crate::type_::Type;
use crate::llvm::{self, Metadata, Type, Value};
use crate::type_of::LayoutLlvmExt;
use crate::va_arg::emit_va_arg;
use crate::value::Value;
fn call_simple_intrinsic<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,

View file

@ -14,6 +14,7 @@
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
#![feature(macro_derive)]
#![feature(rustdoc_internals)]
#![feature(slice_as_array)]
#![feature(try_blocks)]
@ -65,6 +66,7 @@ mod errors;
mod intrinsic;
mod llvm;
mod llvm_util;
mod macros;
mod mono_item;
mod type_;
mod type_of;
@ -74,6 +76,8 @@ mod value;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub(crate) use macros::TryFromU32;
#[derive(Clone)]
pub struct LlvmCodegenBackend(());

View file

@ -0,0 +1,115 @@
//! Conversions from backend-independent data types to/from LLVM FFI types.
use rustc_codegen_ssa::common::{AtomicRmwBinOp, IntPredicate, RealPredicate};
use rustc_middle::ty::AtomicOrdering;
use rustc_session::config::DebugInfo;
use rustc_target::spec::SymbolVisibility;
use crate::llvm;
/// Helper trait for converting backend-independent types to LLVM-specific
/// types, for FFI purposes.
pub(crate) trait FromGeneric<T> {
fn from_generic(other: T) -> Self;
}
impl FromGeneric<SymbolVisibility> for llvm::Visibility {
fn from_generic(visibility: SymbolVisibility) -> Self {
match visibility {
SymbolVisibility::Hidden => Self::Hidden,
SymbolVisibility::Protected => Self::Protected,
SymbolVisibility::Interposable => Self::Default,
}
}
}
impl FromGeneric<IntPredicate> for llvm::IntPredicate {
fn from_generic(int_pred: IntPredicate) -> Self {
match int_pred {
IntPredicate::IntEQ => Self::IntEQ,
IntPredicate::IntNE => Self::IntNE,
IntPredicate::IntUGT => Self::IntUGT,
IntPredicate::IntUGE => Self::IntUGE,
IntPredicate::IntULT => Self::IntULT,
IntPredicate::IntULE => Self::IntULE,
IntPredicate::IntSGT => Self::IntSGT,
IntPredicate::IntSGE => Self::IntSGE,
IntPredicate::IntSLT => Self::IntSLT,
IntPredicate::IntSLE => Self::IntSLE,
}
}
}
impl FromGeneric<RealPredicate> for llvm::RealPredicate {
fn from_generic(real_pred: RealPredicate) -> Self {
match real_pred {
RealPredicate::RealPredicateFalse => Self::RealPredicateFalse,
RealPredicate::RealOEQ => Self::RealOEQ,
RealPredicate::RealOGT => Self::RealOGT,
RealPredicate::RealOGE => Self::RealOGE,
RealPredicate::RealOLT => Self::RealOLT,
RealPredicate::RealOLE => Self::RealOLE,
RealPredicate::RealONE => Self::RealONE,
RealPredicate::RealORD => Self::RealORD,
RealPredicate::RealUNO => Self::RealUNO,
RealPredicate::RealUEQ => Self::RealUEQ,
RealPredicate::RealUGT => Self::RealUGT,
RealPredicate::RealUGE => Self::RealUGE,
RealPredicate::RealULT => Self::RealULT,
RealPredicate::RealULE => Self::RealULE,
RealPredicate::RealUNE => Self::RealUNE,
RealPredicate::RealPredicateTrue => Self::RealPredicateTrue,
}
}
}
impl FromGeneric<AtomicRmwBinOp> for llvm::AtomicRmwBinOp {
fn from_generic(op: AtomicRmwBinOp) -> Self {
match op {
AtomicRmwBinOp::AtomicXchg => Self::AtomicXchg,
AtomicRmwBinOp::AtomicAdd => Self::AtomicAdd,
AtomicRmwBinOp::AtomicSub => Self::AtomicSub,
AtomicRmwBinOp::AtomicAnd => Self::AtomicAnd,
AtomicRmwBinOp::AtomicNand => Self::AtomicNand,
AtomicRmwBinOp::AtomicOr => Self::AtomicOr,
AtomicRmwBinOp::AtomicXor => Self::AtomicXor,
AtomicRmwBinOp::AtomicMax => Self::AtomicMax,
AtomicRmwBinOp::AtomicMin => Self::AtomicMin,
AtomicRmwBinOp::AtomicUMax => Self::AtomicUMax,
AtomicRmwBinOp::AtomicUMin => Self::AtomicUMin,
}
}
}
impl FromGeneric<AtomicOrdering> for llvm::AtomicOrdering {
fn from_generic(ordering: AtomicOrdering) -> Self {
match ordering {
AtomicOrdering::Relaxed => Self::Monotonic,
AtomicOrdering::Acquire => Self::Acquire,
AtomicOrdering::Release => Self::Release,
AtomicOrdering::AcqRel => Self::AcquireRelease,
AtomicOrdering::SeqCst => Self::SequentiallyConsistent,
}
}
}
impl FromGeneric<DebugInfo> for llvm::debuginfo::DebugEmissionKind {
fn from_generic(kind: DebugInfo) -> Self {
// We should be setting LLVM's emission kind to `LineTablesOnly` if
// we are compiling with "limited" debuginfo. However, some of the
// existing tools relied on slightly more debuginfo being generated than
// would be the case with `LineTablesOnly`, and we did not want to break
// these tools in a "drive-by fix", without a good idea or plan about
// what limited debuginfo should exactly look like. So for now we are
// instead adding a new debuginfo option "line-tables-only" so as to
// not break anything and to allow users to have 'limited' debug info.
//
// See https://github.com/rust-lang/rust/issues/60020 for details.
match kind {
DebugInfo::None => Self::NoDebug,
DebugInfo::LineDirectivesOnly => Self::DebugDirectivesOnly,
DebugInfo::LineTablesOnly => Self::LineTablesOnly,
DebugInfo::Limited | DebugInfo::Full => Self::FullDebug,
}
}
}

View file

@ -6,7 +6,7 @@ use rustc_span::InnerSpan;
pub(crate) use self::Diagnostic::*;
use self::OptimizationDiagnosticKind::*;
use super::{DiagnosticInfo, SMDiagnostic};
use crate::value::Value;
use crate::llvm::Value;
#[derive(Copy, Clone, Debug)]
pub(crate) enum OptimizationDiagnosticKind {

View file

@ -19,8 +19,6 @@ use std::ptr;
use bitflags::bitflags;
use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t};
use rustc_macros::TryFromU32;
use rustc_target::spec::SymbolVisibility;
use super::RustString;
use super::debuginfo::{
@ -28,8 +26,8 @@ use super::debuginfo::{
DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram,
DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind,
};
use crate::llvm;
use crate::llvm::MetadataKindId;
use crate::{TryFromU32, llvm};
/// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`,
/// which has a different ABI from Rust or C++ `bool`.
@ -220,16 +218,6 @@ pub(crate) enum Visibility {
Protected = 2,
}
impl Visibility {
pub(crate) fn from_generic(visibility: SymbolVisibility) -> Self {
match visibility {
SymbolVisibility::Hidden => Visibility::Hidden,
SymbolVisibility::Protected => Visibility::Protected,
SymbolVisibility::Interposable => Visibility::Default,
}
}
}
/// LLVMUnnamedAddr
#[repr(C)]
pub(crate) enum UnnamedAddr {
@ -319,24 +307,6 @@ pub(crate) enum IntPredicate {
IntSLE = 41,
}
impl IntPredicate {
pub(crate) fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self {
use rustc_codegen_ssa::common::IntPredicate as Common;
match intpre {
Common::IntEQ => Self::IntEQ,
Common::IntNE => Self::IntNE,
Common::IntUGT => Self::IntUGT,
Common::IntUGE => Self::IntUGE,
Common::IntULT => Self::IntULT,
Common::IntULE => Self::IntULE,
Common::IntSGT => Self::IntSGT,
Common::IntSGE => Self::IntSGE,
Common::IntSLT => Self::IntSLT,
Common::IntSLE => Self::IntSLE,
}
}
}
/// LLVMRealPredicate
#[derive(Copy, Clone)]
#[repr(C)]
@ -359,30 +329,6 @@ pub(crate) enum RealPredicate {
RealPredicateTrue = 15,
}
impl RealPredicate {
pub(crate) fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self {
use rustc_codegen_ssa::common::RealPredicate as Common;
match realp {
Common::RealPredicateFalse => Self::RealPredicateFalse,
Common::RealOEQ => Self::RealOEQ,
Common::RealOGT => Self::RealOGT,
Common::RealOGE => Self::RealOGE,
Common::RealOLT => Self::RealOLT,
Common::RealOLE => Self::RealOLE,
Common::RealONE => Self::RealONE,
Common::RealORD => Self::RealORD,
Common::RealUNO => Self::RealUNO,
Common::RealUEQ => Self::RealUEQ,
Common::RealUGT => Self::RealUGT,
Common::RealUGE => Self::RealUGE,
Common::RealULT => Self::RealULT,
Common::RealULE => Self::RealULE,
Common::RealUNE => Self::RealUNE,
Common::RealPredicateTrue => Self::RealPredicateTrue,
}
}
}
/// Must match the layout of `LLVMTypeKind`.
///
/// Use [`RawEnum<TypeKind>`] for values of `LLVMTypeKind` returned from LLVM,
@ -458,25 +404,6 @@ pub(crate) enum AtomicRmwBinOp {
AtomicUMin = 10,
}
impl AtomicRmwBinOp {
pub(crate) fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self {
use rustc_codegen_ssa::common::AtomicRmwBinOp as Common;
match op {
Common::AtomicXchg => Self::AtomicXchg,
Common::AtomicAdd => Self::AtomicAdd,
Common::AtomicSub => Self::AtomicSub,
Common::AtomicAnd => Self::AtomicAnd,
Common::AtomicNand => Self::AtomicNand,
Common::AtomicOr => Self::AtomicOr,
Common::AtomicXor => Self::AtomicXor,
Common::AtomicMax => Self::AtomicMax,
Common::AtomicMin => Self::AtomicMin,
Common::AtomicUMax => Self::AtomicUMax,
Common::AtomicUMin => Self::AtomicUMin,
}
}
}
/// LLVMAtomicOrdering
#[derive(Copy, Clone)]
#[repr(C)]
@ -493,19 +420,6 @@ pub(crate) enum AtomicOrdering {
SequentiallyConsistent = 7,
}
impl AtomicOrdering {
pub(crate) fn from_generic(ao: rustc_middle::ty::AtomicOrdering) -> Self {
use rustc_middle::ty::AtomicOrdering as Common;
match ao {
Common::Relaxed => Self::Monotonic,
Common::Acquire => Self::Acquire,
Common::Release => Self::Release,
Common::AcqRel => Self::AcquireRelease,
Common::SeqCst => Self::SequentiallyConsistent,
}
}
}
/// LLVMRustFileType
#[derive(Copy, Clone)]
#[repr(C)]
@ -940,28 +854,6 @@ pub(crate) mod debuginfo {
DebugDirectivesOnly,
}
impl DebugEmissionKind {
pub(crate) fn from_generic(kind: rustc_session::config::DebugInfo) -> Self {
// We should be setting LLVM's emission kind to `LineTablesOnly` if
// we are compiling with "limited" debuginfo. However, some of the
// existing tools relied on slightly more debuginfo being generated than
// would be the case with `LineTablesOnly`, and we did not want to break
// these tools in a "drive-by fix", without a good idea or plan about
// what limited debuginfo should exactly look like. So for now we are
// instead adding a new debuginfo option "line-tables-only" so as to
// not break anything and to allow users to have 'limited' debug info.
//
// See https://github.com/rust-lang/rust/issues/60020 for details.
use rustc_session::config::DebugInfo;
match kind {
DebugInfo::None => DebugEmissionKind::NoDebug,
DebugInfo::LineDirectivesOnly => DebugEmissionKind::DebugDirectivesOnly,
DebugInfo::LineTablesOnly => DebugEmissionKind::LineTablesOnly,
DebugInfo::Limited | DebugInfo::Full => DebugEmissionKind::FullDebug,
}
}
}
/// LLVMRustDebugNameTableKind
#[derive(Clone, Copy)]
#[repr(C)]
@ -1061,7 +953,7 @@ unsafe extern "C" {
pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type;
pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type;
pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type;
pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type;
pub(crate) safe fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type;
pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint;
@ -1090,7 +982,7 @@ unsafe extern "C" {
) -> &'a Type;
// Operations on array, pointer, and vector types (sequence types)
pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type;
pub(crate) safe fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type;
pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type;
pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type;
@ -1991,6 +1883,15 @@ unsafe extern "C" {
Block: &'ll BasicBlock,
) -> &'ll DbgRecord;
pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>(
Builder: &DIBuilder<'ll>,
Val: &'ll Value,
VarInfo: &'ll Metadata,
Expr: &'ll Metadata,
DebugLoc: &'ll Metadata,
Block: &'ll BasicBlock,
) -> &'ll DbgRecord;
pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>(
Builder: &DIBuilder<'ll>,
Scope: &'ll Metadata,

View file

@ -11,10 +11,12 @@ use rustc_llvm::RustString;
pub(crate) use self::CallConv::*;
pub(crate) use self::CodeGenOptSize::*;
pub(crate) use self::conversions::*;
pub(crate) use self::ffi::*;
pub(crate) use self::metadata_kind::*;
use crate::common::AsCCharPtr;
mod conversions;
pub(crate) mod diagnostic;
pub(crate) mod enzyme_ffi;
mod ffi;

View file

@ -0,0 +1,22 @@
macro_rules! TryFromU32 {
derive() (
$(#[$meta:meta])*
$vis:vis enum $Type:ident {
$(
$(#[$varmeta:meta])*
$Variant:ident $(= $discr:expr)?
),* $(,)?
}
) => {
impl ::core::convert::TryFrom<u32> for $Type {
type Error = u32;
#[allow(deprecated)] // Don't warn about deprecated variants.
fn try_from(value: u32) -> ::core::result::Result<$Type, Self::Error> {
$( if value == const { $Type::$Variant as u32 } { return Ok($Type::$Variant) } )*
Err(value)
}
}
}
}
pub(crate) use TryFromU32;

View file

@ -13,12 +13,10 @@ use rustc_middle::ty::{self, Ty};
use rustc_target::callconv::{CastTarget, FnAbi};
use crate::abi::{FnAbiLlvmExt, LlvmType};
use crate::common;
use crate::context::{CodegenCx, GenericCx, SCx};
pub(crate) use crate::llvm::Type;
use crate::llvm::{FALSE, Metadata, TRUE, ToLlvmBool};
use crate::llvm::{self, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value};
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use crate::{common, llvm};
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
@ -63,7 +61,7 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
///x Creates an integer type with the given number of bits, e.g., i24
pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type {
unsafe { llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) }
llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint)
}
pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type {
@ -178,7 +176,7 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
}
fn type_i128(&self) -> &'ll Type {
unsafe { llvm::LLVMIntTypeInContext(self.llcx(), 128) }
self.type_ix(128)
}
fn type_isize(&self) -> &'ll Type {
@ -210,11 +208,11 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
}
fn type_ptr(&self) -> &'ll Type {
self.type_ptr_ext(AddressSpace::ZERO)
llvm_type_ptr(self.llcx())
}
fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type {
unsafe { llvm::LLVMPointerTypeInContext(self.llcx(), address_space.0) }
llvm_type_ptr_in_address_space(self.llcx(), address_space)
}
fn element_type(&self, ty: &'ll Type) -> &'ll Type {
@ -253,15 +251,15 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
}
}
impl Type {
/// Creates an integer type with the given number of bits, e.g., i24
pub(crate) fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type {
unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) }
}
pub(crate) fn llvm_type_ptr(llcx: &llvm::Context) -> &Type {
llvm_type_ptr_in_address_space(llcx, AddressSpace::ZERO)
}
pub(crate) fn ptr_llcx(llcx: &llvm::Context) -> &Type {
unsafe { llvm::LLVMPointerTypeInContext(llcx, AddressSpace::ZERO.0) }
}
pub(crate) fn llvm_type_ptr_in_address_space<'ll>(
llcx: &'ll llvm::Context,
addr_space: AddressSpace,
) -> &'ll Type {
llvm::LLVMPointerTypeInContext(llcx, addr_space.0)
}
impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {

View file

@ -11,7 +11,7 @@ use rustc_span::{DUMMY_SP, Span};
use tracing::debug;
use crate::common::*;
use crate::type_::Type;
use crate::llvm::Type;
fn uncached_llvm_type<'a, 'tcx>(
cx: &CodegenCx<'a, 'tcx>,

View file

@ -9,9 +9,8 @@ use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use crate::builder::Builder;
use crate::type_::Type;
use crate::llvm::{Type, Value};
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
fn round_up_to_alignment<'ll>(
bx: &mut Builder<'_, 'll, '_>,

View file

@ -1,8 +1,7 @@
use std::hash::{Hash, Hasher};
use std::{fmt, ptr};
use crate::llvm;
pub(crate) use crate::llvm::Value;
use crate::llvm::{self, Value};
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {

View file

@ -97,10 +97,10 @@ codegen_ssa_invalid_literal_value = invalid literal value
codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}`
codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}`
codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}`

View file

@ -1490,7 +1490,7 @@ fn print_native_static_libs(
NativeLibKind::Static { bundle: None | Some(true), .. }
| NativeLibKind::LinkArg
| NativeLibKind::WasmImportModule
| NativeLibKind::RawDylib => None,
| NativeLibKind::RawDylib { .. } => None,
}
})
// deduplication of consecutive repeated libraries, see rust-lang/rust#113209
@ -2364,13 +2364,13 @@ fn linker_with_args(
cmd.add_object(&output_path);
}
} else {
for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
sess,
codegen_results.crate_info.used_libraries.iter(),
&raw_dylib_dir,
) {
// Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
cmd.link_dylib_by_name(&link_path, true, false);
cmd.link_dylib_by_name(&link_path, true, as_needed);
}
}
// As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
@ -2411,13 +2411,13 @@ fn linker_with_args(
cmd.add_object(&output_path);
}
} else {
for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
sess,
native_libraries_from_nonstatics,
&raw_dylib_dir,
) {
// Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
cmd.link_dylib_by_name(&link_path, true, false);
cmd.link_dylib_by_name(&link_path, true, as_needed);
}
}
@ -2726,7 +2726,7 @@ fn add_native_libs_from_crate(
cmd.link_framework_by_name(name, verbatim, as_needed.unwrap_or(true))
}
}
NativeLibKind::RawDylib => {
NativeLibKind::RawDylib { as_needed: _ } => {
// Handled separately in `linker_with_args`.
}
NativeLibKind::WasmImportModule => {}

View file

@ -31,7 +31,7 @@ fn collate_raw_dylibs_windows<'a>(
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
if let NativeLibKind::RawDylib { .. } = lib.kind {
let ext = if lib.verbatim { "" } else { ".dll" };
let name = format!("{}{}", lib.name, ext);
let imports = dylib_table.entry(name.clone()).or_default();
@ -128,12 +128,12 @@ pub(super) fn create_raw_dylib_dll_import_libs<'a>(
fn collate_raw_dylibs_elf<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
) -> Vec<(String, Vec<DllImport>)> {
) -> Vec<(String, Vec<DllImport>, bool)> {
// Use index maps to preserve original order of imports and libraries.
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
let mut dylib_table = FxIndexMap::<String, (FxIndexMap<Symbol, &DllImport>, bool)>::default();
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
if let NativeLibKind::RawDylib { as_needed } = lib.kind {
let filename = if lib.verbatim {
lib.name.as_str().to_owned()
} else {
@ -142,17 +142,19 @@ fn collate_raw_dylibs_elf<'a>(
format!("{prefix}{}{ext}", lib.name)
};
let imports = dylib_table.entry(filename.clone()).or_default();
let (stub_imports, stub_as_needed) =
dylib_table.entry(filename.clone()).or_insert((Default::default(), true));
for import in &lib.dll_imports {
imports.insert(import.name, import);
stub_imports.insert(import.name, import);
}
*stub_as_needed = *stub_as_needed && as_needed.unwrap_or(true);
}
}
sess.dcx().abort_if_errors();
dylib_table
.into_iter()
.map(|(name, imports)| {
(name, imports.into_iter().map(|(_, import)| import.clone()).collect())
.map(|(name, (imports, as_needed))| {
(name, imports.into_iter().map(|(_, import)| import.clone()).collect(), as_needed)
})
.collect()
}
@ -161,10 +163,10 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
raw_dylib_so_dir: &Path,
) -> Vec<String> {
) -> Vec<(String, bool)> {
collate_raw_dylibs_elf(sess, used_libraries)
.into_iter()
.map(|(load_filename, raw_dylib_imports)| {
.map(|(load_filename, raw_dylib_imports, as_needed)| {
use std::hash::Hash;
// `load_filename` is the *target/loader* filename that will end up in NEEDED.
@ -205,7 +207,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
});
};
temporary_lib_name
(temporary_lib_name, as_needed)
})
.collect()
}

View file

@ -263,6 +263,19 @@ fn process_builtin_attrs(
AttributeKind::Used { used_by, .. } => match used_by {
UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
UsedBy::Default => {
let used_form = if tcx.sess.target.os == "illumos" {
// illumos' `ld` doesn't support a section header that would represent
// `#[used(linker)]`, see
// https://github.com/rust-lang/rust/issues/146169. For that target,
// downgrade as if `#[used(compiler)]` was requested and hope for the
// best.
CodegenFnAttrFlags::USED_COMPILER
} else {
CodegenFnAttrFlags::USED_LINKER
};
codegen_fn_attrs.flags |= used_form;
}
},
AttributeKind::FfiConst(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST

View file

@ -260,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(),
}
}
fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) {
// Debuginfo does not generate actual code.
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -1320,6 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
for statement in &data.statements {
self.codegen_statement(bx, statement);
}
self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos);
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
if let MergingSucc::False = merging_succ {

View file

@ -253,6 +253,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
spill_slot
}
// Indicates that local is set to a new value. The `layout` and `projection` are used to
// calculate the offset.
pub(crate) fn debug_new_val_to_local(
&self,
bx: &mut Bx,
local: mir::Local,
base: PlaceRef<'tcx, Bx::Value>,
projection: &[mir::PlaceElem<'tcx>],
) {
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
if !full_debug_info {
return;
}
let vars = match &self.per_local_var_debug_info {
Some(per_local) => &per_local[local],
None => return,
};
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, projection, base.layout);
for var in vars.iter() {
let Some(dbg_var) = var.dbg_var else {
continue;
};
let Some(dbg_loc) = self.dbg_loc(var.source_info) else {
continue;
};
bx.dbg_var_value(
dbg_var,
dbg_loc,
base.val.llval,
direct_offset,
&indirect_offsets,
&var.fragment,
);
}
}
pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
let ty = self.monomorphize(self.mir.local_decls[local].ty);
let layout = bx.cx().layout_of(ty);
let to_backend_ty = bx.cx().immediate_backend_type(layout);
let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
self.debug_new_val_to_local(bx, local, place_ref, &[]);
}
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
/// or initializing the local with an operand (whichever applies).
pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
@ -424,7 +471,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
alloca.val.llval,
Size::ZERO,
&[Size::ZERO],
var.fragment,
&var.fragment,
);
} else {
bx.dbg_var_addr(
@ -433,7 +480,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
base.val.llval,
direct_offset,
&indirect_offsets,
var.fragment,
&var.fragment,
);
}
}
@ -455,7 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
bx.clear_dbg_loc();
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
}
}
}

View file

@ -71,16 +71,23 @@ pub enum OperandValue<V> {
}
impl<V: CodegenObject> OperandValue<V> {
/// Return the data pointer and optional metadata as backend values
/// if this value can be treat as a pointer.
pub(crate) fn try_pointer_parts(self) -> Option<(V, Option<V>)> {
match self {
OperandValue::Immediate(llptr) => Some((llptr, None)),
OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))),
OperandValue::Ref(_) | OperandValue::ZeroSized => None,
}
}
/// Treat this value as a pointer and return the data pointer and
/// optional metadata as backend values.
///
/// If you're making a place, use [`Self::deref`] instead.
pub(crate) fn pointer_parts(self) -> (V, Option<V>) {
match self {
OperandValue::Immediate(llptr) => (llptr, None),
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
_ => bug!("OperandValue cannot be a pointer: {self:?}"),
}
self.try_pointer_parts()
.unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}"))
}
/// Treat this value as a pointer and return the place to which it points.

View file

@ -1,4 +1,4 @@
use rustc_middle::mir::{self, NonDivergingIntrinsic};
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
use rustc_middle::span_bug;
use tracing::instrument;
@ -8,6 +8,7 @@ use crate::traits::*;
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "debug", skip(self, bx))]
pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
self.codegen_stmt_debuginfos(bx, &statement.debuginfos);
self.set_debug_loc(bx, statement.source_info);
match statement.kind {
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@ -101,4 +102,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| mir::StatementKind::Nop => {}
}
}
pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
match debuginfo {
StmtDebugInfo::AssignRef(dest, place) => {
let local_ref = match self.locals[place.local] {
// For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place.
// The place is an indirect pointer, we can refer to it directly.
LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())),
// For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`.
// The deref projection is no-op here.
LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => {
Some((operand_ref.deref(bx.cx()), &place.projection[1..]))
}
// For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`,
// we cannot get the address.
// N.B. `non_ssa_locals` returns that this is an SSA local.
LocalRef::Operand(_) => None,
LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None,
}
.filter(|(_, projection)| {
// Drop unsupported projections.
projection.iter().all(|p| p.can_use_in_debuginfo())
});
if let Some((base, projection)) = local_ref {
self.debug_new_val_to_local(bx, *dest, base, projection);
} else {
// If the address cannot be calculated, use poison to indicate that the value has been optimized out.
self.debug_poison_to_local(bx, *dest);
}
}
StmtDebugInfo::InvalidAssign(local) => {
self.debug_poison_to_local(bx, *local);
}
}
}
pub(crate) fn codegen_stmt_debuginfos(
&mut self,
bx: &mut Bx,
debuginfos: &[StmtDebugInfo<'tcx>],
) {
for debuginfo in debuginfos {
self.codegen_stmt_debuginfo(bx, debuginfo);
}
}
}

View file

@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
indirect_offsets: &[Size],
// Byte range in the `dbg_var` covered by this fragment,
// if this is a fragment of a composite `DIVariable`.
fragment: Option<Range<Size>>,
fragment: &Option<Range<Size>>,
);
fn dbg_var_value(
&mut self,
dbg_var: Self::DIVariable,
dbg_loc: Self::DILocation,
value: Self::Value,
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
// Byte range in the `dbg_var` covered by this fragment,
// if this is a fragment of a composite `DIVariable`.
fragment: &Option<Range<Size>>,
);
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
fn clear_dbg_loc(&mut self);

View file

@ -231,9 +231,6 @@ const_eval_mutable_borrow_escaping =
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind}
.note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
const_eval_non_const_await =
@ -302,6 +299,9 @@ const_eval_panic = evaluation panicked: {$msg}
const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str`
const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind}
.note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
const_eval_partial_pointer_read =
unable to read parts of a pointer from memory at {$ptr}
const_eval_pointer_arithmetic_overflow =
@ -476,6 +476,7 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero
const_eval_validation_null_box = {$front_matter}: encountered a {$maybe ->
[true] maybe-null
*[false] null
@ -485,7 +486,6 @@ const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe ->
[true] maybe-null
*[false] null
} reference
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero
const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range}
const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers
const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected}

View file

@ -2021,7 +2021,7 @@ impl HumanEmitter {
if let Some(width) = self.diagnostic_width {
width.saturating_sub(code_offset)
} else if self.ui_testing || cfg!(miri) {
DEFAULT_COLUMN_WIDTH
DEFAULT_COLUMN_WIDTH.saturating_sub(code_offset)
} else {
termize::dimensions()
.map(|(w, _)| w.saturating_sub(code_offset))
@ -2412,7 +2412,7 @@ impl HumanEmitter {
// too bad to begin with, so we side-step that issue here.
for (i, line) in snippet.lines().enumerate() {
let line = normalize_whitespace(line);
let row = row_num - 2 - (newlines - i - 1);
let row = (row_num - 2 - (newlines - i - 1)).max(2);
// On the first line, we highlight between the start of the part
// span, and the end of that line.
// On the last line, we highlight between the start of the line, and

View file

@ -95,6 +95,8 @@ expand_malformed_feature_attribute =
malformed `feature` attribute input
.expected = expected just one word
expand_meta_var_dif_seq_matchers = {$msg}
expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth
.label = expected repetition
@ -102,8 +104,6 @@ expand_metavariable_wrong_operator = meta-variable repeats with different Kleene
.binder_label = expected repetition
.occurrence_label = conflicting repetition
expand_meta_var_dif_seq_matchers = {$msg}
expand_missing_fragment_specifier = missing fragment specifier
.note = fragment specifiers must be provided
.suggestion_add_fragspec = try adding a specifier here
@ -118,8 +118,9 @@ expand_module_file_not_found =
.note = if there is a `mod {$name}` elsewhere in the crate already, import it with `use crate::...` instead
expand_module_in_block =
cannot declare a non-inline module inside a block unless it has a path attribute
.note = maybe `use` the module `{$name}` instead of redeclaring it
cannot declare a file module inside a block unless it has a path attribute
.help = maybe `use` the module `{$name}` instead of redeclaring it
.note = file modules are usually placed outside of blocks, at the top level of the file
expand_module_multiple_candidates =
file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}"
@ -198,12 +199,12 @@ expand_trailing_semi_macro = trailing semicolon in macro used in expression posi
expand_unknown_macro_variable = unknown macro variable `{$name}`
expand_unsupported_key_value =
key-value macro attributes are not supported
expand_unused_builtin_attribute = unused attribute `{$attr_name}`
.note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}`
.suggestion = remove the attribute
expand_unsupported_key_value =
key-value macro attributes are not supported
expand_wrong_fragment_kind =
non-{$kind} macro in {$kind} position: {$name}

View file

@ -265,6 +265,7 @@ pub(crate) struct ModuleCircular {
#[derive(Diagnostic)]
#[diag(expand_module_in_block)]
#[note]
pub(crate) struct ModuleInBlock {
#[primary_span]
pub span: Span,
@ -273,7 +274,7 @@ pub(crate) struct ModuleInBlock {
}
#[derive(Subdiagnostic)]
#[note(expand_note)]
#[help(expand_help)]
pub(crate) struct ModuleInBlockName {
#[primary_span]
pub span: Span,

View file

@ -93,6 +93,16 @@ impl Features {
&self.enabled_features
}
/// Returns a iterator of enabled features in stable order.
pub fn enabled_features_iter_stable_order(
&self,
) -> impl Iterator<Item = (Symbol, Span)> + Clone {
self.enabled_lang_features
.iter()
.map(|feat| (feat.gate_name, feat.attr_sp))
.chain(self.enabled_lib_features.iter().map(|feat| (feat.gate_name, feat.attr_sp)))
}
/// Is the given feature enabled (via `#[feature(...)]`)?
pub fn enabled(&self, feature: Symbol) -> bool {
self.enabled_features.contains(&feature)

View file

@ -146,12 +146,13 @@ impl Deprecation {
}
/// There are three valid forms of the attribute:
/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable.
/// `#[used]`, which is equivalent to `#[used(linker)]` on targets that support it, but `#[used(compiler)]` if not.
/// `#[used(compiler)]`
/// `#[used(linker)]`
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum UsedBy {
Default,
Compiler,
Linker,
}
@ -308,7 +309,10 @@ pub enum NativeLibKind {
},
/// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library.
/// On Linux, it refers to a generated shared library stub.
RawDylib,
RawDylib {
/// Whether the dynamic library will be linked only if it satisfies some undefined symbols
as_needed: Option<bool>,
},
/// A macOS-specific kind of dynamic libraries.
Framework {
/// Whether the framework will be linked only if it satisfies some undefined symbols
@ -331,11 +335,10 @@ impl NativeLibKind {
NativeLibKind::Static { bundle, whole_archive } => {
bundle.is_some() || whole_archive.is_some()
}
NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => {
as_needed.is_some()
}
NativeLibKind::RawDylib
| NativeLibKind::Unspecified
NativeLibKind::Dylib { as_needed }
| NativeLibKind::Framework { as_needed }
| NativeLibKind::RawDylib { as_needed } => as_needed.is_some(),
NativeLibKind::Unspecified
| NativeLibKind::LinkArg
| NativeLibKind::WasmImportModule => false,
}
@ -348,7 +351,9 @@ impl NativeLibKind {
pub fn is_dllimport(&self) -> bool {
matches!(
self,
NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified
NativeLibKind::Dylib { .. }
| NativeLibKind::RawDylib { .. }
| NativeLibKind::Unspecified
)
}
}

View file

@ -1,9 +1,9 @@
use rustc_abi::ExternAbi;
use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar};
use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
use rustc_hir::{self as hir, HirId};
use rustc_middle::bug;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use crate::errors;
@ -164,44 +164,48 @@ fn is_valid_cmse_output<'tcx>(
) -> Result<bool, &'tcx LayoutError<'tcx>> {
// this type is only used for layout computation, which does not rely on regions
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
let return_type = fn_sig.output();
// `impl Trait` is already disallowed with `cmse-nonsecure-call`, because that ABI is only
// allowed on function pointers, and function pointers cannot contain `impl Trait` in their
// signature.
//
// Here we explicitly disallow `impl Trait` in the `cmse-nonsecure-entry` return type too, to
// prevent query cycles when calculating the layout. This ABI is meant to be used with
// `#[no_mangle]` or similar, so generics in the type really don't make sense.
//
// see also https://github.com/rust-lang/rust/issues/147242.
if return_type.has_opaque_types() {
return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type)));
}
let typing_env = ty::TypingEnv::fully_monomorphized();
let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
let mut ret_ty = fn_sig.output();
let layout = tcx.layout_of(typing_env.as_query_input(ret_ty))?;
Ok(is_valid_cmse_output_layout(layout))
}
/// Returns whether the output will fit into the available registers
fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
let size = layout.layout.size().bytes();
if size <= 4 {
return Ok(true);
return true;
} else if size > 8 {
return Ok(false);
return false;
}
// next we need to peel any repr(transparent) layers off
'outer: loop {
let ty::Adt(adt_def, args) = ret_ty.kind() else {
break;
};
// Accept scalar 64-bit types.
let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else {
return false;
};
if !adt_def.repr().transparent() {
break;
}
let Scalar::Initialized { value, .. } = scalar else {
return false;
};
// the first field with non-trivial size and alignment must be the data
for variant_def in adt_def.variants() {
for field_def in variant_def.fields.iter() {
let ty = field_def.ty(tcx, args);
let layout = tcx.layout_of(typing_env.as_query_input(ty))?;
if !layout.layout.is_1zst() {
ret_ty = ty;
continue 'outer;
}
}
}
}
Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64))
}
fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {

View file

@ -7,9 +7,8 @@ use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::def::DefKind;
use rustc_hir::{self as hir, ExprKind, HirId, Node};
use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
@ -486,13 +485,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.resolve_vars_if_possible(ty.value);
let guar = match *ty.kind() {
ty::Infer(ty::TyVar(_)) => {
// We want to get the variable name that the method
// is being called on. If it is a method call.
let err_span = match (mode, self.tcx.hir_node(scope_expr_id)) {
(
Mode::MethodCall,
Node::Expr(hir::Expr {
kind: ExprKind::MethodCall(_, recv, ..),
..
}),
) => recv.span,
_ => span,
};
let raw_ptr_call = bad_ty.reached_raw_pointer
&& !self.tcx.features().arbitrary_self_types();
// FIXME: Ideally we'd use the span of the self-expr here,
// not of the method path.
let mut err = self.err_ctxt().emit_inference_failure_err(
self.body_id,
span,
err_span,
ty.into(),
TypeAnnotationNeeded::E0282,
!raw_ptr_call,

View file

@ -392,6 +392,14 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
lint_improper_ctypes_union_layout_reason = this union has unspecified layout
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance
.note = this is dangerous because dereferencing the resulting pointer is undefined behavior
.note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance
.help_transmute = for more information about transmute, see <https://doc.rust-lang.org/std/mem/fn.transmute.html#transmutation-between-pointers-and-integers>
.help_exposed_provenance = for more information about exposed provenance, see <https://doc.rust-lang.org/std/ptr/index.html#exposed-provenance>
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`
lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
.label = use a different label that doesn't start with `0` or `1`
.help = start numbering with `2` instead
@ -439,14 +447,6 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://
lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance
.note = this is dangerous because dereferencing the resulting pointer is undefined behavior
.note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance
.help_transmute = for more information about transmute, see <https://doc.rust-lang.org/std/mem/fn.transmute.html#transmutation-between-pointers-and-integers>
.help_exposed_provenance = for more information about exposed provenance, see <https://doc.rust-lang.org/std/ptr/index.html#exposed-provenance>
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`
lint_lintpass_by_hand = implementing `LintPass` by hand
.help = try using `declare_lint_pass!` or `impl_lint_pass!` instead

View file

@ -2331,13 +2331,9 @@ declare_lint_pass!(
impl EarlyLintPass for IncompleteInternalFeatures {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
let features = cx.builder.features();
let lang_features =
features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
let lib_features =
features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
lang_features
.chain(lib_features)
features
.enabled_features_iter_stable_order()
.filter(|(name, _)| features.incomplete(*name) || features.internal(*name))
.for_each(|(name, span)| {
if features.incomplete(name) {

View file

@ -220,21 +220,21 @@ fn check_struct_for_power_alignment<'tcx>(
adt_def: AdtDef<'tcx>,
) {
let tcx = cx.tcx;
// repr(C) structs also with packed or aligned representation
// should be ignored.
if adt_def.repr().c()
&& !adt_def.repr().packed()
&& adt_def.repr().align.is_none()
&& tcx.sess.target.os == "aix"
&& !adt_def.all_fields().next().is_none()
{
// Only consider structs (not enums or unions) on AIX.
if tcx.sess.target.os != "aix" || !adt_def.is_struct() {
return;
}
// The struct must be repr(C), but ignore it if it explicitly specifies its alignment with
// either `align(N)` or `packed(N)`.
if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() {
let struct_variant_data = item.expect_struct().2;
for field_def in struct_variant_data.fields().iter().skip(1) {
// Struct fields (after the first field) are checked for the
// power alignment rule, as fields after the first are likely
// to be the fields that are misaligned.
let def_id = field_def.def_id;
let ty = tcx.type_of(def_id).instantiate_identity();
let ty = tcx.type_of(field_def.def_id).instantiate_identity();
if check_arg_for_power_alignment(cx, ty) {
cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
}

View file

@ -58,6 +58,7 @@ using namespace llvm::object;
// This opcode is an LLVM detail that could hypothetically change (?), so
// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM.
static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000);
static_assert(dwarf::DW_OP_stack_value == 0x9f);
// LLVMAtomicOrdering is already an enum - don't create another
// one.

View file

@ -18,7 +18,6 @@ mod print_attribute;
mod query;
mod serialize;
mod symbols;
mod try_from;
mod type_foldable;
mod type_visitable;
mod visitable;
@ -176,14 +175,6 @@ decl_derive!(
applicability)] => diagnostics::subdiagnostic_derive
);
decl_derive! {
[TryFromU32] =>
/// Derives `TryFrom<u32>` for the annotated `enum`, which must have no fields.
/// Each variant maps to the value it would produce under an `as u32` cast.
///
/// The error type is `u32`.
try_from::try_from_u32
}
decl_derive! {
[PrintAttribute] =>
/// Derives `PrintAttribute` for `AttributeKind`.

View file

@ -1,55 +0,0 @@
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::Data;
use syn::spanned::Spanned;
use synstructure::Structure;
pub(crate) fn try_from_u32(s: Structure<'_>) -> TokenStream {
let span_error = |span, message: &str| {
quote_spanned! { span => const _: () = ::core::compile_error!(#message); }
};
// Must be applied to an enum type.
if let Some(span) = match &s.ast().data {
Data::Enum(_) => None,
Data::Struct(s) => Some(s.struct_token.span()),
Data::Union(u) => Some(u.union_token.span()),
} {
return span_error(span, "type is not an enum (TryFromU32)");
}
// The enum's variants must not have fields.
let variant_field_errors = s
.variants()
.iter()
.filter_map(|v| v.ast().fields.iter().map(|f| f.span()).next())
.map(|span| span_error(span, "enum variant cannot have fields (TryFromU32)"))
.collect::<TokenStream>();
if !variant_field_errors.is_empty() {
return variant_field_errors;
}
let ctor = s
.variants()
.iter()
.map(|v| v.construct(|_, _| -> TokenStream { unreachable!() }))
.collect::<Vec<_>>();
// FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here?
#[allow(keyword_idents_2024)]
s.gen_impl(quote! {
// The surrounding code might have shadowed these identifiers.
use ::core::convert::TryFrom;
use ::core::primitive::u32;
use ::core::result::Result::{self, Ok, Err};
gen impl TryFrom<u32> for @Self {
type Error = u32;
#[allow(deprecated)] // Don't warn about deprecated variants.
fn try_from(value: u32) -> Result<Self, Self::Error> {
#( if value == const { #ctor as u32 } { return Ok(#ctor) } )*
Err(value)
}
}
})
}

View file

@ -98,13 +98,6 @@ metadata_full_metadata_not_found =
metadata_global_alloc_required =
no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
metadata_incompatible_with_immediate_abort =
the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort`
metadata_incompatible_with_immediate_abort_core =
the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort`
.help = consider building the standard library from source with `cargo build -Zbuild-std`
metadata_incompatible_panic_in_drop_strategy =
the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}`
@ -132,6 +125,13 @@ metadata_incompatible_target_modifiers_r_missed =
.note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_with_immediate_abort =
the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort`
metadata_incompatible_with_immediate_abort_core =
the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort`
.help = consider building the standard library from source with `cargo build -Zbuild-std`
metadata_install_missing_components =
maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview`
@ -197,6 +197,8 @@ metadata_prev_alloc_error_handler =
metadata_prev_global_alloc =
previous global allocator defined here
metadata_raw_dylib_malformed =
link name must be well-formed if link kind is `raw-dylib`
metadata_raw_dylib_unsupported_abi =
ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
@ -236,12 +238,3 @@ metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$fla
metadata_wasm_c_abi =
older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88
metadata_wasm_import_form =
wasm import module must be of the form `wasm_import_module = "string"`
metadata_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind
metadata_raw_dylib_malformed =
link name must be well-formed if link kind is `raw-dylib`

View file

@ -218,7 +218,7 @@ impl<'tcx> Collector<'tcx> {
.flatten()
{
let dll_imports = match attr.kind {
NativeLibKind::RawDylib => foreign_items
NativeLibKind::RawDylib { .. } => foreign_items
.iter()
.map(|&child_item| {
self.build_dll_import(

View file

@ -95,15 +95,15 @@ middle_layout_normalization_failure =
middle_layout_references_error =
the type has an unknown layout
middle_layout_size_overflow =
values of the type `{$ty}` are too big for the target architecture
middle_layout_simd_too_many =
the SIMD type `{$ty}` has more elements than the limit {$max_lanes}
middle_layout_simd_zero_length =
the SIMD type `{$ty}` has zero elements
middle_layout_size_overflow =
values of the type `{$ty}` are too big for the target architecture
middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
middle_layout_unknown =

View file

@ -167,7 +167,7 @@ impl CtfeProvenance {
}
impl Provenance for CtfeProvenance {
// With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
// With the `CtfeProvenance` as provenance, the `offset` is interpreted *relative to the allocation*,
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
const OFFSET_IS_ADDR: bool = false;

View file

@ -1298,6 +1298,10 @@ pub struct BasicBlockData<'tcx> {
/// List of statements in this block.
pub statements: Vec<Statement<'tcx>>,
/// All debuginfos happen before the statement.
/// Put debuginfos here when the last statement is eliminated.
pub after_last_stmt_debuginfos: StmtDebugInfos<'tcx>,
/// Terminator for this block.
///
/// N.B., this should generally ONLY be `None` during construction.
@ -1325,7 +1329,12 @@ impl<'tcx> BasicBlockData<'tcx> {
terminator: Option<Terminator<'tcx>>,
is_cleanup: bool,
) -> BasicBlockData<'tcx> {
BasicBlockData { statements, terminator, is_cleanup }
BasicBlockData {
statements,
after_last_stmt_debuginfos: StmtDebugInfos::default(),
terminator,
is_cleanup,
}
}
/// Accessor for terminator.
@ -1360,6 +1369,36 @@ impl<'tcx> BasicBlockData<'tcx> {
self.terminator().successors()
}
}
pub fn retain_statements<F>(&mut self, mut f: F)
where
F: FnMut(&Statement<'tcx>) -> bool,
{
// Place debuginfos into the next retained statement,
// this `debuginfos` variable is used to cache debuginfos between two retained statements.
let mut debuginfos = StmtDebugInfos::default();
self.statements.retain_mut(|stmt| {
let retain = f(stmt);
if retain {
stmt.debuginfos.prepend(&mut debuginfos);
} else {
debuginfos.append(&mut stmt.debuginfos);
}
retain
});
self.after_last_stmt_debuginfos.prepend(&mut debuginfos);
}
pub fn strip_nops(&mut self) {
self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
}
pub fn drop_debuginfo(&mut self) {
self.after_last_stmt_debuginfos.drop_debuginfo();
for stmt in self.statements.iter_mut() {
stmt.debuginfos.drop_debuginfo();
}
}
}
///////////////////////////////////////////////////////////////////////////
@ -1664,10 +1703,10 @@ mod size_asserts {
use super::*;
// tidy-alphabetical-start
static_assert_size!(BasicBlockData<'_>, 128);
static_assert_size!(BasicBlockData<'_>, 152);
static_assert_size!(LocalDecl<'_>, 40);
static_assert_size!(SourceScopeData<'_>, 64);
static_assert_size!(Statement<'_>, 32);
static_assert_size!(Statement<'_>, 56);
static_assert_size!(Terminator<'_>, 96);
static_assert_size!(VarDebugInfo<'_>, 88);
// tidy-alphabetical-end

View file

@ -719,6 +719,11 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> {
let mut current_location = Location { block, statement_index: 0 };
for statement in &data.statements {
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
for debuginfo in statement.debuginfos.iter() {
writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;
}
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
if self.options.include_extra_comments {
writeln!(
@ -749,6 +754,10 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> {
current_location.statement_index += 1;
}
for debuginfo in data.after_last_stmt_debuginfos.iter() {
writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;
}
// Terminator at the bottom.
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
if data.terminator.is_some() {
@ -829,6 +838,19 @@ impl Debug for Statement<'_> {
}
}
impl Debug for StmtDebugInfo<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self {
StmtDebugInfo::AssignRef(local, place) => {
write!(fmt, "{local:?} = &{place:?}")
}
StmtDebugInfo::InvalidAssign(local) => {
write!(fmt, "{local:?} = &?")
}
}
}
}
impl Display for NonDivergingIntrinsic<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View file

@ -1,5 +1,7 @@
//! Functionality for statements, operands, places, and things that appear in them.
use std::ops;
use tracing::{debug, instrument};
use super::interpret::GlobalAlloc;
@ -15,17 +17,28 @@ use crate::ty::CoroutineArgsExt;
pub struct Statement<'tcx> {
pub source_info: SourceInfo,
pub kind: StatementKind<'tcx>,
/// Some debuginfos appearing before the primary statement.
pub debuginfos: StmtDebugInfos<'tcx>,
}
impl<'tcx> Statement<'tcx> {
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
/// invalidating statement indices in `Location`s.
pub fn make_nop(&mut self) {
self.kind = StatementKind::Nop
pub fn make_nop(&mut self, drop_debuginfo: bool) {
if matches!(self.kind, StatementKind::Nop) {
return;
}
let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop);
if !drop_debuginfo {
let Some(debuginfo) = replaced_stmt.as_debuginfo() else {
bug!("debuginfo is not yet supported.")
};
self.debuginfos.push(debuginfo);
}
}
pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self {
Statement { source_info, kind }
Statement { source_info, kind, debuginfos: StmtDebugInfos::default() }
}
}
@ -63,6 +76,17 @@ impl<'tcx> StatementKind<'tcx> {
_ => None,
}
}
pub fn as_debuginfo(&self) -> Option<StmtDebugInfo<'tcx>> {
match self {
StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place)))
if let Some(local) = place.as_local() =>
{
Some(StmtDebugInfo::AssignRef(local, *ref_place))
}
_ => None,
}
}
}
///////////////////////////////////////////////////////////////////////////
@ -503,6 +527,20 @@ impl<'tcx> PlaceRef<'tcx> {
})
}
/// Return the place accessed locals that include the base local.
pub fn accessed_locals(self) -> impl Iterator<Item = Local> {
std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj {
ProjectionElem::Index(local) => Some(*local),
ProjectionElem::Deref
| ProjectionElem::Field(_, _)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::UnwrapUnsafeBinder(_) => None,
}))
}
/// Generates a new place by appending `more_projections` to the existing ones
/// and interning the result.
pub fn project_deeper(
@ -967,3 +1005,71 @@ impl RawPtrKind {
}
}
}
#[derive(Default, Debug, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct StmtDebugInfos<'tcx>(Vec<StmtDebugInfo<'tcx>>);
impl<'tcx> StmtDebugInfos<'tcx> {
pub fn push(&mut self, debuginfo: StmtDebugInfo<'tcx>) {
self.0.push(debuginfo);
}
pub fn drop_debuginfo(&mut self) {
self.0.clear();
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn prepend(&mut self, debuginfos: &mut Self) {
if debuginfos.is_empty() {
return;
};
debuginfos.0.append(self);
std::mem::swap(debuginfos, self);
}
pub fn append(&mut self, debuginfos: &mut Self) {
if debuginfos.is_empty() {
return;
};
self.0.append(debuginfos);
}
pub fn extend(&mut self, debuginfos: &Self) {
if debuginfos.is_empty() {
return;
};
self.0.extend_from_slice(debuginfos);
}
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&StmtDebugInfo<'tcx>) -> bool,
{
self.0.retain(f);
}
}
impl<'tcx> ops::Deref for StmtDebugInfos<'tcx> {
type Target = Vec<StmtDebugInfo<'tcx>>;
#[inline]
fn deref(&self) -> &Vec<StmtDebugInfo<'tcx>> {
&self.0
}
}
impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> {
#[inline]
fn deref_mut(&mut self) -> &mut Vec<StmtDebugInfo<'tcx>> {
&mut self.0
}
}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub enum StmtDebugInfo<'tcx> {
AssignRef(Local, Place<'tcx>),
InvalidAssign(Local),
}

View file

@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> {
self.kind.successors()
}
/// Return `Some` if all successors are identical.
#[inline]
pub fn identical_successor(&self) -> Option<BasicBlock> {
let mut successors = self.successors();
let first_succ = successors.next()?;
if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
}
#[inline]
pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
self.kind.successors_mut(f)

View file

@ -95,6 +95,14 @@ macro_rules! make_mir_visitor {
self.super_source_scope_data(scope_data);
}
fn visit_statement_debuginfo(
&mut self,
stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
location: Location
) {
self.super_statement_debuginfo(stmt_debuginfo, location);
}
fn visit_statement(
&mut self,
statement: & $($mutability)? Statement<'tcx>,
@ -301,6 +309,7 @@ macro_rules! make_mir_visitor {
{
let BasicBlockData {
statements,
after_last_stmt_debuginfos,
terminator,
is_cleanup: _
} = data;
@ -312,8 +321,11 @@ macro_rules! make_mir_visitor {
index += 1;
}
let location = Location { block, statement_index: index };
for debuginfo in after_last_stmt_debuginfos as & $($mutability)? [_] {
self.visit_statement_debuginfo(debuginfo, location);
}
if let Some(terminator) = terminator {
let location = Location { block, statement_index: index };
self.visit_terminator(terminator, location);
}
}
@ -376,14 +388,45 @@ macro_rules! make_mir_visitor {
}
}
fn super_statement_debuginfo(
&mut self,
stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
location: Location
) {
match stmt_debuginfo {
StmtDebugInfo::AssignRef(local, place) => {
self.visit_local(
$(& $mutability)? *local,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
);
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
);
},
StmtDebugInfo::InvalidAssign(local) => {
self.visit_local(
$(& $mutability)? *local,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
);
}
}
}
fn super_statement(
&mut self,
statement: & $($mutability)? Statement<'tcx>,
location: Location
) {
let Statement { source_info, kind } = statement;
let Statement { source_info, kind, debuginfos } = statement;
self.visit_source_info(source_info);
for debuginfo in debuginfos as & $($mutability)? [_] {
self.visit_statement_debuginfo(debuginfo, location);
}
match kind {
StatementKind::Assign(box (place, rvalue)) => {
self.visit_assign(place, rvalue, location);

View file

@ -0,0 +1,134 @@
//! Helper functions that serve as the immediate implementation of
//! `tcx.$query(..)` and its variations.
use std::fmt::Debug;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_query_system::dep_graph::{DepKind, DepNodeParams};
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use crate::dep_graph;
use crate::query::IntoQueryParam;
use crate::query::erase::{self, Erase, EraseType};
use crate::ty::TyCtxt;
/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
/// for all queries.
#[inline(always)]
pub(crate) fn query_get_at<'tcx, Cache>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
span: Span,
key: Cache::Key,
) -> Cache::Value
where
Cache: QueryCache,
{
let key = key.into_query_param();
match try_get_cached(tcx, query_cache, &key) {
Some(value) => value,
None => execute_query(tcx, span, key, QueryMode::Get).unwrap(),
}
}
/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries,
/// and `tcx.ensure_done().$query(..)` for all queries.
#[inline]
pub(crate) fn query_ensure<'tcx, Cache>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
key: Cache::Key,
check_cache: bool,
) where
Cache: QueryCache,
{
let key = key.into_query_param();
if try_get_cached(tcx, query_cache, &key).is_none() {
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache });
}
}
/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that
/// have the `return_result_from_ensure_ok` modifier.
#[inline]
pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
key: Cache::Key,
check_cache: bool,
) -> Result<(), ErrorGuaranteed>
where
Cache: QueryCache<Value = Erase<Result<T, ErrorGuaranteed>>>,
Result<T, ErrorGuaranteed>: EraseType,
{
let key = key.into_query_param();
if let Some(res) = try_get_cached(tcx, query_cache, &key) {
erase::restore(res).map(drop)
} else {
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
.map(erase::restore)
.map(|res| res.map(drop))
// Either we actually executed the query, which means we got a full `Result`,
// or we can just assume the query succeeded, because it was green in the
// incremental cache. If it is green, that means that the previous compilation
// that wrote to the incremental cache compiles successfully. That is only
// possible if the cache entry was `Ok(())`, so we emit that here, without
// actually encoding the `Result` in the cache or loading it from there.
.unwrap_or(Ok(()))
}
}
/// Common implementation of query feeding, used by `define_feedable!`.
pub(crate) fn query_feed<'tcx, Cache, Value>(
tcx: TyCtxt<'tcx>,
dep_kind: DepKind,
hasher: Option<fn(&mut StableHashingContext<'_>, &Value) -> Fingerprint>,
cache: &Cache,
key: Cache::Key,
erased: Erase<Value>,
) where
Cache: QueryCache<Value = Erase<Value>>,
Cache::Key: DepNodeParams<TyCtxt<'tcx>>,
Value: EraseType + Debug,
{
let value = erase::restore::<Value>(erased);
match try_get_cached(tcx, cache, &key) {
Some(old) => {
let old = erase::restore::<Value>(old);
if let Some(hasher) = hasher {
let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx
.with_stable_hashing_context(|mut hcx| {
(hasher(&mut hcx, &value), hasher(&mut hcx, &old))
});
if old_hash != value_hash {
// We have an inconsistency. This can happen if one of the two
// results is tainted by errors. In this case, delay a bug to
// ensure compilation is doomed, and keep the `old` value.
tcx.dcx().delayed_bug(format!(
"Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
old value: {old:?}\nnew value: {value:?}",
));
}
} else {
// The query is `no_hash`, so we have no way to perform a sanity check.
// If feeding the same value multiple times needs to be supported,
// the query should not be marked `no_hash`.
bug!(
"Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
old value: {old:?}\nnew value: {value:?}",
)
}
}
None => {
let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key);
let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher);
cache.complete(key, erased, dep_node_index);
}
}
}

View file

@ -70,7 +70,6 @@ use std::sync::Arc;
use rustc_abi::Align;
use rustc_arena::TypedArena;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::steal::Steal;
@ -88,9 +87,7 @@ use rustc_index::IndexVec;
use rustc_lint_defs::LintId;
use rustc_macros::rustc_queries;
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached,
};
use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState};
use rustc_session::Limits;
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::cstore::{
@ -103,6 +100,8 @@ use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_target::spec::{PanicStrategy, SanitizerSet};
use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir};
pub use self::keys::{AsLocalKey, Key, LocalCrate};
pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk};
use crate::infer::canonical::{self, Canonical};
use crate::lint::LintExpectation;
use crate::metadata::ModChild;
@ -119,9 +118,7 @@ use crate::mir::interpret::{
};
use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions};
use crate::query::erase::{Erase, erase, restore};
use crate::query::plumbing::{
CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at,
};
use crate::query::plumbing::{CyclePlaceholder, DynamicQuery};
use crate::traits::query::{
CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal,
CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal,
@ -145,12 +142,11 @@ use crate::{dep_graph, mir, thir};
mod arena_cached;
pub mod erase;
pub(crate) mod inner;
mod keys;
pub use keys::{AsLocalKey, Key, LocalCrate};
pub mod on_disk_cache;
#[macro_use]
pub mod plumbing;
pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk};
// Each of these queries corresponds to a function pointer field in the
// `Providers` struct for requesting a value of that type, and a method

View file

@ -8,7 +8,8 @@ use rustc_query_system::HandleCycleError;
use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex};
pub(crate) use rustc_query_system::query::QueryJobId;
use rustc_query_system::query::*;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_span::{ErrorGuaranteed, Span};
pub use sealed::IntoQueryParam;
use crate::dep_graph;
use crate::dep_graph::DepKind;
@ -165,78 +166,17 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
#[inline(always)]
pub fn query_get_at<'tcx, Cache>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
span: Span,
key: Cache::Key,
) -> Cache::Value
where
Cache: QueryCache,
{
let key = key.into_query_param();
match try_get_cached(tcx, query_cache, &key) {
Some(value) => value,
None => execute_query(tcx, span, key, QueryMode::Get).unwrap(),
}
}
#[inline]
pub fn query_ensure<'tcx, Cache>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
key: Cache::Key,
check_cache: bool,
) where
Cache: QueryCache,
{
let key = key.into_query_param();
if try_get_cached(tcx, query_cache, &key).is_none() {
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache });
}
}
#[inline]
pub fn query_ensure_error_guaranteed<'tcx, Cache, T>(
tcx: TyCtxt<'tcx>,
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
query_cache: &Cache,
key: Cache::Key,
check_cache: bool,
) -> Result<(), ErrorGuaranteed>
where
Cache: QueryCache<Value = super::erase::Erase<Result<T, ErrorGuaranteed>>>,
Result<T, ErrorGuaranteed>: EraseType,
{
let key = key.into_query_param();
if let Some(res) = try_get_cached(tcx, query_cache, &key) {
super::erase::restore(res).map(drop)
} else {
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
.map(super::erase::restore)
.map(|res| res.map(drop))
// Either we actually executed the query, which means we got a full `Result`,
// or we can just assume the query succeeded, because it was green in the
// incremental cache. If it is green, that means that the previous compilation
// that wrote to the incremental cache compiles successfully. That is only
// possible if the cache entry was `Ok(())`, so we emit that here, without
// actually encoding the `Result` in the cache or loading it from there.
.unwrap_or(Ok(()))
}
}
macro_rules! query_ensure {
/// Calls either `query_ensure` or `query_ensure_error_guaranteed`, depending
/// on whether the list of modifiers contains `return_result_from_ensure_ok`.
macro_rules! query_ensure_select {
([]$($args:tt)*) => {
query_ensure($($args)*)
crate::query::inner::query_ensure($($args)*)
};
([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => {
query_ensure_error_guaranteed($($args)*).map(|_| ())
crate::query::inner::query_ensure_error_guaranteed($($args)*)
};
([$other:tt $($modifiers:tt)*]$($args:tt)*) => {
query_ensure!([$($modifiers)*]$($args)*)
query_ensure_select!([$($modifiers)*]$($args)*)
};
}
@ -432,7 +372,7 @@ macro_rules! define_callbacks {
self,
key: query_helper_param_ty!($($K)*),
) -> ensure_ok_result!([$($modifiers)*]) {
query_ensure!(
query_ensure_select!(
[$($modifiers)*]
self.tcx,
self.tcx.query_system.fns.engine.$name,
@ -447,7 +387,7 @@ macro_rules! define_callbacks {
$($(#[$attr])*
#[inline(always)]
pub fn $name(self, key: query_helper_param_ty!($($K)*)) {
query_ensure(
crate::query::inner::query_ensure(
self.tcx,
self.tcx.query_system.fns.engine.$name,
&self.tcx.query_system.caches.$name,
@ -472,7 +412,7 @@ macro_rules! define_callbacks {
#[inline(always)]
pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V
{
restore::<$V>(query_get_at(
restore::<$V>(crate::query::inner::query_get_at(
self.tcx,
self.tcx.query_system.fns.engine.$name,
&self.tcx.query_system.caches.$name,
@ -565,48 +505,19 @@ macro_rules! define_feedable {
let tcx = self.tcx;
let erased = queries::$name::provided_to_erased(tcx, value);
let value = restore::<$V>(erased);
let cache = &tcx.query_system.caches.$name;
let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name;
let hasher: Option<fn(&mut StableHashingContext<'_>, &_) -> _> = hash_result!([$($modifiers)*]);
match try_get_cached(tcx, cache, &key) {
Some(old) => {
let old = restore::<$V>(old);
if let Some(hasher) = hasher {
let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx|
(hasher(&mut hcx, &value), hasher(&mut hcx, &old))
);
if old_hash != value_hash {
// We have an inconsistency. This can happen if one of the two
// results is tainted by errors. In this case, delay a bug to
// ensure compilation is doomed, and keep the `old` value.
tcx.dcx().delayed_bug(format!(
"Trying to feed an already recorded value for query {} key={key:?}:\n\
old value: {old:?}\nnew value: {value:?}",
stringify!($name),
));
}
} else {
// The query is `no_hash`, so we have no way to perform a sanity check.
// If feeding the same value multiple times needs to be supported,
// the query should not be marked `no_hash`.
bug!(
"Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
stringify!($name),
)
}
}
None => {
let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::dep_kinds::$name, &key);
let dep_node_index = tcx.dep_graph.with_feed_task(
dep_node,
tcx,
&value,
hash_result!([$($modifiers)*]),
);
cache.complete(key, erased, dep_node_index);
}
}
$crate::query::inner::query_feed(
tcx,
dep_kind,
hasher,
cache,
key,
erased,
);
}
})*
}
@ -694,10 +605,6 @@ mod sealed {
}
}
pub use sealed::IntoQueryParam;
use super::erase::EraseType;
#[derive(Copy, Clone, Debug, HashStable)]
pub struct CyclePlaceholder(pub ErrorGuaranteed);

View file

@ -839,7 +839,7 @@ pub enum PatKind<'tcx> {
/// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are
/// much simpler.
/// * raw pointers derived from integers, other raw pointers will have already resulted in an
// error.
/// error.
/// * `String`, if `string_deref_patterns` is enabled.
Constant {
value: ty::Value<'tcx>,

View file

@ -207,8 +207,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
from_entry(entry)
}
fn evaluation_is_concurrent(&self) -> bool {
self.sess.threads() > 1
fn assert_evaluation_is_concurrent(&self) {
// Turns out, the assumption for this function isn't perfect.
// See trait-system-refactor-initiative#234.
}
fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {

View file

@ -618,11 +618,11 @@ impl<'tcx> Instance<'tcx> {
// be directly reified because it's closure-like. The reify can handle the
// unresolved instance.
resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
// Reify `Trait::method` implementations
// FIXME(maurer) only reify it if it is a vtable-safe function
// Reify `Trait::method` implementations if the trait is dyn-compatible.
} else if let Some(assoc) = tcx.opt_associated_item(def_id)
&& let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) =
assoc.container
&& tcx.is_dyn_compatible(assoc.container_id(tcx))
{
// If this function could also go in a vtable, we need to `ReifyShim` it with
// KCFI because it can only attach one type per function.

View file

@ -689,7 +689,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, "kind: PatKind {", depth_lvl);
match pat_kind {
PatKind::Missing => unreachable!(),
PatKind::Missing => {
print_indented!(self, "Missing", depth_lvl + 1);
}
PatKind::Wild => {
print_indented!(self, "Wild", depth_lvl + 1);
}

View file

@ -210,6 +210,7 @@ impl DefUse {
/// All of the caveats of `MaybeLiveLocals` apply.
pub struct MaybeTransitiveLiveLocals<'a> {
always_live: &'a DenseBitSet<Local>,
debuginfo_locals: &'a DenseBitSet<Local>,
}
impl<'a> MaybeTransitiveLiveLocals<'a> {
@ -217,8 +218,48 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
/// considered live.
///
/// This should include at least all locals that are ever borrowed.
pub fn new(always_live: &'a DenseBitSet<Local>) -> Self {
MaybeTransitiveLiveLocals { always_live }
pub fn new(
always_live: &'a DenseBitSet<Local>,
debuginfo_locals: &'a DenseBitSet<Local>,
) -> Self {
MaybeTransitiveLiveLocals { always_live, debuginfo_locals }
}
pub fn can_be_removed_if_dead<'tcx>(
stmt_kind: &StatementKind<'tcx>,
always_live: &DenseBitSet<Local>,
debuginfo_locals: &'a DenseBitSet<Local>,
) -> Option<Place<'tcx>> {
// Compute the place that we are storing to, if any
let destination = match stmt_kind {
StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove()
// FIXME: We are not sure how we should represent this debugging information for some statements,
// keep it for now.
&& (!debuginfo_locals.contains(place.local)
|| (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some())))
.then_some(*place),
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
(!debuginfo_locals.contains(place.local)).then_some(**place)
}
StatementKind::FakeRead(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(..)
| StatementKind::AscribeUserType(..)
| StatementKind::PlaceMention(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
| StatementKind::ConstEvalCounter
| StatementKind::BackwardIncompatibleDropHint { .. }
| StatementKind::Nop => None,
};
if let Some(destination) = destination
&& !destination.is_indirect()
&& !always_live.contains(destination.local)
{
return Some(destination);
}
None
}
}
@ -243,32 +284,12 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
statement: &mir::Statement<'tcx>,
location: Location,
) {
// Compute the place that we are storing to, if any
let destination = match &statement.kind {
StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0),
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
Some(**place)
}
StatementKind::FakeRead(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(..)
| StatementKind::AscribeUserType(..)
| StatementKind::PlaceMention(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
| StatementKind::ConstEvalCounter
| StatementKind::BackwardIncompatibleDropHint { .. }
| StatementKind::Nop => None,
};
if let Some(destination) = destination {
if !destination.is_indirect()
&& !state.contains(destination.local)
&& !self.always_live.contains(destination.local)
{
// This store is dead
return;
}
if let Some(destination) =
Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals)
&& !state.contains(destination.local)
{
// This store is dead
return;
}
TransferFunction(state).visit_statement(statement, location);
}

View file

@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck {
CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. },
)
| StatementKind::FakeRead(..)
| StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(),
| StatementKind::BackwardIncompatibleDropHint { .. } => {
statement.make_nop(true)
}
StatementKind::Assign(box (
_,
Rvalue::Cast(

View file

@ -138,7 +138,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind
&& self.storage_to_remove.contains(l)
{
stmt.make_nop();
stmt.make_nop(true);
return;
}
@ -150,7 +150,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
*rhs
&& lhs == rhs
{
stmt.make_nop();
stmt.make_nop(true);
}
}
}

View file

@ -411,7 +411,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind
&& self.remap.contains(l)
{
s.make_nop();
s.make_nop(true);
}
}

View file

@ -22,21 +22,22 @@ use rustc_mir_dataflow::impls::{
LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals,
};
use crate::simplify::UsedInStmtLocals;
use crate::util::is_within_packed;
/// Performs the optimization on the body
///
/// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this
/// body. It can be generated via the [`borrowed_locals`] function.
fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
/// Returns true if any instruction is eliminated.
fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
let borrowed_locals = borrowed_locals(body);
// If the user requests complete debuginfo, mark the locals that appear in it as live, so
// we don't remove assignments to them.
let mut always_live = debuginfo_locals(body);
always_live.union(&borrowed_locals);
let debuginfo_locals = debuginfo_locals(body);
let mut live = MaybeTransitiveLiveLocals::new(&always_live)
let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals)
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
@ -75,47 +76,36 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
}
for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
let loc = Location { block: bb, statement_index };
if let StatementKind::Assign(assign) = &statement.kind {
if !assign.1.is_safe_to_remove() {
continue;
}
}
match &statement.kind {
StatementKind::Assign(box (place, _))
| StatementKind::SetDiscriminant { place: box place, .. }
| StatementKind::Deinit(box place) => {
if !place.is_indirect() && !always_live.contains(place.local) {
live.seek_before_primary_effect(loc);
if !live.get().contains(place.local) {
patch.push(loc);
}
}
}
StatementKind::Retag(_, _)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Coverage(_)
| StatementKind::Intrinsic(_)
| StatementKind::ConstEvalCounter
| StatementKind::PlaceMention(_)
| StatementKind::BackwardIncompatibleDropHint { .. }
| StatementKind::Nop => {}
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
bug!("{:?} not found in this MIR phase!", statement.kind)
if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead(
&statement.kind,
&borrowed_locals,
&debuginfo_locals,
) {
let loc = Location { block: bb, statement_index };
live.seek_before_primary_effect(loc);
if !live.get().contains(destination.local) {
let drop_debuginfo = !debuginfo_locals.contains(destination.local);
// When eliminating a dead statement, we need to address
// the debug information for that statement.
assert!(
drop_debuginfo || statement.kind.as_debuginfo().is_some(),
"don't know how to retain the debug information for {:?}",
statement.kind
);
patch.push((loc, drop_debuginfo));
}
}
}
}
if patch.is_empty() && call_operands_to_move.is_empty() {
return;
return false;
}
let eliminated = !patch.is_empty();
let bbs = body.basic_blocks.as_mut_preserves_cfg();
for Location { block, statement_index } in patch {
bbs[block].statements[statement_index].make_nop();
for (Location { block, statement_index }, drop_debuginfo) in patch {
bbs[block].statements[statement_index].make_nop(drop_debuginfo);
}
for (block, argument_index) in call_operands_to_move {
let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
@ -125,6 +115,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Operand::Copy(place) = *arg else { bug!() };
*arg = Operand::Move(place);
}
eliminated
}
pub(super) enum DeadStoreElimination {
@ -145,7 +137,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
eliminate(tcx, body);
if eliminate(tcx, body) {
UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
for data in body.basic_blocks.as_mut_preserves_cfg() {
data.strip_nops();
}
}
}
fn is_required(&self) -> bool {

View file

@ -276,7 +276,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
StatementKind::StorageDead(local) | StatementKind::StorageLive(local)
if self.merged_locals.contains(*local) =>
{
statement.make_nop();
statement.make_nop(true);
return;
}
_ => (),
@ -291,7 +291,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
// (this includes the original statement we wanted to eliminate).
if dest == place {
debug!("{:?} turned into self-assignment, deleting", location);
statement.make_nop();
statement.make_nop(true);
}
}
_ => {}

View file

@ -91,6 +91,7 @@ use either::Either;
use hashbrown::hash_table::{Entry, HashTable};
use itertools::Itertools as _;
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
use rustc_arena::DroplessArena;
use rustc_const_eval::const_eval::DummyMachine;
use rustc_const_eval::interpret::{
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
@ -129,7 +130,9 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
// Clone dominators because we need them while mutating the body.
let dominators = body.basic_blocks.dominators().clone();
let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
let arena = DroplessArena::default();
let mut state =
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
let opaque = state.new_opaque(body.local_decls[local].ty);
@ -183,8 +186,16 @@ enum AddressKind {
Address(RawPtrKind),
}
#[derive(Debug, PartialEq, Eq, Hash)]
enum Value<'tcx> {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum AddressBase {
/// This address is based on this local.
Local(Local),
/// This address is based on the deref of this pointer.
Deref(VnIndex),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum Value<'a, 'tcx> {
// Root values.
/// Used to represent values we know nothing about.
/// The `usize` is a counter incremented by `new_opaque`.
@ -201,7 +212,9 @@ enum Value<'tcx> {
// Aggregates.
/// An aggregate value, either tuple/closure/struct/enum.
/// This does not contain unions, as we cannot reason with the value.
Aggregate(VariantIdx, Vec<VnIndex>),
Aggregate(VariantIdx, &'a [VnIndex]),
/// A union aggregate value.
Union(FieldIdx, VnIndex),
/// A raw pointer aggregate built from a thin pointer and metadata.
RawPtr {
/// Thin pointer component. This is field 0 in MIR.
@ -213,7 +226,10 @@ enum Value<'tcx> {
Repeat(VnIndex, ty::Const<'tcx>),
/// The address of a place.
Address {
place: Place<'tcx>,
base: AddressBase,
// We do not use a plain `Place` as we want to be able to reason about indices.
// This does not contain any `Deref` projection.
projection: &'a [ProjectionElem<VnIndex, Ty<'tcx>>],
kind: AddressKind,
/// Give each borrow and pointer a different provenance, so we don't merge them.
provenance: VnOpaque,
@ -240,15 +256,15 @@ enum Value<'tcx> {
/// This data structure is mostly a partial reimplementation of `FxIndexMap<VnIndex, (Value, Ty)>`.
/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction,
/// like opaque values, address with provenance and non-deterministic constants.
struct ValueSet<'tcx> {
struct ValueSet<'a, 'tcx> {
indices: HashTable<VnIndex>,
hashes: IndexVec<VnIndex, u64>,
values: IndexVec<VnIndex, Value<'tcx>>,
values: IndexVec<VnIndex, Value<'a, 'tcx>>,
types: IndexVec<VnIndex, Ty<'tcx>>,
}
impl<'tcx> ValueSet<'tcx> {
fn new(num_values: usize) -> ValueSet<'tcx> {
impl<'a, 'tcx> ValueSet<'a, 'tcx> {
fn new(num_values: usize) -> ValueSet<'a, 'tcx> {
ValueSet {
indices: HashTable::with_capacity(num_values),
hashes: IndexVec::with_capacity(num_values),
@ -263,7 +279,7 @@ impl<'tcx> ValueSet<'tcx> {
fn insert_unique(
&mut self,
ty: Ty<'tcx>,
value: impl FnOnce(VnOpaque) -> Value<'tcx>,
value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>,
) -> VnIndex {
let value = value(VnOpaque);
@ -284,7 +300,7 @@ impl<'tcx> ValueSet<'tcx> {
/// Insert a `(Value, Ty)` pair to be deduplicated.
/// Returns `true` as second tuple field if this value did not exist previously.
#[allow(rustc::pass_by_value)] // closures take `&VnIndex`
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) {
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) {
debug_assert!(match value {
Value::Opaque(_) | Value::Address { .. } => false,
Value::Constant { disambiguator, .. } => disambiguator.is_none(),
@ -319,8 +335,8 @@ impl<'tcx> ValueSet<'tcx> {
/// Return the `Value` associated with the given `VnIndex`.
#[inline]
fn value(&self, index: VnIndex) -> &Value<'tcx> {
&self.values[index]
fn value(&self, index: VnIndex) -> Value<'a, 'tcx> {
self.values[index]
}
/// Return the type associated with the given `VnIndex`.
@ -336,7 +352,7 @@ impl<'tcx> ValueSet<'tcx> {
}
}
struct VnState<'body, 'tcx> {
struct VnState<'body, 'a, 'tcx> {
tcx: TyCtxt<'tcx>,
ecx: InterpCx<'tcx, DummyMachine>,
local_decls: &'body LocalDecls<'tcx>,
@ -346,7 +362,7 @@ struct VnState<'body, 'tcx> {
/// Locals that are assigned that value.
// This vector does not hold all the values of `VnIndex` that we create.
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
values: ValueSet<'tcx>,
values: ValueSet<'a, 'tcx>,
/// Values evaluated as constants if possible.
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
/// Cache the deref values.
@ -354,9 +370,10 @@ struct VnState<'body, 'tcx> {
ssa: &'body SsaLocals,
dominators: Dominators<BasicBlock>,
reused_locals: DenseBitSet<Local>,
arena: &'a DroplessArena,
}
impl<'body, 'tcx> VnState<'body, 'tcx> {
impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
fn new(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
@ -364,6 +381,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ssa: &'body SsaLocals,
dominators: Dominators<BasicBlock>,
local_decls: &'body LocalDecls<'tcx>,
arena: &'a DroplessArena,
) -> Self {
// Compute a rough estimate of the number of values in the body from the number of
// statements. This is meant to reduce the number of allocations, but it's all right if
@ -385,6 +403,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ssa,
dominators,
reused_locals: DenseBitSet::new_empty(local_decls.len()),
arena,
}
}
@ -393,7 +412,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
#[instrument(level = "trace", skip(self), ret)]
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex {
let (index, new) = self.values.insert(ty, value);
if new {
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
@ -420,7 +439,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
/// Create a new `Value::Address` distinct from all the others.
#[instrument(level = "trace", skip(self), ret)]
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
let pty = place.ty(self.local_decls, self.tcx).ty;
let ty = match kind {
AddressKind::Ref(bk) => {
@ -428,14 +447,34 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
};
let index =
self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance });
let mut projection = place.projection.iter();
let base = if place.is_indirect_first_projection() {
let base = self.locals[place.local]?;
// Skip the initial `Deref`.
projection.next();
AddressBase::Deref(base)
} else {
AddressBase::Local(place.local)
};
// Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
let projection =
projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(()));
let projection = self.arena.try_alloc_from_iter(projection).ok()?;
let index = self.values.insert_unique(ty, |provenance| Value::Address {
base,
projection,
kind,
provenance,
});
let evaluated = self.eval_to_const(index);
let _index = self.evaluated.push(evaluated);
debug_assert_eq!(index, _index);
let _index = self.rev_locals.push(SmallVec::new());
debug_assert_eq!(index, _index);
index
Some(index)
}
#[instrument(level = "trace", skip(self), ret)]
@ -464,7 +503,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
#[inline]
fn get(&self, index: VnIndex) -> &Value<'tcx> {
fn get(&self, index: VnIndex) -> Value<'a, 'tcx> {
self.values.value(index)
}
@ -495,8 +534,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
self.insert(ty, Value::Constant { value, disambiguator: None })
}
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values))
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex {
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values)))
}
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
@ -521,7 +560,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
} else {
return None;
};
let op = match *self.get(value) {
let op = match self.get(value) {
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
Opaque(_) => return None,
@ -563,6 +602,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
return None;
}
}
Union(active_field, field) => {
let field = self.evaluated[field].as_ref()?;
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
{
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?;
self.ecx.copy_op(field, &field_dest).discard_err()?;
self.ecx
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
.discard_err()?;
dest.into()
} else {
return None;
}
}
RawPtr { pointer, metadata } => {
let pointer = self.evaluated[pointer].as_ref()?;
let metadata = self.evaluated[metadata].as_ref()?;
@ -585,14 +639,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let elem = elem.try_map(|_| None, |()| ty.ty)?;
self.ecx.project(base, elem).discard_err()?
}
Address { place, kind: _, provenance: _ } => {
if !place.is_indirect_first_projection() {
return None;
}
let local = self.locals[place.local]?;
let pointer = self.evaluated[local].as_ref()?;
Address { base, projection, .. } => {
debug_assert!(!projection.contains(&ProjectionElem::Deref));
let pointer = match base {
AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?,
// We have no stack to point to.
AddressBase::Local(_) => return None,
};
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
for elem in place.projection.iter().skip(1) {
for elem in projection {
// `Index` by constants should have been replaced by `ConstantIndex` by
// `simplify_place_projection`.
let elem = elem.try_map(|_| None, |ty| ty)?;
@ -711,12 +766,38 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
Some(op)
}
/// Represent the *value* we obtain by dereferencing an `Address` value.
#[instrument(level = "trace", skip(self), ret)]
fn dereference_address(
&mut self,
base: AddressBase,
projection: &[ProjectionElem<VnIndex, Ty<'tcx>>],
) -> Option<VnIndex> {
let (mut place_ty, mut value) = match base {
// The base is a local, so we take the local's value and project from it.
AddressBase::Local(local) => {
let local = self.locals[local]?;
let place_ty = PlaceTy::from_ty(self.ty(local));
(place_ty, local)
}
// The base is a pointer's deref, so we introduce the implicit deref.
AddressBase::Deref(reborrow) => {
let place_ty = PlaceTy::from_ty(self.ty(reborrow));
self.project(place_ty, reborrow, ProjectionElem::Deref)?
}
};
for &proj in projection {
(place_ty, value) = self.project(place_ty, value, proj)?;
}
Some(value)
}
#[instrument(level = "trace", skip(self), ret)]
fn project(
&mut self,
place_ty: PlaceTy<'tcx>,
value: VnIndex,
proj: PlaceElem<'tcx>,
from_non_ssa_index: &mut bool,
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
) -> Option<(PlaceTy<'tcx>, VnIndex)> {
let projection_ty = place_ty.projection_ty(self.tcx, proj);
let proj = match proj {
@ -724,6 +805,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
&& projection_ty.ty.is_freeze(self.tcx, self.typing_env())
{
if let Value::Address { base, projection, .. } = self.get(value)
&& let Some(value) = self.dereference_address(base, projection)
{
return Some((projection_ty, value));
}
// An immutable borrow `_x` always points to the same value for the
// lifetime of the borrow, so we can merge all instances of `*_x`.
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
@ -732,11 +819,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
ProjectionElem::Field(f, _) => {
if let Value::Aggregate(_, fields) = self.get(value) {
return Some((projection_ty, fields[f.as_usize()]));
} else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
&& let Value::Aggregate(written_variant, fields) = self.get(*outer_value)
ProjectionElem::Field(f, _) => match self.get(value) {
Value::Aggregate(_, fields) => return Some((projection_ty, fields[f.as_usize()])),
Value::Union(active, field) if active == f => return Some((projection_ty, field)),
Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant))
if let Value::Aggregate(written_variant, fields) = self.get(outer_value)
// This pass is not aware of control-flow, so we do not know whether the
// replacement we are doing is actually reachable. We could be in any arm of
// ```
@ -752,24 +839,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// accessing the wrong variant is not UB if the enum has repr.
// So it's not impossible for a series of MIR opts to generate
// a downcast to an inactive variant.
&& written_variant == read_variant
&& written_variant == read_variant =>
{
return Some((projection_ty, fields[f.as_usize()]));
}
ProjectionElem::Field(f, ())
}
_ => ProjectionElem::Field(f, ()),
},
ProjectionElem::Index(idx) => {
if let Value::Repeat(inner, _) = self.get(value) {
*from_non_ssa_index |= self.locals[idx].is_none();
return Some((projection_ty, *inner));
return Some((projection_ty, inner));
}
let idx = self.locals[idx]?;
ProjectionElem::Index(idx)
}
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
match self.get(value) {
Value::Repeat(inner, _) => {
return Some((projection_ty, *inner));
return Some((projection_ty, inner));
}
Value::Aggregate(_, operands) => {
let offset = if from_end {
@ -838,6 +923,42 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
trace!(?place);
}
/// Represent the *value* which would be read from `place`. If we succeed, return it.
/// If we fail, return a `PlaceRef` that contains the same value.
#[instrument(level = "trace", skip(self), ret)]
fn compute_place_value(
&mut self,
place: Place<'tcx>,
location: Location,
) -> Result<VnIndex, PlaceRef<'tcx>> {
// Invariant: `place` and `place_ref` point to the same value, even if they point to
// different memory locations.
let mut place_ref = place.as_ref();
// Invariant: `value` holds the value up-to the `index`th projection excluded.
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
for (index, proj) in place.projection.iter().enumerate() {
if let Some(local) = self.try_as_local(value, location) {
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
// hold the same value. Therefore, following place holds the value in the original
// `place`.
place_ref = PlaceRef { local, projection: &place.projection[index..] };
}
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
return Err(place_ref);
};
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
return Err(place_ref);
};
(place_ty, value) = ty_and_value;
}
Ok(value)
}
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
/// place with the same value (if that already exists).
#[instrument(level = "trace", skip(self), ret)]
@ -848,67 +969,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
) -> Option<VnIndex> {
self.simplify_place_projection(place, location);
// Invariant: `place` and `place_ref` point to the same value, even if they point to
// different memory locations.
let mut place_ref = place.as_ref();
// Invariant: `value` holds the value up-to the `index`th projection excluded.
let mut value = self.locals[place.local]?;
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
let mut from_non_ssa_index = false;
for (index, proj) in place.projection.iter().enumerate() {
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
&& let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer)
&& let AddressKind::Ref(BorrowKind::Shared) = kind
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
{
value = v;
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
// That local is SSA, but we otherwise have no guarantee on that local's value at
// the current location compared to its value where `pointee` was borrowed.
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
place_ref =
pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
match self.compute_place_value(*place, location) {
Ok(value) => {
if let Some(new_place) = self.try_as_place(value, location, true)
&& (new_place.local != place.local
|| new_place.projection.len() < place.projection.len())
{
*place = new_place;
self.reused_locals.insert(new_place.local);
}
Some(value)
}
if let Some(local) = self.try_as_local(value, location) {
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
// hold the same value. Therefore, following place holds the value in the original
// `place`.
place_ref = PlaceRef { local, projection: &place.projection[index..] };
}
(place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
}
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
&& let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer)
&& let AddressKind::Ref(BorrowKind::Shared) = kind
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
{
value = v;
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
// That local is SSA, but we otherwise have no guarantee on that local's value at
// the current location compared to its value where `pointee` was borrowed.
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
Err(place_ref) => {
if place_ref.local != place.local
|| place_ref.projection.len() < place.projection.len()
{
// By the invariant on `place_ref`.
*place = place_ref.project_deeper(&[], self.tcx);
self.reused_locals.insert(place_ref.local);
}
None
}
}
if let Some(new_local) = self.try_as_local(value, location) {
place_ref = PlaceRef { local: new_local, projection: &[] };
} else if from_non_ssa_index {
// If access to non-SSA locals is unavoidable, bail out.
return None;
}
if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() {
// By the invariant on `place_ref`.
*place = place_ref.project_deeper(&[], self.tcx);
self.reused_locals.insert(place_ref.local);
}
Some(value)
}
#[instrument(level = "trace", skip(self), ret)]
@ -955,11 +1037,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
Rvalue::Ref(_, borrow_kind, ref mut place) => {
self.simplify_place_projection(place, location);
return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
}
Rvalue::RawPtr(mutbl, ref mut place) => {
self.simplify_place_projection(place, location);
return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
return self.new_pointer(*place, AddressKind::Address(mutbl));
}
Rvalue::WrapUnsafeBinder(ref mut op, _) => {
let value = self.simplify_operand(op, location)?;
@ -994,7 +1076,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
let enum_ty = self.ty(place);
if enum_ty.is_enum()
&& let Value::Aggregate(variant, _) = *self.get(place)
&& let Value::Aggregate(variant, _) = self.get(place)
{
let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?;
return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar()));
@ -1026,11 +1108,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fields: &[VnIndex],
) -> Option<VnIndex> {
let Some(&first_field) = fields.first() else { return None };
let Value::Projection(copy_from_value, _) = *self.get(first_field) else { return None };
let Value::Projection(copy_from_value, _) = self.get(first_field) else { return None };
// All fields must correspond one-to-one and come from the same aggregate value.
if fields.iter().enumerate().any(|(index, &v)| {
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v)
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = self.get(v)
&& copy_from_value == pointer
&& from_index.index() == index
{
@ -1042,7 +1124,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
let mut copy_from_local_value = copy_from_value;
if let Value::Projection(pointer, proj) = *self.get(copy_from_value)
if let Value::Projection(pointer, proj) = self.get(copy_from_value)
&& let ProjectionElem::Downcast(_, read_variant) = proj
{
if variant_index == read_variant {
@ -1087,13 +1169,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}
let fields: Vec<_> = field_ops
.iter_mut()
.map(|op| {
self.simplify_operand(op, location)
.unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx)))
})
.collect();
let fields = self.arena.alloc_from_iter(field_ops.iter_mut().map(|op| {
self.simplify_operand(op, location)
.unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx)))
}));
let variant_index = match *kind {
AggregateKind::Array(..) | AggregateKind::Tuple => {
@ -1105,7 +1184,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
| AggregateKind::Coroutine(..) => FIRST_VARIANT,
AggregateKind::Adt(_, variant_index, _, _, None) => variant_index,
// Do not track unions.
AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
AggregateKind::Adt(_, _, _, _, Some(active_field)) => {
let field = *fields.first()?;
return Some(self.insert(ty, Value::Union(active_field, field)));
}
AggregateKind::RawPtr(..) => {
assert_eq!(field_ops.len(), 2);
let [mut pointer, metadata] = fields.try_into().unwrap();
@ -1114,12 +1196,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let mut was_updated = false;
while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } =
self.get(pointer)
&& let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind()
&& let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(cast_value).kind()
&& let ty::RawPtr(_, output_mtbl) = ty.kind()
&& from_mtbl == output_mtbl
&& from_pointee_ty.is_sized(self.tcx, self.typing_env())
{
pointer = *cast_value;
pointer = cast_value;
was_updated = true;
}
@ -1184,16 +1266,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`,
// it's fine to get a projection as the type.
Value::Cast { kind: CastKind::PtrToPtr, value: inner }
if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) =>
if self.pointers_have_same_metadata(self.ty(inner), arg_ty) =>
{
*inner
inner
}
// We have an unsizing cast, which assigns the length to wide pointer metadata.
Value::Cast {
kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _),
value: from,
} if let Some(from) = self.ty(*from).builtin_deref(true)
} if let Some(from) = self.ty(from).builtin_deref(true)
&& let ty::Array(_, len) = from.kind()
&& let Some(to) = self.ty(arg_index).builtin_deref(true)
&& let ty::Slice(..) = to.kind() =>
@ -1202,12 +1284,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
// `&mut *p`, `&raw *p`, etc don't change metadata.
Value::Address { place, kind: _, provenance: _ }
if let PlaceRef { local, projection: [PlaceElem::Deref] } =
place.as_ref()
&& let Some(local_index) = self.locals[local] =>
Value::Address { base: AddressBase::Deref(reborrowed), projection, .. }
if projection.is_empty() =>
{
local_index
reborrowed
}
_ => break,
@ -1221,15 +1301,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
let value = match (op, self.get(arg_index)) {
(UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner),
(UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner),
(UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(inner),
(UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(inner),
(UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => {
Value::BinaryOp(BinOp::Ne, *lhs, *rhs)
Value::BinaryOp(BinOp::Ne, lhs, rhs)
}
(UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => {
Value::BinaryOp(BinOp::Eq, *lhs, *rhs)
Value::BinaryOp(BinOp::Eq, lhs, rhs)
}
(UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata),
(UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(metadata),
// We have an unsizing cast, which assigns the length to wide pointer metadata.
(
UnOp::PtrMetadata,
@ -1238,7 +1318,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
value: inner,
},
) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind()
&& let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() =>
&& let ty::Array(_, len) = self.ty(inner).builtin_deref(true).unwrap().kind() =>
{
return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
}
@ -1271,12 +1351,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
&& lhs_ty.is_any_ptr()
&& let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs)
&& let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs)
&& let lhs_from = self.ty(*lhs_value)
&& lhs_from == self.ty(*rhs_value)
&& let lhs_from = self.ty(lhs_value)
&& lhs_from == self.ty(rhs_value)
&& self.pointers_have_same_metadata(lhs_from, lhs_ty)
{
lhs = *lhs_value;
rhs = *rhs_value;
lhs = lhs_value;
rhs = rhs_value;
if let Some(lhs_op) = self.try_as_operand(lhs, location)
&& let Some(rhs_op) = self.try_as_operand(rhs, location)
{
@ -1410,7 +1490,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if op.is_overflowing() {
let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]);
let false_val = self.insert_bool(false);
Some(self.insert_tuple(ty, vec![result, false_val]))
Some(self.insert_tuple(ty, &[result, false_val]))
} else {
Some(result)
}
@ -1463,11 +1543,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
&& let ty::RawPtr(to_pointee, _) = to.kind()
&& to_pointee.is_sized(self.tcx, self.typing_env())
{
from = self.ty(*pointer);
value = *pointer;
from = self.ty(pointer);
value = pointer;
was_updated_this_iteration = true;
if from == to {
return Some(*pointer);
return Some(pointer);
}
}
@ -1476,7 +1556,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let Transmute = kind
&& let Value::Aggregate(variant_idx, field_values) = self.get(value)
&& let Some((field_idx, field_ty)) =
self.value_is_all_in_one_field(from, *variant_idx)
self.value_is_all_in_one_field(from, variant_idx)
{
from = field_ty;
value = field_values[field_idx.as_usize()];
@ -1487,7 +1567,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
// Various cast-then-cast cases can be simplified.
if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) {
if let Value::Cast { kind: inner_kind, value: inner_value } = self.get(value) {
let inner_from = self.ty(inner_value);
let new_kind = match (inner_kind, kind) {
// Even if there's a narrowing cast in here that's fine, because
@ -1686,7 +1766,7 @@ fn op_to_prop_const<'tcx>(
None
}
impl<'tcx> VnState<'_, 'tcx> {
impl<'tcx> VnState<'_, '_, 'tcx> {
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
/// returns that result as an [`Operand`].
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
@ -1705,7 +1785,7 @@ impl<'tcx> VnState<'_, 'tcx> {
// This was already constant in MIR, do not change it. If the constant is not
// deterministic, adding an additional mention of it in MIR will not give the same value as
// the former mention.
if let Value::Constant { value, disambiguator: None } = *self.get(index) {
if let Value::Constant { value, disambiguator: None } = self.get(index) {
debug_assert!(value.is_deterministic());
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
}
@ -1749,7 +1829,7 @@ impl<'tcx> VnState<'_, 'tcx> {
// If we are here, we failed to find a local, and we already have a `Deref`.
// Trying to add projections will only result in an ill-formed place.
return None;
} else if let Value::Projection(pointer, proj) = *self.get(index)
} else if let Value::Projection(pointer, proj) = self.get(index)
&& (allow_complex_projection || proj.is_stable_offset())
&& let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc)
{
@ -1772,7 +1852,7 @@ impl<'tcx> VnState<'_, 'tcx> {
}
}
impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
@ -1877,7 +1957,7 @@ impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> {
StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
if self.reused_locals.contains(l) =>
{
stmt.make_nop()
stmt.make_nop(true)
}
_ => self.super_statement(stmt, loc),
}

View file

@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span};
use crate::cost_checker::{CostChecker, is_call_like};
use crate::deref_separator::deref_finder;
use crate::simplify::simplify_cfg;
use crate::simplify::{UsedInStmtLocals, simplify_cfg};
use crate::validate::validate_types;
use crate::{check_inline, util};
@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
in_cleanup_block: false,
return_block,
tcx,
always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()),
always_live_locals: UsedInStmtLocals::new(&callee_body).locals,
};
// Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones
@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
// people working on rust can build with or without debuginfo while
// still getting consistent results from the mir-opt tests.
caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
} else {
for bb in callee_body.basic_blocks_mut() {
bb.drop_debuginfo();
}
}
caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut());

View file

@ -156,7 +156,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
patch.add_statement(location, stmt);
}
st.make_nop();
st.make_nop(true);
}
}

View file

@ -270,7 +270,7 @@ impl<'tcx> MirPatch<'tcx> {
body.local_decls.extend(self.new_locals);
for loc in self.nop_statements {
bbs[loc.block].statements[loc.statement_index].make_nop();
bbs[loc.block].statements[loc.statement_index].make_nop(true);
}
let mut new_statements = self.new_statements;

View file

@ -1049,7 +1049,7 @@ fn promote_candidates<'tcx>(
// Eliminate assignments to, and drops of promoted temps.
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
for block in body.basic_blocks_mut() {
block.statements.retain(|statement| match &statement.kind {
block.retain_statements(|statement| match &statement.kind {
StatementKind::Assign(box (place, _)) => {
if let Some(index) = place.as_local() {
!promoted(index)

View file

@ -435,7 +435,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
if self.storage_to_remove.contains(l) =>
{
stmt.make_nop();
stmt.make_nop(true);
}
// Do not remove assignments as they may still be useful for debuginfo.
_ => self.super_statement(stmt, loc),

View file

@ -1,7 +1,7 @@
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use tracing::debug;
use tracing::{debug, instrument};
use crate::patch::MirPatch;
@ -15,6 +15,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
sess.panic_strategy().unwinds()
}
#[instrument(level = "debug", skip(self, _tcx, body))]
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let def_id = body.source.def_id();
debug!(?def_id);
@ -25,7 +26,24 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
.iter_enumerated()
.any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume));
if !has_resume {
debug!("remove_noop_landing_pads: no resume block in MIR");
debug!("no resume block in MIR");
return;
}
let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len());
// This is a post-order traversal, so that if A post-dominates B
// then A will be visited before B.
for (bb, bbdata) in traversal::postorder(body) {
let is_nop_landing_pad = self.is_nop_landing_pad(bbdata, &nop_landing_pads);
debug!("is_nop_landing_pad({bb:?}) = {is_nop_landing_pad}");
if is_nop_landing_pad {
nop_landing_pads.insert(bb);
}
}
if nop_landing_pads.is_empty() {
debug!("no nop landing pads in MIR");
return;
}
@ -36,42 +54,27 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
patch.apply(body);
resume_block
};
debug!("remove_noop_landing_pads: resume block is {:?}", resume_block);
debug!(?resume_block);
let mut jumps_folded = 0;
let mut landing_pads_removed = 0;
let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len());
let basic_blocks = body.basic_blocks.as_mut();
for (bb, bbdata) in basic_blocks.iter_enumerated_mut() {
debug!("processing {:?}", bb);
// This is a post-order traversal, so that if A post-dominates B
// then A will be visited before B.
let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
for bb in postorder {
debug!(" processing {:?}", bb);
if let Some(unwind) = body[bb].terminator_mut().unwind_mut()
if let Some(unwind) = bbdata.terminator_mut().unwind_mut()
&& let UnwindAction::Cleanup(unwind_bb) = *unwind
&& nop_landing_pads.contains(unwind_bb)
{
debug!(" removing noop landing pad");
landing_pads_removed += 1;
*unwind = UnwindAction::Continue;
}
body[bb].terminator_mut().successors_mut(|target| {
bbdata.terminator_mut().successors_mut(|target| {
if *target != resume_block && nop_landing_pads.contains(*target) {
debug!(" folding noop jump to {:?} to resume block", target);
*target = resume_block;
jumps_folded += 1;
}
});
let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads);
if is_nop_landing_pad {
nop_landing_pads.insert(bb);
}
debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad);
}
debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed);
}
fn is_required(&self) -> bool {
@ -82,11 +85,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
impl RemoveNoopLandingPads {
fn is_nop_landing_pad(
&self,
bb: BasicBlock,
body: &Body<'_>,
bbdata: &BasicBlockData<'_>,
nop_landing_pads: &DenseBitSet<BasicBlock>,
) -> bool {
for stmt in &body[bb].statements {
for stmt in &bbdata.statements {
match &stmt.kind {
StatementKind::FakeRead(..)
| StatementKind::StorageLive(_)
@ -119,7 +121,7 @@ impl RemoveNoopLandingPads {
}
}
let terminator = body[bb].terminator();
let terminator = bbdata.terminator();
match terminator.kind {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume

View file

@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention {
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
trace!("Running RemovePlaceMention on {:?}", body.source);
for data in body.basic_blocks.as_mut_preserves_cfg() {
data.statements.retain(|statement| match statement.kind {
data.retain_statements(|statement| match statement.kind {
StatementKind::PlaceMention(..) | StatementKind::Nop => false,
_ => true,
})

View file

@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers {
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
trace!("Running RemoveStorageMarkers on {:?}", body.source);
for data in body.basic_blocks.as_mut_preserves_cfg() {
data.statements.retain(|statement| match statement.kind {
data.retain_statements(|statement| match statement.kind {
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
| StatementKind::Nop => false,

View file

@ -141,7 +141,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
&& let ty = place_for_ty.ty(self.local_decls, self.tcx).ty
&& self.known_to_be_zst(ty)
{
statement.make_nop();
statement.make_nop(true);
} else {
self.super_statement(statement, loc);
}

View file

@ -35,10 +35,12 @@
//! pre-"runtime" MIR!
use itertools::Itertools as _;
use rustc_index::bit_set::DenseBitSet;
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::debuginfo::debuginfo_locals;
use rustc_span::DUMMY_SP;
use smallvec::SmallVec;
use tracing::{debug, trace};
@ -142,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
// statements itself to avoid moving the (relatively) large statements twice.
// We do not push the statements directly into the target block (`bb`) as that is slower
// due to additional reallocations
let mut merged_blocks = Vec::new();
let mut merged_blocks: Vec<BasicBlock> = Vec::new();
let mut outer_changed = false;
loop {
let mut changed = false;
@ -157,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
let mut terminator =
self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
terminator
.successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed));
terminator.successors_mut(|successor| {
self.collapse_goto_chain(successor, &mut changed);
});
let mut inner_changed = true;
merged_blocks.clear();
@ -175,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
if statements_to_merge > 0 {
let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
statements.reserve(statements_to_merge);
let mut parent_bb_last_debuginfos =
std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
for &from in &merged_blocks {
if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos);
}
statements.append(&mut self.basic_blocks[from].statements);
parent_bb_last_debuginfos =
std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
}
self.basic_blocks[bb].statements = statements;
self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
}
self.basic_blocks[bb].terminator = Some(terminator);
@ -218,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
// goto chains. We should probably benchmark different sizes.
let mut terminators: SmallVec<[_; 1]> = Default::default();
let mut current = *start;
// If each successor has only one predecessor, it's a trivial goto chain.
// We can move all debuginfos to the last basic block.
let mut trivial_goto_chain = true;
while let Some(terminator) = self.take_terminator_if_simple_goto(current) {
let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else {
unreachable!();
};
trivial_goto_chain &= self.pred_count[target] == 1;
terminators.push((current, terminator));
current = target;
}
@ -233,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
else {
unreachable!();
};
if trivial_goto_chain {
let mut pred_debuginfos =
std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos);
let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut()
{
&mut stmt.debuginfos
} else {
&mut self.basic_blocks[last].after_last_stmt_debuginfos
};
debuginfos.prepend(&mut pred_debuginfos);
}
*changed |= *target != last;
*target = last;
debug!("collapsing goto chain from {:?} to {:?}", current, target);
@ -303,7 +329,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
fn strip_nops(&mut self) {
for blk in self.basic_blocks.iter_mut() {
blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop))
blk.strip_nops();
}
}
}
@ -476,17 +502,22 @@ fn make_local_map<V>(
/// Keeps track of used & unused locals.
struct UsedLocals {
increment: bool,
arg_count: u32,
use_count: IndexVec<Local, u32>,
always_used: DenseBitSet<Local>,
}
impl UsedLocals {
/// Determines which locals are used & unused in the given body.
fn new(body: &Body<'_>) -> Self {
let mut always_used = debuginfo_locals(body);
always_used.insert(RETURN_PLACE);
for arg in body.args_iter() {
always_used.insert(arg);
}
let mut this = Self {
increment: true,
arg_count: body.arg_count.try_into().unwrap(),
use_count: IndexVec::from_elem(0, &body.local_decls),
always_used,
};
this.visit_body(body);
this
@ -494,10 +525,16 @@ impl UsedLocals {
/// Checks if local is used.
///
/// Return place and arguments are always considered used.
/// Return place, arguments, var debuginfo are always considered used.
fn is_used(&self, local: Local) -> bool {
trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]);
local.as_u32() <= self.arg_count || self.use_count[local] != 0
trace!(
"is_used({:?}): use_count: {:?}, always_used: {}",
local,
self.use_count[local],
self.always_used.contains(local)
);
// To keep things simple, we don't handle debugging information here, these are in DSE.
self.always_used.contains(local) || self.use_count[local] != 0
}
/// Updates the use counts to reflect the removal of given statement.
@ -539,10 +576,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
self.super_statement(statement, location);
}
StatementKind::ConstEvalCounter | StatementKind::Nop => {}
StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {}
StatementKind::ConstEvalCounter
| StatementKind::Nop
| StatementKind::StorageLive(..)
| StatementKind::StorageDead(..) => {}
StatementKind::Assign(box (ref place, ref rvalue)) => {
if rvalue.is_safe_to_remove() {
self.visit_lhs(place, location);
@ -560,7 +597,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
}
}
fn visit_local(&mut self, local: Local, _ctx: PlaceContext, _location: Location) {
fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) {
if matches!(ctx, PlaceContext::NonUse(_)) {
return;
}
if self.increment {
self.use_count[local] += 1;
} else {
@ -583,28 +623,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
for data in body.basic_blocks.as_mut_preserves_cfg() {
// Remove unnecessary StorageLive and StorageDead annotations.
data.statements.retain(|statement| {
let keep = match &statement.kind {
for statement in data.statements.iter_mut() {
let keep_statement = match &statement.kind {
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
used_locals.is_used(*local)
}
StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local),
StatementKind::SetDiscriminant { place, .. }
| StatementKind::BackwardIncompatibleDropHint { place, reason: _ }
| StatementKind::Deinit(place) => used_locals.is_used(place.local),
StatementKind::Nop => false,
_ => true,
StatementKind::Assign(box (place, _))
| StatementKind::SetDiscriminant { box place, .. }
| StatementKind::BackwardIncompatibleDropHint { box place, .. }
| StatementKind::Deinit(box place) => used_locals.is_used(place.local),
_ => continue,
};
if !keep {
trace!("removing statement {:?}", statement);
modified = true;
used_locals.statement_removed(statement);
if keep_statement {
continue;
}
keep
});
trace!("removing statement {:?}", statement);
modified = true;
used_locals.statement_removed(statement);
statement.make_nop(true);
}
data.strip_nops();
}
}
}
@ -619,7 +657,62 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
self.tcx
}
fn visit_statement_debuginfo(
&mut self,
stmt_debuginfo: &mut StmtDebugInfo<'tcx>,
location: Location,
) {
match stmt_debuginfo {
StmtDebugInfo::AssignRef(local, place) => {
if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) {
*stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local);
}
}
StmtDebugInfo::InvalidAssign(_) => {}
}
self.super_statement_debuginfo(stmt_debuginfo, location);
}
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
*l = self.map[*l].unwrap();
}
}
pub(crate) struct UsedInStmtLocals {
pub(crate) locals: DenseBitSet<Local>,
}
impl UsedInStmtLocals {
pub(crate) fn new(body: &Body<'_>) -> Self {
let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) };
this.visit_body(body);
this
}
pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) {
for data in body.basic_blocks.as_mut_preserves_cfg() {
// Remove unnecessary StorageLive and StorageDead annotations.
for statement in data.statements.iter_mut() {
let keep_statement = match &statement.kind {
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
self.locals.contains(*local)
}
_ => continue,
};
if keep_statement {
continue;
}
statement.make_nop(true);
}
}
}
}
impl<'tcx> Visitor<'tcx> for UsedInStmtLocals {
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
if matches!(context, PlaceContext::NonUse(_)) {
return;
}
self.locals.insert(local);
}
}

View file

@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral {
// delete comparison statement if it the value being switched on was moved, which means
// it can not be user later on
if opt.can_remove_bin_op_stmt {
bb.statements[opt.bin_op_stmt_idx].make_nop();
bb.statements[opt.bin_op_stmt_idx].make_nop(true);
} else {
// if the integer being compared to a const integral is being moved into the
// comparison, e.g `_2 = Eq(move _3, const 'x');`
@ -136,7 +136,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral {
}
for (idx, bb_idx) in storage_deads_to_remove {
body.basic_blocks_mut()[bb_idx].statements[idx].make_nop();
body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(true);
}
for (idx, stmt) in storage_deads_to_insert {

View file

@ -318,7 +318,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
for (_, _, fl) in final_locals {
self.patch.add_statement(location, StatementKind::StorageLive(fl));
}
statement.make_nop();
statement.make_nop(true);
}
return;
}
@ -327,7 +327,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
for (_, _, fl) in final_locals {
self.patch.add_statement(location, StatementKind::StorageDead(fl));
}
statement.make_nop();
statement.make_nop(true);
}
return;
}
@ -337,7 +337,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
self.patch
.add_statement(location, StatementKind::Deinit(Box::new(fl.into())));
}
statement.make_nop();
statement.make_nop(true);
return;
}
}
@ -367,7 +367,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
);
}
}
statement.make_nop();
statement.make_nop(true);
return;
}
}
@ -429,7 +429,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
StatementKind::Assign(Box::new((new_local.into(), rvalue))),
);
}
statement.make_nop();
statement.make_nop(true);
return;
}
}

View file

@ -1,5 +1,6 @@
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::debuginfo::debuginfo_locals;
use rustc_session::config::MirStripDebugInfo;
/// Conditionally remove some of the VarDebugInfo in MIR.
@ -30,6 +31,22 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo {
if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE,
)
});
let debuginfo_locals = debuginfo_locals(body);
for data in body.basic_blocks.as_mut_preserves_cfg() {
for stmt in data.statements.iter_mut() {
stmt.debuginfos.retain(|debuginfo| match debuginfo {
StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => {
debuginfo_locals.contains(*local)
}
});
}
data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo {
StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => {
debuginfo_locals.contains(*local)
}
});
}
}
fn is_required(&self) -> bool {

View file

@ -68,6 +68,13 @@ parse_attr_after_generic = trailing attribute after generic parameter
parse_attr_without_generics = attribute without generic parameters
.label = attributes are only permitted when preceding parameters
parse_attribute_on_empty_type = attributes cannot be applied here
.label = attributes are not allowed here
parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments
.label = attributes are not allowed here
.suggestion = remove attribute from here
parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type
.label = attributes are not allowed here
@ -75,13 +82,6 @@ parse_attribute_on_type = attributes cannot be applied to types
.label = attributes are not allowed here
.suggestion = remove attribute from here
parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments
.label = attributes are not allowed here
.suggestion = remove attribute from here
parse_attribute_on_empty_type = attributes cannot be applied here
.label = attributes are not allowed here
parse_bad_assoc_type_bounds = bounds on associated types do not belong here
.label = belongs in `where` clause
@ -868,7 +868,6 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up
parse_too_short_hex_escape = numeric character escape is too short
parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern
parse_trailing_vert_not_allowed_suggestion = remove the `{$token}`
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
parse_trait_alias_cannot_be_const = trait aliases cannot be `const`

Some files were not shown because too many files have changed in this diff Show more