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:
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.Code: Select all
// in <errno.h> extern int errno; // in some .c file int errno = 0;
The C standard endorses lazy evaluation of errno by making it a macro instead:
This way, errno is only "set" when its value is actually requested:Code: Select all
int * __errno(); #define errno *__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.Code: Select all
// "__errno()" returns the location of the current errno value, // the "errno" macro turns it into a value. x = errno;
So far, everything is fine. But it's the other way around that is giving me headaches:
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.Code: Select all
errno = 0;
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?
Detect usage of macro? (C, preprocessor)
Detect usage of macro? (C, preprocessor)
Cross-posting from StackOverflow.com, because I hold the programming skills assembled here in high regard:
Every good solution is obvious once you've found it.
Re: Detect usage of macro? (C, preprocessor)
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.
Cheers,
Brendan
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.
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)?The C standard endorses lazy evaluation of errno by making it a macro instead:
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Detect usage of macro? (C, preprocessor)
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.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.
Positive.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)?
7.5 Errors, paragraph 2:
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.[...] 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()).
Every good solution is obvious once you've found it.
Re: Detect usage of macro? (C, preprocessor)
Hi,
From "errno.h" on my system (GNU C Library):
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
From the documentation for GNU C Library:Solar wrote: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.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.
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.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.
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:Positive.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)?
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()).
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...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.
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
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Detect usage of macro? (C, preprocessor)
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.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.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Detect usage of macro? (C, preprocessor)
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.
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)
Of course. I had that in mind: Normal functions setting an "internal errno", math functions setting a flag to read FPU status.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.
I'll give the whole concept some more thought. Thanks for the great input!
Every good solution is obvious once you've found it.