Skip to contentSkip to conversation

MIPS division by zero and libgcj...

RFC David Daney 4 participants 10 messages Jun 10, 2004 – Jun 11, 2004 MIPS

lore
Share & tools
lore
#0 lore raw

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

This causes the kernel (I am using 2.4.25) to send SIGTRAP.

Gcj when configured for this platform uses the -f-use-divide-subroutine option, causing it to never generate inline division instructions (nor the accompanying break 7), but instead call a runtime function that properly handles throwing ArithmeticException.

Q1: Is this behavior (use of break 7 and SIGTRAP) part of some ABI specification? I'll admit a bit of ignorance in this area.

Q2: Does anyone see any reason that libgcj should not be configured to handle the SIGTRAP and throw the ArithmeticException from the signal handler (similar to what is done on i386).

Q3: Will using SIGTRAP in this manner make debugging programs that divide things by zero very difficult to debug under gdb?

Q4: I appears that on the i386, a divide overflow causes SIGFPE. Why doesn't the mips-linux kernel sent SIGFPE on "break 7"

Q5: prims.cc in libgcj implies that we should be handling SIGFPE to do all this. If I make these changes, won't it be a little confusing that all references to SIGFPE are really SIGTRAP?

David Daney

#1 lore raw
Quoted 2 lines · David Daney

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

I thought that the MIPS never generated a hardware trap for division, but instead there was an assembler macro that did the test for overflow, and the "div" instruction actually generates this test inline. Maybe do a disassembly to check.

Quoted 9 lines

This causes the kernel (I am using 2.4.25) to send SIGTRAP.

Gcj when configured for this platform uses the -f-use-divide-subroutine option, causing it to never generate inline division instructions (nor the accompanying break 7), but instead call a runtime function that properly handles throwing ArithmeticException.

Q1: Is this behavior (use of break 7 and SIGTRAP) part of some ABI specification? I'll admit a bit of ignorance in this area.

See above.

Quoted 3 lines

Q2: Does anyone see any reason that libgcj should not be configured to handle the SIGTRAP and throw the ArithmeticException from the signal handler (similar to what is done on i386).

No, there's no reason not to do it. You'll have to write some hairy code to satisfy all the rules, though.

Quoted 2 lines

Q3: Will using SIGTRAP in this manner make debugging programs that divide things by zero very difficult to debug under gdb?

No.

Quoted 6 lines

Q4: I appears that on the i386, a divide overflow causes SIGFPE. Why doesn't the mips-linux kernel sent SIGFPE on "break 7"

Q5: prims.cc in libgcj implies that we should be handling SIGFPE to do all this. If I make these changes, won't it be a little confusing that all references to SIGFPE are really SIGTRAP?

Feel free to change the comments. :-)

Andrew.

#2 lore raw
Quoted 8 lines · Andrew Haley
Quoted 2 lines · David Daney

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

I thought that the MIPS never generated a hardware trap for division, but instead there was an assembler macro that did the test for overflow, and the "div" instruction actually generates this test inline. Maybe do a disassembly to check.

Here we are:

MIPS Dependent Features

--trap
--no-break

automatically macro expands certain division and multiplication instructions to check for overflow and division by zero. This option causes to generate code to take a trap exception rather than a break exception when an error is detected. The trap instructions are only supported at Instruction Set Architecture level 2 and higher.

--break
--no-trap

Generate code to take a break exception rather than a trap exception when an error is detected. This is the default.

Andrew.

#3 lore raw
In reply to Andrew Haley
Quoted 9 lines · Andrew Haley
Quoted 1 line · David Daney

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

I thought that the MIPS never generated a hardware trap for division, but instead there was an assembler macro that did the test for overflow, and the "div" instruction actually generates this test inline. Maybe do a disassembly to check.

MIPS div instructions never trap. However I think that GCC always emits things like this when it cannot determine that the divisor is non zero:

       div     $0,$17,$16
       bne     $16,$0,1f
       nop
       break   7

1:

Quoted 4 lines
Quoted 4 lines

This causes the kernel (I am using 2.4.25) to send SIGTRAP.

Gcj when configured for this platform uses the -f-use-divide-subroutine option, causing it to never generate inline division instructions (nor the accompanying break 7), but instead call a runtime function that properly handles throwing ArithmeticException.

Quoted 9 lines
Quoted 1 line

Q1: Is this behavior (use of break 7 and SIGTRAP) part of some ABI specification? I'll admit a bit of ignorance in this area.

See above.

Quoted 1 line

Q2: Does anyone see any reason that libgcj should not be configured to handle the SIGTRAP and throw the ArithmeticException from the signal handler (similar to what is done on i386).

No, there's no reason not to do it. You'll have to write some hairy code to satisfy all the rules, though.

What are the rules? Are they more complicated then throw an ArithmeticException when the divisor is zero?

Quoted 6 lines
Quoted 2 lines

