Skip to content

Support building for multiple MIPS ABIs: o32 (default), n32 and eabi32#135

Merged
Yanis002 merged 5 commits intoHackerN64:mainfrom
Thar0:abi
Nov 27, 2025
Merged

Support building for multiple MIPS ABIs: o32 (default), n32 and eabi32#135
Yanis002 merged 5 commits intoHackerN64:mainfrom
Thar0:abi

Conversation

@Thar0
Copy link
Copy Markdown
Contributor

@Thar0 Thar0 commented Jun 14, 2024

The MIPS Application Binary Interface (ABI) dictates code generation conventions such as register sizes and classification, stack frame layout and alignment, and floating-point register behavior. The default ABI that has been used so far is o32, the original 32-bit MIPS ABI that a reader may be familiar with if they have done any assembly hacking.

o32 features:

  • 32 32-bit General Purpose Registers (GPRs)
  • 32-bit pointer size
  • 16 32-bit or 64-bit Floating Point Registers (FPRs) (accessing each half of a 64-bit float in an even or odd numbered floating point register)
  • 2 GPR return-value registers (v0 and v1)
  • 4 GPR argument registers (a0 through a3)
  • Functions with more than 4 args spill the rest onto the stack
  • 2 FPR return-value registers (f0 and f2)
  • 2 FPR argument registers (f12 and f14)
  • Floats that are passed as arguments after integers are passed through GPRs rather than FPRs
  • Stack frame alignment is 8 bytes / 64-bit
  • A caller function must allocate 16 bytes (4x 32-bit) of stack space for a called function to use.

This PR adds support for the n32 and eabi32 ABIs. These are more recent MIPS ABIs that were designed to try to make better use of the evolving hardware. o32 was designed for MIPS I which lacked 64-bit instructions and double-precision floats were split into two 32-bit halves. The VR4300 processor is a MIPS III processor with 64-bit instructions and a more advanced floating-point coprocessor, so it can support these newer ABIs.

n32 features:

  • 32 64-bit General Purpose Registers (GPRs)
  • 32-bit pointer size
  • 32 64-bit Floating Point Registers (FPRs)
  • 2 GPR return-value registers (v0 and v1)
  • 8 GPR argument registers (a0 through a7, where a4 through a7 replace t0 through t3 in o32)
  • Functions with more than 8 args spill the rest onto the stack
  • 2 FPR return-value registers (f0 and f1)
  • 8 FPR argument registers (f12 through f19, including odd numbers)
  • More sane calling convention for functions with mixed integer/float args
  • Stack frame alignment is 16 bytes / 128-bit
  • A caller function has no stack responsibilities with respect to a called function.

eabi32 features:

  • 32 32-bit General Purpose Registers (GPRs)
  • 32-bit pointer size
  • 16 32-bit or 64-bit Floating Point Registers (FPRs) (accessing each half of a 64-bit float in an even or odd numbered floating point register)
  • 2 GPR return-value registers (v0 and v1)
  • 8 GPR argument registers (a0 through a7, where a4 through a7 replace t0 through t3 in o32)
  • 2 FPR return-value registers (f0 and f2)
  • 4 FPR argument registers (f12, f14, f16, f18)
  • More sane calling convention for functions with mixed integer/float args
  • Stack frame alignment is 8 bytes / 64-bit
  • A caller function has no stack responsibilities with respect to a called function.

Each of these ABIs has their own pros and cons.

  • o32 has a poor calling convention with very minimal register usage, often spilling args onto the stack or performing unnecessary moves between GPRs and FPRs. It also has no support for 64-bit arithmetic instructions or load/stores, emulating them with 32-bit instructions instead. However since the GPRs are not treated as 64-bit the stack frame is kept small, which is nice for memory performance on the N64.
  • n32 can use 64-bit arithmetic instructions and has double the number of floating-point registers however the stack frame size grows non-negligibly due to needing to store 64-bit values in the stack frame. Further, context switching during interrupt and exception handling overhead increases due to needing to save double the number of floating-point registers.
  • eabi is generally understood to be strictly better than o32, since it lacks the downsides of n32 while incorporating various nice features from it (mostly calling convention), Compared to n32 it lacks the full set of FPRs and 64-bit arithmetic, however this allows it to keep stack frames small which has a non-negligible impact on memory performance on the N64.

Copy link
Copy Markdown
Collaborator

@sauraen sauraen left a comment

Choose a reason for hiding this comment

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

