Help needed for small assembler script for the iraf package

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Help needed for small assembler script for the iraf package

Ole Streicher
Dear Itanium specialists,

A while ago, I got the "IRAF" package accepted into Debian [1]. Although
the code is claimed to be portable, it needs a piece  of assembler
code for each platform that provides a (sort of) setjmp() to their
Fortran variant.

A "C" implementation for this looks like:

#include <setjmp.h>

int zsvjmp_( long *buf, long *status ) {
  *status = 0;
  ((long **)buf)[0] = status;
  return sigsetjmp ((void *)((long **)buf+1),0);
}

however this does not work, since the "sigsetjmp" call needs to be
replaced by a jump to sigsetjmp instead.

There is already some working code for x64 in the original distribution:

-------------------------------8<-----------------------------------
        .file "zsvjmp.s"
        .globl zsvjmp_
        .type   zsvjmp_, @function

zsvjmp_:
        # %rsi ... &status  %rdi ... &jumpbuf
        movq    %rsi, (%rdi)    # store &status in jmpbuf[0]
        movl    $0, (%rsi)      # zero the value of status
        addq    $8, %rdi        # change point to &jmpbuf[1]
        movl    $0, %esi        # change arg2 to zero
        jmp     __sigsetjmp@PLT # let sigsetjmp do the rest

        .section        .note.GNU-stack,"",@progbits
-------------------------------8<-----------------------------------

The "zdojmp" counterpart is a portable C function.

I created a small repository [2] that contains the assembler I collected
so far as well as two test programs.

However, I have no idea how to write the same for the ia64 platform.
Maybe someone could help me here? Preferably under the IRAF
license [3], so that it can be included upstream later.

One way that worked for x32 and for arm64 was to compule the C snipped
above (having the sigsetjmp function name by something else) with an
optimizing compiler; however I didn't find cross compiler with the ia64
target?

There is no request from the users to have this ported to ia64, and I
doubt that ever will. My main motivation to get it ported is to check
the package for hidden problems on "unusual" architectures.

Best regards

Ole

[1] https://tracker.debian.org/pkg/iraf
[2] https://github.com/olebole/zsvjmp
[3] https://iraf-community.github.io/COPYRIGHT.html

Reply | Threaded
Open this post in threaded view
|

Re: Help needed for small assembler script for the iraf package

James Clarke-2
On 1 Jun 2019, at 15:37, Ole Streicher <[hidden email]> wrote:

>
> Dear Itanium specialists,
>
> A while ago, I got the "IRAF" package accepted into Debian [1]. Although
> the code is claimed to be portable, it needs a piece  of assembler
> code for each platform that provides a (sort of) setjmp() to their
> Fortran variant.
>
> A "C" implementation for this looks like:
>
> #include <setjmp.h>
>
> int zsvjmp_( long *buf, long *status ) {
>  *status = 0;
>  ((long **)buf)[0] = status;
>  return sigsetjmp ((void *)((long **)buf+1),0);
> }
>
> however this does not work, since the "sigsetjmp" call needs to be
> replaced by a jump to sigsetjmp instead.
>
> There is already some working code for x64 in the original distribution:
>
> -------------------------------8<-----------------------------------
> .file "zsvjmp.s"
>        .globl zsvjmp_
> .type   zsvjmp_, @function
>
> zsvjmp_:
> # %rsi ... &status  %rdi ... &jumpbuf
> movq    %rsi, (%rdi)    # store &status in jmpbuf[0]
> movl    $0, (%rsi)      # zero the value of status
> addq    $8, %rdi        # change point to &jmpbuf[1]
> movl    $0, %esi        # change arg2 to zero
> jmp     __sigsetjmp@PLT # let sigsetjmp do the rest
>
> .section        .note.GNU-stack,"",@progbits
> -------------------------------8<-----------------------------------
>
> The "zdojmp" counterpart is a portable C function.
>
> I created a small repository [2] that contains the assembler I collected
> so far as well as two test programs.
>
> However, I have no idea how to write the same for the ia64 platform.
> Maybe someone could help me here? Preferably under the IRAF
> license [3], so that it can be included upstream later.
>
> One way that worked for x32 and for arm64 was to compule the C snipped
> above (having the sigsetjmp function name by something else) with an
> optimizing compiler; however I didn't find cross compiler with the ia64
> target?
>
> There is no request from the users to have this ported to ia64, and I
> doubt that ever will. My main motivation to get it ported is to check
> the package for hidden problems on "unusual" architectures.

