My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
Integer and floating point compare instructions only compare
bits of the specified {Size}.
Conversions between integer and floating point are now also
governed by {Size} so one can directly convert FP64 directly
into {unSigned}×{Int16}--more fully supporting strongly typed
languages.
--------------------------------------------------------------
Integer instructions are now::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
Although I am oscillating whether to support FP8 or FP128.
With this rearrangement of bit in the instruction formats, I
was able to get all Constant and routing control bits in the
same place and format in all {1, 2, and 3}-Operand instructions
uniformly. This simplifies <trifling> the Decoder, but more
importantly; the Operand delivery (and/or reception) mechanism.
I was also able to compress the 7 extended operation formats
into a single extended operation format. The instruction
format now looks like:
inst<31:26> Major OpCode
inst<20:16> {Rd, Cnd field}
inst<25:21> {SRC1, Rbase}
inst<15:10> {SH width, else, {I,d,Sign,Size}}
inst< 9: 6> {Minor OpCode, SRC3}
inst< 4: 0> {offset,SRC2,Rindex,1-OP×}
So there is 1 uniformly positioned field of Minor OpCodes,
and one uniformly interpreted field of Operand Modifiers.
Operand Modifiers applies routing registers and inserting
of constants to XOP Instructions. --------------------------------------------------------------
So, what does this buy the Instruction Set ??
A) All integer calculations are performed at the size and
type of the result as required by the high level language::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}.
This, gets rid of all smash instructions across all data
types. {smash == {sext, zext, ((x<<2^n)>>2^n), ...}
B) I actually gained 1 more extended OpCode for future expansion.
C) assembler/disassembler was simplified
D) and while I did not add any new 'instructions' I made those
already present more uniform and supporting of the requirements
of higher level languages (like ADA) and more suitable to the
stricter typing LLVM provides over GCC.
In some ways I 'doubled' the instruction count while not adding
a single instruction {spelling or field-pattern} to ISA. --------------------------------------------------------------
The elimination of 'smashes' shrinks the instruction count of
GNUPLOT by 4%--maybe a bit more once we sort out all of the
compiler patterns it needs to recognize. --------------------------------------------------------------
I wonder if crow tastes good in shepard's pie ?!?
On 2025-10-02 10:50 p.m., MitchAlsup wrote:No, there are 5-bits--inst<9:5>--woops.
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
Integer and floating point compare instructions only compare
bits of the specified {Size}.
Conversions between integer and floating point are now also
governed by {Size} so one can directly convert FP64 directly
into {unSigned}×{Int16}--more fully supporting strongly typed
languages.
--------------------------------------------------------------
Integer instructions are now::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
Although I am oscillating whether to support FP8 or FP128.
For my arch, I decided to support FP128 thinking that FP8 could be implemented with lookup tables, given that eight bit floats tend to vary
in composition. Of course, I like more precision.
Could it be a build option? Or a bit in a control register to flip
between FP8 and FP128?
With this rearrangement of bit in the instruction formats, I
was able to get all Constant and routing control bits in the
same place and format in all {1, 2, and 3}-Operand instructions
uniformly. This simplifies <trifling> the Decoder, but more
importantly; the Operand delivery (and/or reception) mechanism.
I was also able to compress the 7 extended operation formats
into a single extended operation format. The instruction
format now looks like:
inst<31:26> Major OpCode
inst<20:16> {Rd, Cnd field}
inst<25:21> {SRC1, Rbase}
inst<15:10> {SH width, else, {I,d,Sign,Size}}
inst< 9: 6> {Minor OpCode, SRC3}
inst< 4: 0> {offset,SRC2,Rindex,1-OP×}
Only four bits for SRC3?
So there is 1 uniformly positioned field of Minor OpCodes,
and one uniformly interpreted field of Operand Modifiers.
Operand Modifiers applies routing registers and inserting
of constants to XOP Instructions. --------------------------------------------------------------
So, what does this buy the Instruction Set ??
A) All integer calculations are performed at the size and
type of the result as required by the high level language::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}.
This, gets rid of all smash instructions across all data
types. {smash == {sext, zext, ((x<<2^n)>>2^n), ...}
B) I actually gained 1 more extended OpCode for future expansion.
C) assembler/disassembler was simplified
D) and while I did not add any new 'instructions' I made those
already present more uniform and supporting of the requirements
of higher level languages (like ADA) and more suitable to the
stricter typing LLVM provides over GCC.
In some ways I 'doubled' the instruction count while not adding
a single instruction {spelling or field-pattern} to ISA. --------------------------------------------------------------
The elimination of 'smashes' shrinks the instruction count of
GNUPLOT by 4%--maybe a bit more once we sort out all of the
compiler patterns it needs to recognize. --------------------------------------------------------------
I wonder if crow tastes good in shepard's pie ?!?
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
Integer and floating point compare instructions only compare
bits of the specified {Size}.
Conversions between integer and floating point are now also
governed by {Size} so one can directly convert FP64 directly
into {unSigned}×{Int16}--more fully supporting strongly typed
languages.
--------------------------------------------------------------
Integer instructions are now::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
Although I am oscillating whether to support FP8 or FP128.
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
--------------------------------------------------------------
Integer instructions are now:: {Signed and unSigned}×{Byte, HalfWord, >> Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow mixing.
On 10/2/2025 7:50 PM, MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
I must be missing something. Suppose I have
C := A + B
where A and C are 16 bit signed integers and B is an 8 bit signed
integer. As I understand what you are doing, loading B into a register
will leave the high order 56 bits zero. But the add instruction will presumably be half word, so if B is negative, it will get an incorrect answer (because B is not sign extended to 16 bits).
What am I missing?
MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow.
--------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
Why? Compilers do not have any problem with this
as its been handled by overload resolution since forever.
--------------------------------------------------------------
Integer instructions are now:: {Signed and unSigned}×{Byte, HalfWord, >>> Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow mixing.
Not sure who's confused, but my reading of the above is not some sort of "mixing": I believe Mitch is just saying that his addition operation
(for example) can be specified to operate on either one of int8, uint8, int16, uint16, ...
But that specification applies to all inputs and outputs of the
instruction, so it does not support adding an int8 to an int32, or other "mixes".
MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow. --------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
Why? Compilers do not have any problem with this
as its been handled by overload resolution since forever.
Its people who have the problems following type changes and most
compilers will warn of mixed type operations for exactly that reason.
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
Integer and floating point compare instructions only compare
bits of the specified {Size}.
Conversions between integer and floating point are now also
governed by {Size} so one can directly convert FP64 directly
into {unSigned}×{Int16}--more fully supporting strongly typed
languages.
Strongly typed languages don't natively support mixed type operations.
They come with a set of predefined operations for specific types that
produce specific results.
If YOU want operators/functions that allow mixed types then they force
you to define your own functions to perform your specific operations,
and it forces you to deal with the consequences of your type mixing.
All this does is force YOU, the programmer, to be explicit in your
definition and not depend on invisible compiler specific interpretations.
If you want to support Uns8 * Int8 then it forces you, the programmer,
to deal with the fact that this produces a signed 16-bit result
in the range -128*256..+127*256 = -32768..32512.
Now if you want to convert that result bit pattern to Uns8 by truncating
it to the lower 8 bits,
or worse treat the result as Int8 and take
whatever random value falls in bit [7] as the sign, then that's on you.
They just force you to be explicit what you are doing.
--------------------------------------------------------------
Integer instructions are now::
{Signed and unSigned}×{Byte, HalfWord, Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow mixing. Weak typed languages deal with this in overload resolution and by having predefined invisible type conversions in those operators and then using
the normal single type arithmetic instructions.
Although I am oscillating whether to support FP8 or FP128.
The issue with FP8 support seems to be that everyone who wants it also
wants their own definition so no matter what you do, it will be unused.
The issue with FP128 seems associated with scaling on LD and ST
because now scaling is 1,2,4,8,16 which adds 1 bit to the scale field.
And in the case of a combined int-float register file deciding whether
to expand all registers to 128 bits, or use 64-bit register pairs.
Using 128-bit registers raises the question of 128-bit integer support,
and using register pairs opens a whole new category of pair instructions.
Stefan Monnier <monnier@iro.umontreal.ca> schrieb:
Not sure who's confused, but my reading of the above is not some sort of--------------------------------------------------------------
Integer instructions are now:: {Signed and unSigned}×{Byte, HalfWord,
Word, DoubleWord}
while FP instructions are now:
{Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow mixing. >>
"mixing": I believe Mitch is just saying that his addition operation
(for example) can be specified to operate on either one of int8, uint8,
int16, uint16, ...
But that specification applies to all inputs and outputs of the
instruction, so it does not support adding an int8 to an int32, or other
"mixes".
The outputs are correctly extended to a 64-bit number (signed or
unsigned) so it is possible to pass results to wider operations
without conversion.
One example would be
unsigned long foo (unsigned int a, unsigned int b)
{
return a + b;
}
which would need an adjustment after the add, and which would
just be somethign like
adduw r1,r1,r2
ret
using Mitch's new encoding.
MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it reached the
point where it was time to switch to version 2.0.
Well, its time to eat crow.
--------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both integer and
floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply value
range constraints--just like memory !
Why? Compilers do not have any problem with this
as its been handled by overload resolution since forever.
Its people who have the problems following type changes and most
compilers will warn of mixed type operations for exactly that reason.
ISA 2.0 changes allows calculation instructions; both Integer and
Floating Point; and a few other miscellaneous instructions (not so
easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
Integer and floating point compare instructions only compare
bits of the specified {Size}.
Conversions between integer and floating point are now also
governed by {Size} so one can directly convert FP64 directly
into {unSigned}×{Int16}--more fully supporting strongly typed
languages.
Strongly typed languages don't natively support mixed type operations.
They come with a set of predefined operations for specific types that
produce specific results.
If YOU want operators/functions that allow mixed types then they force
you to define your own functions to perform your specific operations,
and it forces you to deal with the consequences of your type mixing.
All this does is force YOU, the programmer, to be explicit in your
definition and not depend on invisible compiler specific interpretations.
If you want to support Uns8 * Int8 then it forces you, the programmer,
to deal with the fact that this produces a signed 16-bit result
in the range -128*256..+127*256 = -32768..32512.
Now if you want to convert that result bit pattern to Uns8 by truncating
it to the lower 8 bits, or worse treat the result as Int8 and take
whatever random value falls in bit [7] as the sign, then that's on you.
They just force you to be explicit what you are doing.
--------------------------------------------------------------
Integer instructions are now::     {Signed and unSigned}×{Byte,
HalfWord, Word, DoubleWord}
while FP instructions are now:
    {Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow mixing. Weak typed languages deal with this in overload resolution and by having predefined invisible type conversions in those operators and then using
the normal single type arithmetic instructions.
Although I am oscillating whether to support FP8 or FP128.
The issue with FP8 support seems to be that everyone who wants it also
wants their own definition so no matter what you do, it will be unused.
The issue with FP128 seems associated with scaling on LD and ST
because now scaling is 1,2,4,8,16 which adds 1 bit to the scale field.
And in the case of a combined int-float register file deciding whether
to expand all registers to 128 bits, or use 64-bit register pairs.
Using 128-bit registers raises the question of 128-bit integer support,
and using register pairs opens a whole new category of pair instructions.
On 10/2/2025 7:50 PM, MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow.
--------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
I must be missing something. Suppose I have
C := A + B
where A and C are 16 bit signed integers and B is an 8 bit signed
integer. As I understand what you are doing, loading B into a register will leave the high order 56 bits zero. But the add instruction will presumably be half word, so if B is negative, it will get an incorrect > answer (because B is not sign extended to 16 bits).
What am I missing?
LLVM compiles C with stricter typing than GCC resulting in a lot
of smashes:: For example::
int subroutine( int a, int b )
{
return a+b;
}
Compiles into:
subroutine:
ADD R1,R1,R2
SRA R1,R1,<32,0> // limit result to (int)
RET
LLVM thinks the smash is required because [-2^31..+2^31-1] +
[-2^31..+2^31-1] does not always fit into [-2^31..+2^31-1] !!!
and chasing down all the cases is harder than the compiler is
ready to do.
At first I though that the Value propagation in
LLVM would find that the vast majority of arithmetic does not
need smashing. This proved frustrating to both myself and to
Brian. The more I read RSIC-V and ARM assembly code, the more
I realized that adding sized integer arithmetic is the only
way to get through to the LLVM infrastructure.
RISC-V has ADDW (but no ADDH or ADDB) to alleviate the issue on
a majority of calculations.
ARM has word sized Registers to
alleviate the issue. Since ARM started as 32-bits ADDW is natural.
I am exploring how to provide integer arithmetic such that smashing
never has to happen.
So let's look at some egregious cases::
cvtds r2,r2 // convert double to signed 64
srl r3,r2,#0,#32 // convert signed 64 to signed 32
--------
sra r1,r23,#0,#32 // smash to signed 32
sra r2,r20,#0,#32 // smash to signed 32
maxs r23,r2,r1 // max of signed 32
--------
ldd r24,[r24] // LD signed 64
add r1,r28,#1 // innocently add #1
sra r28,r1,#0,#32 // smash to Signed 32
cmp r1,r28,r16 // to match the other operand of CMP
--------
call strspn
srl r2,r1,#0,#32 // smash result Signed 32
add r1,r25,-r1
sra r1,r1,#0,#32 // smash Signed 32
cmp r2,r19,r2
srl r2,r2,#2,#1
add r21,r21,r2 // add Bool to Signed 32
sra r2,r20,#0,#32 // smash Signed 32
maxs r20,r1,r2 // MAX Signed 32
--------
mov r1,r29 // Signed 64
ple0 r17,FFFFFFF // ignore
stw r17,[ip,key_rows] // ignore
add r1,r29,#-1 // innocent subtract
sra r1,r1,#0,#32 // smash to Signed 32
divs r1,r1,r17 // DIV Signed 32
I doubt any compilers will use this feature.
RISC-V and ARM LLVM compilers already do this and use it to eliminate >smashes.
On the other hand:: ILP64 ALSO gets rid of the problem (at a different cost).
The I32LP64 mistake
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
If you are not familiar with them, they are:
- INTEGER takes up one storage unit
- REAL takes up one storage unit
- DOUBLE PRECISION takes up two storage units
where storage units are implementation-defined. Also consider
that 32-bit REALs and 64-bit REALs are both useful and needed,
and that (unofficially) C's integers were identical to
FORTRAN's INTEGER.
AFAIK Rust does not have a machine-word-sized integer type; instead,
each type has its size in its name (e.g., i32, u64).
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
I am not familiar enough with FORTRAN to give a recommendation on
that. However, two observations:
* The Cray-1 is primarily a Fortran machine, and it's C implementation
is ILP64, and it is successful. So obviously an ILP64 C can live
fine with FORTRAN.
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
If you are not familiar with them, they are:--- Synchronet 3.21a-Linux NewsLink 1.2
- INTEGER takes up one storage unit
- REAL takes up one storage unit
- DOUBLE PRECISION takes up two storage units
where storage units are implementation-defined. Also consider
that 32-bit REALs and 64-bit REALs are both useful and needed,
and that (unofficially) C's integers were identical to
FORTRAN's INTEGER.
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
I am not familiar enough with FORTRAN to give a recommendation on
that. However, two observations:
* The Cray-1 is primarily a Fortran machine, and it's C implementation
is ILP64, and it is successful. So obviously an ILP64 C can live
fine with FORTRAN.
* Whatever inconvenience ILP64 would have caused to Fortran
implementors is small compared to the cost in performance and
reliability that I32LP64 has cost in the C world and the cost in
encoding space (and thus code size) and implementation effort and
transistors (probably not that many, but still) that it is costing
all customers of 64-bit processors.
If you are not familiar with them, they are:
- INTEGER takes up one storage unit
- REAL takes up one storage unit
- DOUBLE PRECISION takes up two storage units
where storage units are implementation-defined. Also consider
that 32-bit REALs and 64-bit REALs are both useful and needed,
and that (unofficially) C's integers were identical to
FORTRAN's INTEGER.
And unofficially C's integers were as long as pointers (with a legacy reaching back to BCPL). If I had to choose between breaking an
unofficial FORTRAN-C interface tradition and a C-internal tradition, I
would choose the C-internal tradition every time.
- anton--- Synchronet 3.21a-Linux NewsLink 1.2
EricP <ThatWouldBeTelling@thevillage.com> schrieb:
MitchAlsup wrote:
My 66000 2.0Why? Compilers do not have any problem with this
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow.
--------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
as its been handled by overload resolution since forever.
A non-My66000 example:
int add (int a, int b)
{
return a + b;
}
is translated on powerpc64le-unknown-linux-gnu (with -O3 to)
add 3,3,4
extsw 3,3
blr
extsw fills the 32 high-value bits with because numbers returned
in registers have to be correct, either as 32- or 64-bit values.
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
I am not familiar enough with FORTRAN to give a recommendation on
that. However, two observations:
* The Cray-1 is primarily a Fortran machine, and it's C implementation
is ILP64, and it is successful. So obviously an ILP64 C can live
fine with FORTRAN.
* Whatever inconvenience ILP64 would have caused to Fortran
implementors is small compared to the cost in performance and
reliability that I32LP64 has cost in the C world and the cost in
encoding space (and thus code size) and implementation effort and
transistors (probably not that many, but still) that it is costing
all customers of 64-bit processors.
On Sat, 04 Oct 2025 16:11:37 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
AFAIK Rust does not have a machine-word-sized integer type; instead,
each type has its size in its name (e.g., i32, u64).
Rust has machine-dependent isize and usize types, identical to ptrdiff_t
and size_t in C.
On 10/3/2025 4:04 PM, Thomas Koenig wrote:
Stefan Monnier <monnier@iro.umontreal.ca> schrieb:
--------------------------------------------------------------
Integer instructions are now::     {Signed and unSigned}×{Byte, >>>>> HalfWord,
Word, DoubleWord}
while FP instructions are now:
     {Byte, HalfWord, Word, DoubleWord}
I doubt any compilers will use this feature.
Strong typed languages don't have predefined operators that allow
mixing.
Not sure who's confused, but my reading of the above is not some sort of >>> "mixing": I believe Mitch is just saying that his addition operation
(for example) can be specified to operate on either one of int8, uint8,
int16, uint16, ...
But that specification applies to all inputs and outputs of the
instruction, so it does not support adding an int8 to an int32, or other >>> "mixes".
The outputs are correctly extended to a 64-bit number (signed or
unsigned) so it is possible to pass results to wider operations
without conversion.
One example would be
unsigned long foo (unsigned int a, unsigned int b)
{
  return a + b;
}
which would need an adjustment after the add, and which would
just be somethign like
    adduw   r1,r1,r2
    ret
using Mitch's new encoding.
Yes.
Sign extend signed types, zero extend unsigned types.
Up-conversion is free.
This is something the RISC-V people got wrong IMO, and adding a bunch of ".UW" instructions in an attempt to patch over it is just kinda ugly.
Partly for my own uses revived ADDWU and SUBWU (which had been dropped
in BitManip), because these are less bad than the alternative.
I get annoyed that new extensions keep trying to add ever more ".UW" instructions rather than just having the compiler go over to zero-
extended unsigned and make this whole mess go away.
...
Ironically, the number of new instructions being added to my own ISA has mostly died off recently, largely because there is little particularly relevant to add at this point (within the realm of stuff that could be added).
On Sat, 04 Oct 2025 16:11:37 GMT
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
AFAIK Rust does not have a machine-word-sized integer type; instead,
each type has its size in its name (e.g., i32, u64).
Rust has machine-dependent isize and usize types
identical to ptrdiff_t and size_t in C.
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:...
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
By the time the 64-bit worksations
were being designed, REAL was firmly established as 32-bit and
DOUBLE PRECISION as 64-bit, from the /360, the PDP-11, the VAX
and the very 32-bit workstations that the 64-bit workstations were
supposed to replace.
So, put yourself into the shoes of the people designing workstations
RS4000 they could allow their scientific and technical customers
to use the same codes "as is", with no conversion, or tell them
they cannot use 32-bit REAL any more, and that they need to rewrite
all their software.
What would they have expected their customers to do? Buy a system
which forces them to do this, or buy a competitor's system where
they can just recompile their software?
You're always harping about how compilers should be bug-comptatible
to previous releases.
Not in the least. I did not ask for bug compatibility.
I also did not ask for "compiling as is" on a different architecture,
much less on a system with different address size.
I have actually written up what I ask for: <https://www.complang.tuwien.ac.at/papers/ertl17kps.pdf>. Maybe you
should read it one day, or reread it given that you have forgotten it.
- anton--- Synchronet 3.21a-Linux NewsLink 1.2
On the PDP-11 C's int is 16 bits. I don't know what FORTRAN's INTEGER
is on the PDP-11 (but I remember reading about INTEGER*2 and
INTEGER*4, AFAIK not in a PDP-11 context). In any case, I expect that >FORTRAN's REAL was 32-bit on a PDP-11, and that any rule chain that
requires that C's int is as wide as FORTRAN's REAL is broken at some
point on the PDP-11.
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:...
Thomas Koenig <tkoenig@netcologne.de> writes:
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:
The I32LP64 mistake
If you consider I32LP64 a mistake, how should FORTRAN's (note the
upper case, this is pre-Fortran-90) storage association rules have
been handled, in your opinion?
By the time the 64-bit worksations
were being designed, REAL was firmly established as 32-bit and
DOUBLE PRECISION as 64-bit, from the /360, the PDP-11, the VAX
and the very 32-bit workstations that the 64-bit workstations were
supposed to replace.
On the PDP-11 C's int is 16 bits. I don't know what FORTRAN's INTEGER
is on the PDP-11 (but I remember reading about INTEGER*2 and
INTEGER*4, AFAIK not in a PDP-11 context). In any case, I expect that FORTRAN's REAL was 32-bit on a PDP-11, and that any rule chain that
requires that C's int is as wide as FORTRAN's REAL is broken at some
point on the PDP-11.
So, put yourself into the shoes of the people designing workstations
RS4000 they could allow their scientific and technical customers
to use the same codes "as is", with no conversion, or tell them
they cannot use 32-bit REAL any more, and that they need to rewrite
all their software.
If they want to use their software as-is, and it is written to work
with an ILP32 C implementation, the only solution is to continue using
an ILP32 implementation.
What would they have expected their customers to do? Buy a system
which forces them to do this, or buy a competitor's system where
they can just recompile their software?
If just recompiling is the requirement, what follows is ILP32.
You're always harping about how compilers should be bug-comptatible
to previous releases.
Not in the least. I did not ask for bug compatibility.
If variable v and variable w are "stack variables" local to their own >subroutines, it seems perfectly reasonable to assume that all deallocated >stack variables become inaccessible.
Then, later when new stack space is
allocated those new variables have no relationship to any previously >deallocated variables.
That is: when the stack pointer is incremented the space is no longer >accessible and::
a) any modified cache lines are discarded instead of being written
to memory--the space is no longer accessible so don't waste power
making DRAM coherent with inaccessible stack space.
Later, when the stack pointer is decremented::
b) new cache line area can be "allocated" without reading DRAM and
being <conceptually> initialized to zero.
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:[...]
It is possible to have a two-byte integer and a 32-byte real.
The same held for the Cray-1 - default ingegers (24 bit)
and their weird 64-bit reals
If they want to use their software as-is, and it is written to work
with an ILP32 C implementation, the only solution is to continue using
an ILP32 implementation.
So, kill the 64-bit machines in the scientific marketplace. I'm glad
you agree.
If just recompiling is the requirement, what follows is ILP32.
There is absolutely no problem with 64-bit pointers when recompiling
Fortran.
Thomas Koenig <tkoenig@netcologne.de> writes:<snip>
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:[...]
So, kill the 64-bit machines in the scientific marketplace. I'm glad
you agree.
Not in the least. Most C programs did not run as-is on I32LP64.
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
Thomas Koenig <tkoenig@netcologne.de> writes:<snip>
Anton Ertl <anton@mips.complang.tuwien.ac.at> schrieb:[...]
So, kill the 64-bit machines in the scientific marketplace. I'm glad
you agree.
Not in the least. Most C programs did not run as-is on I32LP64.
The vast majority of C/C++ programs ran just fine on I32LP64. There
were some that didn't, but it was certainly not "most".
Thomas Koenig <tkoenig@netcologne.de> writes:
So, kill the 64-bit machines in the scientific marketplace. I'm glad
you agree.
Not in the least. Most C programs did not run as-is on I32LP64, and
that did not kill these machines, either.
And I am sure that C
programs were much more relevant for selling these machines than
FORTRAN programs.
C programmers changed the programs to run on
I32LP64 (this was called "making them 64-bit-clean"). And until that
was done, ILP32 was used.
Not in the least. Most C programs did not run as-is on I32LP64, and
that did not kill these machines, either.
Only those who assumed sizeof(int) = sizeof(char *). This was
not true on the PDP-11, ...
According to Thomas Koenig <tkoenig@netcologne.de>:
Not in the least. Most C programs did not run as-is on I32LP64, and
that did not kill these machines, either.
Only those who assumed sizeof(int) = sizeof(char *). This was
not true on the PDP-11, ...
The PDP-11 was a 16 bit machine with 16 bit ints and 16 bit pointers.
There were 32 bit long and float, and 64 bit double.
I didn't port a lot of code from the 11 to other machines, but my recollection >is that the widespread assumption in Berkeley Vax code that location zero was >addressable and contained binary zeros was much more painful to fix than
size issues.
MitchAlsup <user5857@newsgrouper.org.invalid> writes:
LLVM compiles C with stricter typing than GCC resulting in a lot
of smashes:: For example::
int subroutine( int a, int b )
{
return a+b;
}
Compiles into:
subroutine:
ADD R1,R1,R2
SRA R1,R1,<32,0> // limit result to (int)
RET
I tested this on AMD64, and did not find sign-extension in the caller, >neither with gcc-14 nor with clang-19; both produce the following code
for your example (with "subroutine" renamed into "subroutine1").
0000000000000000 <subroutine1>:
0: 8d 04 37 lea (%rdi,%rsi,1),%eax
3: c3 ret
It's not about strict or lax typing, it's about what the calling
convention promises about types that are smaller than a machine word.
If the calling convention requires/guarantees that ints are
sign-extended, the compiler must use instructions that produce a >sign-extended result. If the calling convention guarantees that ints
are zero-extended (sounds perverse, but RV64 has the guarantee that
unsigned is passed in sign-extended form, which is equally perverse),
then the compiler must use instructions that produce a zero-extended
result (e.g., AMD64's addl). If the calling convention only requires
and guarantees the low-order 32 bits (I call this garbage-extended),
then the compiler can use instructions that perform 64-bit adds; this
is what we are seeing above.
The other side of the medal is what is needed at the caller: If the
caller needs to cconvert a sign-extended int into a long, it does not
have to do anything. If it needs to convert a zero-extended or >garbage-extended int into a long, it has to sign-extend the value.
In article <2025Oct4.121741@mips.complang.tuwien.ac.at>,
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
MitchAlsup <user5857@newsgrouper.org.invalid> writes:
LLVM compiles C with stricter typing than GCC resulting in a lot
of smashes:: For example::
int subroutine( int a, int b )
{
return a+b;
}
Compiles into:
subroutine:
ADD R1,R1,R2
SRA R1,R1,<32,0> // limit result to (int)
RET
I tested this on AMD64, and did not find sign-extension in the caller, >neither with gcc-14 nor with clang-19; both produce the following code
for your example (with "subroutine" renamed into "subroutine1").
0000000000000000 <subroutine1>:
0: 8d 04 37 lea (%rdi,%rsi,1),%eax
3: c3 ret
It's not about strict or lax typing, it's about what the calling
convention promises about types that are smaller than a machine word.
If the calling convention requires/guarantees that ints are
sign-extended, the compiler must use instructions that produce a >sign-extended result. If the calling convention guarantees that ints
are zero-extended (sounds perverse, but RV64 has the guarantee that >unsigned is passed in sign-extended form, which is equally perverse),
then the compiler must use instructions that produce a zero-extended
result (e.g., AMD64's addl). If the calling convention only requires
and guarantees the low-order 32 bits (I call this garbage-extended),
then the compiler can use instructions that perform 64-bit adds; this
is what we are seeing above.
The other side of the medal is what is needed at the caller: If the
caller needs to cconvert a sign-extended int into a long, it does not
have to do anything. If it needs to convert a zero-extended or >garbage-extended int into a long, it has to sign-extend the value.
AMD64 in hardware does 0 extension of 32-bit operations. From your
example "lea (%rdi,%rsi,1),%eax" (AT&T notation, so %eax is the dest),
the 64-bit register %rax will have 0's written into bits [63:32].
So the AMD64 convention for 32-bit values in 64-bit registers is to zero-extend on writes. And to ignore the upper 32-bits on reads, so
using a 64-bit register should use the %exx name.
I agree with you that I32LP64 was a mistake, but it exists, and I
think ARM64 did a good job handling it. It has all integer operations working on two sizes: 32-bit and 64-bit, and when writing a 32-bit result,
it 0-extends the register value.
You don't want "garbage extend" since you want a predictable answer.
Your choices for writing 32-bit results in a 64-bit register are thus sign-extend (not a good choice) or zero-extend (what almost
everyone chose). RISC-V is in another land, where they effectively have
no 32-bit operations, but rather a convention that all 32-bit inputs
must be sign-extended in a 64-bit register.
For C and C++ code, the standard dictates that all integer operations are done with "int" precision, unless some operand is larger than int, and then do it in that precision. So there's no real need for 8-bit and 16-bit operations to be natively by the CPU--these operations are actually done
as int's already. If you have a variable which is a byte, then assigning
to that variable, and then using that variable again you will need to zero-extend,
but honestly, this is not usually a performance path. It's likely to be stored to memory instead, so no masking or sign extending
should be needed.
If you pick ILP64 for your ABI, then you will get rid of almost all of
these zero- and sign-extensions of 32-bit C and C++ code.
It will just--- Synchronet 3.21a-Linux NewsLink 1.2
work. If you pick I32LP64, then you should have a full suite of 32-bit operations and 64-bit operations, at least for all add, subtract, and
compare operations. And if you do I32LP64, your indexed addressing
modes should have 3 types of indexed registers: 64-bit, 32-bit signed,
and 32-bit unsigned. That worked well for ARM64.
Kent
In article <2025Oct4.121741@mips.complang.tuwien.ac.at>,...
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
MitchAlsup <user5857@newsgrouper.org.invalid> writes:
int subroutine( int a, int b )
{
return a+b;
}
...I tested this on AMD64, and did not find sign-extension in the caller, >>neither with gcc-14 nor with clang-19; both produce the following code
for your example (with "subroutine" renamed into "subroutine1").
0000000000000000 <subroutine1>:
0: 8d 04 37 lea (%rdi,%rsi,1),%eax
3: c3 ret
AMD64 in hardware does 0 extension of 32-bit operations. From your
example "lea (%rdi,%rsi,1),%eax" (AT&T notation, so %eax is the dest),
the 64-bit register %rax will have 0's written into bits [63:32].
So the AMD64 convention for 32-bit values in 64-bit registers is to >zero-extend on writes. And to ignore the upper 32-bits on reads, so
using a 64-bit register should use the %exx name.
I agree with you that I32LP64 was a mistake, but it exists, and I
think ARM64 did a good job handling it. It has all integer operations >working on two sizes: 32-bit and 64-bit, and when writing a 32-bit result,
it 0-extends the register value.
You don't want "garbage extend" since you want a predictable answer.
Your choices for writing 32-bit results in a 64-bit register are thus >sign-extend (not a good choice) or zero-extend (what almost everyone chose).
RISC-V is in another land, where they effectively have
no 32-bit operations, but rather a convention that all 32-bit inputs
must be sign-extended in a 64-bit register.
If you pick ILP64 for your ABI, then you will get rid of almost all of
these zero- and sign-extensions of 32-bit C and C++ code. It will just
work. If you pick I32LP64, then you should have a full suite of 32-bit >operations and 64-bit operations, at least for all add, subtract, and
compare operations.
And if you do I32LP64, your indexed addressing
modes should have 3 types of indexed registers: 64-bit, 32-bit signed,
and 32-bit unsigned. That worked well for ARM64.
kegs@provalid.com (Kent Dickey) writes:
In article <2025Oct4.121741@mips.complang.tuwien.ac.at>,...
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
MitchAlsup <user5857@newsgrouper.org.invalid> writes:
int subroutine( int a, int b )
{
return a+b;
}
...I tested this on AMD64, and did not find sign-extension in the caller, >>>neither with gcc-14 nor with clang-19; both produce the following code >>>for your example (with "subroutine" renamed into "subroutine1").
0000000000000000 <subroutine1>:
0: 8d 04 37 lea (%rdi,%rsi,1),%eax
3: c3 ret
AMD64 in hardware does 0 extension of 32-bit operations. From your
example "lea (%rdi,%rsi,1),%eax" (AT&T notation, so %eax is the dest),
the 64-bit register %rax will have 0's written into bits [63:32].
So the AMD64 convention for 32-bit values in 64-bit registers is to >>zero-extend on writes. And to ignore the upper 32-bits on reads, so
using a 64-bit register should use the %exx name.
Interesting. At some point I got the impression that LEA produces a
64-bit result, because it produces an address, but testing reveals
that LEA has a 32-bit zero-extended variant indeed.
kegs@provalid.com (Kent Dickey) writes:------------------------------------------------------------
In article <2025Oct4.121741@mips.complang.tuwien.ac.at>,
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
MitchAlsup <user5857@newsgrouper.org.invalid> writes:
int subroutine( int a, int b )
{
return a+b;
}
RISC-V is in another land, where they effectively have
no 32-bit operations, but rather a convention that all 32-bit inputs
must be sign-extended in a 64-bit register.
RISC-V has a number of sign-extending 32-bit instructions, and a
calling convention to go with it.
There seem to be the following options:
Have no 32-bit instructions, and insert sign-extension or
zero-extension instructions where necessary (or implicitly in all
operands, as I outlined earlier). SPARC V9 and PowerPC64 seem to take
this approach.
Have 32-bit instructions that sign-extend: MIPS64, Alpha, and RV64.
Have 32-bit instructions that zero-extend: AMD64 and ARM A64.
Have 32-bit instructions that sign-extend and 32-bit instructions that zero-extend. No architecture that does that is known to me. It would
be a good match for the SPARC-V9 and PowerPC64 calling convention.
There is also one instruction set (ARM A64) that has special 32-bit sign-extension and zero-extension forms for some operands.
And you can then adapt the calling convention to match the instruction
set. For "no 32-bit instructions", garbage-extension seems to be the cheapest approach to me, but I expect that when SPARC-V9 and PowerPC64
came on the market, there was enough C code with missing prototypes
around that they preferred a more forgiving calling convention.
If you pick ILP64 for your ABI, then you will get rid of almost all of >these zero- and sign-extensions of 32-bit C and C++ code. It will just >work. If you pick I32LP64, then you should have a full suite of 32-bit >operations and 64-bit operations, at least for all add, subtract, and >compare operations.
For compare, divide, shift-right and rotate, you either first need to sign/zero-extend the register, or you need 32-bit versions (possibly
both signed and unsigned).
And if you do I32LP64, your indexed addressing
modes should have 3 types of indexed registers: 64-bit, 32-bit signed,
and 32-bit unsigned. That worked well for ARM64.
It is certainly part of the way towards my idea of having sign- and zero-extended 32-bit operands for every operand of every instruction.
It would be interesting to see how many sign-extensions and
zero-extensions (whether explicit or implicitly part of the
instruction) are executed in code that is generated from various C
sources (with and without -fwrapv).
I expect that it's highly
dependent on the programming style. Sure there are types like pid_t
where you have no choice, but in frequently occuring cases you can
choose:
for (i=0; i<n; i++) {
... a[i] ...
}
Here you can choose whether to define i as int, unsigned, long,
unsigned long, size_t, etc. If you care for portability to 16-bit
machines, size_t is a good idea here, otherwise long and unsigned long
also are efficient.
If n is unsigned, you can also choose unsigned,
but then this code will be slow on RV64 (and MIPS64 and SPARC V9 and PowerPC64 and Alpha).
If n is int, you can also choose int, and there is actually enough information here to make the code efficient (even with -fwrapv),
because in this code int overflow really cannot happen,
but in code
that's not much different from this one (e.g., using != instead of <), -fwrapv will result in an inserted sign extension on AMD64, and not
using -fwrapv may result in unintended behaviour thanks to the
compiler assuming that int overflow does not happen.
ILP64 would have spared us all these considerations.
- anton--- Synchronet 3.21a-Linux NewsLink 1.2
Stephen Fuld <sfuld@alumni.cmu.edu.invalid> posted:
On 10/2/2025 7:50 PM, MitchAlsup wrote:
My 66000 2.0
After 4-odd years of ISA stability, I ran into a case where
I <pretty much> needed to change the instruction formats.
And after bragging to Quadribloc about its stability--it
reached the point where it was time to switch to version 2.0.
Well, its time to eat crow.
--------------------------------------------------------------
Memory reference instructions already produce 64-bit values
from Byte, HalfWord, Word and DoubleWord memory references
in both Signed and unSigned flavors. These supports both
integer and floating point due to the single register file.
Essentially, I need that property in both integer and floating
point calculations to eliminate instructions that merely apply
value range constraints--just like memory !
ISA 2.0 changes allows calculation instructions; both Integer
and Floating Point; and a few other miscellaneous instructions
(not so easily classified) the same uniformity.
In all cases, an integer calculation produces a 64-bit value
range limited to that of the {Sign}×{Size}--no garbage bits
in the high parts of the registers--the register accurately
represents the calculation as specified {Sign}×{Size}.
I must be missing something. Suppose I have
C := A + B
where A and C are 16 bit signed integers and B is an 8 bit signed
integer. As I understand what you are doing, loading B into a register
will leave the high order 56 bits zero. But the add instruction will
presumably be half word, so if B is negative, it will get an incorrect
answer (because B is not sign extended to 16 bits).
What am I missing?
A is loaded as 16-bits properly sign to 64-bits: range[-32768..32767]
B is loaded as 8-bits properly sign to 64-bits: range[-128..127]
ADDSH Rc,Ra,Rb
Adds 64-bit Ra and 64-bit Rb and then sign extends the result from bit<15>. The result is a properly signed 64-bit value: range [-32768..32767]
anton@mips.complang.tuwien.ac.at (Anton Ertl) posted:...
My 66000 CMP is signless--it compares two integer registers and delivers
a bit vector of all possible comparisons {2 equality, 4 signed, 4 unsigned,
4 range checks, [and in FP land 10-bits are the class of the RS1 operand]}
It is certainly part of the way towards my idea of having sign- and
zero-extended 32-bit operands for every operand of every instruction.
Unnecessary if the integer calculation deliver properly range-limited
64-bit results.
It would be interesting to see how many sign-extensions and
zero-extensions (whether explicit or implicitly part of the
instruction) are executed in code that is generated from various C
sources (with and without -fwrapv).
In GNUPLOT is is just over 4% of instruction count for 64-bit-only
integer calculations.
Counted for() loops are somewhat special in that it is quite easy to >determine that the loop index never exceeds the range-limit of the >container.
If n is unsigned, you can also choose unsigned,
but then this code will be slow on RV64 (and MIPS64 and SPARC V9 and
PowerPC64 and Alpha).
Example please !?!
If n is int, you can also choose int, and there is actually enough
information here to make the code efficient (even with -fwrapv),
because in this code int overflow really cannot happen,
Consider the case where n is int64_t or uint64_t !?!
Consider the C-preprocessor with::
# define int (short int) // !!
in scope.
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 1,071 |
Nodes: | 10 (0 / 10) |
Uptime: | 186:26:29 |
Calls: | 13,762 |
Calls today: | 1 |
Files: | 186,985 |
D/L today: |
8,390 files (2,645M bytes) |
Messages: | 2,427,100 |