Is __ILP32__ and __i386__ a valid configuration?

1.9k views Asked by At

We took a bug report for some X32 code under Clang on OS X (the error is shown below). The user tried to compile code on an Intel x86_64 system using -arch i386. X32 is 32-bit integers, longs and pointers.

According to the System V Application Binary Interface, AMD64 (With LP64 and ILP32 Programming Models) (page 104), the valid configurations are either __ILP32__ alone, or __ILP32__ and __x86_64__ (and friends, like amd64 in place of __x86_64__).

I also checked the SYSTEM V APPLICATION BINARY INTERFACE for Intel386, and it does not mention __ILP32__ and friends. So I don't believe __ILP32__ should be present on purely 32-bit systems.

I think the above should be enough to answer the question, but I am using Clang on an OS X system. The OS X ABI Function Call Guide doe not discuss it, and often defers to the System V guides. However, the referenced System V guide is from 2003, which predates X32, so there is no treatment.

My question is, is __ILP32__ and __i386__ a valid combination or configuration?


For completeness, here is the code and compiler error. I'm not trying to solve this problem.

Source Code

BOOL_X32 is defined when __ILP32__ is defined in the environment.

asm volatile
(
    // save ebx in case -fPIC is being used
# if BOOL_X32 || BOOL_X64
    "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx"
# else // BOOL_X86
    "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
# endif
    : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3])
    : "a" (input), "c" (0)
);

Compiler Error

X32 is mostly 32-bit. However, when interacting with the stack using assembly language, we need to push/pop 64-bit registers and values.

$ make cpu.o
clang++ -DNDEBUG -g2 -O2 -arch i386 -fPIC -pipe -c cpu.cpp
cpu.cpp:104:4: error: register %rbx is only available in 64-bit mode
                        "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx"
                        ^
<inline asm>:1:8: note: instantiated into assembly here
        pushq %rbx; cpuid; mov %ebx, %edi; popq %rbx
              ^~~~~
cpu.cpp:104:4: error: register %rbx is only available in 64-bit mode
                        "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx"
                        ^
<inline asm>:1:42: note: instantiated into assembly here
        pushq %rbx; cpuid; mov %ebx, %edi; popq %rbx
                                                ^~~~
2 errors generated.

Finally, here are Clang's preprocessor macros when using -arch i386 on an x86_64 system:

$ clang++ -arch i386 -dM -E - < /dev/null | egrep -i "(86|64|ilp)"
#define _ILP32 1
#define __ILP32__ 1
...
#define __i386 1
#define __i386__ 1
#define i386 1
1

There are 1 answers

1
AudioBubble On BEST ANSWER

Yes, __i386__ with __ILP32__ is a perfectly valid configuration. It means that you are building code for 32-bit x86 on a compiler where int, long, and pointers are 32 bits wide.

While data model names like "ILP32" are usually used to describe 64-bit systems, the use of such a name, or the presence of a macro related to one, implies nothing at all about the instruction set. While the System V ABI does not state that this macro should be present on a 32-bit system, it also does not state that it must not be present!

Anyways. If you need to determine what instruction set is in use, use __i386__ and __x86_64__; that's what they're there for.