rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.79k stars 12.5k forks source link

Support for pointers with asm_const #128464

Open Darksonn opened 1 month ago

Darksonn commented 1 month ago

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

folkertdev commented 1 month ago

I think this is at least blocked on https://github.com/rust-lang/rust/pull/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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

So then what is the right name for something in inline asm that can evaluate to a relocation with offset? "sym" does not seem right either, since the offset makes it not a symbol.

What does this look like in C?

Would it be a problem to use "const" even when the result is a relocation? This shod not be too surprising since it is how "const" works everywhere else in Rust. For int-typed consts we would still guarantee that the result is bare assembly without linker involvement.

ojeda commented 1 month ago

@rustbot label A-rust-for-linux

joshtriplett commented 1 month ago

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 commented 1 month ago

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.