Sign in Join

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

Mailing lists / fpc-pascal / 2004-07

Default calling convention

12 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

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

Marcel Martin
Marco van de Voort wrote 24 lines
Marcel Martin wrote: > > I noticed that, with FPC 1.9.4, the (default) calling convention > "register" depends on the fact that a routine is or is not embedded > in an other routine. > > With "proc(A,B,C: Longint);", if "proc" is not embedded then > eax = A, edx = B and ecx = C but if "proc" is embedded then edx = A, > ecx = B and C is on the stack. > > Is this convention definitive or temporary? I am not sure it is > definitive since it makes the code of assembler routines dependent > on their status: embedded or not. In general it is not wise anyway to let the assembler become to dependant on calling convention. Keep on using mov eax,param1 mov ecx,param2 etc. Clearer, and doesn't have the problem between procedure/methods/nested procedures.

I agree with you but it doesn't work. For instance, take this procedure

  procedure nx_fill(P: PLongword; Count: Longint; Value: Longword);
  assembler;
  asm
          pushl  %edi
          movl   Value,%eax
          movl   Count,%ecx
          movl   P,%edi
          rep
          stosl
          popl   %edi
  end;

in the .s file, FPC 1.9.4 produces this:

.globl NXKERNEL_NX_FILL$PLONGWORD$LONGINT$LONGWORD pushl %ebp
movl %esp,%ebp
pushl %edi
movl %ecx,%eax // eax is modified
movl %edx,%ecx
movl %eax,%edi // eax is more equal to P, access violation rep
stosl
popl %edi
ret

With this one

  procedure nx_fill(P: PLongword; Count: Longint; Value: Longword);
  assembler;
  asm
          pushl   %edi
          movl    %eax,%edi  // edi <- P
          movl    %ecx,%eax  // eax <- Value
          movl    %edx,%ecx  // ecx <- Count
          rep
          stosl
          popl    %edi
  end;

the produced code is correct but now this proc can no more be used as a nested proc.

Reply by email

Peter Vreman
to Marcel Martin
Show quoted text 73 lines
Marco van de Voort wrote: > Marcel Martin wrote: > > > > I noticed that, with FPC 1.9.4, the (default) calling convention > > "register" depends on the fact that a routine is or is not embedded > > in an other routine. > > > > With "proc(A,B,C: Longint);", if "proc" is not embedded then > > eax = A, edx = B and ecx = C but if "proc" is embedded then edx = A, > > ecx = B and C is on the stack. > > > > Is this convention definitive or temporary? I am not sure it is > > definitive since it makes the code of assembler routines dependent > > on their status: embedded or not. > In general it is not wise anyway to let the assembler become to > dependant > on calling convention. > Keep on using > mov eax,param1 > mov ecx,param2 > etc. Clearer, and doesn't have the problem between > procedure/methods/nested > procedures. I agree with you but it doesn't work. For instance, take this procedure procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); assembler; asm pushl %edi movl Value,%eax movl Count,%ecx movl P,%edi rep stosl popl %edi end; in the .s file, FPC 1.9.4 produces this: .globl NXKERNEL_NX_FILL$PLONGWORD$LONGINT$LONGWORD pushl %ebp movl %esp,%ebp pushl %edi movl %ecx,%eax // eax is modified movl %edx,%ecx movl %eax,%edi // eax is more equal to P, access violation rep stosl popl %edi ret With this one procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); assembler; asm pushl %edi movl %eax,%edi // edi <- P movl %ecx,%eax // eax <- Value movl %edx,%ecx // ecx <- Count rep stosl popl %edi end; the produced code is correct but now this proc can no more be used as a nested proc.

Check what Delphi does for nested procedures. If it is different from FPC then FPC will be adapted.

Reply by email

Thomas Schatzl
to Peter Vreman

Hello,

Show quoted text 11 lines
>>>I noticed that, with FPC 1.9.4, the (default) calling convention >>>"register" depends on the fact that a routine is or is not embedded >>>in an other routine. >>> >>>With "proc(A,B,C: Longint);", if "proc" is not embedded then >>>eax = A, edx = B and ecx = C but if "proc" is embedded then edx = A, >>>ecx = B and C is on the stack. >>> >>>Is this convention definitive or temporary? I am not sure it is >>>definitive since it makes the code of assembler routines dependent >>>on their status: embedded or not.

This is exactly the same as Delphi (7) behaves.

Show quoted text 11 lines
> procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); > assembler; > asm > pushl %edi > movl %eax,%edi // edi <- P > movl %ecx,%eax // eax <- Value > movl %edx,%ecx // ecx <- Count > rep > stosl > popl %edi > end;

In this case

   procedure nx_fill(P: PLongword; Count: Longint; Value: Longword);
   assembler;
   asm
           pushl   %edi
           movl    P,%edi  // edi <- P
           movl    Value,%eax  // eax <- Value
           movl    Count,%ecx  // ecx <- Count
           rep
           stosl
           popl    %edi
   end;

works both as embedded and non-embedded assembler procedure because the order of assignments is ok in both cases.

Btw, there's already a method in the system unit which does the same thing as your routine: filldword()...

Regards,
Thomas

Reply by email

Florian Klaempfl
to Thomas Schatzl
Thomas Schatzl wrote 20 lines
In this case procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); assembler; asm pushl %edi movl P,%edi // edi <- P movl Value,%eax // eax <- Value movl Count,%ecx // ecx <- Count rep stosl popl %edi end; works both as embedded and non-embedded assembler procedure because the order of assignments is ok in both cases. Btw, there's already a method in the system unit which does the same thing as your routine: filldword()...

... and with better instruction selection ;)

Reply by email

Marcel Martin
to Peter Vreman: Check what Delphi does for nested procedures. If it is different from FPC then FPC will…
Peter Vreman wrote
Check what Delphi does for nested procedures. If it is different from FPC then FPC will be adapted.

Overall, no :-) Delphi (at least the version 5.01) is bugged. For instance, with

  function Add: Longword;
    var x : Longword;
    function DoAdd(a: Longword): Longword; register;
      var y : Longword;
    asm
      mov  y, 1
      add  eax, x
      add  eax, y
    end;
  begin
    x := 7;
    Result := DoAdd(1);
  end;

The result should be 9 (1+7+1) but for Delphi it is only 3. "DoAdd" is coded this way

mov [ebp-$04], $00000001 // mov y, 1
add eax, [ebp-$4] // add eax, x
add eax, [ebp-$4] // add eax, y

i.e., for Delphi 5.01, x and y are the same thing!

I think the best thing that could be done would be to manage so that FPC always makes use of [ebp-something] to access a parameter when the programmer makes use of the parameter name, i,e., with any calling convention the instruction "mov eax,param" should never be coded as "mov eax,reg" but always as "mov eax,[ebp-something]".

Reply by email

Marcel Martin
to Thomas Schatzl: This is exactly the same as Delphi (7) behaves. In this case procedure nx_fill(P:…
Thomas Schatzl wrote 16 lines
Hello, >>>>I noticed that, with FPC 1.9.4, the (default) calling convention >>>>"register" depends on the fact that a routine is or is not embedded >>>>in an other routine. >>>> >>>>With "proc(A,B,C: Longint);", if "proc" is not embedded then >>>>eax = A, edx = B and ecx = C but if "proc" is embedded then edx = A, >>>>ecx = B and C is on the stack. >>>> >>>>Is this convention definitive or temporary? I am not sure it is >>>>definitive since it makes the code of assembler routines dependent >>>>on their status: embedded or not. This is exactly the same as Delphi (7) behaves.

I don't know, I have Delphi 5.01.

Show quoted text 28 lines
>> procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); >> assembler; >> asm >> pushl %edi >> movl %eax,%edi // edi <- P >> movl %ecx,%eax // eax <- Value >> movl %edx,%ecx // ecx <- Count >> rep >> stosl >> popl %edi >> end; In this case procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); assembler; asm pushl %edi movl P,%edi // edi <- P movl Value,%eax // eax <- Value movl Count,%ecx // ecx <- Count rep stosl popl %edi end; works both as embedded and non-embedded assembler procedure because the order of assignments is ok in both cases.

