ALTERNATIVE to the previous patch -- apply one or the other, not both.
This one applies to an unpatched tree, not on top of 1/2.
Hand-writing this helper is close to unique. ia64 is the only other target
with a moddi3 in assembly, and its reason does not transfer -- lib1funcs.S
builds it out of the FP divide sequence from the Intel optimization guide.
visium supplies a C version, pa rebuilds the generic one for a 32-bit word,
and arm/symbian, avr, bpf and nds32-mculib exclude the helper outright. No
other port hand-writes radix-2 soft division. Since this one turned out to
be wrong for almost every input, the obvious question is whether it should
exist at all.
Drop it and stop excluding _moddi3. __udivmoddi4, which the generic
__moddi3 calls, is not in LIB2FUNCS_EXCLUDE, so nothing else is needed.
Xilinx already did this, twelve years ago. Commit 7090a49d007 in their GCC
fork, 2014-04-18, by Nagaraju Mekala -- "[Patch, microblaze] Removed moddi3
routinue" -- deletes the file, with the whole message being "Using the default
moddi3 function as the existing implementation has many bugs". Its
t-microblaze hunk is identical to the one below. It was never submitted
upstream, so both their 4.8.3 and 13.3.0 trees have been on the generic path
while upstream kept the routine.
This removes 121 lines of assembly no other port has an equivalent of, and
removes the endianness hazard entirely: the C version reads the halves
through a union rather than by register number.
The cost is size and speed. Measured on qemu-system-microblaze and
qemu-system-microblazeel over 4701 values, counting executed instructions
exactly with -icount 0, against the assembly as 1/2 leaves it:
code size executed insns
assembly __moddi3 44 be 2777735 le 2777672
generic C at -O2 62 be 3995016 le 3988505
+ __udivmoddi4 be 1030
le 1028
total be 1092 +43.8% +43.6%
le 1090
So the assembly is about 25 times smaller and runs about 30% fewer
instructions, some 258 per call. The size figure is the worst case:
__udivmoddi4 is
shared, so a program that already does 64-bit unsigned division links it
anyway and the generic __moddi3 then costs only 62 instructions.
The two also differ for x % 0, which is undefined either way. The assembly
returns zero, matching the port's other software helpers. The generic
version returns the low word of the dividend -- 0x123456789abc % 0 gives
0x9abc -- because libgcc2.c does "d0 = 1 / d0" to provoke a hardware trap,
and on MicroBlaze that is a call to __udivsi3, which itself returns zero
for a zero divisor, so no trap occurs and the algorithm runs on with d0
still zero. For reference, neither matches the hardware-divide path: with
idiv available GCC implements a % b as a - (a / b) * b, so a % 0 gives a.
Correctness is otherwise equal. Both pass
gcc.c-torture/execute/arith-rand-ll.c on both byte orders, where the
unpatched tree aborts, and both answer all 8192 of a corner-case set that
drives the assembly's partial remainder to 2^63 -- every pair there has
LLONG_MIN as the dividend, which is where the unpatched routine either hangs
or returns nonsense.
libgcc/
* config/microblaze/moddi3.S: Delete.
* config/microblaze/t-microblaze (LIB2FUNCS_EXCLUDE): Do not
exclude _moddi3.
(LIB2ADD): Remove moddi3.S.
Signed-off-by: Sam Price <[email protected]>
Assisted-by: Claude (Anthropic)
---
libgcc/config/microblaze/moddi3.S | 121 --------------------------
libgcc/config/microblaze/t-microblaze | 3 +-
2 files changed, 1 insertion(+), 123 deletions(-)
delete mode 100644 libgcc/config/microblaze/moddi3.S
diff --git a/libgcc/config/microblaze/moddi3.S
b/libgcc/config/microblaze/moddi3.S
deleted file mode 100644
index 71a67c11c..000000000
--- a/libgcc/config/microblaze/moddi3.S
+++ /dev/null
@@ -1,121 +0,0 @@
-###################################
-#
-# Copyright (C) 2009-2026 Free Software Foundation, Inc.
-#
-# Contributed by Michael Eager <[email protected]>.
-#
-# This file is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 3, or (at your option) any
-# later version.
-#
-# GCC is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-# License for more details.
-#
-# Under Section 7 of GPL version 3, you are granted additional
-# permissions described in the GCC Runtime Library Exception, version
-# 3.1, as published by the Free Software Foundation.
-#
-# You should have received a copy of the GNU General Public License and
-# a copy of the GCC Runtime Library Exception along with this program;
-# see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
-# <http://www.gnu.org/licenses/>.
-#
-# modsi3.S
-#
-# modulo operation for 64 bit integers.
-#
-#######################################
-
-
-/* An executable stack is *not* required for these functions. */
-#ifdef __linux__
-.section .note.GNU-stack,"",%progbits
-.previous
-#endif
-
- .globl __moddi3
- .ent __moddi3
-__moddi3:
- .frame r1,0,r15
-
-#Change the stack pointer value and Save callee saved regs
- addik r1,r1,-24
- swi r25,r1,0
- swi r26,r1,4
- swi r27,r1,8 # used for sign
- swi r28,r1,12 # used for loop count
- swi r29,r1,16 # Used for div value High
- swi r30,r1,20 # Used for div value Low
-
-#Check for Zero Value in the divisor/dividend
- OR r9,r5,r6 # Check for the op1 being zero
- BEQID r9,$LaResult_Is_Zero # Result is zero
- OR r9,r7,r8 # Check for the dividend being
zero
- BEQI r9,$LaDiv_By_Zero # Div_by_Zero # Division Error
- BGEId r5,$La1_Pos
- XOR r27,r5,r7 # Get the sign of the result
- RSUBI r6,r6,0 # Make dividend positive
- RSUBIC r5,r5,0 # Make dividend positive
-$La1_Pos:
- BGEI r7,$La2_Pos
- RSUBI r8,r8,0 # Make Divisor Positive
- RSUBIC r9,r9,0 # Make Divisor Positive
-$La2_Pos:
- ADDIK r4,r0,0 # Clear mod low
- ADDIK r3,r0,0 # Clear mod high
- ADDIK r29,r0,0 # clear div high
- ADDIK r30,r0,0 # clear div low
- ADDIK r28,r0,64 # Initialize the loop count
- # First part try to find the first '1' in the r5/r6
-$LaDIV1:
- ADD r6,r6,r6
- ADDC r5,r5,r5 # left shift logical r5
- BGEID r5,$LaDIV1
- ADDIK r28,r28,-1
-$LaDIV2:
- ADD r6,r6,r6
- ADDC r5,r5,r5 # left shift logical r5/r6 get the '1' into the
Carry
- ADDC r4,r4,r4 # Move that bit into the Mod register
- ADDC r3,r3,r3 # Move carry into high mod register
- rsub r18,r7,r3 # Compare the High Parts of Mod and Divisor
- bnei r18,$L_High_EQ
- rsub r18,r6,r4 # Compare Low Parts only if Mod[h] == Divisor[h]
-$L_High_EQ:
- rSUB r26,r8,r4 # Subtract divisor[L] from Mod[L]
- rsubc r25,r7,r3 # Subtract divisor[H] from Mod[H]
- BLTi r25,$LaMOD_TOO_SMALL
- OR r3,r0,r25 # move r25 to mod [h]
- OR r4,r0,r26 # move r26 to mod [l]
- ADDI r30,r30,1
- ADDC r29,r29,r0
-$LaMOD_TOO_SMALL:
- ADDIK r28,r28,-1
- BEQi r28,$LaLOOP_END
- ADD r30,r30,r30 # Shift in the '1' into div [low]
- ADDC r29,r29,r29 # Move the carry generated into high
- BRI $LaDIV2 # Div2
-$LaLOOP_END:
- BGEI r27,$LaRETURN_HERE
- rsubi r30,r30,0
- rsubc r29,r29,r0
- BRI $LaRETURN_HERE
-$LaDiv_By_Zero:
-$LaResult_Is_Zero:
- or r29,r0,r0 # set result to 0 [High]
- or r30,r0,r0 # set result to 0 [Low]
-$LaRETURN_HERE:
-# Restore values of CSRs and that of r29 and the divisor and the dividend
-
- lwi r25,r1,0
- lwi r26,r1,4
- lwi r27,r1,8
- lwi r28,r1,12
- lwi r29,r1,16
- lwi r30,r1,20
- rtsd r15,8
- addik r1,r1,24
- .end __moddi3
-
diff --git a/libgcc/config/microblaze/t-microblaze
b/libgcc/config/microblaze/t-microblaze
index 96959f029..8d954a495 100644
--- a/libgcc/config/microblaze/t-microblaze
+++ b/libgcc/config/microblaze/t-microblaze
@@ -1,8 +1,7 @@
-LIB2FUNCS_EXCLUDE += _divsi3 _modsi3 _moddi3 _mulsi3 _udivsi3 _umodsi3
+LIB2FUNCS_EXCLUDE += _divsi3 _modsi3 _mulsi3 _udivsi3 _umodsi3
LIB2ADD += \
$(srcdir)/config/microblaze/divsi3.S \
- $(srcdir)/config/microblaze/moddi3.S \
$(srcdir)/config/microblaze/modsi3.S \
$(srcdir)/config/microblaze/muldi3_hard.S \
$(srcdir)/config/microblaze/mulsi3.S \
--
2.39.5 (Apple Git-154)