Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd support for naked functions. #1201
Conversation
This comment has been minimized.
This comment has been minimized.
|
|
nrc
added
the
T-lang
label
Jul 13, 2015
This comment has been minimized.
This comment has been minimized.
DanielKeep
commented
Jul 14, 2015
|
How does this interact with the calling convention? I recently looked into writing some inline assembly, using If all this does is let us replace the contents of an I'm inclined to suggest that |
This comment has been minimized.
This comment has been minimized.
Tobba
commented
Jul 14, 2015
|
This doesn't seem like a terribly good idea, LLVM lacked this for a very long time due to the undefined and extremely iffy semantics involved, essentially the only thing that can safely be done in a naked function is using a single asm block. LLVM already has had support for module-level assembly blocks as long as I can remember. Although I wouldn't let any remotely complicated hand-written code get within 50 feet of LLVMs built-in assembler either (it tends to be somewhat broken) |
This comment has been minimized.
This comment has been minimized.
I think you're right that anything but parameterless functions returning
Without Rust support though, that doesn't get us very far. The advantage of putting this directly on functions is that we allow the compiler to generate a function definition, including name mangling. Despite the much greater implementation complexity, I'm coming around to the |
This comment has been minimized.
This comment has been minimized.
DanielKeep
commented
Jul 14, 2015
|
@tari: My point wasn't "you can't write naked functions that take arguments", it was "you can't write naked functions that take arguments using the undefined I think the good solution is to make sure that naked functions aren't "desugared" into
|
This comment has been minimized.
This comment has been minimized.
Tobba
commented
Jul 14, 2015
|
Either way, the compiler could provide name mangling for module-level assembly blocks, which would be much saner. |
This comment has been minimized.
This comment has been minimized.
|
Isn't name mangling seperate from the ABI already in rust, ie. One problem with For this reason, I think it would be better to leave Finally, there may be cases where the naked function doesn't follow any standard ABI, in which case it shouldn't be callable at all from rust code. This "unspecified" ABI could be selected via |
This comment has been minimized.
This comment has been minimized.
The trouble with that approach I see is that it becomes impossible to safely call such a naked function from Rust. We could choose to forbid native rust calls to such a function, but that seems to me like an unusual exception to the rule that all declared functions are callable.
Right; I started conflating the goal and means a bit here. Clarifying: my ultimate goal is to support writing interrupt handlers in Rust-only. The proposed means to that is introducing naked functions which allow the programmer to use an arbitrary calling convention. As Changing this approach would have the implication that some calls to functions defined in Rust become wrong because they assume a different calling convention- a hypothetical Given the previous points, the simplest approach to supporting arbitrary ABIs appears to be implementing them in the compiler. This is much less flexible than supporting naked functions, but permits more efficient code generation since the compiler has detailed knowledge of what the system is doing. For the motivating use case this could be Addition of calling conventions doesn't preclude a generic mechanism for naked functions, but it does sidestep the current issues involved in making naked functions useful. |
This comment has been minimized.
This comment has been minimized.
DanielKeep
commented
Jul 14, 2015
A naked function is no less safe to call than any other non-local I get the feeling that we're starting to talk across purposes, here, so let's clarify. I'm talking about something like: #[cfg(target_arch="x86")]
extern "C" fn add_two(a: i32, b: i32) -> i32 {
#![naked]
asm!(r#"
mov eax, [esp+4]
mov ecx, [esp+8]
add eax, ecx
"#)
}
fn main() {
assert_eq!(add_two(4, 6), 10);
}Unless I've got the assembler wrong (it's been a while), the above should be perfectly safe to call. It's a function that uses the C ABI... that it's written in assembler and doesn't have the standard function prelude is just an implementation detail the user doesn't need to worry about. I mean, |
This comment has been minimized.
This comment has been minimized.
The difference is that the only reason to use a naked function is when the compiler does not understand the ABI you need to use and is thus virtually guaranteed to generate code that will do something unexpected (and wrong) if you try to call it. You can implement a naked function that uses the C ABI, but there's no reason to do so because the compiler already understands how to deal with that for you. |
This comment has been minimized.
This comment has been minimized.
|
@tari That's simply not true - there are many reasons to require a naked function even when using an ABI that the compiler understands. |
This comment has been minimized.
This comment has been minimized.
|
Can you provide some examples of situations you'd want to do so? The way I see it, naked functions are useful to fill holes in the compiler's ABI support and I don't see the point of using one when the compiler is capable of doing what you need. |
This comment has been minimized.
This comment has been minimized.
|
Sometimes you can implement a method more efficiently than the compiler's default prolog/epilog would allow. For example, I needed to write a function which returned the address of the caller. With a naked function, this boils down to two instructions: mov eax, [ebp+4]
retThere's just no way to generate code like that without getting rid of the default prolog and epilog. |
This comment has been minimized.
This comment has been minimized.
main--
commented
Jul 14, 2015
|
@Diggsey While specific stack layout (e.g. caller address being at #![feature(asm)]
pub fn myfunc() -> u32 {
let ret: u32;
unsafe { asm!("mov $0, [ebp+4]" : "=r"(ret) ::: "intel"); }
return ret;
}
// yes, I'm compiling this on x86_64, so it won't ever work like this anyways but it really doesn't matter for this example
And there you go. Exactly what you wanted, without a naked function. |
This comment has been minimized.
This comment has been minimized.
|
It seems we have two somewhat distinct use cases for naked functions, where I only had the first in mind while writing this RFC:
Neither of these works well with the current proposal, because the ABI of function calls generated by the compiler is unspecified. For a compiler-generated call to such a function, we should require that the ABI in use be well-defined (specifically it must not use the Rust ABI, and we may want to restrict it to If the calling convention of a naked function which is called from Rust code is constrained to be well-defined, the problems are purely in implementation- @main-- that may be technically possible, but it's too fragile for me to be comfortable encouraging such a thing. We can make the static guarantees of such a function stronger by allowing it to be declared as naked without affecting the rest of the language, so I believe we should. |
This comment has been minimized.
This comment has been minimized.
main--
commented
Jul 14, 2015
|
I agree that especially for things like interrupt handlers, naked functions are simply a necessity. Of course one could try and hack around that by calling into an embedded inline asm block, but that's far from perfect. I do have doubts regarding ABI emulation (providing functions in a specific ABI without compiler support): Every function needs its own trampoline, right? So one would probably write a macro to create all those trampolines which ... surprisingly isn't even that messy, but still less pretty than it could be ( When calling a method, we need to know how. In case of a "standard" naked function, this gets us into a funny situation: Just like with any normal function, we're supposed to use the Rust ABI which is fine for us - but at the same time we're sure that they (callee) can never handle this correctly due to that ABI being undefined. I'd suggest making those functions uncallable and forcing them to be parameterless. On the other hand, when a naked function's ABI has a well-known definition (like The other difficult part are local variables. Should you write "heavy" rust code within naked functions? Probably not. But it should definitely be possible within certain limits. Why else would you use Rust if you could just write your function in plain asm (much better tooling than inline asm)? Straight up banning locals is not a great solution, but since the compiler can't really reserve stack space either (naked --> no prolog) something like MSVC's __LOCAL_SIZE is probably inevitable. The really ugly part about that particular approach is that the compiler is forced to emit even useless prologs and epilogs (when no stack spilling is required). So one can either sacrifice performance by having them or sacrifice non-optimized builds altogether since rustc (like many compilers) likes to just hold everything on the stack and then rely heavily on the optimizer to eliminate these useless stack operations, simply resulting in broken code without that. |
This comment has been minimized.
This comment has been minimized.
aidancully
commented
Jul 16, 2015
|
Sorry for this, but... is this a problem that needs solving? I do a lot of systems programming, and do my best to avoid inline assembly where I can. Usually, what I'll do instead (especially for something like an interrupt handler), is write an assembly file and have it call out to a C-ABI routine to get a trampoline to more maintainable code. I don't yet see how |
This comment has been minimized.
This comment has been minimized.
|
@aidancully arbitrary codegen driven by Rust code, basically. static HANDLERS: [AtomicPtr<()>; 256] = [AtomicPtr::new(); 256];
#[naked]
unsafe fn handle_interrupt<F: Fn()>(number: u8) {
(*(HANDLERS[number as usize].load(Relaxed) as *const F))();
asm!("iret");
} |
This comment has been minimized.
This comment has been minimized.
|
Are there links to LLVM discussion on this topic? |
This comment has been minimized.
This comment has been minimized.
|
I haven't found any meaningful LLVM discussion on it. Support was added in 2009, r76198, which may be a good starting point for searching. |
This comment has been minimized.
This comment has been minimized.
Tobba
commented
Jul 16, 2015
|
@eddyb That code is most certainly broken. If the compiler ever spills a register, you're fucked. Since it's also an interrupt handler, it should avoid clobbering registers, which is impossible to do efficiently in any way that will please LLVM. In general the only way you're ever going to get the correct output is by having a single asm block within the function, which is just a hacky version of module-level assembly. |
This comment has been minimized.
This comment has been minimized.
main--
commented
Jul 17, 2015
While the RFC currently doesn't address this issue it's not a problem with naked functions in general as solving it is possible (and probably necessary; see my previous comment). |
This comment has been minimized.
This comment has been minimized.
aidancully
commented
Jul 18, 2015
|
@eddyb For what it's worth, I find that type of use plausible as a motivator, but I also worry that it might be very challenging to make static HANDLERS: [AtomicPtr<()>; 256] = [AtomicPtr::new(); 256];
#[naked]
unsafe fn handle_interrupt<F: Fn()>(number: u8)
{
// F::ABI's static functions are #[naked] #[inline].
F::ABI::create_calling_env();
(*(HANDLERS[number as usize].load(Relaxed) as *const F))();
F::ABI::destroy_calling_env();
asm!("iret");
}But if this sort of thing is necessary to safely support generic naked functions, it'd probably need to be reflected in the RFC. (Note that I don't think the whole change would need to land at once, the initial patch sounds pretty small and may be acceptable in the short term. But the design space in which the feature would be used probably needs to be explored better... I think there's a significant risk that, even though |
This comment has been minimized.
This comment has been minimized.
|
@aidancully if you don't generate multiple monomorphizations, you need to use indirect calls, which might be a measurable cost during interrupt handling - but it is a micro-optimization and I could very well be wrong about it. |
This comment has been minimized.
This comment has been minimized.
|
If I understand the current tack of the discussion now, the main concern is that the effect of writing code that is not inline assembly in a naked function is unpredictable. The correctness of the A way to ask the compiler how much space it needs for locals would permit a solution, but there's no mechanism to enable that in LLVM. I see a way forward in having an LLVM intrinsic function that is guaranteed to lower to a constant load representing the necessary size of locals, but I think such a significant change is beyond the scope of this proposal. With the |
This comment has been minimized.
This comment has been minimized.
Both cases could be accommodated by putting support for Rust statements in naked functions behind a feature gate; something like |
This comment has been minimized.
This comment has been minimized.
|
In a recent lang subteam meeting, we decided to close this PR. This RFC seems to have a lot in common with #55, which was closed by @brson with the following rationale:
The situation seems not to have changed very much -- basically it seems a bit premature to be introducing these sorts of constructs. The concerns about running Rust code in naked functions seem particularly salient. (Taking off my lang subteam hat for a moment and speaking personally, I think I would prefer to address this by writing naked functions in pure assembly and linking them in with LTO -- or having a variant on inline assembly that generates an entire function.) |
aturon
removed
the
I-nominated
label
Mar 17, 2016
This comment has been minimized.
This comment has been minimized.
vitiral
commented
Mar 18, 2016
|
I've only worked with interrupts in microcontrollers, but why can't you have the interrupt just use the existing stack? It is the nature of interrupts that they have to return before anything else can run. (this is how it works on a microcontroller) |
This comment has been minimized.
This comment has been minimized.
|
I've added an RFC (#1548) for module-level inline assembly. I think this feature can replace the need for naked functions in many cases. |
This comment has been minimized.
This comment has been minimized.
|
@Amanieu - That sounds like a much better idea. I've been pretty hesitant on naked functions, because they're either unknown magic, or exceptionally limited. All you get over module-level inline assembly is free naming and symbol size (assuming the pure-bare version of naked functions). |
This comment has been minimized.
This comment has been minimized.
arcnmx
commented
Mar 18, 2016
As I touched on in #1201 (comment), I personally feel like this is actually the main advantage of the RFC. You should be able to simulate both module-level asm and asm-only naked functions with a macro, so mostly I see this as an ergonomics discussion. Using rust code in naked functions is something I'm not really missing and don't have as much of an opinion on, but maybe following the llvm/clang/gcc semantics is good enough... I'd also support module-level inline assembly, as it's a rather useful feature, though if you can't use symbols/constants/etc. that's a pretty unfortunate limitation... Both module-level asm and naked functions are just plain things that should exist in Rust IMO. All too often the lower level aspects get neglected despite being branded as a systems language... |
This comment has been minimized.
This comment has been minimized.
ketsuban
commented
Mar 18, 2016
|
I have to admit the comments on this RFC have kind of lost me. Perhaps this just needs the ELI5 treatment. I started off following the interrupt calling convention RFC and followed the pointer here; I got the impression there were other unrelated problems and naked functions are a more general solution to both domains. Now I'm here, this RFC is in FCP and I still don't really understand why naked functions which can only contain a single block of inline assembly are useful. That's enough to call a proper function, sure, but at that point I might as well just make a small assembly file—I already have a larger assembly file which does the necessary prep work to start running Rust code in the first place, so it's not like I can avoid invoking an assembler at all. By comparison with the Visual C++ example on the OSdev wiki (the only listed compiler which uses naked functions rather than a special naked fn interrupt_handler() {
asm!("push rax");
asm!("push rcx");
// push everything ...
// Rust code to service the interrupt goes here
// pop everything ...
asm!("pop rcx");
asm!("pop rax");
asm!("iret");
}Am I mistaken? |
This comment has been minimized.
This comment has been minimized.
arcnmx
commented
Mar 18, 2016
Note that this isn't really true in all cases, so there's value in being able to avoid bringing in an external assembler entirely! I have an armv7m kernel that only uses asm for interrupt handler trampolines, all inline, and the entry point is pure rust, for example. |
This comment has been minimized.
This comment has been minimized.
The proposed #[naked] attribute indicates that the specified function does not need prologue/epilogue sequences generated in the compilation process. It is up to the programmer to provide these sequences. In basic terms, this makes rustc capable of doing all the low-level stuff which C is able to. Such as:
All in Rust, without needing external files. This is especially important when dealing with low-level stuff, such as OS development. We for example need this in Redox (an OS written in pure Rust) to get x86 support working. So this is really an important feature to us. An example for the curious would be a custom interrupt handler function:
This function will currently generate code as:
(truncated for simplicity) With the #[naked] attribute (which is implemented with PR #29189), it will bypass the prologue and epilogue sequences. And the output will be like:
Namely, it is a way of bypassing the automatically generated sequences, which pop/push to the stack. |
This comment has been minimized.
This comment has been minimized.
rustonaut
commented
Mar 21, 2016
|
Given that rust is meant to be a system level programming language, I think it is needed to have this feature (or a similar on) in rust. Through it is needed for low level tasks for many people given that this feature is not needed or even bad style in less low level code I would propose that (if available in stable) there is per default lint like Also even through module level asm seems to* be able to emulate naked fn's I think naked fn's are much clearer for use cases like interrupts. (* I might have missed something, but naked fn's can contains (some) "normal" rust code, but module level asm can't, or?) |
This comment has been minimized.
This comment has been minimized.
|
@Naicode Naked functions have to be assembly-only. |
This comment has been minimized.
This comment has been minimized.
rustonaut
commented
Mar 21, 2016
|
Ok, so module level asm and naked functions most likely can emulate each other. Hm, I't might be able to implement platform-specific interrupt handling by combining macros with naked functions, leaving naked functions still there for the other use cases. |
This comment has been minimized.
This comment has been minimized.
|
@Naicode You would want to implement the whole Rust language in macros? |
This comment has been minimized.
This comment has been minimized.
rustonaut
commented
Mar 21, 2016
|
@eddyb No! that would be horribly ;=) What I meant, is that, if the naked fn's can only contain asm, you would either write the complete ISR in assembly or delegate to a rust-fn at some point. This boiler part could be be written as a macro... e.g. the example from the rfc: #[naked]
#[cfg(target_arch="x86")]
unsafe fn isr_3() {
asm!("pushad
call increment_breakpoint_count
popad
iretd" :::: "volatile");
intrinsics::unreachable();
}could be written like: delegate_isr_3!(increment_breakpoint_count)Through in the end having a rust native ISR ABI might be more performant/useful Also you could pass a codeblock to a macro, but it probably wouldn't be a good idea to use it without wrapping it in a function because it might contain a return statement (and possible other "bad" thinks), but you could also have a way to forbid return statements in code block passed to macro, well that is a different topic. |
This comment has been minimized.
This comment has been minimized.
|
Huzzah! The @rust-lang/lang team has decided to accept this RFC. As previously stated, this is being accepted on a particularly experimental basis, especially with respect to the semantics (or lack thereof) of Rust code embedded in a naked function. One plausible route for stability would be to stabilize the |
nikomatsakis
referenced this pull request
Mar 21, 2016
Open
Tracking issue for naked fns (RFC #1201) #32408
nikomatsakis
merged commit 1cc857f
into
rust-lang:master
Mar 21, 2016
This comment has been minimized.
This comment has been minimized.
|
Tracking issue rust-lang/rust#32408. |
This comment has been minimized.
This comment has been minimized.
|
Thanks, thanks, thanks, @rust-lang/lang. |
This comment has been minimized.
This comment has been minimized.
|
PR submitted: rust-lang/rust#32410 |
tari commentedJul 11, 2015
•
edited by mbrubeck
Rendered.
[edited to link to the final rendered version]