about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2019-09-26 23:46:09 -0400
committerRich Felker <dalias@aerifal.cx>2019-09-26 23:46:09 -0400
commit370679ba984d8a466d635234dc7a3084e98c0071 (patch)
tree87045090bc2ba82ec0927dd46e214595b7e95ae6 /src
parent9f6dd78593e1d3b06f6e72f8479c91361e28ac28 (diff)
downloadmusl-370679ba984d8a466d635234dc7a3084e98c0071.tar.gz
musl-370679ba984d8a466d635234dc7a3084e98c0071.tar.xz
musl-370679ba984d8a466d635234dc7a3084e98c0071.zip
fix mips setjmp/longjmp fpu state on r6, related issues
mips32 has two fpu register file variants: FR=0 with 32 32-bit
registers, where pairs of neighboring even/odd registers are used to
represent doubles, and FR=1 with 32 64-bit registers, each of which
can store a single or double.

up through r5 (our "mips" arch), the supported ABI uses FR=0, but
modern compilers generate "fpxx" model code that can safely operate
with either model. r6, which is an incompatible but similar ISA, drops
FR=0 and only provides the FR=1 model. as such, setjmp and longjmp,
which depended on being able to save and restore call-saved doubles by
storing and loading their 32-bit halves, were completely broken in the
presence of floating point code on mips r6.

to fix this, use the s.d and l.d mnemonics to store and load fpu
registers. these expand to the existing swc1 and lwc1 instructions for
pairs of 32-bit fpu registers on mips1, but on mips2 and later they
translate directly to the 64-bit sdc1 and ldc1.

with FR=0, sdc1 and ldc1 behave just like the pairs of swc1 and lwc1
instructions they replace, storing or loading the even/odd pair of fpu
registers that can be treated as separate single-precision floats or
as a unit representing a double. but with FR=1, they store/load
individual 64-bit registers. this yields the ABI-correct behavior on
mips r6, and should make linking of pre-r6 (plain "mips") code with
"fp64" model code workable, although this is and will likely remain
unsupported usage.

in addition to the mips r6 problem this change fixes, reportedly
clang's internal assembler refuses to assemble swc1 and lwc1
instructions for odd register indices when building for "fpxx" model
(the default). this caused setjmp and longjmp not to build. by using
the s.d and l.d forms, this problem is avoided too.

as a bonus, code size is reduced everywhere but mips1.
Diffstat (limited to 'src')
-rw-r--r--src/setjmp/mips/longjmp.S18
-rw-r--r--src/setjmp/mips/setjmp.S18
2 files changed, 12 insertions, 24 deletions
diff --git a/src/setjmp/mips/longjmp.S b/src/setjmp/mips/longjmp.S
index fdb6c95d..ecf40855 100644
--- a/src/setjmp/mips/longjmp.S
+++ b/src/setjmp/mips/longjmp.S
@@ -12,18 +12,12 @@ longjmp:
 	addu    $2, $2, 1
 1:
 #ifndef __mips_soft_float
-	lwc1    $20, 56($4)
-	lwc1    $21, 60($4)
-	lwc1    $22, 64($4)
-	lwc1    $23, 68($4)
-	lwc1    $24, 72($4)
-	lwc1    $25, 76($4)
-	lwc1    $26, 80($4)
-	lwc1    $27, 84($4)
-	lwc1    $28, 88($4)
-	lwc1    $29, 92($4)
-	lwc1    $30, 96($4)
-	lwc1    $31, 100($4)
+	l.d     $f20, 56($4)
+	l.d     $f22, 64($4)
+	l.d     $f24, 72($4)
+	l.d     $f26, 80($4)
+	l.d     $f28, 88($4)
+	l.d     $f30, 96($4)
 #endif
 	lw      $ra,  0($4)
 	lw      $sp,  4($4)
diff --git a/src/setjmp/mips/setjmp.S b/src/setjmp/mips/setjmp.S
index 501d5264..7ae8832d 100644
--- a/src/setjmp/mips/setjmp.S
+++ b/src/setjmp/mips/setjmp.S
@@ -22,18 +22,12 @@ setjmp:
 	sw      $30, 40($4)
 	sw      $28, 44($4)
 #ifndef __mips_soft_float
-	swc1    $20, 56($4)
-	swc1    $21, 60($4)
-	swc1    $22, 64($4)
-	swc1    $23, 68($4)
-	swc1    $24, 72($4)
-	swc1    $25, 76($4)
-	swc1    $26, 80($4)
-	swc1    $27, 84($4)
-	swc1    $28, 88($4)
-	swc1    $29, 92($4)
-	swc1    $30, 96($4)
-	swc1    $31, 100($4)
+	s.d     $f20, 56($4)
+	s.d     $f22, 64($4)
+	s.d     $f24, 72($4)
+	s.d     $f26, 80($4)
+	s.d     $f28, 88($4)
+	s.d     $f30, 96($4)
 #endif
 	jr      $ra
 	li      $2, 0