#187 proposal: change in calling convention (C51)

open
nobody
None
5
2006-12-26
2006-12-12
Anonymous
No

Wouldn't it be better to use general registers r2..r7 for parameter passing?
The use of dptr/b/a for passing the first parameter seems a bad choice to me, because these registers are used mostly for other purposes in the called functions and therefore will be copied to
general registers or to the stack in the entry code of the function.
For example, the C function

int AddInt(int a, int b)
{
return a+b;
}

leads to the following assembler code (with --stack-auto)

_AddInt:
push _bp
mov _bp,sp
mov r2,dpl
mov r3,dph
mov a,_bp
add a,#0xfffffffc
mov r0,a
mov a,@r0
add a,r2
mov dpl,a
inc r0
mov a,@r0
addc a,r3
mov dph,a
pop _bp
ret

Assumed that all 4 parameter bytes were passed in registers the code would look sth. like:

_AddInt:
mov a,r4
add a,r2
mov r2,a
mov a,r5
addc a,r3
mov r3,a
ret

(Also assumed that the return value is in R2/R3)

This concept would also lead to tighter code in the calling function.

Best Regards,
Ralf

Discussion

  • Bernhard Held

    Bernhard Held - 2006-12-12

    Logged In: YES
    user_id=203539
    Originator: NO

    > This concept would also lead to tighter code in the calling function.
    No, this is not always true. There's only an advantage in simple functions. In calling functions with high register pressure many push/pop operations will appear in order to free the required registers. This will eat up the whole advantage and will result in even longer code. Unfortunately you meet 'high register pressure' in all non-trivial functions with only 8 bytes register space.

    Try --parms-in-bank1 and/or --callee-saves for better results. Remember to recompile your library with the same options.

     
  • Bernhard Held

    Bernhard Held - 2006-12-12
    • assigned_to: nobody --> bernhardheld
    • status: open --> pending
     
  • Nobody/Anonymous

    Logged In: NO

    Bernhard,
    I could not find any documenation on the --parms-in-bank1, but if it does what I think it does, it will void reentrancy, which I need for my application. Still, it does not help with the first function parameter. In many cases I inspected there's a sequence like

    mov r2, dpl
    mov r3, dph

    in the entry of the function, even if R2/R3 is never used.

    Both commercial compilers (KEIL and TASKING) use general registers for parameter passing with proves to deliver good results.

     
  • Nobody/Anonymous

    • status: pending --> open
     
  • Bernhard Held

    Bernhard Held - 2006-12-26

    Logged In: YES
    user_id=203539
    Originator: NO

    Unfortunately a "simple" renaming of registers doesn't help and is even counter productive. Prior to this a much bigger improvement of sdcc is needed. Let's have a look at:
    char xdata *foo (char xdata *p)
    {
    return p;
    }

    It compiles to:
    _foo:
    ret

    This looks nice, but the comment
    ;p Allocated to registers r2 r3
    tells us the truth. To see what really happens compile it with --no-peep. Now we get:
    _foo:
    ; genReceive
    mov r2,dpl
    mov r3,dph
    ; genRet
    mov dpl,r2
    mov dph,r3
    ret
    Yes, this is really ugly. It was just the peephole optimizer, which made the code looking somewhat nicer. What happens? The phases in sdcc are totally separate - the last phases are:
    - register allocation (RA)
    - assembler code generation
    - peephole optimization
    The register allocator allocates registers for all variables (and all temporary variables, which you don't always see in the source code). It doesn't know if the code generator will ever use this register, or if the peephole optimizer will remove the usage. If a register is allocated but unused in the final code, then it's simply lost. There's no 2nd pass, which could feed back this information in the register allocator.
    This is the first and even bigger thing, which needs improvement. Then we might think about changing the calling convention. There's a long, long way to go until sdcc will emit code given in your initial post.

    Please remember that sdcc is a retargetable C-compiler. I'm afraid that 1) commercial compilers use optimization technics, which can't be implemented in sdcc and 2) sdcc will never ever reach the code quality of commercial compilers.

     
  • Bernhard Held

    Bernhard Held - 2006-12-26
    • assigned_to: bernhardheld --> nobody
     
  • Ralf Guetlein

    Ralf Guetlein - 2006-12-29

    Logged In: YES
    user_id=145736
    Originator: NO

    Bernhard,

    I appreciate your and your fellows' work. Please, do not think that I unfairly compare SDCC to a commercial tool chain that costs thousands of euros. After all, the commercial compilers have their quirks, too (no ANSI compatibility, no bit fields in structs with more than 8 bits, "wrong" endian).
    Still, I think my proposal is worth thinking about, for future improvements. I am not an expert in compiler design, but if I will find the time I will take a deeper look into this issue.

    I wish every member of this project all the best for the New Year.
    Ralf

     
  • Maarten Brock

    Maarten Brock - 2006-12-30

    Logged In: YES
    user_id=888171
    Originator: NO

    There is another reason why it could be advantageous not to use DPTR, B, A for passing parameters.

    SDCC could use JMP @A+DPTR for calling function pointers and banked functions. Obviously DPTR and A are necessary for this instruction. The current implementation for void-void function pointers generates something like:

    mov a,ret_addr
    push acc
    mov a,ret_addr>>8
    push acc
    push ar2 ;assume r2:r3 contain the function pointer
    push ar3
    ret ;make the call
    ret_addr:

    If it could always use DPTR it could be:

    mov dpl,r2
    mov dph,r3
    lcall indirect_call

    And in a library module:

    indirect_call::
    clr a
    jmp @a+dptr

    As my example shows this currently can only be used for functions with no parameters.

    The current implementation for function pointers uses registers r0,r1,r2 for the destination address and the bank. This can then be changed to DPTR and A or B for the bank. However if getting the pointer needs the DPTR it gets more complicated.

    Maarten

     

Log in to post a comment.