Hello world. In many languages, It's not possible to use an i64 on a 32bits architecture, or an i128 on a 64bits arch. Is it also true in Rust ?
No, it's not true. The core integer types are always available, but they are emulated in software if necessary. (LLVM takes care of all of this, and it's pretty good at generating optimized code for wider-than-native types that's only slightly slower.)
In the future, you can find ou the answer to this kind of question for yourself, e.g. using Godbolt.
Rust offers all integer types all the way up to 128-bit ones on all architectures. As far as I’m aware, for architectures that don’t natively support the larger types, the values will be put into multiple registers and calculations will proceed in multiple steps to emulate the same behavior.
I'm wondering in what languages this true?
in C. No ?
Maybe i'm wrong.
Godbolt's x64 GCC version (whatever it may be) is perfectly happy to compile __i128
, although it generates somewhat longer code than LLVM.
Then is it necessary to compile seperatly a 32 and a 64 bit program ?
I don't understand what exactly you mean by that. Certainly, a 32 and a 64 bit platform are still different, so they will run different executables, if that's what you are asking. You can't just run a 64-bit executable on a 32-bit platform.
Out of interest, I walked through a few of the 128
-bit operations on normal 64-bit architectures. Examples compiled on godbolt:
pub fn f(x: i128, y: i128) -> i128 {
x + y
}
example::f:
mov rax, rdi
add rax, rdx
adc rsi, rcx
mov rdx, rsi
ret
pub fn f(x: i128, y: i128) -> i128 {
x * y
}
example::f:
mov r8, rdx
mov rax, rdx
mul rdi
imul rsi, r8
add rdx, rsi
imul rcx, rdi
add rdx, rcx
ret
or, with -C target-cpu=native
example::f:
imul rsi, rdx
mulx rdx, rax, rdi
add rdx, rsi
imul rcx, rdi
add rdx, rcx
ret
pub fn f(x: u128, y: u128) -> u128 {
x / y
}
example::f:
push rax
mov rax, rdx
or rax, rcx
je .LBB0_2
call qword ptr [rip + __udivti3@GOTPCREL]
pop rcx
ret
.LBB0_2:
lea rdi, [rip + str.0]
lea rdx, [rip + .L__unnamed_1]
mov esi, 25
call qword ptr [rip + core::panicking::panic@GOTPCREL]
ud2
.L__unnamed_2:
.ascii "/app/example.rs"
.L__unnamed_1:
.quad .L__unnamed_2
.asciz "\017\000\000\000\000\000\000\000\002\000\000\000\005\000\000"
str.0:
.ascii "attempt to divide by zero"
pub fn f(x: i128, y: i128) -> i128 {
x / y
}
example::f:
push rax
mov rax, rdx
or rax, rcx
je .LBB0_4
movabs rax, -9223372036854775808
xor rax, rsi
or rax, rdi
jne .LBB0_5
mov rax, rdx
and rax, rcx
cmp rax, -1
je .LBB0_3
.LBB0_5:
call qword ptr [rip + __divti3@GOTPCREL]
pop rcx
ret
.LBB0_4:
lea rdi, [rip + str.0]
lea rdx, [rip + .L__unnamed_1]
mov esi, 25
call qword ptr [rip + core::panicking::panic@GOTPCREL]
ud2
.LBB0_3:
lea rdi, [rip + str.1]
lea rdx, [rip + .L__unnamed_1]
mov esi, 31
call qword ptr [rip + core::panicking::panic@GOTPCREL]
ud2
.L__unnamed_2:
.ascii "/app/example.rs"
.L__unnamed_1:
.quad .L__unnamed_2
.asciz "\017\000\000\000\000\000\000\000\002\000\000\000\005\000\000"
str.0:
.ascii "attempt to divide by zero"
str.1:
.ascii "attempt to divide with overflow"
right… that’s kinda long, looks like it handles division by zero, overflow, and signs, then calls an existing function called __divti3
As far as I know C supports all its integer widths on all platforms: C data types - Wikipedia
Although 128 bit is not specified in the C standard but may be provided my an implementation's language extensions.
Of course things like 'int' change width depending on implementation. Rather like Rust's 'isize' and 'usize'.
Thank you for the examples
Thanks to all you for the explanations. Please put up with my stupid questions. I come from the web universe. I still have a lot to learn to become a full system programmer.
The bit width of the platform refers to the width of native memory pointers. 32-bit platforms use 32-bit pointers and thus may address at most 2^32 = 4294967296
bytes of memory. 64-bit platforms can similarly address at most 2^64 = 18446744073709551616
bytes of memory. In practice these will be lower, e.g. Windows reserves an entire half of address space for its purposes on 32-bit systems, while on 64-bit systems the upper bits are reserved by processor architectures, or the total memory may be restricted by the OS (link).
This in unrelated to the width of supported integer types, although in practice the CPU integer register sizes are often the same as its pointer size. Regardless, the compiler can emulate any integer type in software, so the concern about CPU integer register size is mostly about performance optimization.
Hmmm... Except, back in the day 8 bit machines had 16 bit pointers so as to be able to access a typical 64KByte memory space. Actually they still do in the form of Atmel, Microchip and other micro-controllers.
The 16 bit Intel 8086 had 16 bit data registers but FAR pointers are 24 bits.
The Motorola 68000 has 32 bit data registers and instructions but only 24 bit address registers.
My whole life I have interpreted a processors "bit width" as the width of data processors ALU worked on.
That, in turn, is complicated by superscalar architectures, but I guess one could slightly amend your definition to use the phrase "width of the largest scalar the ALU can work with" or similar. In any case, I would think that at least since 1990 or so, all mainstream architectures have had scalar width equal to address width, no?
You are probably right. I gave up following what is inside the new fangled processors since about 1990
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.