Detect usage of macro? (C, preprocessor)

Programming, for all ages and all languages.
Post Reply
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Detect usage of macro? (C, preprocessor)

Post 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?
Every good solution is obvious once you've found it.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

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

Post 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
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.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

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

Post 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. ;-)
Every good solution is obvious once you've found it.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

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

Post 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
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.
User avatar
qw
Member
Member
Posts: 792
Joined: Mon Jan 26, 2009 2:48 am

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

Post 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.
User avatar
Combuster
Member
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)

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

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

Post 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!
Every good solution is obvious once you've found it.
Post Reply