Rollup merge of #137816 - folkertdev:naked-asm-xcoff, r=Noratrieb

attempt to support `BinaryFormat::Xcoff` in `naked_asm!`

Fixes https://github.com/rust-lang/rust/issues/137219

So, the inline assembly support for xcoff is extremely limited. The LLVM [XCOFFAsmParser](1b25c0c4da/llvm/lib/MC/MCParser/XCOFFAsmParser.cpp) does not support many of the attributes that LLVM itself emits, and that should exist based on [the assembler docs](https://www.ibm.com/docs/en/ssw_aix_71/assembler/assembler_pdf.pdf). It also does accept some that should not exist based on those docs.

So, I've tried to do the best I can given those limitations. At least it's better than emitting the directives for elf and having that fail somewhere deep in LLVM. Given that inline assembly for this target is incomplete (under `asm_experimental_arch`), I think that's OK (and again I don't see how we can do better given the limitations in LLVM).

r? ```@Noratrieb``` (given that you reviewed https://github.com/rust-lang/rust/pull/136637)

It seems reasonable to ping the [`powerpc64-ibm-aix` target maintainers](https://doc.rust-lang.org/rustc/platform-support/aix.html), hopefully they have thoughts too: ```@daltenty``` ```@gilamn5tr```
This commit is contained in:
Matthias Krüger 2025-03-13 11:28:20 +01:00 committed by GitHub
commit 762acf53cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 76 additions and 9 deletions

View file

@ -125,7 +125,8 @@ fn prefix_and_suffix<'tcx>(
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
// if no alignment is specified, an alignment of 4 bytes is used.
let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
let align_bytes =
Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
let (arch_prefix, arch_suffix) = if is_arm {
@ -157,12 +158,16 @@ fn prefix_and_suffix<'tcx>(
}
Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
match asm_binary_format {
BinaryFormat::Elf
| BinaryFormat::Coff
| BinaryFormat::Wasm
| BinaryFormat::Xcoff => {
BinaryFormat::Elf | BinaryFormat::Coff | BinaryFormat::Wasm => {
writeln!(w, ".weak {asm_name}")?;
}
BinaryFormat::Xcoff => {
// FIXME: there is currently no way of defining a weak symbol in inline assembly
// for AIX. See https://github.com/llvm/llvm-project/issues/130269
emit_fatal(
"cannot create weak symbols from inline assembly for this target",
)
}
BinaryFormat::MachO => {
writeln!(w, ".globl {asm_name}")?;
writeln!(w, ".weak_definition {asm_name}")?;
@ -189,7 +194,7 @@ fn prefix_and_suffix<'tcx>(
let mut begin = String::new();
let mut end = String::new();
match asm_binary_format {
BinaryFormat::Elf | BinaryFormat::Xcoff => {
BinaryFormat::Elf => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
let progbits = match is_arm {
@ -203,7 +208,7 @@ fn prefix_and_suffix<'tcx>(
};
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
writeln!(begin, ".balign {align}").unwrap();
writeln!(begin, ".balign {align_bytes}").unwrap();
write_linkage(&mut begin).unwrap();
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".hidden {asm_name}").unwrap();
@ -224,7 +229,7 @@ fn prefix_and_suffix<'tcx>(
BinaryFormat::MachO => {
let section = link_section.unwrap_or("__TEXT,__text".to_string());
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
writeln!(begin, ".balign {align}").unwrap();
writeln!(begin, ".balign {align_bytes}").unwrap();
write_linkage(&mut begin).unwrap();
if let Visibility::Hidden = item_data.visibility {
writeln!(begin, ".private_extern {asm_name}").unwrap();
@ -240,7 +245,7 @@ fn prefix_and_suffix<'tcx>(
BinaryFormat::Coff => {
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
writeln!(begin, ".balign {align}").unwrap();
writeln!(begin, ".balign {align_bytes}").unwrap();
write_linkage(&mut begin).unwrap();
writeln!(begin, ".def {asm_name}").unwrap();
writeln!(begin, ".scl 2").unwrap();
@ -279,6 +284,33 @@ fn prefix_and_suffix<'tcx>(
// .size is ignored for function symbols, so we can skip it
writeln!(end, "end_function").unwrap();
}
BinaryFormat::Xcoff => {
// the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the
// documented directives.
//
// - https://github.com/llvm/llvm-project/blob/1b25c0c4da968fe78921ce77736e5baef4db75e3/llvm/lib/MC/MCParser/XCOFFAsmParser.cpp
// - https://www.ibm.com/docs/en/ssw_aix_71/assembler/assembler_pdf.pdf
//
// Consequently, we try our best here but cannot do as good a job as for other binary
// formats.
// FIXME: start a section. `.csect` is not currently implemented in LLVM
// fun fact: according to the assembler documentation, .align takes an exponent,
// but LLVM only accepts powers of 2 (but does emit the exponent)
// so when we hand `.align 32` to LLVM, the assembly output will contain `.align 5`
writeln!(begin, ".align {}", align_bytes).unwrap();
write_linkage(&mut begin).unwrap();
if let Visibility::Hidden = item_data.visibility {
// FIXME apparently `.globl {asm_name}, hidden` is valid
// but due to limitations with `.weak` (see above) we can't really use that in general yet
}
writeln!(begin, "{asm_name}:").unwrap();
writeln!(end).unwrap();
// FIXME: end the section?
}
}
(begin, end)

View file

@ -0,0 +1,35 @@
//@ revisions: elfv1-be aix
//@ add-core-stubs
//@ assembly-output: emit-asm
//
//@[elfv1-be] compile-flags: --target powerpc64-unknown-linux-gnu
//@[elfv1-be] needs-llvm-components: powerpc
//
//@[aix] compile-flags: --target powerpc64-ibm-aix
//@[aix] needs-llvm-components: powerpc
#![crate_type = "lib"]
#![feature(no_core, naked_functions, asm_experimental_arch, f128, linkage, fn_align)]
#![no_core]
// tests that naked functions work for the `powerpc64-ibm-aix` target.
//
// This target is special because it uses the XCOFF binary format
// It is tested alongside an elf powerpc target to pin down commonalities and differences.
//
// https://doc.rust-lang.org/rustc/platform-support/aix.html
// https://www.ibm.com/docs/en/aix/7.2?topic=formats-xcoff-object-file-format
extern crate minicore;
use minicore::*;
// elfv1-be: .p2align 2
// aix: .align 2
// CHECK: .globl blr
// CHECK-LABEL: blr:
// CHECK: blr
#[no_mangle]
#[naked]
unsafe extern "C" fn blr() {
naked_asm!("blr")
}