Project

General

Profile

Need help with test case 00041

Added by Rochus Keller over 1 year ago

Meanwhile I was able to successfully compile and run ~130 test cases with my chibicc Eigen intermediate code generator and ECS 0.0.40 (not yet upgraded).

Here is the tested commit: https://github.com/rochus-keller/EiGen/tree/chibicc 769a375.

Testcase 00041 (not yet on Github) makes an infinite loop though and I'm simply not able to recognize the reason for this in the intermediate code.

Please find attached the modified 00041.c (all code not required to reproduce the issue is commented out) and the generated cod, obf, map and lst files.

I haven't looked yet at the generated x86 assembler, but just try to understand what's happening in the intermediate code. For this purpose I also added a version of the cod file with my manual comments. From my point of view the IR doesn't represent an infinite loop, but maybe I just overlook the bug.

cdcheck doesn't complain; cdrun gives an error with all tried cod files (so not related to this one).

Could you please have a quick look at the cod files whether you see anything suspicious, thanks. I will compile the 0.0.41 version on the Debian machine and also try with this version.


Replies (42)

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

No matter what front-end you base your code generators on, I think it is still a good idea to target the intermediate code interpreter first and foremost, since it is more strict but also allows to change the size and alignments of its types, like 32-bit pointers and 64-bit pointers. If you successfully validate your compiler against this target it will run just as fine on other architectures.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

I think it is still a good idea to target the intermediate code interpreter first and foremost

Sure. I just don't feel like redesigning the C parser just to avoid the cdrun errors.

But I just saw that a project I've been following for a long time released 1.0 last week. The project also includes a C compiler, which in my opinion is more professional than chibicc and also works with my reference project, and there is a well-made intermediate language that I can use.

I have already isolated the compiler and made stubs for the missing procedures (see https://github.com/rochus-keller/EiGen/tree/c2mir/c2mir). Now I'm going to check the code and see how I could integrate ECS IR. If it's not too much work, I'll switch on this extra round. From what I understand so far, the IR from this compiler should be more compatible with ECS than codegen from chibicc.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Now I have spent to evenings with the MIR source code and managed to refactor it so that the parser runs without the VM code and that the code generator is separated/replaceable. My impression of the code was somewhat overshadowed though because it is full of magic numbers assuming a 64 bit target. The IR (and maybe the backend api) also needs some refactoring for the translation to Eigen IR. I will continue with chibicc for now.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Meanwhile I cross-compiled all 149 test cases for arma32 linux (on my x86 linux machine) and run it on a Raspberry Pi 3.

132 of 149 cases work, the remaining make a segfault (besides 00031 which has an illegal instruction). Only 3 of th non working cases correlate with the cdrun fails (00087, 00089 and 00124). Tonight I will check the generated assembler and repeat the tests on x64 and arm64.

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

Make sure to use exactly the same layout and platform descriptor as the ARM back-end does. In particular, the ARM architecture uses the link register rather than pushing return addresses on the stack when calling a function.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Make sure to use exactly the same layout and platform descriptor as the ARM back-end does

I think I have done so (though it's not straight forward to judge when directly generating IR text).

In my version of chibicc I added these defines to chibicc.h:

#if defined TARGET_ARM32
#define CHIBICC_POINTER_WIDTH 4
#define CHIBICC_INT_WIDTH 4
#define CHIBICC_LONG_WIDTH 4
#define CHIBICC_STACK_ALIGN 4
#else
#define CHIBICC_POINTER_WIDTH sizeof(void*)
#define CHIBICC_INT_WIDTH sizeof(int)
#define CHIBICC_LONG_WIDTH sizeof(long)
#define CHIBICC_STACK_ALIGN sizeof(void*)
#endif

On my development machine the result is essentially the same.

the ARM architecture uses the link register rather than pushing return addresses on the stack when calling a function.

Am I supposed to manage $lnk myself, or is this automatically done by cdarma32? My code generator so far doesn't use $lnk at all.

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

Yes, you have to manage the link register yourself, since the call instruction has a different behaviour if the target architecture supports it, see Section 23.4.12. The easiest solution is to push the link register upon entering a function. Keep in mind that the main function is not called, just executed, see Section 23.2.4. The intermediate code interpreter also supports the link register if its platform descriptor is configured accordingly.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

The easiest solution is to push the link register upon entering a function

Ok, I saw that in the sandbox and indeed added it to codegen.c, but got an error from cdamd32 and thus commented it out. Now I again added it and now indeed all test cases on the 32bit arm raspi work successfully! Not actually sure why (I wouldn't have concluded the need to push $lnk from 23.4.12, rather the contrary), but anyway I will add an option in chibicc.h to optionally enable this push.

Thanks for your support. I will now continue with x64 and arm64.

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

The call instruction pushes the return address onto the stack if and only if the target architecture like AMD64 has no link register. Otherwise it stores the return address in the link register, which does not necessarily have to be pushed on the stack. This is typically used for leaf procedures for example.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

I assumed the $lnk register should be pushed to preserve its value (which apparently isnt' the case if used for leaf procedures). But who actually pops it? And if it's not pushed to preserve the value, why not automatically push it with enter?

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

What exactly is not clear about the link register? There are hardware architectures that store the return address of a function call in a register rather than on the stack. That is all and everything else is up to the callee.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Why do I have to push $lnk to avoid the segfault?

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

And who makes pop $lnk? The leave instruction?

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

The ret instruction pops the return address from the stack before jumping back, see Section 23.4.41. Without a prior push that address is probably invalid and execution resumes anywhere. The segmentation fault indicates that the return address presumably lies in unmapped memory.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Ok, that makes sense, thanks.

RE: Need help with test case 00041 - Added by Rochus Keller over 1 year ago

Meanwhile I managed to successfully build and run all 149 test cases on Linux x86_64. That's it for tonight. Thanks again for your support which I very much appreciate.

RE: Need help with test case 00041 - Added by Florian Negele over 1 year ago

The instruction pair enter and leave is optional and only responsible for managing stack frames. Their names maybe misleading but directly correspond to instructions of some architectures.

(26-42/42)