Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#[inline(required)] #3711

Closed
wants to merge 5 commits into from
Closed

Conversation

davidtwco
Copy link
Member

@davidtwco davidtwco commented Oct 14, 2024

rustc supports inlining functions with the #[inline]/#[inline(always)]/#[inline(never)] attributes, but these are only hints to the compiler.

Sometimes it is desirable to require inlining, not just suggest inlining: security APIs can depend on being inlined for their security properties, and performance intrinsics can work poorly if not inlined.

Add a forever-unstable #[inline(required)] and #[inline(must)] which will always inline regardless of cost heuristics and will emit a deny/warn lint if inlining was not possible.

See rust-lang/rust#131687 for draft implementation.

Rendered

@BurntSushi
Copy link
Member

I like the idea generally. Two obvious questions occur to me that I don't see addressed in this RFC though.

#[inline(must)] and #[inline(required)] are intended to remain unstable indefinitely and be used only within the standard library (e.g. on intrinsics). This could be relaxed if there were sufficient motivation for use of these inlining attributes in user code.

First question is, why are these intended to remain unstable definitely? One of the motivations is for security, but it seems like user code could benefit from that too. And I've also often wanted "inline this function or yell at me" when writing SIMD code. I use inline(always), and a non-zero number of times, I've had perf bugs where for one reason or another, the functions don't get inlined.

Second question is, why does this propose both a inline(must) and inline(required)? If they're lints, then it seems like you could have just one of them and make it warn-by-default. Then you could do deny(must_inline) (or whatever) to turn it into a deny lint. (If you do go with both must and required, then given they are synonyms, I predict it will be difficult to remember which one is warn-by-default and which one is deny-by-default.)

@davidtwco
Copy link
Member Author

davidtwco commented Oct 14, 2024

I'll update the RFC shortly to address these questions too.

First question is, why are these intended to remain unstable definitely? One of the motivations is for security, but it seems like user code could benefit from that too. And I've also often wanted "inline this function or yell at me" when writing SIMD code. I use inline(always), and a non-zero number of times, I've had perf bugs where for one reason or another, the functions don't get inlined.

