Z88dk: newlib +yaz180: add copt rules file to allow inlining of asm functions

Created on 13 Sep 2017  路  9Comments  路  Source: z88dk/z88dk

From #373.
Add mechanism to yaz180 to inline asm instructions without affecting optimizer.

bug enhancement

All 9 comments

I can see this becoming a thing where each target may get additional post-processing steps. We probably should have a better place to store the various peephole rules for copt, sccz80 and sdcc.

For __BREAK, I've changed the yaz180.h proto header to this:

// Halt the YAZ180 with single step hardware.

#define __BREAK  __BREAK_HELPER()
__OPROTO(`a,d,e,h,l,iy,iyh',`a,d,e,h,l,iyl,iyh',void,,__BREAK_HELPER,void)

The define to __BREAK_HELPER() allows __BREAK; to be used as is inside c code.

__BREAK_HELPER is being defined as a function that preserves all registers except bc. This way inlining the asm won't affect any registers zsdcc is using.

In a new file yaz180_rule.1, a copt rule is added to replace the function call the compilers are generating with inlined asm:

    call    ___BREAK_HELPER
=
    IFNDEF __IO_BREAK
        EXTERN  __IO_BREAK
    ENDIF
    ld  bc,__IO_BREAK
    out (c),c

    call    __BREAK_HELPER
=
    IFNDEF __IO_BREAK
        EXTERN  __IO_BREAK
    ENDIF
    ld  bc,__IO_BREAK
    out (c),c

zsdcc adds another leading underscore to the name, sccz80 does not. I'm checking if __IO_BREAK is defined in case someone does something bizarre like include "config_private.inc" somehow as that will then include a file defining that label; I think an EXTERN will cause an error out of z80asm in that case. The EXTERN will work because the crt exports those labels as PUBLIC.

I can see this becoming a thing where each target may get additional post-processing steps. We probably should have a better place to store the various peephole rules for copt, sccz80 and sdcc.

Yes, that was my thinking behind: https://github.com/z88dk/z88dk/tree/feature/more_build_changes/lib/arch/z88 Something didn't feel quite right about what I was doing though so I halted work on it.

I'm afraid that I've got a case where this doesn't quite work.

EDIT:
Just thinking that although these are simple examples below, the real use case will be optimised to be broken too. If the ___BREAK; function is called just before a return; then sdcc will optimise this into a jp ___BREAK_HELPER which will be missed by the copt solution. Putting ___BREAK; just before a return; is something that is likely to happen often, to check what is actually left on the stack by seeing where the ret takes us.

Is there not some way to instruct sdcc to NEVER ever ever, optimise a function?
END EDIT

Where there is a simple assignment of function, the ___BREAK_HELPER doesn't work.

#include <stdlib.h>
#include <arch/yaz180.h>

void main(void)
{
    __BREAK;
    return;
}

The list looks like this

346   0000                  SECTION code_compiler
347   0000              ;./atest.c:4: void main(void)
348   0000              ;   ---------------------------------
349   0000              ; Function main
350   0000              ; ---------------------------------
351   0000              ;./atest.c:6: __BREAK;
352   0000              ;./atest.c:8: return;
353   0000                  defc _main  =   ___BREAK_HELPER
354   0000                  SECTION IGNORE
z80asm --cpu=z180 -b -d  -o"break_app" -m -s -L.  -L"/home/phillip/Z80/z88dk/lib/config/../..//libsrc/_DEVELOPMENT/lib/sdcc_iy"   -D__SDCC -D__SDCC_IY -I"/home/phillip/Z80/z88dk/lib/config/../..//libsrc/_DEVELOPMENT/target/yaz180" --list  -im -iyaz180    "@/tmp/tmpXXFAOtpi.lst"
Error at file '/tmp/tmpXX8g7pVZ.asm' line 353: symbol '___BREAK_HELPER' not defined
1 errors occurred during assembly
Errors in source file /home/phillip/Z80/z88dk/lib/config/../..//libsrc/_DEVELOPMENT/target/yaz180/yaz180_crt.asm:
Error at file '/tmp/tmpXX8g7pVZ.asm' line 353: symbol '___BREAK_HELPER' not defined
                   ^ ----   defc _main  =   ___BREAK_HELPER



md5-07b3a4db8f54df7eb5cda9366a9b5cf8



```347   0000               SECTION code_compiler
348   0000              ;./atest.c:4: void anotherfunction(void)
349   0000              ;   ---------------------------------
350   0000              ; Function anotherfunction
351   0000              ; ---------------------------------
352   0000              ;./atest.c:6: __BREAK;
353   0000                  defc _anotherfunction   =   ___BREAK_HELPER
354   0000                  SECTION code_compiler
355   0000              ;./atest.c:9: void main(void)
356   0000              ;   ---------------------------------
357   0000              ; Function main
358   0000              ; ---------------------------------
359   0000              ;./atest.c:11: anotherfunction();
360   0000              ;./atest.c:12: return;
361   0000                  defc _main  =   _anotherfunction
362   0000                  SECTION IGNORE

Otherwise, it works as desired.
Thanks :+1:

Could something like this work?

#define __BREAK       \
_Pragma("save") /* save the current optimization settings */       \
_Pragma("no-peep-return") /* do not let the peep hole optimizer do optimizations */       \
    __BREAK_HELPER()        \
_Pragma("restore") /* turn the optimizations back on */

At least to remove the ret optimisation issue?
Null function optimisation is a moot point, really.

Answer: doesn't work...
sdcc doesn't accept #pragma within a function.

I'll take a look at your solution tomorrow (it's getting too late here!). I'm a little nervous about depending on whitespace to sort out problems but I think it may be possible to make exceptions for certain names.

Yes, white space makes me nervous too (not a python fan). Hence I mentioned it.
The yaz180_rules.1 copt seems to need to match exactly with the output the compiler and initial copt produces.

The call ___BREAK_HELPER comes straight from the compiler with a tab character.
The jp ___BREAK_HELPER comes from copt with two spaces.
So yaz180_rules.1 has to match these inputs exactly, or it breaks.
I tested it.
Seems fragile to me.

I've fixed the space brittleness issue. In reality, sdcc's peephole optmizer is very fragile, depending on exact formatting of instructions and spacing for it to work.

It has made you a s**t load of work, just to insert two lines of asm.
Sounds like technical overhang to me. :sweat_smile:

Anyway, that solution looks solid.
The defc case is a moot one anyway.

(Unrelated, but worth celebrating... just fixed a longstanding PLD issue with my implementation of a soft flash programmer, so the last outstanding yaz180 hardware concern is a I2C proof of life. :hand: )

Was this page helpful?
0 / 5 - 0 ratings