nlsandler / write_a_c_compiler

Test suite to help you write your own C compiler
https://norasandler.com/2017/11/29/Write-a-Compiler.html
MIT License
855 stars 97 forks source link

Signed integer division #20

Closed CDaut closed 3 years ago

CDaut commented 3 years ago

I have a question: I do not really understand how and why stage_3/valid/div_neg.c ((-12)/5;) should work. My code crashes with 30274 floating point exception (core dumped) ./div_neg , whereas the result should be 254 (this is the result, when the programm is compiled with gdb). Why should the output be 254?

                            s = $"{Generate(rootNode.Children[0])}" +
                                "push %rax\n" +
                                $"{Generate(rootNode.Children[1])}" +
                                "movl %eax, %ecx\n" + //move calculated divisor to %ecx
                                "pop %rax\n" + //pop divident do %eax
                                "cdq\n" +
                                "divl %ecx\n"; //eax contains the result, edx the rest

This is my code for the division. Any help would be appreciated! This is my code: https://github.com/Clemens-Dautermann/lcc

kalj commented 3 years ago

It would be easier to help if you simply showed what your generated assembly looks like.

Regarding the 254 value: how the returned int is treated is an OS specific thing. On Unix, I think it is converted to an unsigned 8 bit value, which matches the 254 value you see. What OS do you use?

CDaut commented 3 years ago

Oh yeah sorry. Here is the assembly:

.globl main
main:
movl $12, %eax
neg %eax
push %rax
movl $5, %eax
movl %eax, %ecx
pop %rax
cdq
divl %ecx
ret

I am on x86 Gnu/Linux (Arch). Thank you!

kalj commented 3 years ago

The message "floating point exception" is a bit unfortunate, since it can mean other things than errors from floating point computations. Rather, it is a general arithmetic exception. (See SIGFPE here: https://www.gnu.org/software/libc/manual/html_node/Program-Error-Signals.html)

To see why, first note that you are using the unsigned integer division (div) rather than the signed integer division (idiv) that Nora suggests. Now, the div instruction can produce a number of exceptions (https://c9x.me/x86/html/file_module_x86_id_72.html). In particular, one of them is that if the result of the division doesn't fit in the output register, an overflow exception occurs.

In your case, when you do cdq, %eax is sign extended into %edx, which for -12 means %edx becomes all ones. Then you do unsigned division of the result in %rax, i.e. 18446744073709551604, by 5. This becomes 3689348814741910320, which is much too large to fit in a 32-bit register.

Basically, replace div by idiv.

CDaut commented 3 years ago

Well that's a simple fix. Thank you for the detailed answer! It is from those mistakes and answers I personally learn the most!

kalj commented 3 years ago

Yeah, I also learned something from this :)