Skip to content

LLVM for the Z80 – Another Source of Inspiration

December 30, 2014

The Zilog Z80 was enormously more popular with hobbyists than the 1802 and it’s the only cpu of its vintage that i’ve found an llvm back end for. There’s no web site for the compiler that i can find, just the github source repository loaded in 2012 and some discussion posts from the same time period that may or may not be related.

I’ve downloaded and tried it out and, for simple examples, it does seem to work.

short int foo(short int a, short int b) {
    short int result = a + b;   // r0 + r1
    return result;        // r0
}
	.file	"ex1.c"
	.text
	.globl	foo
	.type	foo,@function
foo:
	push	ix
	push	bc
	ld	ix, 0
	add	ix, sp
	ld	sp, ix
	ld	b, h
	ld	c, l
	ld	l, (ix+6)
	ld	h, (ix+7)
	add	hl, bc
	pop	bc
	pop	ix
	ret
.tmp0:
	.size	foo, .tmp0-foo
; ModuleID = 'ex1.c'
target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-n8:16"
target triple = "z80-unknown-unknown"

; Function Attrs: nounwind readnone
define i16 @foo(i16 %a, i16 %b) #0 {
  %1 = add nsw i16 %b, %a
  ret i16 %1
}

attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = metadata !{metadata !"clang version 3.4 "}

The fragment target datalayout = “e-p:16:8:8-i8:8:8-i16:8:8-n8:16” means that the target is little-endian, pointers are 16 bits byte aligned, integers can be 8 or 16 bits, byte aligned, and n8:16 means that native integers can be 8 or 16 bits.

I’m also happy to note that it supports a bigger range of input than the LEG example including inline assembly. My current assembler would choke on the output format but that’s just a small matter of programming.

void foo(void) {
	while(1){
		asm(".byte 0x7B\n\t");
		asm(".byte 0x7A\n\t");
	}
}
	.file	"asm2.c"
	.text
	.globl	foo
	.type	foo,@function
foo:
.BB0_1:
	#APP
	.byte 0x7B
	
	#NO_APP
	#APP
	.byte 0x7A
	
	#NO_APP
	jp	.BB0_1
.tmp0:
	.size	foo, .tmp0-foo

As evidenced by the following, I don’t think it supports elf output. I’m going to try to contact the developers to learn what i can.

0  llc                      0x00000001064d020e llvm::sys::PrintStackTrace(__sFILE*) + 46
1  llc                      0x00000001064d051b PrintStackTraceSignalHandler(void*) + 27
2  llc                      0x00000001064d084c SignalHandler(int) + 412
3  libsystem_platform.dylib 0x00007fff82f62f1a _sigtramp + 26
4  libsystem_platform.dylib 0x0000000305f32267 _sigtramp + 2197615463
5  llc                      0x0000000105d48986 llvm::LLVMTargetMachine::addPassesToEmitFile(llvm::PassManagerBase&, llvm::formatted_raw_ostream&, llvm::TargetMachine::CodeGenFileType, bool, void const*, void const*) + 1894
6  llc                      0x00000001050ff3e1 compileModule(char**, llvm::LLVMContext&) + 5969
7  llc                      0x00000001050fdc22 main + 226
8  libdyld.dylib            0x00007fff8c6e15c9 start + 1
Stack dump:
0.	Program arguments: ./bin/llc -march z80 -o ex1.o -filetype=obj ex1.ll 
Segmentation fault: 11

The github pages identify Dmitriy Limonov as the owner. I also see some work attributed to Stephan Hilb.

UPDATE:
Dmitry did respond to my note. He confirmed that there was no .elf support and said no one is working on llvm-z80 at the moment.

I had assumed from the target data layout above that it would only support 8 and 16 bit ints. Dmitry said that llvm would compute the 32 bit results based on his work. I tried adding two longs and it seemed to work as follows:

long int foo(long int a, long int b) {
    long int result = a + b;   // r0 + r1
    return result;        // r0
}
; ModuleID = 'exlong.c'
target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-n8:16"
target triple = "z80-unknown-unknown"

; Function Attrs: nounwind
define i32 @foo(i32 %a, i32 %b) #0 {
  %1 = alloca i32, align 1
  %2 = alloca i32, align 1
  %result = alloca i32, align 1
  store i32 %a, i32* %1, align 1
  store i32 %b, i32* %2, align 1
  %3 = load i32* %1, align 1
  %4 = load i32* %2, align 1
  %5 = add nsw i32 %3, %4
  store i32 %5, i32* %result, align 1
  %6 = load i32* %result, align 1
  ret i32 %6
}

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = metadata !{metadata !"clang version 3.4 "}

Needless to say, the emitted z80 code for the 32 bit version is longer!

	.file	"exlong.c"
	.text
	.globl	foo
	.type	foo,@function
