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

Support for pointers with asm_const #128464

Open
Darksonn opened this issue Jul 31, 2024 · 23 comments
Open

Support for pointers with asm_const #128464

Darksonn opened this issue Jul 31, 2024 · 23 comments
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-rust-for-linux Relevant for the Rust-for-Linux project C-feature-request Category: A feature request, i.e: not implemented / a PR. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. F-asm `#![feature(asm)]` (not `llvm_asm`) I-lang-nominated Nominated for discussion during a lang team meeting. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@Darksonn
Copy link
Contributor

Inline assembly supports an unstable feature called asm_const with tracking issue #93332 that lets you pass constants to inline assembly. However, it only supports integer types. This issue is a feature request to support raw pointers with asm_const.

This would enable code such as the following:

#![feature(const_refs_to_static)]
#![feature(asm_const)]
use core::arch::asm;
use core::ptr::addr_of;
use core::ffi::c_void;

trait Helper {
    const MY_PTR: *const c_void;
}

fn my_asm_wrapper<T: Helper>() {
    unsafe { asm!("mov {},eax", const T::MY_PTR) };
}


extern "C" {
    static FOO: usize;
}

struct HelperForPassingPointerAsConstGeneric {}
impl Helper for HelperForPassingPointerAsConstGeneric {
    const MY_PTR: *const c_void = addr_of!(FOO).cast();
}

fn main() {
    my_asm_wrapper::<HelperForPassingPointerAsConstGeneric>();
}

Currently, the above code would require you to convert my_asm_wrapper into a macro_rules! so that you can write out the path to the global using the sym operand. Supporting this would be useful for the Rust for Linux project, as implementing support for static keys requires a long list of workarounds at the moment.

cc @Amanieu @oli-obk

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jul 31, 2024
@oli-obk oli-obk added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) F-asm `#![feature(asm)]` (not `llvm_asm`) E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. P-low Low priority C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Aug 1, 2024
@folkertdev
Copy link
Contributor

I think this is at least blocked on #125558 right now (which is done but runs into an error message ordering difference between CI and local that we don't know how to fix).

@Amanieu
Copy link
Member

Amanieu commented Aug 1, 2024

First of all, it doesn't make sense to pass a pointer as a const because... it's not actually a constant. What the assembly code would expect to receive is a symbol which points to the same address that the pointer is pointing to. So it would make more sense to support this for sym operands.

Secondly, what you are actually proposing is a generalization of sym, where rather than only supporting static and fn, it supports any arbitrary pointer and rustc will figure out a symbol expression that resolves to that address. This is probably possible but would require a new specification of exactly what kinds of expressions sym now accepts. It is also somewhat a breaking change for existing usage of sym for static since you would now need to take the address of a static instead of just naming it directly.

@Darksonn
Copy link
Contributor Author

Darksonn commented Aug 1, 2024

I understand that const <integer> and const <pointer> would need two different implementations, but I still think it is a reasonable UX to accept pointers with the const operand. Yes, there will be overlap with the things you can do with the sym operand, but is that really a problem? Making const accept it avoids changing the syntax of sym.

@Darksonn
Copy link
Contributor Author

Darksonn commented Aug 1, 2024

where rather than only supporting static and fn, it supports any arbitrary pointer and rustc will figure out a symbol expression that resolves to that address.

Indeed. Note that in my real use-case sym is actually not enough, because I need the address of a field in the static. So right now I'm combining a sym operand with a const ::core::mem::offset_of!(...) and adding them together.

@RalfJung
Copy link
Member

RalfJung commented Aug 2, 2024

First of all, it doesn't make sense to pass a pointer as a const because... it's not actually a constant.

Everywhere else in the language, we do support pointers as "constants", and our backends do the right thing. You can use pointers as initial values for static and const and we'll generate code with appropriate relocations etc. So why would inline assembly not follow suit?

@nikic
Copy link
Contributor

nikic commented Aug 5, 2024

First of all, it doesn't make sense to pass a pointer as a const because... it's not actually a constant.

Everywhere else in the language, we do support pointers as "constants", and our backends do the right thing. You can use pointers as initial values for static and const and we'll generate code with appropriate relocations etc. So why would inline assembly not follow suit?

That may indicate that const asm operands are misnamed. The defining semantic for these operands is:

The value of the expression is formatted as a string and substituted directly into the asm template string.

Which doesn't really make sense for relocatable constants. The whole point of const here is that it does not reach the backend and does not get treated as an actual input operand constraint.

It's really more of a str or literal operand than a const operand.

@RalfJung
Copy link
Member

RalfJung commented Aug 5, 2024 via email

@joshtriplett joshtriplett added the I-lang-nominated Nominated for discussion during a lang team meeting. label Aug 14, 2024
@ojeda
Copy link
Contributor

ojeda commented Aug 14, 2024

@rustbot label A-rust-for-linux

@rustbot rustbot added the A-rust-for-linux Relevant for the Rust-for-Linux project label Aug 14, 2024
@joshtriplett
Copy link
Member

Nominating this for lang to discuss the question of whether we should support use of const in asm! for things that can't just be textually substituted, or whether we should give that a different name.

@Amanieu, any input you'd like to provide would be helpful.

