Page 1 of 1

Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 8:08 am
by Solar
Cross-posting from StackOverflow.com, because I hold the programming skills assembled here in high regard:
This is very specific, and a bit difficult to explain, and quite likely impossible, but here goes. Perhaps some C cracks out there have an idea...

I want to implement <errno.h>. (My hobby project is implementing a Standard C library.)

The naive way to go about it is:

Code: Select all

// in <errno.h>
extern int errno;

// in some .c file
int errno = 0;
This works. But it has one disadvantage: If a math library function is called, it always has to query the FPU status after execution to set errno as appropriate. This stalls the FPU in math-heavy applications.

The C standard endorses lazy evaluation of errno by making it a macro instead:

Code: Select all

int * __errno();
#define errno *__errno()
This way, errno is only "set" when its value is actually requested:

Code: Select all

// "__errno()" returns the location of the current errno value,
// the "errno" macro turns it into a value.
x = errno;
Given some logic in the rest of the library, the FPU status needs only be queried if the last library function called was actually one using the FPU, and the value of errno is actually requested.

So far, everything is fine. But it's the other way around that is giving me headaches:

Code: Select all

errno = 0;
The value of errno is not requested at all. But __errno() does not know that, and will query the FPU status if the last library function called was using the FPU.

Right now I don't see a way to avoid that (i.e., have the errno macro or the __errno() function somehow work differently depending on whether they are used to the left or the right of the assignment operator), and am almost content to accept this.

But perhaps any of you OSDev'ers has a brilliant idea?

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 8:42 am
by Brendan
Hi,

If there was no error then errno must be unchanged, so you must examine the FPU status to determine if errno (or __errno) should be changed or not. The only alternative I can think of (for 80x86) is using the FPU exception (or int 0x13?) to update __errno when an error does occur, to avoid overhead when no error occurs.
The C standard endorses lazy evaluation of errno by making it a macro instead:
Are you sure that C endorses lazy invalidation in this case, and that errno isn't a macro for other reasons (e.g. so that the macro can resolve to an address in thread local storage if someone includes pthreads)?


Cheers,

Brendan

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 9:03 am
by Solar
Brendan wrote:If there was no error then errno must be unchanged, so you must examine the FPU status to determine if errno (or __errno) should be changed or not.
Yes, but the lazy evaluation allows me to examine the FPU status only when errno is actually queried after the function. In a series of math functions, where only the last one is followed by a check of errno, I only have to check the FPU status once, instead of after each individual function.
Are you sure that C endorses lazy invalidation in this case, and that errno isn't a macro for other reasons (e.g. so that the macro can resolve to an address in thread local storage if someone includes pthreads)?
Positive.

7.5 Errors, paragraph 2:
[...] errno
which expands to a modifiable lvalue171) that has type int, the value of which is set to a
positive error number by several library functions.

171) The macro errno need not be the identifier of an object. It might expand to a modifiable lvalue
resulting from a function call (for example, *errno()).
The idea that this can be used for lazy evaluation with regards to FPU operations is from P.J. Plaugher's "The C Standard Library". Plaugher being a long-time member in the C standard committee, I assume he knows what he's talking about. ;-)

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 10:08 am
by Brendan
Hi,
Solar wrote:
Brendan wrote:If there was no error then errno must be unchanged, so you must examine the FPU status to determine if errno (or __errno) should be changed or not.
Yes, but the lazy evaluation allows me to examine the FPU status only when errno is actually queried after the function. In a series of math functions, where only the last one is followed by a check of errno, I only have to check the FPU status once, instead of after each individual function.
From the documentation for GNU C Library:
GCLIB wrote:The initial value of errno at program startup is zero. Many library functions are guaranteed to set it to certain nonzero values when they encounter certain kinds of errors. These error conditions are listed for each function. These functions do not change errno when they succeed; thus, the value of errno after a successful call is not necessarily zero, and you should not use errno to determine whether a call failed. The proper way to do that is documented for each function. If the call failed, you can examine errno.
Imagine code that uses 2 functions and then checks errno. What happens when the first function has an error but the second function has no errors? In this case, when errno is checked it should contain the error from the first function, not the "no error" from the second function. This can't be done (in a sane way) with lazy evaluation.
Solar wrote:
Are you sure that C endorses lazy invalidation in this case, and that errno isn't a macro for other reasons (e.g. so that the macro can resolve to an address in thread local storage if someone includes pthreads)?
Positive.

7.5 Errors, paragraph 2:
[...] errno
which expands to a modifiable lvalue171) that has type int, the value of which is set to a
positive error number by several library functions.

171) The macro errno need not be the identifier of an object. It might expand to a modifiable lvalue
resulting from a function call (for example, *errno()).
This only says that errno can be a macro, and doesn't say why errno is allowed to be macro. I'm still thinking the idea is that errno was bad design (specifically for multi-threading), and it's allowed to be a macro because this is one of the only ways to fix the symptoms of this bad design.
Solar wrote:The idea that this can be used for lazy evaluation with regards to FPU operations is from P.J. Plaugher's "The C Standard Library". Plaugher being a long-time member in the C standard committee, I assume he knows what he's talking about. ;-)
I'd assume he knows what he's talking about, but this doesn't imply that anyone else knows what he's talking about, or that he knows what anyone else is talking about...

From "errno.h" on my system (GNU C Library):

Code: Select all

/* Declare the `errno' variable, unless it's defined as a macro by
   bits/errno.h.  This is the case in GNU, where it is a per-thread
   variable.  This redeclaration using the macro still works, but it
   will be a function declaration without a prototype and may trigger
   a -Wstrict-prototypes warning.  */
#ifndef errno
extern int errno;
#endif
From this I assume that GNU C Library doesn't use lazy evaluation for errno, as it'd break when errno is an integer.


Cheers,

Brendan

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 2:47 pm
by qw
This works. But it has one disadvantage: If a math library function is called, it always has to query the FPU status after execution to set errno as appropriate. This stalls the FPU in math-heavy applications.
See D.J. Delorie's C library. In most cases, his math functions check their arguments (not using the FPU) before doing the actual calculation. Querying the FPU status is hardly necessary. I couldn't tell what this does to performance though.

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 4:11 pm
by Combuster
To (hopefully) answer the original question:
There's one other problem - lazily evaluating errno has one other side-effect. Generally when you poll the FPU status you also want to reset it so that the error's you just checked for don't ignorantly reappear in the next readout of errno.

Which breaks when you want to avoid the call to calc_errno() when executing errno = 0; if there are, at that point, pending errors in the status word, the next time you read errno it will give you the wrong answer. So imo you can forget about optimizing that away in some way or another.


Second problem (if you thought about it) - have you thought about the fact that non-fpu functions might yield an change in errno, and you want to make sure that errno reflects the latest error. So you can't blindly check the FPU status word in any lazy scheme and set errno accordingly.

Re: Detect usage of macro? (C, preprocessor)

Posted: Fri Aug 28, 2009 11:06 pm
by Solar
Combuster wrote:Second problem (if you thought about it) - have you thought about the fact that non-fpu functions might yield an change in errno, and you want to make sure that errno reflects the latest error. So you can't blindly check the FPU status word in any lazy scheme and set errno accordingly.
Of course. I had that in mind: Normal functions setting an "internal errno", math functions setting a flag to read FPU status.

I'll give the whole concept some more thought. Thanks for the great input!