Differences between revisions 140 and 141
Revision 140 as of 2017-08-14 16:53:23
Size: 18921
Editor: ?EdmundGrimleyEvans
Comment:
Revision 141 as of 2017-08-15 08:38:57
Size: 18929
Editor: ?EdmundGrimleyEvans
Comment: Page sizes for arm64 and arm64ilp32
Deletions are marked like this. Additions are marked like this.
Line 21: Line 21:
||~-'''[[Arm64Port|arm64]]'''-~ ||8 ||16||unsigned||LE||down||4K, 64K ||H ||H ||S ||`__aarch64__ && __LP64__`||
||~-'''[[Arm64ilp32Port|arm64ilp32]]'''-~ ||4 ||16||unsigned||LE||down||4K       ||H ||H ||S ||`__aarch64__ && __ILP32__`||
||~-'''[[Arm64Port|arm64]]'''-~ ||8 ||16||unsigned||LE||down||4K, 16K, 64K ||H ||H ||S ||`__aarch64__ && __LP64__`||
||~-'''[[Arm64ilp32Port|arm64ilp32]]'''-~ ||4 ||16||unsigned||LE||down||4K, 16K, 64K ||H ||H ||S ||`__aarch64__ && __ILP32__`||

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.

All Debian architectures have:
sizeof(short) == 2
sizeof(int) == 4
sizeof(long) == sizeof(void *)
sizeof(long long) == 8
sizeof(float) == 4
sizeof(double) == 8

DEB ARCH

sizeof (void*)

sizeof (long double)

char

endian

stack grows

page sizes

float1

double2

long double2

GCC pre-defined macro(s)

alpha

8

163

signed

LE

down

8K

?

?

?

__alpha__

*amd644

8

16

signed

LE

down

4K, 2M

H

H

H5

__x86_64__ && __LP64__6

arm

4

?

unsigned

LE

down

?

S

S

S

__arm__

arm64

8

16

unsigned

LE

down

4K, 16K, 64K

H

H

S

__aarch64__ && __LP64__

arm64ilp32

4

16

unsigned

LE

down

4K, 16K, 64K

H

H

S

__aarch64__ && __ILP32__

armel

4

8

unsigned

LE

down

4K

S

S

S

__armel__

armhf

4

8

unsigned

LE

down

4K

H

H

H

__arm__ && __ARM_PCS_VFP

avr32

4

8

unsigned

BE

down

?

?

?

?

?

hppa

4

8

signed

BE

up

?

?

?

?

?

*i3867

4

12

signed

LE

down

4K, 2M/4M

H+

H+

H5

__i386__

ia64

8

16

signed

LE

down

?

H

H

H

__ia64__

m68k

4

12

signed

BE

down

4K

S?

S?

S?

?

mips

4

8

signed

BE

down

4K-64K

H

H

H

__mips__ && _MIPSEB && _MIPS_SIM==_ABIO32

mips64el

8

16

signed

LE

down

4K-64K

H

H

H

__mips__ && _MIPSEL && _MIPS_SIM==_ABI64

mipsel

4

8

signed

LE

down

4K-64K

H

H

H

__mips__ && _MIPSEL && _MIPS_SIM==_ABIO32

powerpc

4

163

unsigned

BE

down

4K-64K

?

?

?

__powerpc__

ppc64

8

16

unsigned

BE

down

4K-64K

?

?

?

__powerpc__ && __ppc64__

ppc64el

8

16

unsigned

LE

down

4K-64K-16M

H

H

H~

__powerpc__ && __ppc64__ && __LITTLE_ENDIAN__

riscv648

8

16(8)9

unsigned

LE

down

4K

H

H

H~

__riscv__ && __riscv64

s390

4

163

unsigned

BE

down

?

?

?

?

__s390__

s390x

8

16

unsigned

BE

down

4K, 1M, 2G

H

H

H

__s390x__

sh4

4

8

signed

LE

down

4K

?

?

?

__sh__

sparc

4

163

signed

BE

down

?

?

?

?

__sparc__

sparc64

8

16

signed

BE

down

8K, 64K, 512K, 4M, 32M, 256M, 2G, 16G

?

?

?

__sparc__ && __arch64__

x32

4

16

signed

LE

down

4K, 2M

H

H

H5

