Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2011-03

Converting 32bit intel asm to Pascal or 64bit at&t asm

6 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Andrew Haines

Hi,

I'm trying to convert the following 32bit asm to pascal so it's portable. Also to 64bit asm.

It's from ACS audio component suite.

What am I doing wrong?

I tested my Pascal code with linux 32bit but the output differs from the 32bit asm. In 64bit linux it differs further but this could be from the ACS indicator component.

I attached screenshots of the various configurations.

I am using fpc 2.5.1 for 32bit and 64bit.

Regards,

Andrew

TACSComplex = record
  Im, Re: Double;
end;

<original code>

procedure LgMagnitude(InData : PACSComplex; OutData : PDouble; DataSize,
Shift : Integer);
  var
    LogBase  : Double;
  begin
    asm
                FLD1;
                FLDL2T;
                FDIVP;
                FSTP LogBase;
                MOV EDX, DataSize;
                SHL EDX, 3;
                MOV ECX, OutData;
                ADD EDX, ECX;
                MOV EAX, InData;
      @test:    CMP EDX, ECX;
                JE @out;
                FLD QWORD[EAX];
                FMUL ST(0), ST(0);
                ADD EAX, 8;
                FLD QWORD[EAX];
                FMUL ST(0), ST(0);
                FADDP;
                FSQRT;
                FTST;
                PUSH EAX;
                FSTSW AX;
                SAHF;
                JE @skip;
                FLD LogBase;
                FXCH;
                FYL2X;
                FIADD Shift;
                FTST;
                FSTSW AX;
                SAHF;
                JAE @skip;
                FSTP QWORD[ECX];
                FLDZ;
      @skip:    POP EAX;
                ADD EAX, 8;
                FSTP QWORD[ECX];
                ADD ECX, 8;
                JMP @test;
      @out:     ;
    end;
  end;
end;

</original code>

<pascal code>
  var
    LogBase  : Double;
    i: Integer;
    Im, Re: Double;
    Tmp: Double;
  begin
    asm
      FLD1;
      FLDL2T;
      FDIVP;
      FSTP LogBase;
    end;
    for i := 0 to DataSize-1 do
    begin
      Im := InData[i].Im*InData[i].Im;
      Re := InData[i].Re*InData[i].Re;
      Tmp := sqr(Im+Re);
      if Tmp  Im then
      begin
        //Tmp := log2(Tmp)*LogBase+Shift; // is this the same as the
following asm code?
        asm
          FLD LogBase;
          FLD Tmp;
          FYL2X;
          FIADD Shift;
          FSTP Tmp;
        end;
        if Tmp  0.0 then
        begin
          if 0.0 >= Tmp then
          begin
            Tmp := 0;
          end;
        end;
      end;
      OutData[i] := Tmp;
    end;
  end;
end;
</pascal code>
<64bit asm>
 SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide,
                   exOverflow, exUnderflow, exPrecision]);
    asm
                FLD1;
                FLDL2T;
                FDIVP;
                FSTP LogBase;
                MOV DataSize,  %RDX;
                SHL $3,        %RDX;
                MOV OutData,   %RCX;
                ADD %RCX,      %RDX;
                MOV InData,    %RAX;
                MOV Shift,     %rsi;
      .Ltest:   CMP %RCX,      %RDX;
                JE .Lout;
                FLD  (%RAX); //FLD QWORD[%EAX];
                FMUL %st(0), %st(0);
                ADD $8,        %RAX;
                FLD (%RAX);//FLD QWORD[EAX]; // sigsegv here with fpu
exceptions off. only happens after many iterations but seems immediate
in real time.
                FMUL %st(0), %st(0);
                FADDP;
                FSQRT;
                FTST;
                PUSH %RAX;
                FSTSW %AX; //with fpu exceptions enabled I get a FPU
exception here after some iterations but again it is immediate in realtime.
                //SAHF; // not available in amd64!
                //JE .Lskip;
                bt $14, %ax; // // copy fpu C3 flag to cpu carry flag
                JC .Lskip;
                FLD LogBase;
                FXCH;
                FYL2X;
                FIADD Shift;
                FTST;
                FSTSW %AX;
                //SAHF; // not available in amd64!
                //JAE .Lskip;
                bt $8, %ax // copy fpu C0 flag to cpu carry flag
                JNC .Lskip
                FSTP (%RCX);//FSTP QWORD[ECX];
                FLDZ;
      .Lskip:   POP %RAX;
                ADD $8,        %RAX;
                FSTP (%RCX);//FSTP QWORD[RCX];
                ADD $8, %RCX;
                JMP .Ltest;
      .Lout:     ;
    end;