Yes, but this way a program becomes a mineland. You write this, a few weeks later, for some reason, you change the line order, and you waste time to find why, suddenly, your program crashes (and yet, assuming you immediately activate the bug).

Reply by email

Thomas Schatzl
to Marcel Martin: Overall, no :-) Delphi (at least the version 5.01) is bugged. For instance, with function…
Show quoted text 9 lines
Peter Vreman a écrit : >Check what Delphi does for nested procedures. If it is different from FPC >then FPC will be adapted. > Overall, no :-) Delphi (at least the version 5.01) is bugged. [example code snipped]

Interestingly, D7 has the same bug.

Show quoted text 6 lines
I think the best thing that could be done would be to manage so that FPC always makes use of [ebp-something] to access a parameter when the programmer makes use of the parameter name, i,e., with any calling convention the instruction "mov eax,param" should never be coded as "mov eax,reg" but always as "mov eax,[ebp-something]".

See the other answer about the 'pascal' modifier.

Regards,
Thomas

Reply by email

Thomas Schatzl
to Marcel Martin: I don't know, I have Delphi 5.01. Yes, but this way a program becomes a mineland. You…

Hello,

Show quoted text 23 lines
> >In this case > > procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); > assembler; > asm > pushl %edi > movl P,%edi // edi <- P > movl Value,%eax // eax <- Value > movl Count,%ecx // ecx <- Count > rep > stosl > popl %edi > end; > >works both as embedded and non-embedded assembler procedure because the >order of assignments is ok in both cases. Yes, but this way a program becomes a mineland. You write this, a few weeks later, for some reason, you change the line order, and you waste time to find why, suddenly, your program crashes (and yet, assuming you immediately activate the bug).

:( That's true.

I usually add a 'pascal' procedure modifier to most (if not all) assembler routines exactly because of that. This forces parameter passing on the stack.
This is a little slower, but if your hand-coded assembler proc is only faster if the parameters are passed through registers, it might be a good idea to use Pascal right away...

Regards,
Thomas

Reply by email

Peter Vreman
to Marcel Martin: Overall, no :-) Delphi (at least the version 5.01) is bugged. For instance, with function…
Show quoted text 18 lines
Peter Vreman a écrit : > > Check what Delphi does for nested procedures. If it is different from > FPC > then FPC will be adapted. > Overall, no :-) Delphi (at least the version 5.01) is bugged. For instance, with function Add: Longword; var x : Longword; function DoAdd(a: Longword): Longword; register; var y : Longword; asm mov y, 1 add eax, x add eax, y

This is unsupported, Accessing variables not in the current stackframe need additional assembler instructions. That is the reason why FPC gives an error for this:

p.pp(7,17) Error: You can not reach X from that code

Don't forget that you should be familair with the code generated by the compiler when programming assembler functions. If you don't know, then keep away from programming assembler.

Show quoted text 17 lines
end; begin x := 7; Result := DoAdd(1); end; The result should be 9 (1+7+1) but for Delphi it is only 3. "DoAdd" is coded this way mov [ebp-$04], $00000001 // mov y, 1 add eax, [ebp-$4] // add eax, x add eax, [ebp-$4] // add eax, y i.e., for Delphi 5.01, x and y are the same thing! I think the best thing that could be done would be to manage so that FPC always makes use of [ebp-something] to access a parameter when

This is your opinion for 'best', other ppl like the register calling more.

the programmer makes use of the parameter name, i,e., with any calling convention the instruction "mov eax,param" should never be coded as "mov eax,reg" but always as "mov eax,[ebp-something]".

That is already supported:

Solution 1:
add a begin...end around the assembler block. The parameters will then first be loaded to the local stackframe and the assembler uses [ebp-xx]. IIRC delphi works the same.