__x86_64__ && __ILP32__

  1. H=hard, S=soft, P=partial, +=excess precision, ~=not usual standard (1)

  2. Note that the columns refer to C types. H=hard, S=soft, P=partial, +=excess precission, ~=not usual standard (2 3)

  3. long double changed size from 8 to 16 in Lenny (4 5 6 7)

  4. amd64, kfreebsd-amd64 (8)

  5. Use 80bits extended precision see extended precision (9 10 11)

  6. it's a common gotcha for x32, you need to use __LP64__/__ILP32__ to tell them apart (12)

  7. i386, kfreebsd-i386, hurd-i386 (13)

  8. A riscv64 port is currently in the planning & preparation stage. Upstreaming of RISC-V architecture support for binutils/gcc/glibc and the Linux kernel is still WIP and the ABI has not yet fully stabilized. (14)

  9. 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 (15)

Older version of table follows.

feature

alpha

arm

armel

armhf

arm64

avr32

hppa

*-i3861

?x32

*-amd642

ia64

m68k

mips

mipsel

mips64el

powerpc

ppc64

ppc64el

s390

s390x

sparc

sparc64

sh4

size of short

2

2

2

2

2

2

2

2

2

2

2

2

size of int

4

4

4

4

4

4

4

4

4

4

4

4

size of long

8

4

8

4

4

4

8

8

4

4

8

4

8

4

8

4

8

4

size of long long

8

8

8

8

8

8

8

8

8

8

8

8

alignment of long long

?

8

4

?

4

8

?

?

?

?

?

?

?

?

8

?

?

?

?

4

size of float

4

4

4

4

4

4

4

4

4

4

4

4

size of double

8

8

8

8

8

8

8

8

8

8

8

8

size of long double

163

8

16

8

8

12

16

16

12

8

16

163

16

16 3

16

163

16

8

alignment of long double

?

?

?

8

?

4

?

4

16

?

?

?

?

?

?

?

?

8

?

?

4

size of data pointer

8

4

8

4

4

4

8

8

4

4

8

4

8

4

8

4

8

4

max alignment

8

4

8

?

?

?

?

8

?

8

?

?

8

?

?

?

?

?

?

?

?

4

unaligned access4

T

M/T

M

T

O

T

Y?

N

?

Y

Y

N

?

N

endian5

L

L

B

B

L

L

B

B

L

B

L

B

B

L

char

±

+

+

±

±

±

±

±

+

+

±

±

stack grows6

func param location7

R

R

?

?

S

R

R

S

R

?

R

R

R

?

R

func return ptr location8

R

R

?

?

S

R

S

R

?

R

?

?

R

?

R

page size(s)

8K

4K

4K, 64K

?

?

4K, 2M/4M

4K, 2M

?

4K

4K-64K

?

?

4K-64K-16M

4K

?

?

4k

float 9

?

S

H

H

?

?

H+

H

H

S?

?

?

?

?

?

H

?

?

?

?

double 9

?

S

H

H

?

?

H+

H

H

S?

?

?

?

?

?

H

?

?

?

?

long double 9

?

S

S 2D?

S 2D?

?

?

H10

H

S?

?

?

?

?

?

H~

?

?

?

?

feature

alpha

arm

armel

armhf

arm64

avr32

hppa

*-i3861

?x32

*-amd642

ia64

m68k

mips

mipsel

mips64el

powerpc

ppc64

ppc64el

s390

s390x

sparc

sparc64

sh4

  1. i386, kfreebsd-i386, hurd-i386 (1 2)

  2. amd64, kfreebsd-amd64 (3 4)

  3. long double changed size from 8 to 16 in Lenny (5 6 7 8)

  4. Y=Yes, O=Often generally ok but ma fail in some specific case, T=Traps may be fixed by kernel (super slow),M=Maybe generally not ok, N=No raise SIGBUS. See detail below (9)

  5. L for little endian,B for big endian (10)

  6. ↑ for growing up,↓ for growing down (11)

  7. R=registers,S=stack (12)

  8. R=register,S=stack (13)

  9. H=hard, S=soft, P=partial, +=excess precission, ~=not usual standard, 2D=soft double double (14 15 16)

  10. Use 80bits extended precision see extended precision (17)

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 everywhere 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.

alpha

Some alpha may emulate see kernel source

armel/armhf/arm64

High-level summary for software engineers: Don't do unaligned access. It's always inefficient, sometimes extremely inefficent, 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 alignement.

ia64

Depending of config may fix with trap handler unaligned 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.

Obtaining this information

via a special tool

#include <alloca.h>
#include <endian.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 == __LITTLE_ENDIAN
  printf("byte order = little endian\n");
#elif __BYTE_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.


CategoryDeveloper CategoryPorts