end;
</64bit asm>
Andrew Haines
to Andrew Haines
Andrew Haines wrote 9 lines
Hi, I'm trying to convert the following 32bit asm to pascal so it's portable. Also to 64bit asm. It's from ACS audio component suite. What am I doing wrong?

For anyone who's interested I fixed the 64bit assembly version which is good enough for me but I had hoped to write pascal code for the function.

I converted the intel32 asm to att32 asm which showed my first problem "FDIVP" on intel becomes "FDIVRP" but on att it stays the same which causes a different result. Beyond that, there wern't many problems. I had some fun wondering why the RCX register wasn't a reasonable value until I realized I was copying 64bits from a 32bit variable.

The following is my result. The 32bit and 64bit asm work but the pascal still does not. Any suggestions are welcome.

 procedure LgMagnitude(InData : PACSComplex; OutData : PDouble;
DataSize, Shift : Integer);
  {$IFDEF CPUI386}
  var
    LogBase  : Double;
  begin
    asm
                FLD1;
                FLDL2T;
                FDIVrP;
                FSTPQ LogBase;
                MOVL DataSize, %ecx;
                MOVL OutData, %edi;
                MOVL InData, %esi;
      .Ltest:   DEC %ecx
                JZ .Lout;
                FLDQ (%esi);
                FMULQ %ST(0), %ST(0);
                ADDL $8, %esi;
                FLDQ (%esi);
                FMULQ %ST(0), %ST(0);
                FADDP;
                FSQRTQ;
                FTSTQ;
                FSTSW %AX;
                SAHF;
                JE .Lskip;
                FLDQ LogBase;
                FXCH;
                FYL2X;
                FIADDL Shift;
                FTSTQ;
                FSTSW %AX;
                SAHF;
                JAE .Lskip;
                FSTPQ (%edi);
                FLDZ;
      .Lskip:
                ADDL $8, %esi;
                FSTPQ (%edi);
                ADDL $8, %edi;
                JMP .Ltest;
      .Lout:     ;
    end;
  end;
  {$ELSE} {$IFDEF CPUX86_64}
  var
    LogBase  : Double;
  begin
    SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide,
exOverflow, exUnderflow, exPrecision]);
    asm
                FLD1;
                FLDL2T;
                FDIVrP;
                FSTPQ   LogBase;
                MOV     DataSize, %ecx;
                MOV     OutData,  %rdi;
                MOV     InData,   %rsi;
      .Ltest:   DEC     %ecx
                JZ .Lout;
                FLDL    (%rsi);
                FMUL    %ST(0),   %ST(0);
                ADD     $8,       %rsi;
                FLDL    (%rsi);
                FMUL    %ST(0),   %ST(0);
                FADDP;
                FSQRT;
                FTST;
                FSTSW   %AX;
                // SAHF is not available on all x86_64 processors so
this is the workaround
                bt      $14,      %ax; // // copy fpu C3 flag to cpu
carry flag
                JC .Lskip;
                FLD     LogBase;
                FXCH;
                FYL2X;
                FIADDL  Shift;
                FTST;
                FSTSW   %AX;
                // SAHF is not available on all x86_64 processors so

this is the workaround
bt $8, %ax // copy fpu C0 flag to cpu carry flag JNC .Lskip

                FSTPL   (%rdi);
                FLDZ;
      .Lskip:
                ADDL    $8,    %rsi;
                FSTPL   (%rdi);
                ADDL    $8,    %rdi;
                JMP .Ltest;
      .Lout:     ;
    end;
  end;
  {$ELSE}
  {$ERROR LgMagnitude Unimplemented}
  // the following is not correct but was my best effort at pascalizing
the asm code
  var
    LogBase  : Double;
    i: Integer;
    Im, Re: Double;
    Tmp: Double;
  begin
    asm
      FLD1;
      FLDL2T;
      FDIVRP;
      FSTP LogBase;
    end;
    for i := 0 to DataSize-1 do
    begin
      Im := InData[i].Im*InData[i].Im;
      Re := InData[i].Re*InData[i].Re;
      Tmp := sqr(Im+Re);
      if Tmp  Im then
      begin
        //Tmp := ln(Tmp)*LogBase+Shift; // same as the following asm proc?
        asm
          FLD LogBase;
          FLD Tmp;
          FYL2X;
          FIADD Shift;
          FSTP Tmp;
        end;
        if Tmp  0.0 then
        begin
          if 0.0 >= Tmp then
          begin
            Tmp := 0;
          end;
        end;
      end;
      OutData[i] := Tmp;
    end;
  end;
  {$ENDIF CPUX86_64}
  {$ENDIF CPUI386}
Thomas Schatzl
to Andrew Haines

Hi,

