Skip to content

Conversation

joboet
Copy link
Member

@joboet joboet commented Sep 14, 2025

By lowering the is_val_statically_known to an Rvalue instead of passing it through to codegen, it becomes easier to optimise in the MIR-opt const propagation passes.

@rustbot
Copy link
Collaborator

rustbot commented Sep 14, 2025

⚠️ #[rustc_intrinsic_const_stable_indirect] controls whether intrinsics can be exposed to stable const
code; adding it needs t-lang approval.

cc @rust-lang/wg-const-eval

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

This PR changes rustc_public

cc @oli-obk, @celinval, @ouz-a

Some changes occurred to constck

cc @fee1-dead

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @vakaras

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Sep 14, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 14, 2025

r? @SparrowLii

rustbot has assigned @SparrowLii.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

}
CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)),
WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
StaticallyKnown(..) => todo!(),
Copy link
Member Author

@joboet joboet Sep 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I just add this to the public API or is there a special procedure for that?

@rust-log-analyzer

This comment has been minimized.

@RalfJung
Copy link
Member

No fundamental objection, but I am slightly concerned that if we have to make every intrinsic that optimizations should recognize into a MIR primitive, things will get unwieldy.

It's not that hard to check if a given function call invokes a particular intrinsic (and we can add more convenience operations to make that simpler). What's the problem with using that approach?

@oli-obk
Copy link
Contributor

oli-obk commented Sep 15, 2025

I am slightly concerned that if we have to make every intrinsic that optimizations should recognize into a MIR primitive, things will get unwieldy.

It also avoids requiring a terminator and new block, so for some intrinsics, changing them to an rvalue is very useful. I have not investigated whether that is true here, but it was one of the original motivations for lowering some intrinsics to rvalues

@joboet joboet force-pushed the is_val_statically_known_rvalue branch from 751ecb9 to bbcf44f Compare September 15, 2025 10:43
@rustbot
Copy link
Collaborator

rustbot commented Sep 15, 2025

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@oli-obk
Copy link
Contributor

oli-obk commented Sep 15, 2025

Ok gave the changes a review.

It's a lot of extra stuff to handle for sth that only one specific kind of optimization cares about. Does gvn so far have no optimizations that act on terminators, possibly replacing them wholesale?

@joboet
Copy link
Member Author

joboet commented Sep 15, 2025

No fundamental objection, but I am slightly concerned that if we have to make every intrinsic that optimizations should recognize into a MIR primitive, things will get unwieldy.

It's not that hard to check if a given function call invokes a particular intrinsic (and we can add more convenience operations to make that simpler). What's the problem with using that approach?

It also avoids requiring a terminator and new block, so for some intrinsics, changing them to an rvalue is very useful. I have not investigated whether that is true here, but it was one of the original motivations for lowering some intrinsics to rvalues

That's one of the reasons here – is_val_statically_known is mostly used in conjunction with things like comparisons, so knowing that it does not have side-effects could be very helpful, as it might allow optimizations to simplify is_val_statically_known() && /* some comparison */ to is_val_statically_known & /* some comparison */, which might simplify the CFG a lot.

Also, this just felt right to me: is_val_statically_known is a very fundamental and one-of-a-kind primitive in the optimisation pipeline as it one of the only observables of optimization.

But at the end of the day, this is my second compiler PR and I mostly made this decision just to follow the established pattern, so I don't feel too strongly.

@joboet
Copy link
Member Author

joboet commented Sep 15, 2025

Ok gave the changes a review.

It's a lot of extra stuff to handle for sth that only one specific kind of optimization cares about. Does gvn so far have no optimizations that act on terminators, possibly replacing them wholesale?

Not really, but I guess it wouldn't be too difficult:

fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
if let Some(local) = destination.as_local()
&& self.ssa.is_ssa(local)
{
let ty = self.local_decls[local].ty;
let opaque = self.new_opaque(ty);
self.assign(local, opaque);
}
}
// Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
let safe_to_preserve_derefs = matches!(
terminator.kind,
TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. }
);
if !safe_to_preserve_derefs {
self.invalidate_derefs();
}
self.super_terminator(terminator, location);
}
}

@rust-log-analyzer
Copy link
Collaborator

The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[RUSTC-TIMING] declare_clippy_lint test:false 0.092
    Checking unicode-width v0.1.14
[RUSTC-TIMING] cargo_metadata test:false 0.777
    Checking quine-mc_cluskey v0.2.4
error[E0004]: non-exhaustive patterns: `&rustc_middle::mir::Rvalue::StaticallyKnown(_)` not covered
    --> src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs:127:11
     |
 127 |     match rvalue {
     |           ^^^^^^ pattern `&rustc_middle::mir::Rvalue::StaticallyKnown(_)` not covered
     |
note: `rustc_middle::mir::Rvalue<'_>` defined here
    --> /checkout/compiler/rustc_middle/src/mir/syntax.rs:1368:1
     |
1368 | pub enum Rvalue<'tcx> {
     | ^^^^^^^^^^^^^^^^^^^^^
...
1498 |     StaticallyKnown(Operand<'tcx>),
     |     --------------- not covered
     = note: the matched value is of type `&rustc_middle::mir::Rvalue<'_>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
     |
 214 ~         },
 215 ~         &rustc_middle::mir::Rvalue::StaticallyKnown(_) => todo!(),
     |

[RUSTC-TIMING] toml_edit test:false 1.399
    Checking toml v0.7.8
[RUSTC-TIMING] unicode_width test:false 0.158

///
/// From an operational semantics perspective, the result is
/// non-deterministically chosen.
StaticallyKnown(Operand<'tcx>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this will still evaluate the Operand for UB side-effects?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, at least it might. Skipping it should be sound though, right? And miri should detect the UB because const-eval calls eval_operand...

pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
false
}
#[rustc_intrinsic_const_stable_indirect]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this attribute should not be needed for the rvalue refactor. Please don't just do a const-stabilization as a drive-by change! It's not even mentioned in the PR description.

As the comment at the top of this file explains, this needs t-lang FCP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, there was a rustc_const_stable_indirect before. Things just got reordered making this harder to see.

It's probably silly to keep an unused fallback body just to avoid this attribute. I assume t-lang won't insist on an FCP but we should still ping them when we resolved the other discussions in this PR.

Comment on lines -172 to -174
// FIXME: should we check for validity here? It's tricky because we do not have a
// place. Codegen does not seem to set any attributes like `noundef` for intrinsic
// calls, so we don't *have* to do anything.
Copy link
Member

@RalfJung RalfJung Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still relevant. Please be careful not to remove critical comments like this.

@bors
Copy link
Collaborator

bors commented Sep 17, 2025

☔ The latest upstream changes (presumably #146666) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants