jacobly0 / llvm-project

This fork of the canonical git mirror of the LLVM subversion repository adds (e)Z80 targets. Please refer to the wiki for important build instructions.
https://github.com/jacobly0/llvm-project/wiki
123 stars 15 forks source link

Variadic arguments doesn't work #27

Closed cocoacrumbs closed 2 years ago

cocoacrumbs commented 2 years ago

Hi,

I tried to implement the printf() routine, which requires variadic arguments. Unfortunately the generated code seems to be incorrect.

I tried this small sample code:

#include "stdio.h"
#include "stdarg.h"

void va_test(char * fmt, ...)
{
    va_list vl;
    char* val;

    va_start(vl, fmt);

    puts(fmt);

    val=va_arg(vl, char*);
    puts(val);

    val=va_arg(vl, char*);
    puts(val);

    va_end(vl);
} /* end va_test */

int main(void)
{
    va_test("fmt string", "argument 1", "argument 2");
} /* end main */

The expected output is this (verified with a normal clang):

fmt string
argument 1
argument 2

With the eZ80 backend, I get this:

fmt string
���
!

I.e. garbage when printing out the arguments while the format string is OK.

I stepped through the code. I can see that the code uses a wrong offset to pick up the pointers to the arguments on the stack (I can see the pointers to the strings on the stack, so that seems to be OK). Unfortunately, this is about as far as I could get for this issue. The only workaround I can think of right now is coding part of printf() in assembly to fetch the pointers to the arguments and then switch back to C again to handle those arguments.

Best regards,

Koen

PS. For completeness sake, this is the generated assembly code:

    .section    .text,"ax",@progbits
    .global _va_test
    .type   _va_test,@function
_va_test:
    push    ix
    ld  ix, 0
    add ix, sp
    ld  hl, -9
    add hl, sp
    ld  sp, hl
    ld  hl, (ix + 6)
    lea iy, ix - 3
    ld  (ix - 3), hl
    lea hl, iy + 3
    ld  (ix - 6), hl
    ld  hl, (ix - 3)
    push    hl
    call    _puts
    ld  hl, 3
    add hl, sp
    ld  sp, hl
    ld  iy, (ix - 6)
    lea hl, iy + 3
    ld  (ix - 6), hl
    ld  hl, (iy)
    ld  (ix - 9), hl
    ld  hl, (ix - 9)
    push    hl
    call    _puts
    ld  hl, 3
    add hl, sp
    ld  sp, hl
    ld  iy, (ix - 6)
    lea hl, iy + 3
    ld  (ix - 6), hl
    ld  hl, (iy)
    ld  (ix - 9), hl
    ld  hl, (ix - 9)
    push    hl
    call    _puts
    ld  hl, 12
    add hl, sp
    ld  sp, hl
    pop ix
    ret
    .section    .text,"ax",@progbits
    .local  .Lfunc_end1
.Lfunc_end1:
    .size   _va_test, .Lfunc_end1-_va_test

    .section    .text,"ax",@progbits
    .global _main
    .type   _main,@function
_main:
    push    ix
    ld  ix, 0
    add ix, sp
    ld  hl, L_.str
    ld  de, L_.str.1
    ld  bc, L_.str.2
    push    bc
    push    de
    push    hl
    call    _va_test
    ld  hl, 9
    add hl, sp
    ld  sp, hl
    or  a, a
    sbc hl, hl
    pop ix
    ret
    .section    .text,"ax",@progbits
    .local  .Lfunc_end2
.Lfunc_end2:
    .size   _main, .Lfunc_end2-_main

    .section    .rodata,"a",@progbits
    .local  L_.str
L_.str:
    db  "fmt string", 0

    .section    .rodata,"a",@progbits
    .local  L_.str.1
L_.str.1:
    db  "argument 1", 0

    .section    .rodata,"a",@progbits
    .local  L_.str.2
L_.str.2:
    db  "argument 2", 0
jacobly0 commented 2 years ago

Works for me

$ for opt in 0 1 2 3 s z fast; do ez80-clang -S -O$opt -I ../../../Source/csmith-2.3.0/build/runtime -iquote . test.c -o ez80.asm && fasmg linker_script -i 'source "ez80.asm"' ez80.bin && ./runez80 ez80.bin; done
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1176 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.8 seconds, 1128 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1128 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1128 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1123 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1123 bytes.
fmt string
argument 1
argument 2
flat assembler  version g.jdp2
11 passes, 0.7 seconds, 1128 bytes.
fmt string
argument 1
argument 2

With -O0 asm:

    section .text,"ax",@progbits
    assume  adl = 1
    section .text,"ax",@progbits
    public  _va_test
_va_test:
    push    ix
    ld  ix, 0
    add ix, sp
    ld  hl, -9
    add hl, sp
    ld  sp, hl
    ld  hl, (ix + 6)
    ld  (ix - 3), hl
    lea hl, ix + 9
    ld  (ix - 6), hl
    ld  hl, (ix - 3)
    push    hl
    call    _puts
    ld  hl, 3
    add hl, sp
    ld  sp, hl
    ld  iy, (ix - 6)
    lea hl, iy + 3
    ld  (ix - 6), hl
    ld  hl, (iy)
    ld  (ix - 9), hl
    ld  hl, (ix - 9)
    push    hl
    call    _puts
    ld  hl, 3
    add hl, sp
    ld  sp, hl
    ld  iy, (ix - 6)
    lea hl, iy + 3
    ld  (ix - 6), hl
    ld  hl, (iy)
    ld  (ix - 9), hl
    ld  hl, (ix - 9)
    push    hl
    call    _puts
    ld  hl, 12
    add hl, sp
    ld  sp, hl
    pop ix
    ret
    section .text,"ax",@progbits

    section .text,"ax",@progbits
    public  _main
_main:
    push    ix
    ld  ix, 0
    add ix, sp
    ld  hl, L_.str
    ld  de, L_.str.1
    ld  bc, L_.str.2
    push    bc
    push    de
    push    hl
    call    _va_test
    ld  hl, 9
    add hl, sp
    ld  sp, hl
    or  a, a
    sbc hl, hl
    pop ix
    ret
    section .text,"ax",@progbits

    section .rodata,"a",@progbits
    private L_.str
L_.str:
    db  "fmt string", 0

    section .rodata,"a",@progbits
    private L_.str.1
L_.str.1:
    db  "argument 1", 0

    section .rodata,"a",@progbits
    private L_.str.2
L_.str.2:
    db  "argument 2", 0

    ident   "clang version 14.0.0 (https://github.com/jacobly0/llvm-project 88b9fd3e15c3b217f6e6ad8caa90c2aa412ba142)"
    extern  __Unwind_SjLj_Register
    extern  __Unwind_SjLj_Unregister
    extern  _puts

Presumably you are not using an implementation of stdarg.h that is compatible with clang and the ez80.

cocoacrumbs commented 2 years ago

Hi,

Thanks for the tip. I was indeed using the stdarg.h from the Zilog ZDS II toolchain. With the one from Clang it works as expected. I'll have to be more careful in the future when porting other parts of the ZDS II runtime.

Thanks again.

Koen