Love4Boobies wrote:Why would you want to avoid the standard headers entirely? The C standard defines two types of implementations: freestanding (for GCC, use -ffreestanding, or -fno-hosted) and hosted (for GCC, use -fhosted, or -fno-freestanding). The freestanding headers that are available to you under both types of implementations are: <float.h>, <iso646.h>, <limits.h>, <stdalign.h> (C1X only), <stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.
I didn't know that these are available in a freestanding environment as well, so thanks for pointing that out. I guess the reason why I didn't use them before is that I'm using C++ and the C++ standard (I'm working with C++0x) defines some headers such as cstdint, but those are part of the C++ standard library and I couldn't find anything about the availability of those headers in a freestanding environment.
It seems like a code design flaw: you have to use assembly to do the CPU detection but you move bits of the code to C; in doing that, you're also exposing the implementation. Take a look at malloc for instance---you have no idea how it works but the API is portable and well encapsulated. CPU detection should be similar, esp. if you intend to port your code to other architectures: your build system will just link a different object file.
Actually the CPU detection code I posted is written solely for x86 architectures - both 32 bit and 64 bit. Of course I could write to assembler files, one for 32 bit, one for 64 bit, put the whole CPU detection in there, play around with some bits and so on. But why should I do that when all this can be done within a single file of C++ code, which can be compiled for either 32 or 64 bits, and the only assembly instruction I need to take care of is CPUID?
And indeed you are right, for a different CPU architecture I need a different CPU detection mechanism. That's a fact which is independent of using either inline assembly or separate assembler code, so it doesn't matter which of these approaches I use for feature detection of different CPUs.
Isn't this solution more elegant than having a big chunk of code with #ifdef's for picking the architecture, inline assembly (which is not only optional but also implemented differently by every compiler) mixed with C (which is meant to be portable but in this case is only used for some operations that logically belong to the assembly block)? IMO, it's a matter of readability and maintainability.
In fact, I don't need any #ifdef's here. The CPU detection code belongs to the x86 specific part of my kernel (both 32 and 64 bit) and compiled and linked in only if the host architecture is set to x86. If I compile for, say, m68k, this file is not compiled at all, and it is not even used by any code which is not x86 specific. The reason is that I need the information my x86 CPU class provides only for the low level x86 specific routines. (For example: Is fxsave / fxrstor supported? If yes, let the CPU know we want to use it.) It would be completely useless for a m68k CPU anyway, where I need to ask completely different questions (Are CPU32 instructions available?).
Owen wrote:In what way is it undefined behavior? If you're referring to the <iohw.h> component of TR 18037, that operates at a completely different level of abstraction than that which we are discussing. volatile pointers have explicitly defined behavior, and were indeed defined for access to MMIO registers. Note that <iohw.h> on its own defines nothing useful.
That's what I thought as well - a volatile variable is some piece of memory about which the compiler cannot make any assumptions. It may change randomly, so it needs to be read and written explicitly even if the compiler "thinks" that this is not necessary.
One place I might use IOHW would be to abstract things like PCI IO space to keep that code portable... but the lack of clean integration with the named address space support that TR also adds is a huge annoyance. I think IOHW will probably prove more useful when interacting with busses like I2C (and maybe SPI) where there is no corresponding "processor address space".
Coping with different address spaces indeed is a good point. But again I see no reason why the support routines for accessing these different address spaces should be written entirely in assembly.