Hi,
Managed to get this to work, with one caveat (explained afterwards):

>
> .file "zsvjmp.s"
> .explicit
> .text
> .align 64
> .global zsvjmp_#
> .type zsvjmp_#, @function
> .proc zsvjmp_#
> zsvjmp_:
> # Normally, tail calls to a different shared object are forbidden, since a
> # function in our shared object can call us without needing to save/restore
> # its gp register, but if we tail call a function outside the shared object
> # then the PLT stub will clobber gp. However, since we're writing this in
> # assembly in its own file, the compiler shouldn't be able to prove that
> # any callers are in the same shared object as us, and therefore will be
> # forced to pessimistically spill gp, so we should be safe to break the ABI
> # and let gp be trashed by our tail call.
>
> st8 [r33] = r0
> st8 [r32] = r33, 16
> ;;
> mov r33 = r0
> br.cond.sptk.many __sigsetjmp#
> .endp zsvjmp_#
> .section .note.GNU-stack,"",@progbits

The caveat is that IA-64 has 128-bit floats saved as part of setjmp/longjmp,
and these must be aligned, so the entire jmp_buf must be 16-byte aligned.
Moreover, since zsvjmp_ uses the first long (8 bytes) of the buffer for its
status pointer, the second long now also needs to be reserved so we maintain
the alignment when passing it to __sigsetjmp. Thus, I needed to make two
changes to your test code on IA-64, which will need to be mirrored in the
actual Fortran callers (I didn't change jmptest.f, it just works by chance):

> diff --git a/zdojmp.c b/zdojmp.c
> index 547d739..093d350 100644
> --- a/zdojmp.c
> +++ b/zdojmp.c
> @@ -11,7 +11,7 @@
>  void zdojmp_ (long *jmpbuf, long *status) {
>      int stat = *status ? *status : 1;
>      long *status_ptr = ((long **)jmpbuf)[0];
> -    void *jb = (long **)jmpbuf+1;
> +    void *jb = (long **)jmpbuf+2;
>      *status_ptr = stat;
>      siglongjmp (jb, stat);
>  }
> diff --git a/zzdebug.c b/zzdebug.c
> index 824b3e3..1f350e5 100644
> --- a/zzdebug.c
> +++ b/zzdebug.c
> @@ -6,7 +6,7 @@ void zsvjmp_(void*, volatile long *);
>  void zdojmp_(void*, volatile long *);
>  
>  int main(void) {
> -    long jmpbuf[180];
> +    long jmpbuf[180] __attribute__((__aligned__(16)));
>      volatile long status = 9;
>      volatile long step = 0;
>      zsvjmp_((void *)jmpbuf, &status);


Regards,
James

Reply | Threaded
Open this post in threaded view
|

Re: Help needed for small assembler script for the iraf package

Ole Streicher
Hi James,

great, thank you very much! This was the most important missing piece!

Is there an ia64 machine available where I could test the package?

Best regards

Ole

On 02.06.19 00:57, James Clarke wrote:

> On 1 Jun 2019, at 15:37, Ole Streicher <[hidden email]> wrote:
>>
>> Dear Itanium specialists,
>>
>> A while ago, I got the "IRAF" package accepted into Debian [1]. Although
>> the code is claimed to be portable, it needs a piece  of assembler
>> code for each platform that provides a (sort of) setjmp() to their
>> Fortran variant.
>>
>> A "C" implementation for this looks like:
>>
>> #include <setjmp.h>
>>
>> int zsvjmp_( long *buf, long *status ) {
>>  *status = 0;
>>  ((long **)buf)[0] = status;
>>  return sigsetjmp ((void *)((long **)buf+1),0);
>> }
>>
>> however this does not work, since the "sigsetjmp" call needs to be
>> replaced by a jump to sigsetjmp instead.
>>
>> There is already some working code for x64 in the original distribution:
>>
>> -------------------------------8<-----------------------------------
>> .file "zsvjmp.s"
>>        .globl zsvjmp_
>> .type   zsvjmp_, @function
>>
>> zsvjmp_:
>> # %rsi ... &status  %rdi ... &jumpbuf
>> movq    %rsi, (%rdi)    # store &status in jmpbuf[0]
>> movl    $0, (%rsi)      # zero the value of status
>> addq    $8, %rdi        # change point to &jmpbuf[1]
>> movl    $0, %esi        # change arg2 to zero
>> jmp     __sigsetjmp@PLT # let sigsetjmp do the rest
>>
>> .section        .note.GNU-stack,"",@progbits
>> -------------------------------8<-----------------------------------
>>
>> The "zdojmp" counterpart is a portable C function.
>>
>> I created a small repository [2] that contains the assembler I collected
>> so far as well as two test programs.
>>
>> However, I have no idea how to write the same for the ia64 platform.
>> Maybe someone could help me here? Preferably under the IRAF
>> license [3], so that it can be included upstream later.
>>
>> One way that worked for x32 and for arm64 was to compule the C snipped
>> above (having the sigsetjmp function name by something else) with an
>> optimizing compiler; however I didn't find cross compiler with the ia64
>> target?
>>
>> There is no request from the users to have this ported to ia64, and I
>> doubt that ever will. My main motivation to get it ported is to check
>> the package for hidden problems on "unusual" architectures.
>
> Hi,
> Managed to get this to work, with one caveat (explained afterwards):
>
>>
>> .file "zsvjmp.s"
>> .explicit
>> .text
>> .align 64
>> .global zsvjmp_#
>> .type zsvjmp_#, @function
>> .proc zsvjmp_#
>> zsvjmp_:
>> # Normally, tail calls to a different shared object are forbidden, since a
>> # function in our shared object can call us without needing to save/restore
>> # its gp register, but if we tail call a function outside the shared object
>> # then the PLT stub will clobber gp. However, since we're writing this in
>> # assembly in its own file, the compiler shouldn't be able to prove that
>> # any callers are in the same shared object as us, and therefore will be
>> # forced to pessimistically spill gp, so we should be safe to break the ABI
>> # and let gp be trashed by our tail call.
>>
>> st8 [r33] = r0
>> st8 [r32] = r33, 16
>> ;;
>> mov r33 = r0
>> br.cond.sptk.many __sigsetjmp#
>> .endp zsvjmp_#
>> .section .note.GNU-stack,"",@progbits
>
> The caveat is that IA-64 has 128-bit floats saved as part of setjmp/longjmp,
> and these must be aligned, so the entire jmp_buf must be 16-byte aligned.
> Moreover, since zsvjmp_ uses the first long (8 bytes) of the buffer for its
> status pointer, the second long now also needs to be reserved so we maintain
> the alignment when passing it to __sigsetjmp. Thus, I needed to make two
> changes to your test code on IA-64, which will need to be mirrored in the
> actual Fortran callers (I didn't change jmptest.f, it just works by chance):
>
>> diff --git a/zdojmp.c b/zdojmp.c
>> index 547d739..093d350 100644
>> --- a/zdojmp.c
>> +++ b/zdojmp.c
>> @@ -11,7 +11,7 @@
>>  void zdojmp_ (long *jmpbuf, long *status) {
>>      int stat = *status ? *status : 1;
>>      long *status_ptr = ((long **)jmpbuf)[0];
>> -    void *jb = (long **)jmpbuf+1;
>> +    void *jb = (long **)jmpbuf+2;
>>      *status_ptr = stat;
>>      siglongjmp (jb, stat);
>>  }
>> diff --git a/zzdebug.c b/zzdebug.c
>> index 824b3e3..1f350e5 100644
>> --- a/zzdebug.c
>> +++ b/zzdebug.c
>> @@ -6,7 +6,7 @@ void zsvjmp_(void*, volatile long *);
>>  void zdojmp_(void*, volatile long *);
>>  
>>  int main(void) {
>> -    long jmpbuf[180];
>> +    long jmpbuf[180] __attribute__((__aligned__(16)));
>>      volatile long status = 9;
>>      volatile long step = 0;
>>      zsvjmp_((void *)jmpbuf, &status);
>
>
> Regards,
> James
>