Q3: Will using SIGTRAP in this manner make debugging programs that divide things by zero very difficult to debug under gdb?

No.

I have not tried it. But I think gdb uses "break" and SIGTRAP for breakpoints. Is it possible to get gdb to pass the signal to the debugee, so that it could be handled by the runtime support?

#4 lore raw
Quoted 13 lines · David Daney

Andrew Haley wrote:

MIPS div instructions never trap. However I think that GCC always emits things like this when it cannot determine that the divisor is non zero:

        div     $0,$17,$16
        bne     $16,$0,1f
        nop
        break   7

1:

Quoted 5 lines
Quoted 3 lines

No, there's no reason not to do it. You'll have to write some hairy code to satisfy all the rules, though.

What are the rules? Are they more complicated then throw an ArithmeticException when the divisor is zero?

Yes. You also have to do

  if (dividend == (jint) 0x80000000L && divisor == -1)
    return dividend;

and not throw an exception.

Quoted 9 lines
Quoted 6 lines
Quoted 2 lines

Q3: Will using SIGTRAP in this manner make debugging programs that divide things by zero very difficult to debug under gdb?

No.

I have not tried it. But I think gdb uses "break" and SIGTRAP for breakpoints. Is it possible to get gdb to pass the signal to the debugee, so that it could be handled by the runtime support?

Well, gcj will generate either break or trap instructions. You can tell gdb to ignore either.

Andrew.

#6 lore raw
In reply to Andrew Haley
Quoted 4 lines · Andrew Haley

I thought that the MIPS never generated a hardware trap for division, but instead there was an assembler macro that did the test for overflow, and the "div" instruction actually generates this test inline. Maybe do a disassembly to check.

Linux/MIPS's behaviour is consistent with all MIPS UNIX flavours I know of both those following the SysV ABI and others such as Ultrix.

Ralf

#5 lore raw
In reply to Andrew Haley
Quoted 16 lines · Andrew Haley
Quoted 15 lines · David Daney

Andrew Haley wrote:

MIPS div instructions never trap. However I think that GCC always emits things like this when it cannot determine that the divisor is non zero:

        div     $0,$17,$16
        bne     $16,$0,1f
        nop
        break   7

1:

Quoted 3 lines

No, there's no reason not to do it. You'll have to write some hairy code to satisfy all the rules, though.

Quoted 9 lines
Quoted 1 line

What are the rules? Are they more complicated then throw an ArithmeticException when the divisor is zero?

Yes. You also have to do

if (dividend == (jint) 0x80000000L && divisor == -1)
return dividend;

and not throw an exception.

That is evidently what you have to do on i386. MIPS gives the right answer without faulting (i.e. hitting the break 7).

David Daney.

#7 lore raw
In reply to David Daney
Quoted 4 lines · David Daney

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

This causes the kernel (I am using 2.4.25) to send SIGTRAP.

It looks like you have a problem in your configuration. A "break 7"
(or "teq <divisor>,$zero,7" -- but that's currently implemented in gas only) is indeed emitted and exectuted in the case of division by zero, but Linux has the ability to recognize this special break code and sends SIGFPE instead. There are actually two special codes defined, the other being "6" for an overflow. Both are handled by Linux, with si_code in struct siginfo being set to FPE_INTDIV or FPE_INTOVF, respectively. You can handle this appropriately in a signal handler.

Signature · 3 lines
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: [email protected], PGP key available        +
#8 lore raw
Quoted 10 lines · Maciej W. Rozycki
Quoted 5 lines · David Daney

It appears that gcc configured for mipsel-linux will execute a "break 7" instruction on integer division by zero.

This causes the kernel (I am using 2.4.25) to send SIGTRAP.

Quoted 8 lines

It looks like you have a problem in your configuration. A "break 7" (or "teq <divisor>,$zero,7" -- but that's currently implemented in gas only) is indeed emitted and exectuted in the case of division by zero, but
Linux has the ability to recognize this special break code and sends
SIGFPE instead. There are actually two special codes defined, the other
being "6" for an overflow. Both are handled by Linux, with si_code in
struct siginfo being set to FPE_INTDIV or FPE_INTOVF, respectively. You
can handle this appropriately in a signal handler.

My kernel sources are from linux-mips.org, it is little-endian running on:

# cat /proc/cpuinfo
system type             : ATI-Xilleon
processor               : 0
cpu model               : MIPS 4Kc V0.7
BogoMIPS                : 299.00
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 16
extra interrupt vector  : yes
hardware watchpoint     : yes
VCED exceptions         : not available
VCEI exceptions         : not available

Could you point me to where in the kernel source this is handled? I will try to see what when wrong.

David Daney.

#9 lore raw
Quoted 2 lines · David Daney

Could you point me to where in the kernel source this is handled? I will try to see what when wrong.

For 2.4 it's arch/mips{,64}/kernel/traps.c, functions do_bp() and
do_tr().

Signature · 3 lines
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: [email protected], PGP key available        +