Author: Alberto
Date: 2026-10-12T13:12:17+01:00
New Revision: 69f90dd74332a117fd7988309ea00a1cba2aca3d

URL: 
https://github.com/llvm/llvm-project/commit/69f90dd74332a117fd7988309ea00a1cba2aca3d
DIFF: 
https://github.com/llvm/llvm-project/commit/69f90dd74332a117fd7988309ea00a1cba2aca3d.diff

LOG: [libunwind][MIPS] Add o32 single-float support (#218994)

On FPUs that only support single precision, such as the PSP's Allegrex,
libunwind fails to assemble for o32 because the save/restore code uses
sdc1/ldc1. Save and restore all 32 FPRs with swc1/lwc1 instead, keeping
each register in an 8-byte slot so the context layout is the same for
every o32 FPU configuration.

Check __mips_single_float before __mips_fpr: GCC defines __mips_fpr as
32 for single-float targets and Clang defines it as 0, but both define
__mips_single_float.

In Registers.hpp, treat each FPR as an independent 32-bit value when
__mips_single_float is set, rather than pairing even/odd registers.
Add a compile-only test that checks the size of the o32 context.

Added: 
    libunwind/test/mips_o32_context_layout.compile.pass.cpp
    libunwind/test/mips_o32_single_float_registers.pass.cpp

Modified: 
    libunwind/src/Registers.hpp
    libunwind/src/UnwindRegistersRestore.S
    libunwind/src/UnwindRegistersSave.S

Removed: 
    


################################################################################
diff  --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 7101bb21abda5..4870e315962e0 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>
 
@@ -3068,6 +3069,15 @@ 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
+  // UnwindRegisters{Save,Restore}.S address this area as (4 * 36 + 8 * N)($4).
+  static_assert(offsetof(Registers_mips_o32, _floats) == 4 * 36,
+                "mips_o32 float area offset does not match the .S files");
+  static_assert(sizeof(_floats[0]) == 8,
+                "mips_o32 float slot stride does not match the .S files");
+  static_assert(sizeof(_floats) == 8 * 32,
+                "mips_o32 float area must cover f0-f31");
+#endif
   memcpy(&_registers, static_cast<const uint8_t *>(registers),
          sizeof(_registers));
 }
@@ -3091,9 +3101,11 @@ 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)
+#if defined(__mips_single_float) || __mips_fpr == 32
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31)
     return true;
+#endif
 #endif
   // FIXME: DSP accumulator registers, MSA registers
   return false;
@@ -3102,7 +3114,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)
+#if defined(__mips_single_float)
+  // Single float has 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 __mips_fpr == 32
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
     uint32_t *p;
 
@@ -3112,6 +3132,7 @@ inline uint32_t Registers_mips_o32::getRegister(int 
regNum) const {
       p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1;
     return *p;
   }
+#endif
 #endif
 
   switch (regNum) {
@@ -3134,7 +3155,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)
+#if defined(__mips_single_float)
+  if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
+    memcpy(&_floats[regNum - UNW_MIPS_F0], &value, sizeof(value));
+    return;
+  }
+#elif __mips_fpr == 32
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) {
     uint32_t *p;
 
@@ -3145,6 +3172,7 @@ inline void Registers_mips_o32::setRegister(int regNum, 
uint32_t value) {
     *p = value;
     return;
   }
+#endif
 #endif
 
   switch (regNum) {
@@ -3167,35 +3195,49 @@ 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)
+#if defined(__mips_single_float) || __mips_fpr == 64
   if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31)
     return true;
-#else
-  (void)regNum;
 #endif
+#endif
+  (void)regNum;
   return false;
 }
 
 inline double Registers_mips_o32::getFloatRegister(int regNum) const {
-#if defined(__mips_hard_float) && __mips_fpr == 64
+#if defined(__mips_hard_float)
+#if defined(__mips_single_float)
+  assert(validFloatRegister(regNum));
+  float result;
+  memcpy(&result, &_floats[regNum - UNW_MIPS_F0], sizeof(result));
+  return result;
+#elif __mips_fpr == 64
   assert(validFloatRegister(regNum));
   return _floats[regNum - UNW_MIPS_F0];
-#else
+#endif
+#endif
   (void)regNum;
   _LIBUNWIND_ABORT("mips_o32 float support not implemented");
-#endif
 }
 
 inline void Registers_mips_o32::setFloatRegister(int regNum,
                                                  double value) {
-#if defined(__mips_hard_float) && __mips_fpr == 64
+#if defined(__mips_hard_float)
+#if defined(__mips_single_float)
+  assert(validFloatRegister(regNum));
+  float single = static_cast<float>(value);
+  memcpy(&_floats[regNum - UNW_MIPS_F0], &single, sizeof(single));
+  return;
+#elif __mips_fpr == 64
   assert(validFloatRegister(regNum));
   _floats[regNum - UNW_MIPS_F0] = value;
-#else
+  return;
+#endif
+#endif
   (void)regNum;
   (void)value;
   _LIBUNWIND_ABORT("mips_o32 float support not implemented");
-#endif
 }
 
 inline bool Registers_mips_o32::validVectorRegister(int /* regNum */) const {

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..8e0cb23b95763
--- /dev/null
+++ b/libunwind/test/mips_o32_context_layout.compile.pass.cpp
@@ -0,0 +1,29 @@
+// -*- 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.*}}
+
+// The o32 context is sized for 32 double-width float slots even when the FPU
+// only holds 32-bit values, so that one context size covers every o32 FPU
+// configuration. Registers.hpp asserts the base offset and stride that
+// UnwindRegisters{Save,Restore}.S depend on; this is the outer size check.
+
+#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