Just to avoid it being put on items unnecessarily in an attempt to improve performance. I spoke with some other contributors at EuroRust last week about this and we felt that was a reasonable concern. I expect that we'd put these attributes on SIMD intrinsics in std and places like that, so users of those intrinsics would get warnings if they weren't inlined (even if they can't use these attributes themselves). I don't feel strongly about this detail though, if there were reasonable cases where one might want to annotate user code with this then we can make it available to users.

Second question is, why does this propose both a inline(must) and inline(required)? If they're lints, then it seems like you could have just one of them and make it warn-by-default. Then you could do deny(must_inline) (or whatever) to turn it into a deny lint. (If you do go with both must and required, then given they are synonyms, I predict it will be difficult to remember which one is warn-by-default and which one is deny-by-default.)

This is an alternative that we could consider, but I went with having two lints and two attributes because of the use case where we require inlining to uphold security properties, I wanted that to be a hard error.

I wouldn't want a toolchain version bump resulting in a security intrinsic no longer being inlined and that going unnoticed in CI and compromising the security of an application. You're absolutely right that we could just make this a warning, and ask that users of any security intrinsics add the deny(must_inline) themselves, but I preferred adding must and required so that users of a security intrinsic wouldn't need to think about that extra detail.

@clarfonthey
Copy link

I think that it's substantially more confusing to have must and required (two synonyms) instead of just, explicitly setting the warning level for inlining yourself.

So, to me:

  • #[inline(always)] is lint level allow
  • #[inline(must)] is lint level warn
  • #[inline(required)] is lint level deny

And while, yes, inline(always) doesn't quite work that way, I think it's reasonable to compare these three in this way. So, perhaps it would be better to have, instead of these, #[inline(always, failure = allow, reason = whatever)] to cover the basics, bikeshedding for whatever terms you'd prefer to use instead.

@fmease fmease changed the title #[inline(required) #[inline(required)] Oct 14, 2024
@davidtwco
Copy link
Member Author

davidtwco commented Oct 14, 2024

I think that it's substantially more confusing to have must and required (two synonyms) instead of just, explicitly setting the warning level for inlining yourself.

I'm definitely open to renaming must and/or required if there is consensus that they are confusing and we have better names.

And while, yes, inline(always) doesn't quite work that way, I think it's reasonable to compare these three in this way. So, perhaps it would be better to have, instead of these, #[inline(always, failure = allow, reason = whatever)] to cover the basics, bikeshedding for whatever terms you'd prefer to use instead.

I'm also open to something like this too if there's consensus. I didn't go with something like this initially because I felt having two new inlining kinds and only having those unconditionally run the inliner seemed tidier.

@clarfonthey
Copy link

I'm also open to something like this too if there's consensus. I didn't go with something like this initially because I felt having two new inlining kinds and only having those unconditionally run the inliner seemed tidier.

I mean, as far as I'm concerned, it's exactly the same thing, just slightly wordier. And, I'd prefer wordiness over confusion. To me, seeing always, failure = warn makes more sense than must and just having to remember that means warn, and required means error.

@burdges
Copy link

burdges commented Oct 14, 2024

I'd think inline(must) should be inline(or_warn) or inline(required_or_warn) or similar. inline(required) is fine, but inline(or_deny) maybe reasonable.

Is there a reasons why inline(always) etc should be upgraded at call sites? I could imagine SIMD calls being only inline(always) usually, but then you'd invoke then like inline(required) intrinsics::... or similar ocasionally. Would that matter?

@bluebear94
Copy link

A different solution could be to rename must to expected, in line with the #[expect(..)] attribute.

@tgross35
Copy link
Contributor

Cc @saethlin who was working on #[inline(usually)] rust-lang/rust#130679

@hanna-kruppe
Copy link

Regarding user-controlled "try your best to inline this and warn me if you can't" annotations for performance reasons - I've also wished for something like this in the past. However, I think there's two subtly different use cases behind this:

It would be nice to get warnings if core::arch intrinsics won't get inlined, even in release builds, because I forgot a #[target_feature(enable = ...)] somewhere. In principle this applies just as much to user-defined functions that involve target_feature-fu even if they're larger and it's not obvious that they should always be inlined! On the contrary, in many cases I'm happy to leave LLVM's inliner to do its thing, I just want to know if there's a roadblock I need to remove.

On the other hand, sometimes I really do want to force inlining... when building with optimizations. Most recently, I've had to slap inline(always) on a trivial closure passed to a higher-order helper function shared by several architecture-specific functions with #[target_feature(enable = ...)] annotations because otherwise the closure wouldn't get inlined for some reason. inline(always) is relatively reliable for such this today, but really I don't like using it because it also applies in debug builds (and involving the MIR inliner might extend that to cargo check!). That's a waste of time. It only happens because there's currently no way to distinguish "this must be inlined even under opt-level=0 for cursed reasons" from the more common meaning of "LLVM isn't inlining hard enough for my taste". I'd prefer to have something like the proposed inline(usually) which only kicks in when compiling with optimizations. A warning when this doesn't work is useful, but would only apply to optimized builds.

rely on being inlined to guarantee their security properties. Rust should make
it easier to guarantee that these intrinsics will be inlined and emit an error
if that is not possible or has not occurred.
- Arm's Neon and SVE performance intrinsics have work poorly if not inlined.
Copy link
Member

Choose a reason for hiding this comment

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

I don't know exactly what words you meant here, but "have work poorly" is not it.

Suggested change
- Arm's Neon and SVE performance intrinsics have work poorly if not inlined.
- Arm's Neon and SVE performance intrinsics perform quite poorly if not inlined.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, yeah, just missed that while rewriting this sentence at some point.

Comment on lines +34 to +35
In general, for many intrinsics, inlining is not an optimisation but an
important part of the semantics, regardless of optimisation level.
Copy link
Member

Choose a reason for hiding this comment

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

If you truly do mean that inlining is part of the semantics, then I think a lot of work needs to be done here, including making this "inlining" happen for the MIR that Miri executes.

Copy link
Member

Choose a reason for hiding this comment

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

Assuming I'm reading this and the draft implementation right, -Zmir-enable-passes=-Inline in the draft PR is unsound.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think my wording was too strong here, there wouldn't necessarily be unsoundness, but for something like SIMD intrinsics, they basically only make sense if you inline them - it isn't wrong if you don't, but it isn't more debuggable or anything like that, just slower. These intrinsics only make sense inlined.

Comment on lines +56 to +57
warn-by-default lint and `#[inline(required)]` emits a deny-by-default lint,
which can be used by items which must be inlined to uphold security properties.
Copy link
Member

Choose a reason for hiding this comment

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

I do not think that a deny-by-default lint is sufficiently stern for upholding security properties, especially if the point of this RFC is that auditing the assembly is not an option. A deny-by-default lint is silent when it fires in a dependency, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm unclear if anyone outside of a crypto library would ever use this for "security". Does an everyday user need to be forcing inlining when writing a webserver or whatever the heck?

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not think that a deny-by-default lint is sufficiently stern for upholding security properties, especially if the point of this RFC is that auditing the assembly is not an option. A deny-by-default lint is silent when it fires in a dependency, right?

Yeah, you need to trust your dependencies which use inline(required) functions to not just #[allow(..)]'ing those lints. I made it a lint so that you could allow/expect it yourself if limitations in the inliner were causing the lint to trigger and there was nothing you could do about that.

I'm unclear if anyone outside of a crypto library would ever use this for "security". Does an everyday user need to be forcing inlining when writing a webserver or whatever the heck?

See #3711 (comment) for an example of the type of function where inline(required) would be intended to be used. I don't anticipate that the average user would want to be using this to write their webserver, it's to support a very specific thing, which is why I'm entirely open to it being a rustc_must_inline attribute or being perma-unstable or something like that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not trying to be too concerned about the process of it all, but would this idea better fit a Compiler MCP, rather than a general RFC? If it's intended to just eternally be some implementation detail. Generally, users expect RFCs to result in something that they can eventually use on stable. That also might reduce the bikeshed.

@saethlin
Copy link
Member

saethlin commented Oct 15, 2024

As @hanna-kruppe points out, I think the existing inline attributes are a very poor fit for how the attributes are used in practice. #[inline(always)] is probably the worst because I think aho-corasick has doubled the unoptimized incremental build time of the regex crate by applying the attribute so liberally.

I don't like using it because it also applies in debug builds (and involving the MIR inliner might extend that to cargo check!)

Normal MIR inlining doesn't run in cargo check builds, at the very least it needs you to disable incremental and use a -Copt-level past 0 or a -Zmir-opt-level past 1. And it also does not do anything special for inline(always) over plain inline. It did at one point, and that was a dubious design decision that I corrected.

That being said, I have serious concerns about using the MIR inliner for this.

The MIR inliner isn't able to resolve all calls, for a few reasons. When it fails to resolve a call, we can't know if that was a #[inline(required)] call. Running the MIR inliner on monomorphized MIR right before codegen would let it resolve all static calls, but it is unclear to me if the compile-time overhead of such post-mono MIR optimizations can be made tolerable.

The RFC says "Failures to force inline should not occur sporadically," but to the best of my knowledge, the query system requires the MIR inliner to have query cycle avoidance logic which is based on analyzing unoptimized MIR. Calls that are optimized out in MIR can cause a cycle to be detected and avoided by not doing some inlining. That seems bad.

I suspect there are other problems. Have you considered changing MIR building or codegen to correctly lower calls to these must-be-inlined functions instead of leaning on the MIR inliner?

How do you plan to handle indirect calls to functions with these attributes? Can we ban coercing these functions to function pointers?

@BurntSushi
Copy link
Member

#[inline(always)] is probably the worst because I think aho-corasick has doubled the unoptimized incremental build time of the regex crate by applying the attribute so liberally.

That's the first I've heard of this problem. I would encourage you to file an issue. But as the author of aho-corasick, I generally do not apply inline(always) unless it's supported by a benchmark or profiling. I don't just sprinkle them about when I feel like it.

Comment on lines +39 to +41
already implemented by Rust. Explicit pointer authentication intrinsics are not
yet available in Rust. Exposing pointer authentication intrinsics would require
this RFC, or something providing equivalent guarantees, as a prerequisite.
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain or provide a link to why these pointer authentication intrinsics require inlining for correctness?

Also my knee-jerk reaction to this need is to... make them actual intrinsics. And not even add the attribute this RFC proposes. If the attribute is going to be perma-unstable/internal-only, what's the difference between that and a lang item?

I do not find this use-case compelling, and I'd much rather the RFC be motivated by something that we can't do using a well-established pattern based on lang items.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have a link, but for pointer authentication, the idea would be to upstream some functions into stdarch which use inline assembly1 and must be inlined. They have to be inlined because otherwise you're left with a sequence of instructions that can be used as a gadget to sign whatever you want, so you want to mix those sequences into the functions so they can't be used as a gadget.

For these functions, inline(required) would be necessary to make sure that if you're using these intrinsics that they actually have a benefit. The important part of inline(required) for this case is really the diagnostic, inline(always) gives us about as much inlining. If there's a more limited form of this proposal which - for just these intrinsics - can give us the diagnostic, then that works too.

Footnotes

  1. LLVM does have intrinsics that for pointer authentication, but using inline assembly gives us more guarantees around upholding the security properties of the pointer authentication, e.g. calls to identical intrinsics can be optimised away, unencrypted values could be spilled to memory briefly, and using inline assembly can avoid these issues.

@traviscross traviscross added T-lang Relevant to the language team, which will review and decide on the RFC. I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. labels Oct 15, 2024
@hanna-kruppe
Copy link

@saethlin

Normal MIR inlining doesn't run in cargo check builds, at the very least it needs you to disable incremental and use a -Copt-level past 0 or a -Zmir-opt-level past 1. And it also does not do anything special for inline(always) over plain inline.

To be clear, I was referring to (1) the current behavior of inline(always) in the LLVM backend as opt-level=0, and (2) the RFC as written proposing to run (a tuned down variant of) the MIR inliner even at -O0 for the sake of implementing the new attributes and diagnostics. I extrapolated that the diagnostics might be desired even in cargo check, and running that subset of MIR inlining even for check builds might be the easiest way to achieve that.

@davidtwco
Copy link
Member Author

It would be nice to get warnings if core::arch intrinsics won't get inlined, even in release builds, because I forgot a #[target_feature(enable = ...)] somewhere. In principle this applies just as much to user-defined functions that involve target_feature-fu even if they're larger and it's not obvious that they should always be inlined! On the contrary, in many cases I'm happy to leave LLVM's inliner to do its thing, I just want to know if there's a roadblock I need to remove.

I think that the performance motivation part of this (and consequently inline(must)) can probably be dropped and instead - outside of this RFC - relaxing the restriction on inline(always) being used with target_feature (rust-lang/stdarch#404) would be sufficient, e.g. SIMD intrinsics should really be inline(always) but can't be. That's a separate issue, and having some warnings/errors around inlining and target_feature and allowing inline(always) would resolve that motivation of this RFC.

@davidtwco
Copy link
Member Author

That being said, I have serious concerns about using the MIR inliner for this.

The MIR inliner isn't able to resolve all calls, for a few reasons. When it fails to resolve a call, we can't know if that was a #[inline(required)] call. Running the MIR inliner on monomorphized MIR right before codegen would let it resolve all static calls, but it is unclear to me if the compile-time overhead of such post-mono MIR optimizations can be made tolerable.

The RFC says "Failures to force inline should not occur sporadically," but to the best of my knowledge, the query system requires the MIR inliner to have query cycle avoidance logic which is based on analyzing unoptimized MIR. Calls that are optimized out in MIR can cause a cycle to be detected and avoided by not doing some inlining. That seems bad.

I suspect there are other problems. Have you considered changing MIR building or codegen to correctly lower calls to these must-be-inlined functions instead of leaning on the MIR inliner?

How do you plan to handle indirect calls to functions with these attributes? Can we ban coercing these functions to function pointers?

That makes sense, the MIR inliner may not be a viable approach, at least for a vaguely general-purpose attribute like this RFC currently proposes.

@kpreid
Copy link
Contributor

kpreid commented Oct 16, 2024

First question is, why are these intended to remain unstable definitely? ...

Just to avoid it being put on items unnecessarily in an attempt to improve performance. ... if there were reasonable cases where one might want to annotate user code with this then we can make it available to users.

Not making them available to users makes the feature non-compositional: I can't write a wrapper function around a std function that has identical behavior except for remapping one argument or whatever, without losing the desired required-inlining behavior. It should always be possible to write a function that is identical to another function with chosen exceptions.

@nikomatsakis
Copy link
Contributor

@davidtwco If this is intended to be perma-unstable, my take is that no RFC is needed. This is an implementation detail for how rustc manages certain intrinsic functions, and it should just use an attribute like #[rustc_inline_always] or even #[inline(rustc_always)]. If we want to stabilize it as something that end-users might use to build abstractions (which, as @BurntSushi suggests, may make sense), then I think an RFC is required — but that RFC would likely benefit from implementation experience regardless.

@davidtwco
Copy link
Member Author

Thanks everyone for the comments! I'm no longer convinced that the approach described in this RFC is feasible and I think I've got alternative solutions to address the motivating issues that I'll pursue instead, so closing for now.

@davidtwco davidtwco closed this Oct 16, 2024
@traviscross traviscross removed the I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. label Oct 16, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 9, 2024
mir_transform: implement `#[rustc_force_inline]`

Adds `#[rustc_force_inline]` which is similar to always inlining but reports an error if the inlining was not possible.

- `#[rustc_force_inline]` can only be applied to free functions to guarantee that the MIR inliner will be able to resolve calls.
- `rustc_mir_transform::inline::Inline` is refactored into two passes (`Inline` and `ForceInline`), sharing the vast majority of the implementation.
  - `rustc_mir_transform::inline::ForceInline` can't be disabled so annotated items are always inlined.
  - `rustc_mir_transform::inline::ForceInline` runs regardless of optimisation level.
- `#[rustc_force_inline]` won't inline unless target features match, as with normal inlining.
- MIR validation will ICE if a `#[rustc_force_inline]` isn't inlined, to guarantee that it will never be codegened independently. As a further guarantee, monomorphisation collection will always decide that `#[rustc_force_inline]` functions cannot be codegened locally.
- Like other intrinsics, `#[rustc_force_inline]` annotated functions cannot be cast to function pointers.
- As with other rustc attrs, this cannot be used by users, just within the compiler and standard library.
- This is only implemented within rustc, so should avoid any limitations of LLVM's inlining.

It is intended that this attribute be used with intrinsics that must be inlined for security reasons. For example, pointer authentication intrinsics would allow Rust users to make use of pointer authentication instructions, but if these intrinsic functions were in the binary then they could be used as gadgets with ROP attacks, defeating the point of introducing them. We don't have any intrinsics like this today, but I expect to upstream some once a force inlining mechanism such as this is available.

cc rust-lang#131687 rust-lang/rfcs#3711 - this approach should resolve the concerns from these previous attempts

r? `@saethlin`
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 12, 2024
mir_transform: implement `#[rustc_force_inline]`

Adds `#[rustc_force_inline]` which is similar to always inlining but reports an error if the inlining was not possible.

- `#[rustc_force_inline]` can only be applied to free functions to guarantee that the MIR inliner will be able to resolve calls.
- `rustc_mir_transform::inline::Inline` is refactored into two passes (`Inline` and `ForceInline`), sharing the vast majority of the implementation.
  - `rustc_mir_transform::inline::ForceInline` can't be disabled so annotated items are always inlined.
  - `rustc_mir_transform::inline::ForceInline` runs regardless of optimisation level.
- `#[rustc_force_inline]` won't inline unless target features match, as with normal inlining.
- MIR validation will ICE if a `#[rustc_force_inline]` isn't inlined, to guarantee that it will never be codegened independently. As a further guarantee, monomorphisation collection will always decide that `#[rustc_force_inline]` functions cannot be codegened locally.
- Like other intrinsics, `#[rustc_force_inline]` annotated functions cannot be cast to function pointers.
- As with other rustc attrs, this cannot be used by users, just within the compiler and standard library.
- This is only implemented within rustc, so should avoid any limitations of LLVM's inlining.

It is intended that this attribute be used with intrinsics that must be inlined for security reasons. For example, pointer authentication intrinsics would allow Rust users to make use of pointer authentication instructions, but if these intrinsic functions were in the binary then they could be used as gadgets with ROP attacks, defeating the point of introducing them. We don't have any intrinsics like this today, but I expect to upstream some once a force inlining mechanism such as this is available.

cc rust-lang#131687 rust-lang/rfcs#3711 - this approach should resolve the concerns from these previous attempts

r? `@saethlin`
bors added a commit to rust-lang-ci/rust that referenced this pull request Jan 10, 2025
mir_transform: implement `#[rustc_force_inline]`

Adds `#[rustc_force_inline]` which is similar to always inlining but reports an error if the inlining was not possible.

- `#[rustc_force_inline]` can only be applied to free functions to guarantee that the MIR inliner will be able to resolve calls.
- `rustc_mir_transform::inline::Inline` is refactored into two passes (`Inline` and `ForceInline`), sharing the vast majority of the implementation.
  - `rustc_mir_transform::inline::ForceInline` can't be disabled so annotated items are always inlined.
  - `rustc_mir_transform::inline::ForceInline` runs regardless of optimisation level.
- `#[rustc_force_inline]` won't inline unless target features match, as with normal inlining.
- MIR validation will ICE if a `#[rustc_force_inline]` isn't inlined, to guarantee that it will never be codegened independently. As a further guarantee, monomorphisation collection will always decide that `#[rustc_force_inline]` functions cannot be codegened locally.
- Like other intrinsics, `#[rustc_force_inline]` annotated functions cannot be cast to function pointers.
- As with other rustc attrs, this cannot be used by users, just within the compiler and standard library.
- This is only implemented within rustc, so should avoid any limitations of LLVM's inlining.

It is intended that this attribute be used with intrinsics that must be inlined for security reasons. For example, pointer authentication intrinsics would allow Rust users to make use of pointer authentication instructions, but if these intrinsic functions were in the binary then they could be used as gadgets with ROP attacks, defeating the point of introducing them. We don't have any intrinsics like this today, but I expect to upstream some once a force inlining mechanism such as this is available.

cc rust-lang#131687 rust-lang/rfcs#3711 - this approach should resolve the concerns from these previous attempts

r? `@saethlin`
bors added a commit to rust-lang-ci/rust that referenced this pull request Jan 10, 2025
mir_transform: implement `#[rustc_force_inline]`

Adds `#[rustc_force_inline]` which is similar to always inlining but reports an error if the inlining was not possible.

- `#[rustc_force_inline]` can only be applied to free functions to guarantee that the MIR inliner will be able to resolve calls.
- `rustc_mir_transform::inline::Inline` is refactored into two passes (`Inline` and `ForceInline`), sharing the vast majority of the implementation.
  - `rustc_mir_transform::inline::ForceInline` can't be disabled so annotated items are always inlined.
  - `rustc_mir_transform::inline::ForceInline` runs regardless of optimisation level.
- `#[rustc_force_inline]` won't inline unless target features match, as with normal inlining.
- MIR validation will ICE if a `#[rustc_force_inline]` isn't inlined, to guarantee that it will never be codegened independently. As a further guarantee, monomorphisation collection will always decide that `#[rustc_force_inline]` functions cannot be codegened locally.
- Like other intrinsics, `#[rustc_force_inline]` annotated functions cannot be cast to function pointers.
- As with other rustc attrs, this cannot be used by users, just within the compiler and standard library.
- This is only implemented within rustc, so should avoid any limitations of LLVM's inlining.

It is intended that this attribute be used with intrinsics that must be inlined for security reasons. For example, pointer authentication intrinsics would allow Rust users to make use of pointer authentication instructions, but if these intrinsic functions were in the binary then they could be used as gadgets with ROP attacks, defeating the point of introducing them. We don't have any intrinsics like this today, but I expect to upstream some once a force inlining mechanism such as this is available.

cc rust-lang#131687 rust-lang/rfcs#3711 - this approach should resolve the concerns from these previous attempts

r? `@saethlin`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.