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

codegen #[naked] functions using global asm #128004

Merged
merged 4 commits into from
Dec 12, 2024

Conversation

folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Jul 20, 2024

tracking issue: #90957

Fixes #124375

This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of #[naked] functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc).

I discussed this approach with @Amanieu and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about.

Combined with #127853, if both accepted, I think that resolves all steps from the tracking issue.

r? @Amanieu

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 20, 2024
compiler/rustc_codegen_ssa/src/codegen_attrs.rs Outdated Show resolved Hide resolved
compiler/rustc_codegen_ssa/src/mir/mod.rs Outdated Show resolved Hide resolved
compiler/rustc_codegen_ssa/src/mir/naked_asm.rs Outdated Show resolved Hide resolved
compiler/rustc_codegen_ssa/src/mir/naked_asm.rs Outdated Show resolved Hide resolved
compiler/rustc_codegen_ssa/src/mir/naked_asm.rs Outdated Show resolved Hide resolved
tests/codegen/naked-fn/x86_64-linux.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@tgross35
Copy link
Contributor

On the tracking issue a naked_asm! macro was proposed that would closer follow global_asm! (cc @Lokathor since you liked this idea). It sounds like this PR effectively turns #[naked] + asm! into exactly what naked_asm would do, so that would no longer be necessary?

The changes in this PR seem like good direction.

@Lokathor
Copy link
Contributor

I think that separately from any internal implementation change, the surface syntax of rust should use naked_asm separately from asm, because the two have different enough user interface and semantics.

@folkertdev
Copy link
Contributor Author

I agree that it is still a good idea to add naked_asm! as a public api, if only because it makes the documentation much more straightforward: instead of having to explain the interaction between #[naked] and asm!, we just mandate that #[naked] must use a naked_asm! block and the naked_asm! docs can give the exact details and restrictions.

But that is separate from how the codegen works, which is what this PR is for.

Comment on lines +182 to +216
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".hidden {asm_name}").unwrap();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've not been able to actually generate code that triggers this if. Visibility just seems to always be Default. So this is entirely untested at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

For anything like this, drop a question on Zulip https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp. Unfortunately that enum doesn't seem to be very well documented

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

}

fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
Copy link
Member

Choose a reason for hiding this comment

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

This function can probably just take TyCtxt.

cx.tcx(),
value.span,
const_value,
cx.layout_of(value.ty()),
Copy link
Member

Choose a reason for hiding this comment

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

Ah, this layout_of call uses the CodegenCx. This is during codegen though, so RevealAllLayoutCx (a wrapper around TyCtxt) is enough: https://github.com/rust-lang/rustc_codegen_cranelift/blob/b70ad2defd4bb5fba6af7958893e22be0f33dfdd/src/common.rs#L450-L518 Maybe it should be uplifted out of cg_clif?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

seems useful, should that be part of this PR though?

@folkertdev

This comment was marked as resolved.

@folkertdev folkertdev marked this pull request as ready for review July 21, 2024 15:05
@rustbot rustbot added the A-run-make Area: port run-make Makefiles to rmake.rs label Jul 26, 2024
@rustbot

This comment was marked as outdated.

@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the naked-fn-asm branch 2 times, most recently from f54a458 to 6783e26 Compare July 26, 2024 17:32
@bors
Copy link
Contributor

bors commented Jul 28, 2024