foo:
	push	iy
	push	ix
	push	de
	push	bc
	ld	ix, -28
	add	ix, sp
	ld	sp, ix
	ld	b, h
	ld	c, l
	ld	(ix+24), c
	ld	(ix+25), b
	ld	c, (ix+38)
	ld	b, (ix+39)
	ld	a, b
	ld	(ix+27), a
	ld	a, c
	ld	(ix+26), a
	ld	e, (ix+42)
	ld	d, (ix+43)
	ld	a, d
	ld	(ix+23), a
	ld	a, e
	ld	(ix+22), a
	ld	(ix+14), l
	ld	(ix+15), h
	ld	l, (ix+40)
	ld	h, (ix+41)
	ld	(ix+20), l
	ld	(ix+21), h
	ld	a, (ix+26)
	ld	a, (ix+27)
	ld	(ix+12), l
	ld	(ix+13), h
	ld	l, (ix+24)
	ld	h, (ix+25)
	ld	(ix+10), c
	ld	(ix+11), b
	ld	c, (ix+12)
	ld	b, (ix+13)
	add	hl, bc
	ld	(ix+8), l
	ld	(ix+9), h
	push	iy
	pop	hl
	adc	hl, de
	ld	c, (ix+8)
	ld	b, (ix+9)
	ld	(ix+16), c
	ld	(ix+17), b
	ld	a, h
	ld	(ix+19), a
	ld	a, l
	ld	(ix+18), a
	ld	l, (ix+12)
	ld	h, (ix+13)
	ld	c, (ix+10)
	ld	b, (ix+11)
	ld	(ix+6), l
	ld	(ix+7), h
	ld	l, (ix+8)
	ld	h, (ix+9)
	ld	(ix+4), c
	ld	(ix+5), b
	ld	(ix+2), e
	ld	(ix+3), d
	ld	ix, 28
	add	ix, sp
	ld	sp, ix
	pop	bc
	pop	de
	pop	ix
	pop	iy
	ret
.tmp0:
	.size	foo, .tmp0-foo
Advertisements

From → LLVM

10 Comments
  1. César permalink

    Is this backend functional, or unfinished? Also, did you try it with C++? (I agree it might be a joke to use program the z80 in C++, but if it works it would be reaaally cool 🙂

    • It certainly produces what looks like correct z80 assembly. I got the impression from the developer that i spoke to that it was not regarded as finished but no one was working on it.

      I only tried it for a simple example in C but that is Clang(the llvm C++ compiler) doing the compiling so it might very well do C++. Unfortunately I won’t be near that machine for a couple of weeks but i’ll make it a point to try it.

      C++ is not inherently a bad thing for a small system: a lot of the magic happens at compile time and could be quite useful. The AVRs for example have less memory than my 1802 and we routinely use C++ with them.

  2. Romick permalink

    How did you manage to build clang from this repository?

    • honestly, I’ve built llvm/clang in so many variations that they all blur together. It’s usually a matter of running CMAKE and MAKE. If you’re working on a mac or unix I could zip up my directory and make it available to see if that helps. The author also responded to a query from me so there’s that approach. What system are you working on and what problem are you running into?

  3. Romick permalink

    Thank you. I think that the repository is a bit outdated and no longer on friendly terms with the latter Clang. Under the Linux-4.4.6-gentoo x86_64 AMD, I get the following message:

    ==========
    — Targeting Z80
    — Clang version: 3.4.
    CMake Error at tools/clang/cmake/modules/AddClang.cmake:86 (llvm_add_library):
    Unknown CMake command “llvm_add_library”.
    Call Stack (most recent call first):
    tools/clang/lib/Basic/CMakeLists.txt:65 (add_clang_library)
    — Configuring incomplete, errors occurred!
    =========

    I will be very grateful for a link to your archive.

    P.S. I have no problems with the building of the last LLVM & Clang. This is only the z80 repository, which can not be built.

    • I am uploading a llvm-z80.zip to google drive, this would have been on my MacBook as documents/llvm-z80/src and documents/llvm-z80/build. I would normally start by clearing out the build directory and then running make and make in the empty build directory using something like
      cmake -G “Unix Makefiles” -DCMAKE_BUILD_TYPE:STRING=DEBUG -DLLVM_TARGETS_TO_BUILD:STRING=Z80 ../src

      No guarantees, it’s been a couple of years. I tried rerunning make today but it failed because my Xcode/clang directory structure has changed since.

      is your clang/llvm host setup pretty current? The only thing i saw googling that error message seemed to be about clang versions.

      The guys on the llvm developer mailing list are pretty good at helping by the way.

      good luck! Let me know when you’ve pulled down the directory so i can free up the space.

  4. Romick permalink

    Thank you. I took the file, so you can delete it.

  5. César permalink

    I see more forks from the llvm-z80 have been created. If I looked at it right, it seems last commit in a fork was last August. Are there any advances? Is anybody using it?

    • I haven’t been following it. I actually have been doing some z80 programming but i used sdcc and z88dk. I was able to contact the original developer by poking around the repo – maybe you could do that for the new forks.

Trackbacks & Pingbacks

  1. Wow – This is Why I Want an LLVM Compiler! | olduino

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: