Monday, January 29, 2007

ARM port day 1

I've started porting Factor to the ARM architecture, used in embedded devices, handhelds, and phones. Doug asked me to take notes during the porting process, to help anybody who decides to port Factor in the future. At some point I'll be documenting the VM and compiler backend in some detail, so don't expect these notes to be too detailed.

The first step is to get the VM to compile and run a boot image in interpreted mode. It mostly consists of portable C, but there are at least three architecture-specific details which need to be filled out; getting the native stack pointer, native stack frame format, and flushing the instruction cache. I haven't done any research as to whenever the latter is necessary, nor have I studied the ABI extensively yet, so I only filled in the first item for now. All in all, I added the following new files, together with a linux-arm Makefile target:

While testing, I ran into a major problem. A data stack underflow would send the runtime into an endless cycle of signal 11 errors, at seemingly random addresses! This is not what is supposed to happen. When the Unix signal handler receives a memory access violation, it pulls the native stack pointer at the time of the fault out of the signal context, then walks the native stack for compiled Factor frames. Since the ARM port doesn't compile yet this should be a no-op, but in fact the native stack pointer from the faulting context looked like a seemingly arbitrary address.

After some head-scratching, it really seemed like the ucontext_t structure had a different layout in the glibc headers than what the kernel was giving us, and Googling confirmed my suspicions. Turns out this is a known problem and it is already fixed in the glibc trunk, which has not made it to Debian-stable yet. Most less-conservative ARM distros might already have the latest headers. If yours doesn't, you can apply the patch in the e-mail linked above with the following sequence of commands:
cd /usr/include/sys
cat >ucontext.patch
copy and paste the patch here
patch -p8 < ucontext.patch

Alternatively, upgrade your entire libc, but that is not for the faint of heart.

With signal handling working, I ran into a second problem; floating point values saved in the boot image were corrupt. A quick call to double>bits .b revealed that the most significant and least significant cells of the float were swapped from what ARM expects. Fixing this was easy, I added a new bootstrap profile for ARM chips:

Now, all unit tests expect those testing the compiler passed. I stubbed out the compiler backend but didn't write any substantial code for it yet:

For the last several days, I have been studying ARM assembly language, and tomorrow I will begin writing the assembler. Hopefully by the end of the week, I'll have everything except FFI calls compiling. Getting FFI and callbacks right is always the trickiest part, but with ARM I don't have to deal with floating point registers so perhaps it might even be easy.

Anyway, today's efforts were a success:
$ ./f
Factor 0.88 on linux/arm


Sam said...

Sweet Slava! Now I wish I had an ARM to try it out on.

Anonymous said...

Could be extremely useful for something like :) Though the people who develop on that usually shun anything that isn't a mix of ANSI C and assembler ;)