☔ The latest upstream changes (presumably #128298) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Jul 29, 2024

☔ The latest upstream changes (presumably #125443) made this pull request unmergeable. Please resolve the merge conflicts.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 11, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 11, 2024
codegen `#[naked]` functions using global asm

tracking issue: rust-lang#90957

Fixes rust-lang#124375

This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc).

I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about.

Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue.

r? `@Amanieu`
@bors
Copy link
Contributor

bors commented Dec 11, 2024

⌛ Testing commit 9aabef1 with merge 87ea957...

@rust-log-analyzer
Copy link
Collaborator

The job i686-mingw failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
   Compiling rustc_driver v0.0.0 (C:\a\rust\rust\compiler\rustc_driver)
[RUSTC-TIMING] rustc_driver test:false 33.399
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
  |
  = note: "i686-w64-mingw32-gcc" "-fno-use-linker-plugin" "-Wl,--dynamicbase" "-Wl,--disable-auto-image-base" "-Wl,--large-address-aware" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\rsbegin.o" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\symbols.o" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\deps\\rustc_main-659084248a5844d9.rustc_main.92a1136b98cc9864-cgu.0.rcgu.o" "-Wl,-Bdynamic" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\deps\\rustc_driver-feab0383960d185a.dll" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\std-85e8e1afc0a1e2c0.dll" "-Wl,-Bstatic" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libcompiler_builtins-d77c9d76f065957b.rlib" "-Wl,-Bdynamic" "-lpsapi" "-lshell32" "-lole32" "-luuid" "-ladvapi32" "-lws2_32" "-lntdll" "-lkernel32" "-ladvapi32" "-lole32" "-loleaut32" "-ladvapi32" "-lcfgmgr32" "-lgdi32" "-lkernel32" "-lmsimg32" "-lopengl32" "-luser32" "-lwinspool" "-lbcrypt" "-ladvapi32" "-lkernel32" "-lkernel32" "-ladvapi32" "-lntdll" "-luserenv" "-lws2_32" "-ldbghelp" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\advapi32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-errorhandling-l1-1-3.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-file-fromapp-l1-1-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-handle-l1-1-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-ioring-l1-1-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-3.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-4.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-5.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-6.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-7.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-memory-l1-1-8.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-synch-l1-2-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-sysinfo-l1-2-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-sysinfo-l1-2-3.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-sysinfo-l1-2-4.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-sysinfo-l1-2-6.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-util-l1-1-1.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-winrt-error-l1-1-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-winrt-l1-1-0.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-core-wow64-l1-1-1.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\api-ms-win-security-base-l1-2-2.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\avrt.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\bcp47mrm.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\bcryptprimitives.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\clfsw32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\dbghelp.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\elscore.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\gdi32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\icu.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\imagehlp.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\kernel32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\ktmw32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\netapi32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\normaliz.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\ntdll.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\ntdllk.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\ole32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\oleacc.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\oleaut32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\propsys.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\psapi.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\rtworkq.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\txfw32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\user32.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\usp10.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\version.dll_imports_indirect.lib" "C:\\a\\_temp\\msys64\\tmp\\rustcTHle8Z\\wofutil.dll_imports_indirect.lib" "-lgcc_s" "-lmsvcrt" "-lmingwex" "-lmingw32" "-lgcc" "-lmsvcrt" "-lmingwex" "-luser32" "-lkernel32" "-Wl,--nxcompat" "-L" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\build\\stacker-d3f5f2020a9db92d\\out" "-L" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\build\\psm-a8e1851838120981\\out" "-L" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\build\\blake3-0d7379973d66d445\\out" "-L" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\build\\rustc_llvm-9c1d12dc9076cada\\out" "-L" "C:/a/rust/rust/build/i686-pc-windows-gnu/llvm/lib" "-L" "C:/a/rust/rust/mingw32/bin/../lib/gcc/i686-w64-mingw32/14.1.0" "-o" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1-rustc\\i686-pc-windows-gnu\\release\\deps\\rustc_main-659084248a5844d9.exe" "-Wl,--gc-sections" "-no-pie" "-Wl,-O1" "-nodefaultlibs" "C:\\a\\rust\\rust\\build\\i686-pc-windows-gnu\\stage1\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\rsend.o"
  = note: C:/a/rust/rust/mingw32/bin/../lib/gcc/i686-w64-mingw32/14.1.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot open output file C:\a\rust\rust\build\i686-pc-windows-gnu\stage1-rustc\i686-pc-windows-gnu\release\deps\rustc_main-659084248a5844d9.exe: Invalid argument␍
          collect2.exe: error: ld returned 1 exit status

[RUSTC-TIMING] rustc_main test:false 4.157
error: could not compile `rustc-main` (bin "rustc-main") due to 1 previous error
Build completed unsuccessfully in 0:35:17

@bors
Copy link
Contributor

bors commented Dec 11, 2024

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Dec 11, 2024
@tgross35 tgross35 added the CI-spurious-fail-mingw CI spurious failure: target env mingw label Dec 11, 2024
@tgross35
Copy link
Contributor

tgross35 commented Dec 11, 2024

@bors retry

For reference, the x86_64-mingw-1 failure must have been spurious since it passed in the previous run.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 11, 2024
@bors
Copy link
Contributor

bors commented Dec 11, 2024

⌛ Testing commit 9aabef1 with merge 1daec06...

@bors
Copy link
Contributor

bors commented Dec 12, 2024

☀️ Test successful - checks-actions
Approved by: Amanieu
Pushing 1daec06 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 12, 2024
@bors bors merged commit 1daec06 into rust-lang:master Dec 12, 2024
7 checks passed
@rustbot rustbot added this to the 1.85.0 milestone Dec 12, 2024
@Lokathor
Copy link
Contributor

Question: My understanding of global_asm is that it can't be culled during linking, even if the global_asm is never called. Does #[naked] now also prevent a function from being cut from the final binary even when it's not called?

@bors bors mentioned this pull request Dec 12, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (1daec06): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results (primary 1.2%, secondary -4.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
1.2% [1.2%, 1.2%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-4.0% [-4.0%, -4.0%] 1
All ❌✅ (primary) 1.2% [1.2%, 1.2%] 1

Cycles

Results (secondary 3.2%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.2% [2.5%, 4.0%] 5
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 771.021s -> 769.999s (-0.13%)
Artifact size: 330.96 MiB -> 331.03 MiB (0.02%)

@Amanieu
Copy link
Member

Amanieu commented Dec 12, 2024

Question: My understanding of global_asm is that it can't be culled during linking, even if the global_asm is never called. Does #[naked] now also prevent a function from being cut from the final binary even when it's not called?

That's not true, global_asm absolutely can be culled during linking. It usually isn't because it emits to .text by default, but naked functions are emitted to a separate section which can be excluded from linking if they are not used.

@nbdd0121
Copy link
Contributor

This breaks use of CFI instructions in unwinding crate: nbdd0121/unwinding#41

@folkertdev
Copy link
Contributor Author

@bjorn3 is this related to the .eh_frame? that is, should the code author write the .cfg_startproc/.cfi_endproc directives in this case, or is that actually the responsibility of the compiler?

We certainly can emit those directives, but I don't know whether we should, or leave it to the assembly author.

@Amanieu
Copy link
Member

Amanieu commented Dec 13, 2024

These directives need to be provided by the assembly author. It is up to them to declare that the naked function has valid unwind info.

@nbdd0121
Copy link
Contributor

nbdd0121 commented Dec 13, 2024

Before this change the CFI instructions are emitted by LLVM and it's illegal to have .cfi_startproc inside naked asm.

EDIT: This was the behaviour for previous Rust compiler and C compilers: https://godbolt.org/z/qff8hjE8e

@Amanieu
Copy link
Member

Amanieu commented Dec 14, 2024

The previous behavior was actually context-dependent: the compiler may or may not insert cfi_startproc depending on various factors such as whether the panic mode is unwind or abort, whether unwind info is force-enabled, etc.

This makes writing code that uses CFI directives quite brittle and it's a problem I've hit before in libfringe (the predecessor of corosensei). It's much better to let users provide .cfi_startproc when they are providing unwind info for a naked function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-run-make Area: port run-make Makefiles to rmake.rs CI-spurious-fail-mingw CI spurious failure: target env mingw merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ICE: codegen: index out of bounds: the len is 3 but the index is 4