Recent

Author Topic: intel assembly code  (Read 8247 times)

tormods

  • New Member
  • *
  • Posts: 17
intel assembly code
« on: May 21, 2014, 01:01:37 pm »
I have made the following function in assembly located in a separate unit (not an object). By debugging this code everything works fine until it reaches the first ret-instruction where it ends up with an exception with text: External: SIGSEGV. One other thing is when looking at the produced code the final ret instruction seems to be missing. This code is taken directly from one of my Delphi projects, where this code works fine. I belive that Delphi will substitute the END with a ret instruction automatically. Has this something to do with establishing the routine in a separate unit and the compiler can not generate the correct return address?

function __SkrUtilCheckInRange(Low,High,Val : integer) : boolean; assembler;

      asm
        cmp ecx,edx         // check if Val > High
        ja @@1              // return with FALSE
        cmp ecx, eax        // check if Val < Low
        jb @@1              // return with FALSE
        mov eax, 1          // return with TRUE
        ret
  @@1:  xor eax, eax
      end;                                           

Tormods

Tomas Hajny

  • Moderator
  • New Member
  • *****
  • Posts: 36
Re: intel assembly code
« Reply #1 on: May 21, 2014, 01:38:44 pm »
Which compiler version and which options have you used for compiling this code? Can you post the generated assembly? It works in a simple test here performed with FPC 2.6.4 and using -Mdelphi (it wouldn't work correctly without -Mdelphi due to different size of "integer", because the code assumes 32-bit integers, but even then it shouldn't cause SIGSEGV, of course).

Laksen

  • Hero Member
  • *****
  • Posts: 621
    • J-Software
Re: intel assembly code
« Reply #2 on: May 21, 2014, 01:51:08 pm »
Did you compile it for x86_64 by chance?

I just  compiled it for i386-win32 and it worked just fine

Edit: Crashes once in a while when the inline ret is there. Strange... I suppose it's related to the debugger and stack frame.

But anyway. There's no reason to have inline assembler in this day and age :)
This code is almost twice as fast as the assembler version:
Code: [Select]
function __SkrUtilCheckInRange2(Low,High,Val : longword) : boolean;
      begin
         result := longword(val-low)<=longword(high-low);
      end;
« Last Edit: May 21, 2014, 02:06:44 pm by Laksen »

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: intel assembly code
« Reply #3 on: May 21, 2014, 02:42:29 pm »
Using these assumptions will provoke a crash, indeed:
Code: [Select]
{$mode objfpc}{$H+}
{$asmmode intel}

While using the '{$mode delphi}' compatible mode seems to solve the problem.

Or you can remove the 'ret' instruction.
Code: [Select]
function __SkrUtilCheckInRange(Low,High,Val : integer) : boolean; assembler;
  asm
          cmp ecx,edx         // check if Val > High
          ja @@1              // return with FALSE
          cmp ecx, eax        // check if Val < Low
          jb @@1              // return with FALSE
          mov eax, 1          // return with TRUE
          jmp @@2
    @@1:  xor eax, eax
    @@2:
  end;


Anyway, and according to my own experience, using previous Delphi assembly code inside FPC can rapidly turn into a nightmare (even in Delphi mode).

See this one, for instance: http://bugs.freepascal.org/view.php?id=24061, especially Jonas Maebe's answer.
« Last Edit: May 21, 2014, 02:44:42 pm by ChrisF »

tormods

  • New Member
  • *
  • Posts: 17
Re: intel assembly code
« Reply #4 on: May 22, 2014, 07:28:10 pm »
I have tried to check out the code the compiler produces, and found the cause why the code crashes:

New functioning code:

function __SkrUtilCheckInRange(Low,High,Val : integer) : boolean; assembler;

      asm
        cmp ecx,edx         // check if Val > High
        ja @@1              // return with FALSE
        cmp ecx, eax        // check if Val < Low
        jb @@1              // return with FALSE
        mov eax, 1          // return with TRUE
        leave
        ret
  @@1:  xor eax, eax
      end;                 

The compiler produces the following code:

