Jump to content
Mahdi Safsafi

Strange behavior for literals

Recommended Posts

Posted (edited)

@Kas Ob.

Again, shifting outside range is implementation defined as I said before !!! Compilers, Interpreters, CPU, Virtual-Machines, are free to choose how to handle it !!! The behavior may even vary between same vendor products !!!

So comparing to FPC is worthless !!!

Delphi always mask with 0x1F meaning for your example :

X := A shl b; // TConst=UInt64 => B = 63 and $1F = 31.  31 < 64 ... compiles.
X := A shl b; // TConst=UInt32 => B = 63 and $1F = 31.  31 < 32 ... compiles.
X := A shl b; // TConst=UInt16 => B = 63 and $1F = 31.  31 > 16 ... error.

 

Edited by Mahdi Safsafi

Share this post


Link to post
4 minutes ago, Mahdi Safsafi said:

Again, shifting outside range is implementation defined as I said before !!!

Thank you.

 

But at least shouldn't the compiler warns when successfully compiled the case with 

Quote

  TMyVar = UInt32;    //  Delphi                                                                                  
  TConst = UInt32;     //   True     for 32bit and refuse to compile on 64bit                

 I think it should either warn on 32bit that the code is not compliable on 64bit, or refuse to compile on both , can we agree at least on this ?

 

Share this post


Link to post
Posted (edited)

@Kas Ob.

For programming language, the better way for my opinion is to refuse compiling. 

On x86, the CPU may just choose to mask the amount and continue execution.

On ARM, It's even not possible to encode an amount (as immediate) that violates instruction range ! However when amount is specified as register. CPU masks the amount before processing the instruction:

# x86:
shl r/m32, amount  # amount = amount and RegSizeMask.

# aarch32(arm):
lsl Rd Rm, 65      #  you can not encode it !
lsl Rd, Rm, Rs     #  Rs = Rs and RegSizeMask.

# aarch64:
shl Vd, Vn, amount #  UNDEFINED if shift > RegSizeMask!

 

While masking the amount is the most popular way ... its still considered as unpredictable behavior and may vary between implementation and all are corrects !

MSVC:

const int C = 1 << 100 ; // just warning ... but it compiles and gives 0!

int main()
{
	cout << C << endl; // C = 0.	
}

 

Edited by Mahdi Safsafi
  • Thanks 1

Share this post


Link to post

Moreover, compiler on a specific platform such aarch32/aarch64 must not at any case allow shifting outside a given range ! because mostly the opcode is qualified as invalid/reserved. Meaning CPU may raise UD exception. Or worse, you may end-up executing another different instruction.

Share this post


Link to post

Dropped this subject and was working but what to say i came across a note i wrote to my self, so here one last example without negative values or wide shifting range

{$R+}
type
  TMyVar = NativeUInt;
const
  A = $FF;
  B = 8 * (SizeOf(TMyVar) div 2); // 16 for 32bit and 32 for 64bit
var
  X, Y: TMyVar;
begin
  X := A shl B;
  Y := A;
  Y := Y shl B;

  if X = Y then
    Writeln('True')
  else
    Writeln('False');
  Writeln('A = ' + IntToHex(A, 16));
  Writeln('B = ' + IntToHex(B, 16));
  Writeln('X = ' + IntToHex(X, 16));
  Writeln('Y = ' + IntToHex(Y, 16));
  Readln;
end.

The result 

Quote

On 32bit

True
A = 00000000000000FF
B = 0000000000000010
X = 0000000000FF0000
Y = 0000000000FF0000

 

On 64bit

False
A = 00000000000000FF
B = 0000000000000020
X = 00000000000000FF
Y = 000000FF00000000

Here we notice

1) the untyped constant are handled as Cardinal or Integer ( with size of 4 byte ).

2) no warning or failing to compile

3) the value of X should either be 0 when the compiler sees it as Cardinal and shift it by 32 or handled internally by casted it (because it is untyped) as destination in this case NativeUInt (UInt64) and returned the value $FF00000000,

4) there is no mention of the documentation http://docwiki.embarcadero.com/RADStudio/Rio/en/Declared_Constants of such behaviour for untyped constant. 

5) the way X and Y calculated in not mentioned anywhere in documentation of such behaviour when using untyped constant or (from older example different types and values like negative and other), i believe already few example established.

 

is result right, what do you think ?

Share this post


Link to post

@Kas Ob. Yes, you're right ! untyped constant is handled as Integer :

// a fix: this will produce true, true for both (x86, x64):
X := TMyVar(A) shl B;
Y := A;
Y := TMyVar(Y) shl B;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×