Andrew Haines wrote 8 lines
if Tmp Im then begin //Tmp := ln(Tmp)*LogBase+Shift; // same as the following asm proc? asm FLD LogBase; FLD Tmp; FYL2X;

FYL2X calculates the log to the base 2 of tmp, not log to the base e of tmp? Just a guess.

Thomas

Andrew Haines
to Thomas Schatzl
Thomas Schatzl wrote 17 lines
Hi, On Wed, 09 Mar 2011 11:58:08 -0500, Andrew Haines wrote: > if Tmp Im then > begin > //Tmp := ln(Tmp)*LogBase+Shift; // same as the following asm > proc? > asm > FLD LogBase; > FLD Tmp; > FYL2X; FYL2X calculates the log to the base 2 of tmp, not log to the base e of tmp? Just a guess. Thomas

I originally used log2(Tmp) but switched to ln to check if it was correct. Thanks for noticing that, I changed it back to log2.

The current output with the pascal code looks like it did when FLDS was used instead of FLDL to load (%rdi). The intel code was "FLD QWORD[EDI]" I changed this to "FLDQ (%edi)". on x32 it became FLDL and on x64 it became FLDS. changing the line to "FLDL (%rdi)" on x86_64 fixed the last problem I had with the asm translation.

It's all very confusing. Is fldl loading a 32bit floating number? The variable at (%rdi) is a double. flds makes me think it's loading a single.

Andrew

Thomas Schatzl
to Andrew Haines
Andrew Haines wrote 32 lines
On 03/09/11 12:26, Thomas Schatzl wrote: > Hi, > > On Wed, 09 Mar 2011 11:58:08 -0500, Andrew Haines wrote: >> if Tmp Im then >> begin >> //Tmp := ln(Tmp)*LogBase+Shift; // same as the following >> asm >> proc? >> asm >> FLD LogBase; >> FLD Tmp; >> FYL2X; > > FYL2X calculates the log to the base 2 of tmp, not log to the base e > of > tmp? Just a guess. > > Thomas > I originally used log2(Tmp) but switched to ln to check if it was correct. Thanks for noticing that, I changed it back to log2. The current output with the pascal code looks like it did when FLDS was used instead of FLDL to load (%rdi). The intel code was "FLD QWORD[EDI]" I changed this to "FLDQ (%edi)". on x32 it became FLDL and on x64 it became FLDS. changing the line to "FLDL (%rdi)" on x86_64 fixed the last problem I had with the asm translation.

Suffixes for gas for floating point are (from
http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax)

-s 32 bit/single
-l 64 bit/double
-t 80 bit/extended

With Intel syntax you can only hope that the compiler guesses correctly, or add an explicit override.

So you suggest that the assembler changes an inlined "fldq (%edi)" into "flds (%edi)" on x86-64? This looks like a bug if true - it should reject the "q" suffix for floating point operations. At least gas 2.19 on solaris x64 rejects "fldq"; actually the 2.4.2 release compiler transforms "fldq" into "fld" here, which gas rejects too :)

I will write a bug report.

Thomas

Andrew Haines
to Thomas Schatzl
Thomas Schatzl wrote 18 lines
On Wed, 09 Mar 2011 13:23:55 -0500, Andrew Haines wrote: > On 03/09/11 12:26, Thomas Schatzl wrote: >> >> FYL2X calculates the log to the base 2 of tmp, not log to the base e of >> tmp? Just a guess. >> > > I originally used log2(Tmp) but switched to ln to check if it was > correct. Thanks for noticing that, I changed it back to log2. > Suffixes for gas for floating point are (from http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax) -s 32 bit/single -l 64 bit/double -t 80 bit/extended

Thank you this is useful!

I will write a bug report.

Thank you for writing that bug report.

I finally got the plain pascal version to work properly! My major problem was I accidentally used the sqr function instead of sqrt. Here's the final product:

  var
    LogBase  : Double;
    i: Integer;
    Tmp: Double;
  begin
    SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide,
exOverflow, exUnderflow, exPrecision]);
    LogBase:=1.0/(ln(10)/ln(2));
    for i := DataSize-1 downto 0 do
    begin
      Tmp := sqrt(sqr(InData[i].Im) + sqr(InData[i].Re));
      if Tmp > 0.0 then
        Tmp := (ln(Tmp)/ln(2))*LogBase+Shift
      else
        Tmp := 0.0;
      OutData[i] := Tmp;
    end;
  end;

In case anyone is interested, this generates 45 lines of assembly. The handwritten asm code is 40 lines. (not including the procedure pre and post asm for either method) I cheated a little and copied the log2 code directly to avoid the function calls, this resulted in the compiler doing the math ahead of time.

Regards,

Andrew Haines