earl1k / clang-z80

Other
14 stars 2 forks source link

cannot compile C code with z80 inline assembly #1

Closed newchief closed 10 years ago

newchief commented 10 years ago

I'm trying to compile following snippet of code:

unsigned char read() { unsigned char retval = 0;

asm volatile ("in %%a, $31" : "=a"(retval) : : ); return retval; }

According to this article: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html this code should be valid. At least for GCC, but AFAIK, clang adopted a lot of GCC features. Since for this flavour of IN instruction I always need A register, I'm specifying it directly with two % signs. Also, I'm using it explicitly in output list - I want its value to go into 'retval' variable. Unfortunately, clang-z80 complains about it (to avoid unnecessary optimizations, I'm building with -O0):

bash-3.2$ clang -O0 -S -emit-llvm -c test.c test.c:55:20: error: invalid output constraint '=a' in asm : "=a"(retval) ^ 1 error generated.

Note that while building clang-z80, I've configured it for targeting z80-unknown-none triplet by default.

I rearranged the code:

unsigned char read() { unsigned char retval = 0;

asm volatile ("in %%a, $31\n\t" "ld %0, %%a" : "=r"(retval) : : "%a"); return retval; }

Now clang compiles it fine, unfortunately, llc from llvm-z80 now complains:

bash-3.2$ llc -march=z80 -O0 test.ll error: couldn't allocate output register for constraint 'r'

I rewrote this code once again, now it is completely useless (value from IN is thrown away), however, now both clang and llc are happy:

unsigned char read() { unsigned char retval = 0;

asm volatile ("in %%a, $31" : : : "%a"); return retval; }

Assembly produced by llc seems reasonable, however, it seems like % and $ are still there!:

read: # @read

BB#0:

    push    ix
    ld      ix, -1
    add     ix, sp
    ld      sp, ix
    ld      (ix+0), 0
    #APP
    in %a, $31
    #NO_APP
    ld      a, (ix+0)
    ld      ix, 1
    add     ix, sp
    ld      sp, ix
    pop     ix
    ret

Seems like all % and $ should go away from my inlineassembly code. As we can see, old value of register A is preserved before inline assembly block and restored immediately after that (at least, we can see that clobbering list did its job). Still it is useless for me, as I want value obtained by IN to be held in retval variable and then returned by the function...

Of course there is a nasty workaround for this, however, such dirty hacks spoil all benefits of using modern-era compiler infrastructure for immortal z80 devices:

unsigned char read() { asm volatile("in a, 31\n\t" "ret" : : :); return 0; }

I'm taking advantage of the fact that return value for functions returning 8-bit values is always held in register A. The code above is translated to following assembly:

read: # @read

BB#0:

    #APP
    in a, 31
    ret
    #NO_APP
    ld      a, 0
    ret

Seeing unreachable code after first 'ret' makes it even more nasty...

Another question, what assembler are you using to build final binary? llc crashes on -filetype=obj:

bash-3.2$ llc -march=z80 -O0 -filetype=obj test.ll 0 llc 0x0000000100e3f084 llvm::sys::PrintStackTrace(_sFILE) + 38 1 llc 0x0000000100e3f2b3 PrintStackTraceSignalHandler(void_) + 27 2 llc 0x0000000100e3f7bf SignalHandler(int) + 254 3 libSystem.B.dylib 0x00007fff83db21ba _sigtramp + 26 4 libSystem.B.dylib 0x0000000102819600 _sigtramp + 2124837984 5 llc 0x0000000100932ae7 llvm::LLVMTargetMachine::addPassesToEmitFile(llvm::PassManagerBase&, llvm::formatted_rawostream&, llvm::TargetMachine::CodeGenFileType, bool, void const, void const_) + 1457 6 llc 0x00000001000347e0 compileModule(char**, llvm::LLVMContext&) + 3454 7 llc 0x00000001000349e3 main + 199 8 llc 0x0000000100033574 start + 52 9 llc 0x0000000000000005 start + 4294757061 Stack dump:

  1. Program arguments: llc -march=z80 -O0 -filetype=obj test.ll Segmentation fault

Also, sdasz80 (V02.00) nor z80asm (ver. 1.8) want to compile such assembly code format...

earl1k commented 10 years ago

Inline assembler is not ready yet. That's why it don't work.