00552240 55                        push   ebp
00552241 89e5                     mov    ebp,esp
00552243 83ec04                  sub    esp,0x4
00552246 39d1                     cmp    ecx,edx
00552248 770b                     ja     0x552255 <__SKRUTILCHECKINRANGE+21>
0055224A 39c1                     cmp    ecx,eax
0055224C 7207                     jb     0x552255 <__SKRUTILCHECKINRANGE+21>
0055224E b801000000          mov    eax,0x1
00552253 c9                         leave
00552254 c3                         ret 
00552255 31c0                     xor    eax,eax
00552257 c9                         leave 
00552258 c3                         ret   


Since the compiler produces the additional code:

push   ebp
mov    ebp,esp
sub    esp,0x4 

it needs to restore the esp and get back the epb and that is what the leave instruction does - analog code is :
mov esp, ebp
pop ebp

Why is this additional code added? it seems uneccessary. If an additional begin-end is used it produces a longer code:

skrutilities.pas:37               begin
00552240 55                       push   ebp
00552241 89e5                    mov    ebp,esp
00552243 83ec10                 sub    esp,0x10
00552246 8945fc                 mov    DWORD PTR [ebp-0x4],eax
00552249 8955f8                mov    DWORD PTR [ebp-0x8],edx
0055224C 894df4                mov    DWORD PTR [ebp-0xc],ecx
0055224F 39d1                   cmp    ecx,edx 
00552251 770b                   ja     0x55225e <__SKRUTILCHECKINRANGE+30>
00552253 39c1                   cmp    ecx,eax
00552255 7207                   jb     0x55225e <__SKRUTILCHECKINRANGE+30>
00552257 b801000000        mov    eax,0x1
0055225C c9                       leave
0055225D c3                       ret 
0055225E 31c0                    xor    eax,eax
00552260 c9                       leave 
00552261 c3                       ret   

I tried to use delphi mode, this did not change the result.
I personally try to make effective assembly code and as such adding unecessary code is not a good idea.

By the way: it is possible to set the assmbly window in debugger to Intel notation rather than the A&T notation!

Tormods



marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7495
Re: intel assembly code
« Reply #5 on: May 22, 2014, 07:30:28 pm »
Try adding "nostackframe" modifier

tormods

  • New Member
  • *
  • Posts: 17
Re: intel assembly code
« Reply #6 on: May 22, 2014, 10:46:43 pm »
 tried to put the current unit including the function in delphi mode, it seems that the calling unit also need to be in delphi mode does this mean that a complete application all units need to be in delphi mode to make this work or can units not called without stackframe still be in lazarus/fpc mode.

for me it is no issue to convert all my assembly routines originally written for delphi to pure pascal, it is therefore important to know how fpc works at assembly level.

thank you all for answers



tormods

Leledumbo

  • Hero Member
  • *****
  • Posts: 8111
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: intel assembly code
« Reply #7 on: May 24, 2014, 09:10:40 am »
Quote
tried to put the current unit including the function in delphi mode, it seems that the calling unit also need to be in delphi mode does this mean that a complete application all units need to be in delphi mode to make this work or can units not called without stackframe still be in lazarus/fpc mode.
again, check the generated assembly for both callee and caller and see the difference yourself. One of the compiler switches and/or calling convention might be the cause.

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7495
Re: intel assembly code
« Reply #8 on: May 24, 2014, 02:03:08 pm »
If I add nostackframe, no stackframe is generated in $mode objfpc:

function __SkrUtilCheckInRange(Low,High,Val : integer) : boolean; assembler; nostackframe;
(rest the same)

compiling with -a  gives

Code: [Select]
section .text.n_p$program_$$___skrutilcheckinrange$longint$longint$longint$$boolean,"x"
.balign 16,0x90
.globl P$PROGRAM_$$___SKRUTILCHECKINRANGE$LONGINT$LONGINT$LONGINT$$BOOLEAN
P$PROGRAM_$$___SKRUTILCHECKINRANGE$LONGINT$LONGINT$LONGINT$$BOOLEAN:
cmpl %edx,%ecx
ja .Lj5
cmpl %eax,%ecx
jb .Lj5
movl $1,%eax
ret
.Lj5:
xorl %eax,%eax
ret

in the corresponding .s