Solution 2:
Changing the calling convention from register to stdcall.

Reply by email

Marcel Martin
to Florian Klaempfl: ... and with better instruction selection ;)
Florian Klaempfl wrote 24 lines
Thomas Schatzl wrote: > > In this case > > procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); > assembler; > asm > pushl %edi > movl P,%edi // edi <- P > movl Value,%eax // eax <- Value > movl Count,%ecx // ecx <- Count > rep > stosl > popl %edi > end; > > works both as embedded and non-embedded assembler procedure because the > order of assignments is ok in both cases. > > Btw, there's already a method in the system unit which does the same > thing as your routine: filldword()... ... and with better instruction selection ;)

1) This little routine was sent to point out a problem.

2) Better than what? Where do you see an "instruction selection" in my procedure. There is none. And there is none because this is useless. 'nx_fill' is in a unit called 'nxkernel', this unit contains all the low level routines for the library, and with these routines, it is up to the calling procedures to check the validity of the transmitted parameters. This is as simple as that.

Reply by email

Florian Klaempfl
to Marcel Martin
Marcel Martin wrote 30 lines
Florian Klaempfl a écrit : >Thomas Schatzl wrote: > >>In this case >> >> procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); >> assembler; >> asm >> pushl %edi >> movl P,%edi // edi <- P >> movl Value,%eax // eax <- Value >> movl Count,%ecx // ecx <- Count >> rep >> stosl >> popl %edi >> end; >> >>works both as embedded and non-embedded assembler procedure because the >>order of assignments is ok in both cases. >> >>Btw, there's already a method in the system unit which does the same >>thing as your routine: filldword()... > >... and with better instruction selection ;) 1) This little routine was sent to point out a problem. 2) Better than what? Where do you see an "instruction selection"

Push instead of mov. mov is faster on modern machines because it causes less instruction dependencies.

Show quoted text 6 lines
in my procedure. There is none. And there is none because this is useless. 'nx_fill' is in a unit called 'nxkernel', this unit contains all the low level routines for the library, and with these routines, it is up to the calling procedures to check the validity of the transmitted parameters. This is as simple as that.

Reply by email

Marcel Martin
to Florian Klaempfl
Florian Klaempfl wrote 36 lines
Marcel Martin wrote: > Florian Klaempfl a écrit : > >>Thomas Schatzl wrote: >> >>>In this case >>> >>> procedure nx_fill(P: PLongword; Count: Longint; Value: Longword); >>> assembler; >>> asm >>> pushl %edi >>> movl P,%edi // edi <- P >>> movl Value,%eax // eax <- Value >>> movl Count,%ecx // ecx <- Count >>> rep >>> stosl >>> popl %edi >>> end; >>> >>>works both as embedded and non-embedded assembler procedure because the >>>order of assignments is ok in both cases. >>> >>>Btw, there's already a method in the system unit which does the same >>>thing as your routine: filldword()... >> >>... and with better instruction selection ;) > > > 1) This little routine was sent to point out a problem. > > 2) Better than what? Where do you see an "instruction selection" Push instead of mov. mov is faster on modern machines because it causes less instruction dependencies.

1) That's what you call an 'instruction selection'? Such an answer is what I call bad faith. You were talking about checking the possible negativity of 'Count'.

2) Not with Delphi 5.01. With such a procedure, Delphi 5.01 doesn't generate the instructions "push ebp, etc. ". By using a local, you constraint it to do that. In short, if you want to use 2 mov's instead of push/pop by creating a local, you will have 2 mov's AND a push/pop. Now I agree, this is no more true with FPC 1.9.4 since it seems to generate the sequence "push ebp ..." as soon as there is a parameter (even if there is no local and if this parameter is sent through a register). But you know what? My library was written with (and for) Delphi 5.01 and, I am sorry, but adapting 30,000 lines of code for a compiler I don't know well cannot be done in two weeks. At least, _I_ cannot.

3) Next time I will have a problem with FPC, I will think twice before talking about it on this list.

Reply by email