Board index » delphi » D2 "register" cc bug

D2 "register" cc bug

Subject: D2 register calling convention bug

I have found a problem with D2's register (default) calling convention that
looks suspiciously like a bug to me (code attached below; do a File|New app;
drop a label on the form; trade the default unit1.pas with the one presented
here).

I have a little function ("Get_Number" below) that gets a string in as a VAR
parameter, reads out 5 chars from the start of the string, convert these to a
number, and chop off the 5 chars from the string. Another routine that takes
two (or more in this example) numbers as parameters ("Test" below) gets called
with the output from "Get_Number" as parameters.

The funny thing is, when using register cc and two or three parameters, the
parameters get reversed in their order on the stack (when expected output would
be "1 2 3" it comes out as "3 2 1").

If more than three parameters are used, the last two will come first, and in
reverse order, then the rest comes in the correct order (when expected output
would be "1 2 3 4 5" it comes out as " 5 4 1 2 3").

If I uncomment the pascal cc identifier, it works as expected. I couldn't find
anything in the docs that said VAR strings were to be avoided with register cc,
so I assume it's some D2 problem. BTW, the code as presented uses 'old' short
strings but I get the same result if I define string255 = AnsiString.

---example---

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  string255 = string[255];

  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    prn_str : string255;
    procedure Test(Int1, Int2, Int3, Int4, Int5 : integer); { pascal; }
  end;

Const
  NUMSTR_LENGTH    =   5;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function Get_Number(var prn_str : string255) : longint;
var
  s   : string[NUMSTR_LENGTH];
  len : integer;
begin
  len := length( prn_str );
  if len >= NUMSTR_LENGTH then
  begin
    s := Copy(prn_str,1,NUMSTR_LENGTH);
    prn_str := Copy(prn_str,NUMSTR_LENGTH+1,len-NUMSTR_LENGTH);
    result := StrToint(s);
  end else
  result := 0;
end;

procedure TForm1.Test(Int1, Int2, Int3, Int4, Int5 : integer);
begin
  Label1.Caption := (IntToStr(Int1)+' '+IntToStr(Int2)+' '+IntToStr(Int3)+'    
'+IntToStr(Int4)+' '+IntToStr(Int5));
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  prn_str := '    1    2    3    4    5';
  Test(Get_Number(prn_str), Get_Number(prn_str), Get_Number(prn_str),    
Get_Number(prn_str), Get_Number(prn_str));
end;

end.

--
Mr.Hillbilly (eiv...@infolink.no) http://www.sn.no/~eivind
 tHyperSoft HyperReader 1.0t

 

Re:D2 "register" cc bug


Quote
eiv...@infolink.no (Eivind Bakkestuen) wrote:
>Subject: D2 register calling convention bug

>I have found a problem with D2's register (default) calling convention that
>looks suspiciously like a bug to me (code attached below; do a File|New app;
>drop a label on the form; trade the default unit1.pas with the one presented
>here).

>I have a little function ("Get_Number" below) that gets a string in as a VAR
>parameter, reads out 5 chars from the start of the string, convert these to a
>number, and chop off the 5 chars from the string. Another routine that takes
>two (or more in this example) numbers as parameters ("Test" below) gets called
>with the output from "Get_Number" as parameters.

>The funny thing is, when using register cc and two or three parameters, the
>parameters get reversed in their order on the stack (when expected output would
>be "1 2 3" it comes out as "3 2 1").

>If more than three parameters are used, the last two will come first, and in
>reverse order, then the rest comes in the correct order (when expected output
>would be "1 2 3 4 5" it comes out as " 5 4 1 2 3").

[[snip]]

>procedure TForm1.FormShow(Sender: TObject);
>begin
>  prn_str := '    1    2    3    4    5';
>  Test(Get_Number(prn_str), Get_Number(prn_str), Get_Number(prn_str),    
>Get_Number(prn_str), Get_Number(prn_str));
>end;

Your code contains dangerous assumptions about the order of evaluation
of expressions in parameter lists.  Specifically, you're assuming that
the Get_Number functions will be evaluated in left-to-right order.
This is not guaranteed by the Object Pascal language definition.  The
16 bit compiler happened to evaluate most everything left to right,
but the 32 bit optimizer tries to pick the most convenient order of
evaluation for parameter expressions.

In the case of register calling convention, the compiler evaluates the
parameters that will be passed in registers last, so that it doesn't
have to allocate temporary storage to store their values while
evaluating the rest of the parameter expressions.  Even this is not an
absolute - the compiler may choose some other order of parameter
expression evaluation if, say, one of the parameters is a real doozy
of a calculation.

Not a bug.

-Danny

==============
 Danny Thorpe
 author of "Delphi Component Design" 1996 Addison Wesley
 ISBN 0-201-46136-6
==============

Re:D2 "register" cc bug


On Wed, 30 Oct 1996 21:10:30 +0100, eiv...@infolink.no (Eivind

Quote
Bakkestuen) wrote:
>Subject: D2 register calling convention bug

>I have found a problem with D2's register (default) calling convention that
>looks suspiciously like a bug to me (code attached below; do a File|New app;
>drop a label on the form; trade the default unit1.pas with the one presented
>here).

This isn't a bug:

Quote
>  Test(Get_Number(prn_str), Get_Number(prn_str), Get_Number(prn_str),    
>Get_Number(prn_str), Get_Number(prn_str));

Your code relies on the order of evaluation of the arguments to a
function.  That's not legal Pascal.  The compiler is free to evaluate
the arguments in any order it finds convenient.  It's up to you to
make sure your code produces the same results regardless of the order.
In your case, you'll have to store all those Get_Number results in
temporary variables, and pass those.

BTW, the same warning applies to expressions:  they can be evaluated
in any order.  The only exception there is boolean expressions; if you
have short-circuit evaluation turned on, then boolean operators will
be evaluated from left to right, with the right hand operand only
evaluated if necessary.

Duncan Murdoch

Other Threads