https://github.com/ItsNoHax updated 
https://github.com/llvm/llvm-project/pull/218994

>From c744a02e4c1ac5b476c290d00b92ce0838d9f7b5 Mon Sep 17 00:00:00 2001
From: ItsNoHax <[email protected]>
Date: Wed, 26 Aug 2026 17:25:01 +0000
Subject: [PATCH] [libunwind][MIPS] Add o32 single-float support

A single-precision-only FPU (__mips_single_float, e.g. the PSP's Allegrex)
has no sdc1/ldc1, so both existing o32 register save/restore paths are
unencodable there and libunwind fails to assemble for such a target.

Add a single-float branch using swc1/lwc1 for all 32 registers. Each
register keeps an 8-byte slot, so the o32 context layout and unw_context_t
size are unchanged -- matching the existing note in Registers_mips_o32
about keeping a single context size across FPU configurations. Only the
low half of each slot is used.

The new branch is tested before the existing __mips_fpr checks because the
two compilers disagree on __mips_fpr for this configuration: GCC reports
32 and Clang reports 0. Both define __mips_single_float, so keying on that
first gives the same result under either.

Also teach the accessors about this configuration. Previously
validFloatRegister() returned false and get/setFloatRegister() aborted
with "mips_o32 float support not implemented" for anything other than
__mips_fpr == 64. Under single float each $fN is an independent 32-bit
register with no even/odd pairing, so getRegister()/setRegister() address
the slot directly instead of using the __mips_fpr == 32 pairing scheme.

The float area offsets are duplicated in assembly, where the compiler
cannot check them, so add a compile-only test pinning the o32 context
layout that UnwindRegistersSave.S and UnwindRegistersRestore.S rely on.
---
 libunwind/src/Registers.hpp                   | 47 +++++++++++--
 libunwind/src/UnwindRegistersRestore.S        | 36 +++++++++-
 libunwind/src/UnwindRegistersSave.S           | 38 ++++++++++-
 .../mips_o32_context_layout.compile.pass.cpp  | 33 +++++++++
 .../mips_o32_single_float_registers.pass.cpp  | 67 +++++++++++++++++++
 5 files changed, 215 insertions(+), 6 deletions(-)
 create mode 100644 libunwind/test/mips_o32_context_layout.compile.pass.cpp
 create mode 100644 libunwind/test/mips_o32_single_float_registers.pass.cpp

diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index d79d836d69250..d95a1e610a639 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -12,6 +12,7 @@
 #ifndef __REGISTERS_HPP__
 #define __REGISTERS_HPP__
 
+#include <stddef.h>
 #include <stdint.h>
 #include <string.h>
 
@@ -3067,6 +3068,19 @@ class _LIBUNWIND_HIDDEN Registers_mips_o32 {
 inline Registers_mips_o32::Registers_mips_o32(const void *registers) {
   static_assert((check_fit<Registers_mips_o32, unw_context_t>::does_fit),
                 "mips_o32 registers do not fit into unw_context_t");
+#ifdef __mips_hard_float
+  // UnwindRegistersSave.S and UnwindRegistersRestore.S address the floating
+  // point area as (4 * 36 + 8 * N)($4), for N in [0, 32). Those constants are
+  // duplicated in assembly, where the compiler cannot check them, so pin the
+  // layout they assume here.
+  static_assert(offsetof(Registers_mips_o32, _floats) == 4 * 36,
+                "float area base offset does not match the (4 * 36) used by "
+                "UnwindRegistersSave.S and UnwindRegistersRestore.S");
+  static_assert(sizeof(_floats[0]) == 8,
+                "float slot stride does not match the (8 * N) used by "
+                "UnwindRegistersSave.S and UnwindRegistersRestore.S");
+  static_assert(sizeof(_floats) == 8 * 32, "float area must cover f0-f31");
+#endif
   memcpy(&_registers, static_cast<const uint8_t *>(registers),
          sizeof(_registers));
 }
@@ -3090,7 +3104,8 @@ inline bool Registers_mips_o32::validRegister(int regNum) 
const {
   if (regNum == UNW_MIPS_LO)
     return true;
 #endif
-#if defined(__mips_hard_float) && __mips_fpr == 32
+#if defined(__mips_hard_float) &&                                              
\
+    (__mips_fpr == 32 || defined(__mips_single_float))
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31)
     return true;
 #endif
@@ -3101,7 +3116,15 @@ inline bool Registers_mips_o32::validRegister(int 
regNum) const {
 inline uint32_t Registers_mips_o32::getRegister(int regNum) const {
   if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31)
     return _registers.__r[regNum - UNW_MIPS_R0];
-#if defined(__mips_hard_float) && __mips_fpr == 32
+#if defined(__mips_hard_float) && defined(__mips_single_float)
+  // Every register is an independent 32-bit value held in the low half of its
+  // slot; there is no even/odd pairing.
+  if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
+    uint32_t result;
+    memcpy(&result, &_floats[regNum - UNW_MIPS_F0], sizeof(result));
+    return result;
+  }
+#elif defined(__mips_hard_float) && __mips_fpr == 32
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
     uint32_t *p;
 
@@ -3133,7 +3156,13 @@ inline void Registers_mips_o32::setRegister(int regNum, 
uint32_t value) {
     _registers.__r[regNum - UNW_MIPS_R0] = value;
     return;
   }
-#if defined(__mips_hard_float) && __mips_fpr == 32
+#if defined(__mips_hard_float) && defined(__mips_single_float)
+  // See the matching comment in getRegister().
+  if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
+    memcpy(&_floats[regNum - UNW_MIPS_F0], &value, sizeof(value));
+    return;
+  }
+#elif defined(__mips_hard_float) && __mips_fpr == 32
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
     uint32_t *p;
 
@@ -3166,7 +3195,8 @@ inline void Registers_mips_o32::setRegister(int regNum, 
uint32_t value) {
 }
 
 inline bool Registers_mips_o32::validFloatRegister(int regNum) const {
-#if defined(__mips_hard_float) && __mips_fpr == 64
+#if defined(__mips_hard_float) &&                                              
\
+    (__mips_fpr == 64 || defined(__mips_single_float))
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31)
     return true;
 #else
@@ -3179,6 +3209,11 @@ inline double Registers_mips_o32::getFloatRegister(int 
regNum) const {
 #if defined(__mips_hard_float) && __mips_fpr == 64
   assert(validFloatRegister(regNum));
   return _floats[regNum - UNW_MIPS_F0];
+#elif defined(__mips_hard_float) && defined(__mips_single_float)
+  assert(validFloatRegister(regNum));
+  float result;
+  memcpy(&result, &_floats[regNum - UNW_MIPS_F0], sizeof(result));
+  return result;
 #else
   (void)regNum;
   _LIBUNWIND_ABORT("mips_o32 float support not implemented");
@@ -3190,6 +3225,10 @@ inline void Registers_mips_o32::setFloatRegister(int 
regNum,
 #if defined(__mips_hard_float) && __mips_fpr == 64
   assert(validFloatRegister(regNum));
   _floats[regNum - UNW_MIPS_F0] = value;
+#elif defined(__mips_hard_float) && defined(__mips_single_float)
+  assert(validFloatRegister(regNum));
+  float single = static_cast<float>(value);
+  memcpy(&_floats[regNum - UNW_MIPS_F0], &single, sizeof(single));
 #else
   (void)regNum;
   (void)value;
diff --git a/libunwind/src/UnwindRegistersRestore.S 
b/libunwind/src/UnwindRegistersRestore.S
index 37e8156408cf2..b299bcd0a7594 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -1012,7 +1012,41 @@ 
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv)
   .set noreorder
   .set nomacro
 #ifdef __mips_hard_float
-#if __mips_fpr != 64
+#if defined(__mips_single_float)
+  // See the matching comment in UnwindRegistersSave.S.
+  lwc1  $f0, (4 * 36 + 8 * 0)($4)
+  lwc1  $f1, (4 * 36 + 8 * 1)($4)
+  lwc1  $f2, (4 * 36 + 8 * 2)($4)
+  lwc1  $f3, (4 * 36 + 8 * 3)($4)
+  lwc1  $f4, (4 * 36 + 8 * 4)($4)
+  lwc1  $f5, (4 * 36 + 8 * 5)($4)
+  lwc1  $f6, (4 * 36 + 8 * 6)($4)
+  lwc1  $f7, (4 * 36 + 8 * 7)($4)
+  lwc1  $f8, (4 * 36 + 8 * 8)($4)
+  lwc1  $f9, (4 * 36 + 8 * 9)($4)
+  lwc1  $f10, (4 * 36 + 8 * 10)($4)
+  lwc1  $f11, (4 * 36 + 8 * 11)($4)
+  lwc1  $f12, (4 * 36 + 8 * 12)($4)
+  lwc1  $f13, (4 * 36 + 8 * 13)($4)
+  lwc1  $f14, (4 * 36 + 8 * 14)($4)
+  lwc1  $f15, (4 * 36 + 8 * 15)($4)
+  lwc1  $f16, (4 * 36 + 8 * 16)($4)
+  lwc1  $f17, (4 * 36 + 8 * 17)($4)
+  lwc1  $f18, (4 * 36 + 8 * 18)($4)
+  lwc1  $f19, (4 * 36 + 8 * 19)($4)
+  lwc1  $f20, (4 * 36 + 8 * 20)($4)
+  lwc1  $f21, (4 * 36 + 8 * 21)($4)
+  lwc1  $f22, (4 * 36 + 8 * 22)($4)
+  lwc1  $f23, (4 * 36 + 8 * 23)($4)
+  lwc1  $f24, (4 * 36 + 8 * 24)($4)
+  lwc1  $f25, (4 * 36 + 8 * 25)($4)
+  lwc1  $f26, (4 * 36 + 8 * 26)($4)
+  lwc1  $f27, (4 * 36 + 8 * 27)($4)
+  lwc1  $f28, (4 * 36 + 8 * 28)($4)
+  lwc1  $f29, (4 * 36 + 8 * 29)($4)
+  lwc1  $f30, (4 * 36 + 8 * 30)($4)
+  lwc1  $f31, (4 * 36 + 8 * 31)($4)
+#elif __mips_fpr != 64
   ldc1  $f0, (4 * 36 + 8 * 0)($4)
   ldc1  $f2, (4 * 36 + 8 * 2)($4)
   ldc1  $f4, (4 * 36 + 8 * 4)($4)
diff --git a/libunwind/src/UnwindRegistersSave.S 
b/libunwind/src/UnwindRegistersSave.S
index 37acbf3c69322..c51b5b8d17664 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -229,7 +229,43 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   sw    $8,  (4 * 34)($4)
 #endif
 #ifdef __mips_hard_float
-#if __mips_fpr != 64
+#if defined(__mips_single_float)
+  // A single-precision-only FPU has no sdc1. Each register still occupies
+  // an 8-byte slot so that the context layout matches the other o32 FPU
+  // configurations; only the low half of each slot is used.
+  swc1  $f0, (4 * 36 + 8 * 0)($4)
+  swc1  $f1, (4 * 36 + 8 * 1)($4)
+  swc1  $f2, (4 * 36 + 8 * 2)($4)
+  swc1  $f3, (4 * 36 + 8 * 3)($4)
+  swc1  $f4, (4 * 36 + 8 * 4)($4)
+  swc1  $f5, (4 * 36 + 8 * 5)($4)
+  swc1  $f6, (4 * 36 + 8 * 6)($4)
+  swc1  $f7, (4 * 36 + 8 * 7)($4)
+  swc1  $f8, (4 * 36 + 8 * 8)($4)
+  swc1  $f9, (4 * 36 + 8 * 9)($4)
+  swc1  $f10, (4 * 36 + 8 * 10)($4)
+  swc1  $f11, (4 * 36 + 8 * 11)($4)
+  swc1  $f12, (4 * 36 + 8 * 12)($4)
+  swc1  $f13, (4 * 36 + 8 * 13)($4)
+  swc1  $f14, (4 * 36 + 8 * 14)($4)
+  swc1  $f15, (4 * 36 + 8 * 15)($4)
+  swc1  $f16, (4 * 36 + 8 * 16)($4)
+  swc1  $f17, (4 * 36 + 8 * 17)($4)
+  swc1  $f18, (4 * 36 + 8 * 18)($4)
+  swc1  $f19, (4 * 36 + 8 * 19)($4)
+  swc1  $f20, (4 * 36 + 8 * 20)($4)
+  swc1  $f21, (4 * 36 + 8 * 21)($4)
+  swc1  $f22, (4 * 36 + 8 * 22)($4)
+  swc1  $f23, (4 * 36 + 8 * 23)($4)
+  swc1  $f24, (4 * 36 + 8 * 24)($4)
+  swc1  $f25, (4 * 36 + 8 * 25)($4)
+  swc1  $f26, (4 * 36 + 8 * 26)($4)
+  swc1  $f27, (4 * 36 + 8 * 27)($4)
+  swc1  $f28, (4 * 36 + 8 * 28)($4)
+  swc1  $f29, (4 * 36 + 8 * 29)($4)
+  swc1  $f30, (4 * 36 + 8 * 30)($4)
+  swc1  $f31, (4 * 36 + 8 * 31)($4)
+#elif __mips_fpr != 64
   sdc1  $f0, (4 * 36 + 8 * 0)($4)
   sdc1  $f2, (4 * 36 + 8 * 2)($4)
   sdc1  $f4, (4 * 36 + 8 * 4)($4)
diff --git a/libunwind/test/mips_o32_context_layout.compile.pass.cpp 
b/libunwind/test/mips_o32_context_layout.compile.pass.cpp
new file mode 100644
index 0000000000000..62c20b9e74e29
--- /dev/null
+++ b/libunwind/test/mips_o32_context_layout.compile.pass.cpp
@@ -0,0 +1,33 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: target={{mips.*}}
+
+// UnwindRegistersSave.S and UnwindRegistersRestore.S address the o32 floating
+// point area as (4 * 36 + 8 * N)($4), for N in [0, 32).
+//
+// The base offset and the per-register stride are asserted in Registers.hpp,
+// where _floats is in scope. This is the outer check on the context as a
+// whole, and is what fails first if the floating point area is narrowed to the
+// width the FPU actually uses: the 8-byte stride is deliberate, so that a
+// single context size covers every o32 FPU configuration.
+
+#include "../src/config.h"
+
+#if defined(_LIBUNWIND_TARGET_MIPS_O32) && defined(__mips_hard_float)
+
+#include "../src/Registers.hpp"
+
+using namespace libunwind;
+
+static_assert(sizeof(Registers_mips_o32) == 4 * 36 + 8 * 32,
+              "the o32 context layout no longer matches the offsets hard-coded 
"
+              "in UnwindRegistersSave.S and UnwindRegistersRestore.S");
+
+#endif // _LIBUNWIND_TARGET_MIPS_O32 && __mips_hard_float
diff --git a/libunwind/test/mips_o32_single_float_registers.pass.cpp 
b/libunwind/test/mips_o32_single_float_registers.pass.cpp
new file mode 100644
index 0000000000000..c70391c611b27
--- /dev/null
+++ b/libunwind/test/mips_o32_single_float_registers.pass.cpp
@@ -0,0 +1,67 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: target={{mips.*}}
+
+// Exercise the o32 single-float register accessors. Before single float was
+// supported these paths either reported the floating point registers as
+// invalid or aborted with "mips_o32 float support not implemented".
+
+#include "../src/config.h"
+
+#if defined(_LIBUNWIND_TARGET_MIPS_O32) && defined(__mips_hard_float) &&       
\
+    defined(__mips_single_float)
+
+#include "../src/Registers.hpp"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace libunwind;
+
+int main(int, char **) {
+  Registers_mips_o32 regs;
+
+  for (int n = UNW_MIPS_F0; n <= UNW_MIPS_F31; ++n) {
+    if (!regs.validRegister(n))
+      abort();
+    if (!regs.validFloatRegister(n))
+      abort();
+  }
+
+  // Under single float each register is independent, with no even/odd pairing,
+  // so a distinct value per register has to survive a round trip.
+  for (int n = UNW_MIPS_F0; n <= UNW_MIPS_F31; ++n)
+    regs.setRegister(n, 0xf0000000u + static_cast<uint32_t>(n - UNW_MIPS_F0));
+  for (int n = UNW_MIPS_F0; n <= UNW_MIPS_F31; ++n) {
+    if (regs.getRegister(n) !=
+        0xf0000000u + static_cast<uint32_t>(n - UNW_MIPS_F0))
+      abort();
+  }
+
+  // 2.5 is exactly representable in single precision, so this round trip is
+  // not subject to rounding.
+  regs.setFloatRegister(UNW_MIPS_F7, 2.5);
+  if (regs.getFloatRegister(UNW_MIPS_F7) != 2.5)
+    abort();
+
+  // The raw and floating point views address the same storage.
+  float single = 2.5f;
+  uint32_t bits;
+  memcpy(&bits, &single, sizeof(bits));
+  if (regs.getRegister(UNW_MIPS_F7) != bits)
+    abort();
+
+  return 0;
+}
+
+#else
+int main(int, char **) { return 0; }
+#endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to