22725
Comment: Update predef macro URL
|
22925
add some more links
|
Deletions are marked like this. | Additions are marked like this. |
Line 340: | Line 340: |
* [[https://sourceforge.net/p/predef/wiki/Architectures/|Pre-defined Compiler Macros Wiki - Architectures]] * [[https://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html|GCC Predefined Macros]] |
Contents
This page is a draft. Please help enhancing it by adding lines and filling tables. Comments at the bottom of the text are also welcome.
Summary
This is just a quick array to recall some of the specifics of the architectures found in the Debian project.
DEB ARCH |
sizeof(short) |
sizeof(int) |
sizeof(long) |
sizeof(long long) |
sizeof(float) |
sizeof(double) |
any |
2 |
4 |
sizeof(void *) |
8 |
4 |
8 |
DEB ARCH |
sizeof(void*) |
sizeof(long double) |
char |
endian |
stack grows |
page sizes1 |
float2 |
double3 |
long double3 |
GCC pre-defined macro(s) |
alpha |
8 |
164 |
signed |
LE |
down |
8K |
? |
? |
? |
__alpha__ |
any-amd645 |
8 |
16 |
signed |
LE |
down |
4K |
H |
H |
H6 |
__x86_64__ && __LP64__7 |
arc |
4 |
16 |
unsigned |
LE |
down |
8K |
H |
H |
H |
__arc__ |
arm |
4 |
? |
unsigned |
LE |
down |
? |
S |
S |
S |
__arm__ |
8 |
16 |
unsigned |
LE |
down |
4K, 16K, 64K |
H |
H |
S |
__aarch64__ && __LP64__ |
|
4 |
16 |
unsigned |
LE |
down |
4K, 16K, 64K |
H |
H |
S |
__aarch64__ && __ILP32__ |
|
4 |
8 |
unsigned |
LE |
down |
4K |
S |
S |
S |
__arm__ && __ARM_EABI__ |
|
4 |
8 |
unsigned |
LE |
down |
4K |
H |
H |
H |
__arm__ && __ARM_EABI__ && __ARM_PCS_VFP |
|
avr32 |
4 |
8 |
unsigned |
BE |
down |
? |
? |
? |
? |
? |
hppa |
4 |
8 |
signed |
BE |
up |
? |
? |
? |
? |
__hppa__ |
any-i3868 |
4 |
12 |
signed |
LE |
down |
4K |
H+ |
H+ |
H6 |
__i386__ |
ia64 |
8 |
16 |
signed |
LE |
both9 |
? |
H |
H |
H |
__ia64__ |
8 |
16 |
signed |
LE |
down |
4K-64K |
H |
H |
H |
__loongarch__ && __loongarch64 && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
|
4 |
12 |
signed |
BE |
down |
4K |
S? |
S? |
S? |
__m68k__ |
|
4 |
8 |
signed |
BE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEB && _MIPS_SIM==_ABIO32 |
|
4 |
8 |
signed |
LE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEL && _MIPS_SIM==_ABIO32 |
|
8 |
16 |
signed |
BE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEB && _MIPS_SIM==_ABI64 |
|
8 |
16 |
signed |
LE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEL && _MIPS_SIM==_ABI64 |
|
4 |
8 |
signed |
BE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEB && _MIPS_SIM==_ABIN32 |
|
4 |
8 |
signed |
LE |
down |
4K-64K |
H |
H |
H |
__mips__ && _MIPSEL && _MIPS_SIM==_ABIN32 |
|
4 |
164 |
unsigned |
BE |
down |
4K-64K |
? |
? |
? |
__powerpc__ |
|
8 |
16 |
unsigned |
BE |
down |
4K-64K |
? |
? |
? |
__powerpc__ && __powerpc64__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
|
8 |
16 |
unsigned |
LE |
down |
4K-64K-16M |
H |
H |
H~ |
__powerpc__ && __ppc64__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
|
8 |
16(8)12 |
unsigned |
LE |
down |
4K 13 |
H |
H |
H~ |
__riscv && __riscv_xlen==64 14 |
|
s390 |
4 |
164 |
unsigned |
BE |
down |
? |
? |
? |
? |
__s390__ |
s390x |
8 |
16 |
unsigned |
BE |
down |
4K, 1M, 2G |
H |
H |
H |
__s390x__ |
4 |
8 |
signed |
LE |
down |
4K |
? |
? |
? |
__sh__ |
|
4 |
164 |
signed |
BE |
down |
? |
? |
? |
? |
__sparc__ |
|
8 |
16 |
signed |
BE |
down |
8K, 64K, 512K, 4M, 32M, 256M, 2G, 16G |
? |
? |
? |
__sparc__ && __arch64__ |
|
x32 |
4 |
16 |
signed |
LE |
down |
4K |
H |
H |
H6 |
__x86_64__ && __ILP32__ |
DEB ARCH |
multiarch |
ELFCLASS |
ELF machine EM_ |
ld.so(8) |
uname -m15 |
qemu16 |
|
alpha |
alpha-linux-gnu |
|
|
|
alpha |
|
alpha |
amd64 |
x86_64-linux-gnu |
64 |
X86_64 |
/lib64/ld-linux-x86-64.so.2 |
x86_64 |
x86_64 |
x86_64(-microvm) |
kfreebsd-amd64 |
x86_64-kfreebsd-gnu |
64 |
X86_64 |
/lib/ld-kfreebsd-x86-64.so.1 |
x86_64 |
x86_64 |
x86_64(-microvm) |
arc |
arc-linux-gnu |
32 |
ARC_COMPACT2 |
/lib/ld-linux-arc.so.2 |
arc |
arc |
|
arm |
arm-linux-gnu |
|
|
|
arm*17 |
arm |
arm |
aarch64-linux-gnu |
64 |
AARCH64 |
/lib/ld-linux-aarch64.so.1 |
aarch64 |
aarch64 |
aarch64 |
|
aarch64-linux-gnu_ilp32 |
32? |
AARCH64? |
|
aarch64? |
|
aarch64 |
|
arm-linux-gnueabi |
32 |
ARM |
/lib/ld-linux.so.3 |
arm* |
arm |
arm |
|
arm-linux-gnueabihf |
32 |
ARM |
/lib/ld-linux-armhf.so.3 |
arm* |
arm |
arm |
|
avr32 |
|
|
|
|
|
avr |
avr18 |
hppa |
hppa-linux-gnu |
32 |
PARISC |
/lib/ld.so.1 |
parisc* |
parisc |
hppa |
i386 |
i386-linux-gnu |
32 |
386 |
/lib/ld-linux.so.2 |
i?8619 |
x86 |
i386 |
hurd-i386 |
i386-gnu |
32 |
386 |
/lib/ld.so |
i?86-AT38619 |
x86 |
i386 |
kfreebsd-i386 |
i386-kfreebsd-gnu |
32 |
386 |
/lib/ld.so.1 |
i?8619 |
x86 |
i386 |
ia64 |
ia64-linux-gnu |
64 |
IA_64 |
/lib/ld-linux-ia64.so.2 |
ia64 |
ia64 |
|
|
|
|
|
|
loongarch64 |
||
m68k-linux-gnu |
32 |
68K |
/lib/ld.so.1 |
m68k |
m68k |
m68k |
|
mips-linux-gnu |
32 |
|
/lib/ld.so.1 |
mips |
mips |
mips |
|
mipsel-linux-gnu |
32 |
MIPS |
/lib/ld.so.1 |
mips |
mips |
mipsel |
|
mips64-linux-gnuabi64 |
|
|
|
mips64 |
mips64 |
mips64 |
|
mips64el-linux-gnuabi64 |
64 |
MIPS |
/lib64/ld.so.1 |
mips64 |
mips64 |
mips64el |
|
mips64-linux-gnuabin32 |
|
|
|
|
|
mipsn32 |
|
mips64el-linux-gnuabin32 |
|
|
|
|
|
mipsn32el |
|
powerpc-linux-gnu |
32 |
PPC |
/lib/ld.so.1 |
ppc |
ppc |
ppc |
|
powerpc64-linux-gnu |
64 |
PPC64 |
/lib64/ld64.so.1 |
ppc64 |
ppc64 |
ppc64 |
|
powerpc64le-linux-gnu |
64 |
PPC64 |
/lib64/ld64.so.2 |
ppcle ppc64le |
ppc64 |
ppc64le |
|
riscv64-linux-gnu |
|
|
|
riscv64 |
riscv64 |
riscv64 |
|
s390 |
s390-linux-gnu |
32? |
S390? |
/lib/ld.so.1 |
s390 |
s390 |
(s390x) |
s390x |
s390x-linux-gnu |
64 |
S390 |
/lib/ld64.so.1 |
s390x |
s390x |
s390x |
sh4-linux-gnu |
|
|
|
sh |
sh4 |
sh4 |
|
sparc-linux-gnu |
|
|
|
sparc |
sparc |
sparc(32plus) |
|
sparc64-linux-gnu |
|
|
|
sparc64 |
sparc64 |
sparc64 |
|
x32 |
x86_64-linux-gnux32 |
32 |
X86_64 |
/libx32/ld-linux-x32.so.2 |
x86_64 |
|
x86_64 |
User-visible page size returned by getauxval(AT_PAGESZ) or getconf PAGESIZE, the granularity of the memory map (1)
H=hard, S=soft, P=partial, +=excess precision, ~=not usual standard (2)
Note that the columns refer to C types. H=hard, S=soft, P=partial, +=excess precission, ~=not usual standard (3 4)
Use 80bits extended precision see extended precision (10 11 12)
it's a common gotcha for x32, you need to use __LP64__/__ILP32__ to tell them apart (13)
The conventional stack grows down from the top of the stack mapping, but there is also the Register Backing Store which grows up from the bottom of the stack mapping at the same time (15)
The architecture name has not yet been proposed for integration into dpkg (16 17)
The RV64 ISA spec defines long double as 16 bytes, but older toolchains (pre-"riscv-gcc-6.1.0") have implemented long double as 8 bytes on RV64; cf. https://github.com/riscv/riscv-gcc/commit/54b21fc5ae83cefec44bc2caed4a8c664c274ba0 (19)
4k is the default page size on all RISC-V systems. Depending on the chosen virtual memory mode (Sv32/Sv39/Sv48) additional page sizes are available as well: Sv32 supports 4k and 4M pages, Sv39 supports 4k, 2M and 1G pages and Sv48 supports 4k, 2M, 1G, and 512G pages (20)
Older, pre-mainline RISC-V toolchains used __riscv__ && __riscv64 (21)
Used to classify CPU architectures in CMake and many ad-hoc build systems, but note that this is a kernel and/or libc specific name despite referring to the CPU (22)
e.g. i386 results in qemu(-system)-i386(-static) (23)
Many names starting with arm, e.g. armv5tel (24)
qemu-system-avr only (25)
Alignment
Note that there is alignment on the machine language level and alignment in C. Proper misalignment (for example in a packed struct) is no problem in C and allowed almost everywhere (except of SPARC, where you will receive SIGBUS on a program execution, misaligned variable/memory access) and the compiler will do the right thing (like translating a read of a variable to two loads at machine language level). On the other hand most of the constructs that lead to unaligned access not properly handled by the C compiler (especially most constructs involving pointer casting) have undefined behaviour anyway, so compiling them with optimisation enabled can cause strange effects on all architectures. Read more on a Wikipedia article Data structure alignment.
On the other hand, unaligned synchronization primitives (atomics, etc) tend to be unsupported or extremely slow. Extra joy if they cross cacheline or page boundaries.
alpha
Some alpha may emulate see kernel source
arc
ARCv2 processors (ARC HS38 and ARC HS48 variants, 1-4 cores) support unaligned access in hardware, except for "atomic instructions" where special considerations apply. ARCv2 features 2 types of atomic instructions: the first for 32-bit data ("llock"/"scond" pair) and the second for 64-bit data ("llockd"/"scondd"). Those atomic instructions must work on data aligned naturally. I.e. "llock"/"scond" must be only used on 32-bit word-aligned data, "llockd"/"scondd" must be only used on 64-bit double-word aligned data. If an unaligned access is done by "atomic instruction" in user-space, the offending application gets forcefully killed with visible mention of a segmentation fault. An unaligned access done by “atomic instruction” in kernel-mode will result in an unrecoverable "Illegal instruction" exception.
armel/armhf/arm64
High-level summary for software engineers: Don't do unaligned access. It's always inefficient, sometimes extremely inefficient, and in various cases is not permitted at all.
The short answer for the ARM case is that ARMv6 and up guarantee that the processor can be configured such that some unaligned accesses are fine (or at least just inefficient). Linux by default enable this mode and trap:
All <=32-bit load/store operations can be unaligned.
- NEON load-stores can be unaligned (unless an explicit alignment is specified in the encoding).
But:
- VFP load/stores must be naturally aligned.
Load-multiple/store-multiple and ldrd/strd do not work with <32-bit alignment, but can sometimes get fixed up by the kernel at a substantial performance penalty.
PUSH/POP usually requires 32-bit alignment, but then the ABI requires 64-bit alignment of the stack, so that is less of an issue.
Unaligned load-exclusive/store-exclusive operations are not supported.
No unaligned accesses are permitted to memory regions of Device or Strongly-ordered type. But some permutation of this will be true for most architectures, and in user-space this would only affect things prodding /dev/mem or otherwise mmaping from a device driver.
ARM64 similarly makes it possible to trap unaligned accesses, but if that is not enabled (which it isn't by Linux), everything apart from load-exclusive/store-exclusive, load-acquire/store-release and Device memory accesses will be handled by hardware.
avr32
Unaligned access is ok for 32bits word but not for 16bits or 64bits word see kernel source
i386/x32/amd64
Unaligned access is slower (but not as much as with other architectures as done is hardware and not in software). Moreover some MMX/SSE/vector operation need proper alignment.
Unaligned sync primitives are supported, but there's active effort to defeature them, with Ice Lakes and newer allowing the kernel to trap.
ia64
Depending of config may fix with trap handler unaligned access
riscv64
Short version: Don't use unaligned access.
Long version: Technically the RISC-V ISA specification allows unaligned access for load and store instructions that are part of the base ISA, but only with significant limitations:
- Naturally aligned loads and stores are guaranteed to be atomic, but this is not the case for unaligned loads and stores. This can lead to hard-to-find bugs, so unaligned access should really be avoided.
- Support for unaligned access is usually not implemented in hardware and instead emulated in a trap handler, which makes it very slow.
All synchronization primitives (LR/SC and the atomic memory read-modify-write operations (AMOs)) only allow naturally aligned access.
Floating point
First read academic paper about the pitfall of doing multi arch floating point computation and the classical "What Every Computer Scientist Should Know About Floating-Point Arithmetic" from here.
i386
i387 is a strange beast that suffer from excess precision. Moreover long double is only 80 bits
Internal computation are done with 80 bits of precision that lead to strange and not intuitive result.
mips/mipsel/mips64el
Some mips processors do not contain a hardware floating point unit. In this case the kernel will emulate all FPU instructions at a large performance cost.
C/C++ Preprocessor Symbols
Generally, GNU C tends to define the symbol
__arch__
A list of some common arch-specific symbols can be found on the Pre-defined Architecture Macros page.
Signedness
When programming in C, variables can be signed or unsigned.
Example:
unsigned char c; |
explicit unsigned, 0 <= c <= 255 |
signed char c; |
explicit signed, -128 <= c <= 127 |
char c; |
implicit (un)signedness, depending on architecture |
To force a signedness, either declare it explicitly or use the gcc command line option -f(un)signed-char. The gcc defaults differ only due to optimisation. Other compilers may not have this issue.
Architecture baselines
Each Debian architecture has a baseline indicating the oldest or least capable CPU on which the architecture can be used. The baseline can change between Debian releases. The baseline is mostly defined by the gcc-N package, which is configured to produce baseline binaries when options like -march= are not used.
If a package uses options that enable additional CPU features such as -msse in particular source files, then the maintainer of that package is responsible for making sure those CPU features are only used on CPUs that support them. ioquake3's Altivec support on PowerPC is an example of this technique.
amd64
x86_64 with no optional extensions. The core specification includes MMX, SSE and SSE2 so these are OK, but SSE3 and up are not guaranteed.
arc
A fully featured ARC HS with additional support for double-precision FPU ("-mcpu=hs38_linux"). And this is a default "-mcpu" for ARC starting from GCC 11.
To be more precise it's a baseline ARC HS with HW division ("-mdiv-rem"), atomic instructions ("-matomic"), 64-bit loads/stores ("-mll64"), the most advanced multiplier (including quad half-word & vector operations, "-mmpy-option=plus_qmacw") and double-precision FPU ("-mfpu=fpud_all").
arm64
aarch64 (ARMv8-A) with no optional extensions. VFPv4 and NEON are OK, but ARMv8.1-A and up are not guaranteed.
armel
armv5te since Debian 10 'buster' (gcc-8 8-20171215-1).
Before that, armv4te.
armhf
armv7 with VFPv3-D16 floating point. NEON is not guaranteed.
i386
i686 since Debian 9 'stretch' (gcc-6 6.1.1-1). There's no MMX nor SSE.
Before that, i586 since gcc-4.9 4.9-20140411-1 (2014).
Before that, i486 since gcc-4.1 4.1ds7-0exp7 (2006).
Before that, i386.
mips, mipsel
MIPS32R2 since Debian 9 'stretch'.
Before that, MIPS II.
mips64el
MIPS64R2 and up.
powerpc
(TODO: exact baseline unknown). Altivec is not guaranteed.
powerpcspe
(TODO: exact baseline unknown). Altivec is impossible, even with runtime detection.
ppc64
(TODO: exact baseline unknown). Altivec is not guaranteed.
(Initially Altivec was in the port baseline, but was later removed to support some embedded systems)
ppc64el
POWER8 and up.
s390x
z196 since Debian 10 'buster'.
loongarch64
(TODO: exact baseline unknown).
Obtaining this information
via the C pre-processor
echo | cpp -dM | sort
via a special tool
#include <alloca.h> #include <stddef.h> #include <stdio.h> static void test_size_align(const char *name, size_t size, size_t align) { printf("sizeof(%s) = %zu\n", name, size); if (size != align) printf("alignment(%s) = %zu\n", name, align); } #define TEST_SIZE_ALIGN(type, name) \ struct test_align_##name { char a; type b; }; \ test_size_align(#type, sizeof(type), offsetof(struct test_align_##name, b)) static void test_endian(void) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ printf("byte order = little endian\n"); #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ printf("byte order = big endian\n"); #endif } static void test_char(void) { if ((int)(char)-1 == -1) printf("char signedness = signed\n"); else if ((int)(char)-1 == 255) printf("char signedness = unsigned\n"); } static void test_stack(void) { void *a = alloca(8); void *b = alloca(8); if (a > b) printf("stack = grows down\n"); else printf("stack = grows up\n"); } int main(void) { TEST_SIZE_ALIGN(short, short); TEST_SIZE_ALIGN(int, int); TEST_SIZE_ALIGN(long, long); TEST_SIZE_ALIGN(long long, long_long); TEST_SIZE_ALIGN(float, float); TEST_SIZE_ALIGN(double, double); TEST_SIZE_ALIGN(long double, long_double); TEST_SIZE_ALIGN(void *, pointer); test_endian(); test_char(); test_stack(); return 0; }
via autoconf
AC_INIT(archtest, 0.1) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_CHECK_SIZEOF(float) AC_CHECK_SIZEOF(double) AC_CHECK_SIZEOF(long double) AC_CHECK_SIZEOF(void*) AC_C_BIGENDIAN() AC_C_CHAR_UNSIGNED()
See also
Information about Haskell on different architectures.