Shop OBEX P1 Docs P2 Docs Learn Events
crt0_cog.s improvement proposal — Parallax Forums

crt0_cog.s improvement proposal

jac_goudsmitjac_goudsmit Posts: 418
edited 2012-09-30 20:07 in Propeller 1
As you may know, I've been working on a Wiki page that gives some in-depth information about how the compiler, linker and C runtime startup work. See https://code.google.com/p/propgcc/wiki/PropGccInDepth.

Having such a close look at crt0_cog.s gave me an idea.
  • Currently you CANNOT declare main( ) with the _NATIVE attribute, because crt0_cog.s calls main with the lr calling convention so the RET at the end of main remains uninitialized and will effectively be a JMP #0
  • Restarting a cog by doing a JMP #0 is not possible because the general-purpose registers are stored there

But what if cog location 0 would contain a JMP #__exit instruction?
  • The main( ) function can now be declared _NATIVE because the uninitialized JMP #0 will end up jumping to _exit( ) and will stop the cog
  • It's still not possible to restart a cog, but assembly code that inadvertently jumps to #0 will not execute any random code: it will kill the cog
  • Assembly code that intentionally wants to stop the cog can also jump to #0
Here is some code to demonstrate this:
.text
  .global r0
  .global r1
  .global r2
  .global r3
  .global r4
  .global r5
  .global r6
  .global r7
  .global r8
  .global r9
  .global r10
  .global r11
  .global r12
  .global r13
  .global r14
  .global lr
  .global sp

alt_exit jmp #r0 '' this is changed to jmp #__exit

r0  mov  sp, PAR
r1  mov  r0, sp
  
  '' check for first time run
r2  rdlong  r1, __C_LOCK_PTR wz
r3  IF_NE jmp  #_start
  '' allocate a lock, and clear the bss
r4  locknew  r1
r5  or  r1,#256
r6  wrlong  r1, __C_LOCK_PTR
r7  sub  lr,r13 wz
r8  IF_Z  jmp #_start

__bss_clear
r9  wrbyte  r14,r13
r10  add  r13,#1
r11  djnz  lr,#__bss_clear
r12  jmp  #_start

r13  long  __bss_start
r14  long  0    '' this must remain zero
lr  long  __bss_end
sp  long  0

__C_LOCK_PTR
  long  __C_LOCK
_start
  movs alt_exit, #__exit
  jmpret  lr,#_main
  jmp  #__exit

As you can see, this moves the registers in memory and changes all compiled code, but it's completely compatible with current code at the Assembly level. Also, it adds 2 longs to the code and adds 8 cycles to the initialization time.

I also thought it might be possible to just initialize lr to 0 and changing the JMPRET lr, #_main into JMP #_main; this would eliminate the need for the JMP #__exit at the end. But because this requires a change to the BSS clearing code, I'm leaving it up to you guys.

Another possibility is to implement the __exit code at the start of the cog init code:
  • The startup and shutdown code would be in the same module, (eliminating most of the need for crtend_cog.s)
  • Public symbol __exit is effectively initialized to 0 by the linker
  • It would eliminate the JMP #__exit after main returns (the return from main implicitly jumps to __exit no matter how it's declared), but shutdown timing is probably totally non-critical anyway.
If you guys like the idea, I'd be willing to consider installing hg on my system to take a stab at doing the improvement.

Thanks for reading!

===Jac

Comments

  • ersmithersmith Posts: 6,054
    edited 2012-09-30 20:07
    Thanks for bringing this up. I hadn't really thought about it before, since generally I don't return from main. In one sense it doesn't matter whether main is declared _NATIVE or not, but I can see where people get into the habit of adding _NATIVE and would like to do so for main too.

    Unfortunately the assumption that r0-r15 are at locations 0-15 is built in to some of the tools, so I think it would be dangerous to change this. However, I think there's a way to allow _NATIVE main without using any extra code space or execution time. We can make a weak alias _main_ret for the link register lr, and put the return address in _main_ret. If main is declared normally then this means the return will end up in lr, as expected. However, if main is declared _NATIVE then it will also define _main_ret, and this will override the weak definition in crt0_cog.s. To accomplish this we change the last portion of crt0_cog.s to:
        '' allow main to optionally be declared _NATIVE
        '' if it is _NATIVE then it will define _main_ret, which
        '' will override the weak definition here
        '' if it is not declared _NATIVE then the return address
        '' will be placed in lr, as expected
        .weak _main_ret
        .equ  _main_ret, lr
    _start
        jmpret _main_ret,#_main
        jmp    #__exit
    

    (Weak aliases are one of the cool features of the GNU linker!)
Sign In or Register to comment.