-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
remove @setAlignStack
, replacing it with function incoming stack alignment attributes
#21209
Comments
If I understood correctly this proposal also has a nice bonus of exposing this alignment information to reflection via |
I like this.
Should functions in safe builds assert that the stack pointer is correctly aligned upon entry? This would help catch issues like the one you described calling Zig functions in the kernel, if the user forgot to specify that the stack pointer is expected to be under-aligned. Anecdotally, I had a similar issue where I didn't align the stack correctly while calling Zig from some inline assembly, and it was very hard to debug as the only segfault was deep inside some My hesitations with that safety check are:
|
A safety check would probably be a nice idea. As you say, it could be omitted on all internal functions. A simple rule is that we include the check if a function is Regarding describing the necessary alignment, this should be fine -- this proposal requires Zig to be aware of the details of stack alignment anyway. Implementation-wise, this safety check would probably be a single |
Accepted for now is the |
You don't need a safety check on things that would only ever fail due to a compiler bug. Once you safety check the incoming stack pointer, the alignment at all call sites is guaranteed by the backend and cannot be affected by user error in a way that would not be better served by just checking that certain things don't affect the stack pointer (inline asm, calls with certain calling conventions, etc.). Also an air instruction wouldn't really work because you need to safety check the alignment before the stack is realigned to where it needs to be for the current function which happens before the first air instruction. |
Yes, the safety check I am proposing is precisely what you seem to be talking about there -- a safety check for the incoming stack pointer when a function might be called into from external code. Hence my proposed rule which models the situations in which external code could make such a call:
|
I'm not sure where
That said, I'm not sure how you know whether the address of a function is taken the first time you codegen it. |
Eh, I think it's fine for that to be unchecked. It's not like we can check for this more generally -- for instance, you could also change the actual base cc with a ptrcast, and there's no way to check for safety there. The safety check here is really about ABI boundaries, because they're the thing that's very easy to get wrong.
Um, good point! Okay, simpler rule: we just include the check for any callconv other than |
Another good argument for |
@mlugg here's the branch I was working on: https://github.com/alexrp/zig/commits/call-convs I don't think I have more to do at this point; further (Can also help dig up whatever ABI details you might need to decide on the direction for this.) |
When discussing this change with @andrewrk and @jacobly0, I floated the idea of |
I can imagine a world where |
This commit begins implementing accepted proposal ziglang#21209 by making `std.builtin.CallingConvention` a tagged union. The stage1 dance here is a little convoluted. This commit introduces the new type as `NewCallingConvention`, keeping the old `CallingConvention` around. The compiler uses `std.builtin.NewCallingConvention` exclusively, but when fetching the type from `std` when running the compiler (e.g. with `getBuiltinType`), the name `CallingConvention` is used. This allows a prior build of Zig to be used to build this commit. The next commit will update `zig1.wasm`, and then the compiler and standard library can be updated to completely replace `CallingConvention` with `NewCallingConvention`. The second half of ziglang#21209 is to remove `@setAlignStack`, which will be implemented in another commit after updating `zig1.wasm`.
This commit finishes implementing ziglang#21209 by removing the `@setAlignStack` builtin in favour of `CallingConvention` payloads. The x86_64 backend is updated to use the stack alignment given in the calling convention (the LLVM backend was already updated in a previous commit). Resolves: ziglang#21209
This commit begins implementing accepted proposal ziglang#21209 by making `std.builtin.CallingConvention` a tagged union. The stage1 dance here is a little convoluted. This commit introduces the new type as `NewCallingConvention`, keeping the old `CallingConvention` around. The compiler uses `std.builtin.NewCallingConvention` exclusively, but when fetching the type from `std` when running the compiler (e.g. with `getBuiltinType`), the name `CallingConvention` is used. This allows a prior build of Zig to be used to build this commit. The next commit will update `zig1.wasm`, and then the compiler and standard library can be updated to completely replace `CallingConvention` with `NewCallingConvention`. The second half of ziglang#21209 is to remove `@setAlignStack`, which will be implemented in another commit after updating `zig1.wasm`.
This commit finishes implementing ziglang#21209 by removing the `@setAlignStack` builtin in favour of `CallingConvention` payloads. The x86_64 backend is updated to use the stack alignment given in the calling convention (the LLVM backend was already updated in a previous commit). Resolves: ziglang#21209
This commit begins implementing accepted proposal ziglang#21209 by making `std.builtin.CallingConvention` a tagged union. The stage1 dance here is a little convoluted. This commit introduces the new type as `NewCallingConvention`, keeping the old `CallingConvention` around. The compiler uses `std.builtin.NewCallingConvention` exclusively, but when fetching the type from `std` when running the compiler (e.g. with `getBuiltinType`), the name `CallingConvention` is used. This allows a prior build of Zig to be used to build this commit. The next commit will update `zig1.wasm`, and then the compiler and standard library can be updated to completely replace `CallingConvention` with `NewCallingConvention`. The second half of ziglang#21209 is to remove `@setAlignStack`, which will be implemented in another commit after updating `zig1.wasm`.
This commit begins implementing accepted proposal ziglang#21209 by making `std.builtin.CallingConvention` a tagged union. The stage1 dance here is a little convoluted. This commit introduces the new type as `NewCallingConvention`, keeping the old `CallingConvention` around. The compiler uses `std.builtin.NewCallingConvention` exclusively, but when fetching the type from `std` when running the compiler (e.g. with `getBuiltinType`), the name `CallingConvention` is used. This allows a prior build of Zig to be used to build this commit. The next commit will update `zig1.wasm`, and then the compiler and standard library can be updated to completely replace `CallingConvention` with `NewCallingConvention`. The second half of ziglang#21209 is to remove `@setAlignStack`, which will be implemented in another commit after updating `zig1.wasm`.
This commit finishes implementing ziglang#21209 by removing the `@setAlignStack` builtin in favour of `CallingConvention` payloads. The x86_64 backend is updated to use the stack alignment given in the calling convention (the LLVM backend was already updated in a previous commit). Resolves: ziglang#21209
Background
The
@setAlignStack
builtin is weird. It doesn't provide any meaningful guarantees: all it does is enforce a stack alignment in the function's prologue. There is no guarantee that any local variable is aligned to this boundary, nor that calls to other functions preserve the stack alignment.It does have one theoretical use case, which is correcting the stack alignment when a function will be called with a lower stack alignment than it expects. For instance, the Linux kernel on x86_64 compiles with
-mpreferred-stack-boundary=3
, which overrides the default 16-byte stack alignment on calls mandated by the System V ABI to a lower 8-byte stack alignment. This means that when compiling Zig code to use in a Linux kernel module, the functions are called with a lower stack alignment than necessary. This isn't catastrophic, since the only x86_64 instructions which rely on alignment are for things like SSE which are disabled in kernel code, but LLVM could still theoretically use this incorrect assumption of stack alignment, triggering a miscompilation.Today,
@setAlignStack
does work for this use case with the LLVM backend: however, this is kind of an implementation detail, and self-hosted backends don't do it.We could redefine
@setAlignStack
to be used for this purpose. However, it's a bit weird for this to be a builtin at that point, and its purpose becomes unintuitive.Proposal
Remove the
@setAlignStack
builtin. Add a new syntax to functions, in or aftercallconv
, to override their incoming stack alignment, i.e. the alignment callers will enforce before acall
instruction.Since we're effectively overriding part of the calling convention here, I think it would make sense for this information to be embedded in the
callconv(...)
syntax. For instance, perhapscallconv(.c : .{ .incoming_stack_align = 8 })
, where the init expression there is for astd.builtin.CallingConventionOverrides
:This would allow Zig functions which will be called by e.g. the Linux kernel to specify their incoming stack alignment as 8 bytes. This would instruct code generation to re-align the stack as needed in the function prologue.
Using a struct here opens the door to adding more ways to customise your calling convention in the future. Taking this to an extreme, one could imagine a system where
std.builtin.CallingConvention
becomes a struct detailing every part of the calling convention, but that's probably not desirable due to the complexity of many callconvs.If we want to avoid the
callconv(foo : bar)
syntax (which is arguably a little ugly), we could makeCallingConvention
a struct with a "base" callconv and override fields, and make use of #9938 to make today's uses work:I'm indifferent as to what we go with: I think
callconv(.c : .{ .incoming_stack_align = 8 })
would be fine, but I can certainly see the appeal of unifying this into one struct.The text was updated successfully, but these errors were encountered: