peachsmith wrote:
And just for reference, this is the inline assembly implementation from the wiki:
Code:
static inline void inline_outb(uint16_t port, uint8_t val)
{
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
/* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint).
* Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
* The outb %al, %dx encoding is the only option for all other cases.
* %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */
}
I looked at the assembly generated for the inline example during compilation and tried to copy that in my implementation, but to no avail.
What am I missing here?
One thing you may be missing is the
inline modifier on the wiki's version, possibly because the word 'inline' is overloaded in this usage. This is one of those things which, even if you know how it works (which I expect you do), can be easily overlooked in practice when you aren't expecting it.
For those who aren't aware (which may or may not include the OP), in an
inline function, no callable separate function is generated by the compiler; rather, the function body (which in this case is entirely inline assembly, but it would be just as true for an
inline function with no inline assembly in it at all) is inserted directly into the caller's code stream. No function prolog or epilog are generated, no
CALL or
RET instructions are used (or their equivalents on other ISAs), and arguments are interpolated directly rather passed on the stack.
In some ways, an
inline function is more like a macro than a separate function in the usual sense, though unlike a macro the code might be wholly or partially compiled first (depending on the compiler and the code), rather than being added with a simple string interpolation, and they have all the type checking and other safeguards found in ordinary functions. Indeed, when the standards committee added the
inline keyword to C (I don't recall which standard revision this was, though IIRC, it had been in C++ since the outset, so it very likely was ANSI C in 1988), it was mainly proposed as a safer alternative to the existing pre-processor macros.
Since they are mainly an efficiency tweak,
inline functions are usually only used for small sections of code where the overhead of an ordinary function's prolog/epilog and stack handling would dominate the code generated for the function. Usual practice is to use them sparingly, even for smaller functions, as they do increase the code size of the functions they are 'called' by, and repeated use of even small
inline functions can end up being less space efficient than a full-fledged function, regardless of the calling overhead.
In this case, the desired code is a single assembly instruction, which is the ideal situation for using an
inline function containing inline assembly code. While I am no fan of inline assembly for general use in C, this is one instance where it really makes sense.
I don't recall off-hand if the C standard considers the
inline directive a modifier or a hint, but I am pretty sure that the compiler can ignore it if the optimizer finds that it is less efficient inlined than called. However, this is something of an edge case, regardless. I do know that some compilers require shared
inline functions to be defined in the shared header rather than in one of the source files (one of the few exceptions to the rule of never having a function's implementation inside a header), but I'm not sure if that is actually required by the standard. Again, this isn't necessarily relevant here.