about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-02-28 19:30:01 +0100
committerFolkert de Vries <folkert@folkertdev.nl>2025-03-08 14:10:29 +0100
commitf35bda3997d285618b267f1b25eebff2ff467387 (patch)
tree051f4a7cd2a23e3fddfee7ea452b473e125b914a
parentc620d76ae359d6005b91da7009aa652053ba6b47 (diff)
downloadrust-f35bda3997d285618b267f1b25eebff2ff467387.tar.gz
rust-f35bda3997d285618b267f1b25eebff2ff467387.zip
support XCOFF in `naked_asm!`
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/naked_asm.rs50
-rw-r--r--tests/assembly/naked-functions/aix.rs35
2 files changed, 76 insertions, 9 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
index 0593fb420c3..cfe96c45cbd 100644
--- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
@@ -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)
diff --git a/tests/assembly/naked-functions/aix.rs b/tests/assembly/naked-functions/aix.rs
new file mode 100644
index 00000000000..cc0825b3738
--- /dev/null
+++ b/tests/assembly/naked-functions/aix.rs
@@ -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")
+}