Looks good and eabi32 works for me (builds and seems to run fine on console).

n32 does not work for me, I get Fatal error: selected target format 'elf32-nbigmips' unknown from the assembler.

>hackeroot_clean$ mips64-as --version
GNU assembler (GNU Binutils) 2.39
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `mips64'.
>hackeroot_clean$ which mips64-as
/opt/n64/bin/mips64-as

Is there a reason we don't move to eabi by default?

@Thar0
Copy link
Copy Markdown
Contributor Author

Thar0 commented Jun 19, 2024

Could you show the output of mips64-ld --help | grep "supported targets"?

We may want to consider moving to eabi32 by default at some point, but it can probably be done after the initial support is in.

@sauraen
Copy link
Copy Markdown
Collaborator

sauraen commented Jun 22, 2024

>~$ mips64-ld --help | grep "supported targets"
mips64-ld: supported targets: elf32-bigmips elf32-littlemips elf64-bigmips elf64-littlemips elf64-little elf64-big elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex plugin

@sauraen
Copy link
Copy Markdown
Collaborator

sauraen commented Jun 22, 2024

On my system I also have a mips-linux-gnu- compiler, installed through the package manager, but the manually compiled mips64 one was selected by the Makefile. Note that elf32-nbigmips is not a supported target for this one either.

>~$ mips-linux-gnu-ld --help | grep "supported targets"
mips-linux-gnu-ld: supported targets: elf32-tradbigmips elf32-tradlittlemips ecoff-bigmips ecoff-littlemips elf32-ntradbigmips elf64-tradbigmips elf32-ntradlittlemips elf64-tradlittlemips elf64-little elf64-big elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex plugin
>~$ mips-linux-gnu-as --version
GNU assembler (GNU Binutils for Debian) 2.35.2
Copyright (C) 2020 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `mips-linux-gnu'.
>~$ which mips-linux-gnu-as
/usr/bin/mips-linux-gnu-as

@Thar0
Copy link
Copy Markdown
Contributor Author

Thar0 commented Jun 22, 2024

It looks like your compiled mips64 toolchain wasn't configured to include the n32 elf format. mips-linux-gnu can compile and link n32 using the irix 6 / "trad" formats which are supported by this build system (LD_OUTPUT_FORMAT in the makefile tries to automatically detect an output format) but mips-linux-gnu isn't great since it's often a pretty outdated gcc version.

Is this glank's toolchain? If so, it received an update a little while ago that lets it build for n32.

Copy link
Copy Markdown
Collaborator

@sauraen sauraen left a comment

Choose a reason for hiding this comment

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

n32 also works with the latest glank toolchain. There is a separate issue with this toolchain and function prototypes being moved, but that is unrelated to this PR.

Copy link
Copy Markdown
Collaborator

@sauraen sauraen left a comment

Choose a reason for hiding this comment

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

I take back my last comment. The build issues are introduced by this PR, even though the PR does not change anything directly related to those headers.

Copy link
Copy Markdown
Collaborator

@sauraen sauraen left a comment

Choose a reason for hiding this comment

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

Works for me now. Thanks for changing to a newer ABI by default.

@Yanis002
Copy link
Copy Markdown
Collaborator

I'll have to test this again before merging it, I mixed things up with the lighting engine PR, I think I got build issues on this branch and not the other one

@Yanis002
Copy link
Copy Markdown
Collaborator

Yanis002 commented Nov 4, 2025

I'll try this again soon, I think I had some issues building this but idk maybe it's fine now, if everything's fine I'll merge this

@Yanis002 Yanis002 added the waiting for review Waiting for a contributor to review the pull request label Nov 4, 2025
@Yanis002 Yanis002 changed the base branch from develop/2.1.0 to main November 26, 2025 16:38
Copy link
Copy Markdown
Collaborator

@Yanis002 Yanis002 left a comment

Choose a reason for hiding this comment

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

seems to work properly for me 🎉

Comment thread Makefile
@Yanis002 Yanis002 added merge soon The pull request will be merged in 24 hours if nothing else comes up and removed waiting for review Waiting for a contributor to review the pull request labels Nov 26, 2025
@Yanis002
Copy link
Copy Markdown
Collaborator

will merge once CI is done, sorry for the long wait 🙏

@Yanis002 Yanis002 merged commit e77e668 into HackerN64:main Nov 27, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge soon The pull request will be merged in 24 hours if nothing else comes up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants