Does Petunia support the classic FSAVE-based 68881 FPU detection/initialization used by VBCC?

A forum for general AmigaOS 4.x support questions that are not platform-specific
Post Reply
FlynnTheAvatar
Posts: 13
Joined: Thu Aug 11, 2022 1:41 pm

Does Petunia support the classic FSAVE-based 68881 FPU detection/initialization used by VBCC?

Post by FlynnTheAvatar »

Hello,

I'm investigating an issue with a 68k binary built using VBCC:

Code: Select all

vc +aos68k -cpu=68020 -fpu=68881 -g SpeedTest.c -o SpeedTest -lm881
The complete source code of the test program (not mine and yes, it is ugly):

Code: Select all

/*****************************************
**                                      **
**              SpeedTest               **
**                                      **
**  Kopierrecht 1996/97 bei COMPIUTECK  **
**                                      **
**    Geschrieben von Matthias Henze    **
**                                      **
*************** © 19/03/97 **************/

#include <stdio.h>
#include <time.h>
#include <math.h>

clock_t timestart;
double z = 0;

void end (void)
{
  double y;
  clock_t timeend;
  timeend = clock ();
  y = ((timeend - timestart) / (double)50);
  printf ("Benötigte Zeit: %.5g  s\n", y);
  z = z + y;
}

int main (void)
{
  double d, x;
  double a = 80.9394;
  double b = -80.9394;
  int e, i, loop;

printf("\nGeben Sie bitte die Anzahl der Testdurchläufe an: ");
scanf("%d",&loop);

/*--fabs--*/
printf ("fabs (80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = fabs (a);
end();
printf ("Der absolute Betrag von 80.9394 sollte 80.9394 sein und ist: %g\n\n", x);

printf ("fabs (-80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = fabs (b);
end();
printf ("Der absolute Betrag von -80.9394 sollte 80.9394 sein und ist: %g\n\n", x);

/*--floor--*/
printf ("floor (80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = floor (a);
end();
printf ("Die nächstkleinere Ganzzahl von 80.9394 sollte 80 sein und ist: %g\n\n", x);

printf ("floor (-80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = floor (b);
end();
printf ("Die nächstkleinere Ganzzahl von -80.9394 sollte -81 sein und ist: %g\n\n", x);

/*--sqrt--*/
printf ("sqrt (9)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = sqrt (9);
end();
printf ("Die Quadratwurzel von 9 sollte 3 sein und ist: %g\n\n", x);

printf ("sqrt (-9)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = sqrt (-9);
end();
printf ("Die Quadratwurzel von -9 sollte NaN (Not a Number) sein und ist: %g\n\n", x);

/*--ceil--*/
printf ("ceil (80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = ceil (a);
end();
printf ("Die nächstgrößere Ganzzahl von 80.9394 sollte 81 sein und ist: %g\n\n", x);

printf ("ceil (-80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = ceil (b);
end();
printf ("Die nächstgrößere Ganzzahl von -80.9394 sollte -80 sein und ist: %g\n\n", x);

/*--frexp--*/
printf ("frexp (9)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = frexp (9, &e);
end();
printf ("Die Mantisse von 9 sollte 0.5625 sein und ist: %g\nDer Exponent von 9 sollte 4 sein und ist: %d\n\n", x, e);

printf ("frexp (88)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = frexp (88, &e);
end();
printf ("Die Mantisse von 88 sollte 0.6875 sein und ist: %g\nDer Exponent von 88 sollte 7 sein und ist: %d\n\n", x, e);

/*--modf--*/
printf ("modf (80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = modf (a, &d);
end();
printf ("Der Nachkommateil von 80.9394 sollte 0.9394 sein und ist: %g\nDer Vorkommateil von 80.9394 sollte 80 sein und ist: %g\n\n", x, d);

printf ("modf (-80.9394)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = modf (b, &d);
end();
printf ("Der Nachkommateil von -80.9394 sollte -0.9394 sein und ist: %g\nDer Vorkommateil von -80.9394 sollte -80 sein und ist: %g\n\n", x, d);

/*--fmod--*/
printf ("fmod (5.7, 1.5)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = fmod (5.7,1.5);
end();
printf ("Das Ergebnis dieser Funktion sollte 1.2 sein und ist: %g\n\n", x);

printf ("fmod (-5.7, 1.5)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = fmod (-5.7,1.5);
end();
printf ("Das Ergebnis dieser Funktion sollte -1.2 sein und ist: %g\n\n", x);

/*--ldexp--*/
printf ("ldexp (9, 3)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = ldexp (9,3);
end();
printf ("Das Produkt von 9 * (2³) sollte 72 sein und ist: %g\n\n", x);

printf ("ldexp (-9, 3)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = ldexp (-9,3);
end();
printf ("Das Produkt von -9 * (2³) sollte -72 sein und ist: %g\n\n", x);

/*--cos--*/
printf ("cos (0)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = cos (0);
end();
printf ("Der Cosinus von 0 sollte 1 sein und ist: %g\n\n", x);

printf ("cos (1)\n");
timestart = clock ();
  for (i = 0; i < loop; i++)
    x = cos (1);
end();
printf ("Der Cosinus von 1 sollte 0.540302 sein und ist: %g\n\n", x);

printf ("Gesamtzeit für %d Durchgänge: %g  s\n", loop, z);
return 0;
}
The program runs correctly under E-UAE with a 68881 FPU enabled.

However, under AmigaOS 4.1 using Petunia, nearly all floating-point results become NaN, although the program itself continues to run.

While examining the generated code, I noticed that the VBCC runtime executes the following startup code:

Code: Select all

__math_init:
03d0 MOVEM.L D0-D1/A5-A6,-(A7)
03d4 MOVEA.L ABSEXECBASE.W,A6
03d8 LEA __fpu_init,A5
03de JSR (-30,A6)
03e2 MOVEM.L (A7)+,D0-D1/A5-A6
03e6 RTS
The code passed to Supervisor() is:

Code: Select all

__fpu_init:
               clr.l   -(sp)                   ; 0 : 42a7
               clr.l   -(sp)                   ; 2 : 42a7
               clr.l   -(sp)                   ; 4 : 42a7
               frestore (sp)                   ; 6 : f357
               lea     $c(sp),sp               ; 8 : 4fef 000c
               rte                             ; c : 4e73

               dc.w    $4e71                   ; NOP
This appears to be a traditional 68k FPU detection/initialization mechanism based on executing FSAVE from supervisor mode and relying on the resulting exception/stack frame behaviour.

My questions are:
  • Is this method officially supported by Petunia?
  • Are there any known differences in the handling of FSAVE executed via a Supervisor() callback compared to real 68020/68881 hardware?
  • Does Petunia fully emulate the expected behaviour of FSAVE, FRESTORE, and the related FPU state management used by legacy runtimes?
  • Are there known incompatibilities with older compiler runtimes that detect the FPU this way?
For comparison:
  • The same benchmark compiled with SAS/C runs correctly. Under E-UAE without an FPU, SAS/C detects the missing FPU and reports an error instead of continuing.
  • The VBCC binary emits native 68881 instructions and under Petunia ends up producing NaN results rather than stopping cleanly.
  • At this point I'm trying to determine whether this is a Petunia compatibility issue with the classic Supervisor()+FSAVE detection method, or whether the VBCC runtime is relying on behaviour that Petunia never intended to support.
Any information from people familiar with Petunia's FPU emulation would be greatly appreciated.

Thanks!
FlynnTheAvatar
Posts: 13
Joined: Thu Aug 11, 2022 1:41 pm

Re: Does Petunia support the classic FSAVE-based 68881 FPU detection/initialization used by VBCC?

Post by FlynnTheAvatar »

Update / Additional findings

I have gathered some more data which may help narrow this down.

Behaviour with and without JIT

Using the VBCC-generated binary:
  • With Petunia JIT enabled, the program starts and runs, but most floating-point results become NaN.
  • With Petunia JIT disabled, the program crashes during startup before the benchmark code is reached.
The crash occurs inside the VBCC runtime's FPU initialization sequence:

Code: Select all

__fpu_init:
clr.l -(sp)
clr.l -(sp)
clr.l -(sp)
frestore (sp)
lea 12(sp),sp
rte
nop
The crash dump places the 68k instruction pointer at the end of this sequence, around the RTE return path.

Comparison with SAS/C

For comparison, I built the same benchmark with SAS/C.

The SAS/C binary runs correctly:
  • with Petunia JIT enabled
  • with Petunia JIT disabled
I also examined the SAS/C startup code.

SAS/C clearly uses native 68881/68882 instructions throughout its math library (sin, cos, sqrt, fabs, etc.), so this is not a soft-float implementation.

However, its startup code appears to use a different FPU initialization strategy.

Among other things it emits (corresponding to the SAS/C source):

Code: Select all

    __emit(0xf23c);        /* put out the instruction FMOVE.L #$00,FPCR */
    __emit(0x9000);        /* This is Round toward nearest and use      */
    __emit(0x0000);        /* Extended precision.                       */
    __emit(0x0000);
which initializes the FPCR to a known state (round-to-nearest, extended precision).

The SAS/C startup code also contains an explicit math coprocessor check and the message:
"This program requires a math co-processor"
if no FPU is available.

Has anyone familiar with Petunia's implementation seen issues with this style of FPU detection/initialization before?
Post Reply