The following program behaves differently under 32-bit and 64-bit compilation. In 64-bit the "max()" function returns a large integer when a value is sent out of bounds (output not shown). The point of the max() is to keep a value from going out of bounds. This problem does not occur in 32-bit code. To make sure that range checking was on in both cases I set "a" out of range after the max().
Is this a feature of the cpu option? And/or is there a more robust way of keeping within range?
Thanks for any help.
P.S. I've already received the following response from Frank.
I'm not using a 64 bit machine myself, so I can only speculate. I hope Waldek can say more about it.
I think the problem might be that GPC treats "arange" internally as an unsigned type (i.e., as a subrange of "Cardinal", not "Integer") and on the 64 bit machine performs the "a - 1" unsigned which causes the range error. (On the 32 bit machine it can do it in LongestInt which covers the range of "Cardinal" as well as negative values, but on 64 bit, it currently doesn't support larger integer types. This might explain the different behaviour.)
Of course, this is a GPC bug as, according to ISO, your program is perfectly valid (given a suitable definition of "Max" which isn't part of ISO).
So the bugfix might be to make subrange types signed when the range allows (which would always be the case in valid ISO programs).
Frank
-------------------- program test(input,output);
type arange = 0..5;
var a,b : arange;
begin a:= 0; b := max(a-1,0); /* keep b in range */ writeln('here ',b,' now really go out of range '); a := b-1; /* take a out of range to make sure range checking is on */ writeln('there ',a); end.
--------------------
Output
32-bit: here 0 now really go out of range a.out: value out of range (error #300 at 180e3)
64-bit a.out: value out of range (error #300 at 100009d2b)
32-bit compilation gpc maxtest.pas
64-bit gpc $g64pth $m64opt maxtest.pas
where $m64opt= "-mcpu=ultrasparc3 -m64"
--------------------- Here is the version output
hpc1014@sfnode0$ gpc -v Reading specs from /opt/gpc/20051104/lib/gcc/sparc-sun-solaris2.10/3.4.3/specs Configured with: ../gcc-3.4.3/configure --prefix=/opt/gpc/20051104 --enable-languages=pascal Thread model: posix gpc version 20051104, based on gcc-3.4.3
-- Chris Christopher Ferrall http://econ.queensu.ca/%7Eferrall/ Associate Professor of Economics Department of Economics http://www.econ.queensu.ca Queen's University http://www.queensu.ca Kingston, Ontario CANADA K7L 3N6 ferrallc AT post.queensu.ca Mackintosh-Corry Rm A519 ph: 613-533-6658 fx: 613-533-6668
On 10 Apr 2007, at 00:04, Chris Ferrall wrote:
P.S. I've already received the following response from Frank.
[snip]
So the bugfix might be to make subrange types signed when the range allows (which would always be the case in valid ISO programs). Frank
Note that doing this may cause problems with packed arrays and records, like we had in FPC. If you treat 0..3 as signed, then you can no longer pack it in 2 bits using the current format used by FPC and GPC. The reason is that if a type is signed, both compilers treat the uppermost bit of a packed value as the sign bit. So 2 and 3 would be interpreted as -2 and -1, respectively.
Of course, it is possible to change the format of packed values (e.g., always store them as unsigned, with a bias of -lower_bound in case of signed types), but this may cause backward compatibility issues.
Jonas
On 10 Apr 2007, at 08:52, Jonas Maebe wrote:
Note that doing this may cause problems with packed arrays and records, like we had in FPC. If you treat 0..3 as signed, then you can no longer pack it in 2 bits using the current format used by FPC and GPC. The reason is that if a type is signed, both compilers treat the uppermost bit of a packed value as the sign bit. So 2 and 3 would be interpreted as -2 and -1, respectively.
Of course, it is possible to change the format of packed values (e.g., always store them as unsigned, with a bias of -lower_bound in case of signed types), but this may cause backward compatibility issues.
Or, alternatively, you can of course do the sign extension only if the lower bound is < 0 rather than if the type is signed (maybe that even already happens today in gpc that way). Depending on how the gpc internals work, differentiating between "signed type" and "lower bound < 0" may cause some hairiness.
It's mainly something you simply have to keep in mind when performing such a change, I guess.
Jonas
Jonas Maebe wrote:
So the bugfix might be to make subrange types signed when the range allows (which would always be the case in valid ISO programs).
Frank
Note that doing this may cause problems with packed arrays and records, like we had in FPC. If you treat 0..3 as signed, then you can no longer pack it in 2 bits using the current format used by FPC and GPC. The reason is that if a type is signed, both compilers treat the uppermost bit of a packed value as the sign bit. So 2 and 3 would be interpreted as -2 and -1, respectively.
Indeed. But I was talking about unpacked types only. We have special code for subranges in packed arrays anyway, and that shouldn't change.
Of course, it is possible to change the format of packed values (e.g., always store them as unsigned, with a bias of -lower_bound in case of signed types), but this may cause backward compatibility issues.
No, I wouldn't like to do this. (Even if one could gain a bit occasionally, such as storing -1 .. 2 in 2 bits instead of 3 as now, but it doesn't seem worth the effort.)
Or, alternatively, you can of course do the sign extension only if the lower bound is < 0 rather than if the type is signed (maybe that even already happens today in gpc that way). Depending on how the gpc internals work, differentiating between "signed type" and "lower bound < 0" may cause some hairiness.
Yes, it does, and that's basically the problem here. In the original case, the lower bound was >= 0, but the type was treated as unsigned.
To be clear, I'm only talking about unpacked subrange types whose both bounds fit into (signed) "Integer". When the lower bound is < 0, it's signed anyway, no issue here. But when it's >= 0, the type should get signed as well, not unsigned like now.
OTOH, when the upper bound doesn't fit into "Integer" anymore (this case doesn't affect ISO compliance, as ISO doesn't have any bigger type than "Integer"), we should rather use an unsigned type than a bigger signed type, even when available (e.g., on IA32, use unsigned 32 bit rather than signed 64 bit, as long as the upper bound fits in unsigned 32 bits).
Frank