@Amanieu
Copy link
Member

Amanieu commented Aug 15, 2024

After thinking about it a bit, I think it's probably fine to add this functionality to const. I'm a bit bothered about the duplication with sym, which is already stable.

@Evian-Zhang
Copy link

As asm_const has been stabilized in 1.82.0, and const_refs_to_static is decided to be stabilized in 1.83.0 a few hours ago, what is the progress of this issue? For now, nightly Rust compiler still does not accept pointer type in asm const operands.

@Amanieu
Copy link
Member

Amanieu commented Sep 28, 2024

One issue is that this conflicts with another proposal that has been discussed on Zulip about having const operands accept &'static str to embed a string constant (which is not necessarily a string literal) into asm code.

@nbdd0121
Copy link
Contributor

Interpolating string constant with const seems weird, maybe it's worth adding another interpolate <const &'static str> for that?

@Evian-Zhang
Copy link

@Amanieu The link is two years ago, so I guess we could discuss it here? I don't understand why accepting &'static str whose content is a string constant could solve the problem in this issue. As far as I understand, this issue is aimed to make it possible to refer a static variable in inline-assembly in a function instead of macro.

@Amanieu
Copy link
Member

Amanieu commented Sep 29, 2024

It was a while and I remember having a more in-depth discussion about it, but I can't find it (it may have been an in-person discussion).

The basic problem is that this issue is about inserting a symbol pointing to a constant into assembly code. The other proposal is about taking a &'static str that is a compile-time constant inserting that string verbatim into the assembly code.

Both are reasonable ways in which the current const support can be extended, but it would be confusing to have both. In previous comments here I was arguing that sym const { EXPR } may be a better way to express the desire to have a symbol that points to a constant, which would avoid this confusion.

@Evian-Zhang
Copy link

The &'static str way is still hard to understand. What if user passes a static ref returned by Box<str>::leak? When using the &'static str approach, does the pattern described in the issue still need to be implemented by macros?

@RalfJung
Copy link
Member

The basic problem is that this issue is about inserting a symbol pointing to a constant into assembly code.

I would say this is about inserting the value of the constant into assembly code. A constant of type *mut T has a pointer value, and so the pointer is inserted into assembly. Pointers are represented as symbols (with offsets) at that level. The fact that this pointer points to a constant isn't relevant -- in fact it may not even be true, it may point to a static mut. So I find it confusing to describe this as "symbol point to constant".

It's a bit like formatting: given the const value, how is it to be put into the assembly? For integer-typed constants, there's only really one option. For raw pointers, there's also really only one option. For references, we have the usual choice of either putting in the pointer value of the reference, or putting in the value that the reference points to.

@Evian-Zhang
Copy link

OK I think I understand. By saying "conflict", it is how the pointer is dealt with as inline assembly operand that is conflict.

I agree that using another "keyword" like "interpolate" instead of reusing the "const" operand to format the &'static str is more consistent in semantics.

@nikomatsakis
Copy link
Contributor

We discussed this in the @rust-lang/lang triage meeting today. Meeting consensus was

  1. We agree with @RalfJung and @Darksonn that const would intuitively be used to insert the value that results from evaluating a const expression and that extending from integers to pointers (and especially offsets within symbols) seems logical.
  2. String interpolation (i.e., take the contents of an &str and embed it as is) feels like a quite distinct feature that would be better off with a different keyword, presuming it's needed.

Is there an implementation of this already or would that need to be done?

@nikomatsakis
Copy link
Contributor

@rustbot labels +I-lang-nominated

I am nominating this issue for lang team discussion. The proposal is to extend const notation to support pointer values and not just integers. As described in the OP of this issue, this would enable code like the following:

trait Helper {
    const MY_PTR: *const c_void;
}

fn my_asm_wrapper<T: Helper>() {
    unsafe { asm!("mov {},eax", const T::MY_PTR) };
}

as well as constants like the address of a particular field. This is needed by Rust For Linux.

The question I would like answered is procedural: is this a small enough extension that we should simply "rfcbot fcp merge" in place, or would it better to have an RFC? It seems like a fairly minimal extension, but it also sets a bit of a precedent that const is used for merging values and not string substitution (as indicated by the controversy here).

I would expect to stabilize a minimal version of the feature as a starting point, accepting only raw pointers for example, and only thin ones. (References might be ok, too.)

@Amanieu
Copy link
Member

Amanieu commented Nov 3, 2024

We still need to precisely specify how the constant reference is rendered as a string in the asm code. The most obvious form would be an assembler expression of the form SYMBOL + OFFSET but that may not be usable for all targets.

@nikomatsakis
Copy link
Contributor

@Amanieu are you suggesting that some constants may have be renderable at all on some targets? I would assume that this rendering is target specific.

@Amanieu
Copy link
Member

Amanieu commented Dec 2, 2024

It's fine for the rendering to be target-specific, however we do need to document the rendering in the reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-rust-for-linux Relevant for the Rust-for-Linux project C-feature-request Category: A feature request, i.e: not implemented / a PR. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. F-asm `#![feature(asm)]` (not `llvm_asm`) I-lang-nominated Nominated for discussion during a lang team meeting. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests