This is an archive of the discontinued LLVM Phabricator instance.

These builtin functions set errno. Mark them accordingly.
AbandonedPublic

Authored by mcrosier on May 16 2014, 9:23 AM.

Details

Summary

A number of library calls that set errno were not behaving correctly with -fmath-errno. Specifically, they were being lowered to native instruction and not setting errno. The changes are based on grepping through the man pages. Please have a look and let me know if I've missed something.

Chad

Diff Detail

Event Timeline

mcrosier updated this revision to Diff 9490.May 16 2014, 9:23 AM
mcrosier retitled this revision from to These builtin functions set errno. Mark them accordingly..
mcrosier updated this object.
mcrosier edited the test plan for this revision. (Show Details)
mcrosier added a subscriber: Unknown Object (MLST).May 16 2014, 9:38 AM
chandlerc edited edge metadata.May 16 2014, 11:00 AM

I'm not really a fan of this. I don't know why it is reasonable to expect the builtin to set errno. If you want the libc behavior of a libc function, you should call the libc function. My impression was that one of the primary reasons for the existence of these builtins was to *avoid* calling a libc function.

And just for reference, yes, GCC does call the libc function. But that doesn't seem like useful behavior to me, and we have survived quite some time without this.

Yep; *sigh*.

include/clang/Basic/Builtins.def
165

qq?

hfinkel added inline comments.May 16 2014, 11:41 AM
include/clang/Basic/Builtins.def
279

Also, is this right? sqrt should become an ISD node, which at least on some platforms, gets directly lowered.

Generally, I'm a bit concerned here that we really need target feedback to make this decision properly. These are fine defaults, but we don't want to prevent the intended direct lowering (which is the whole point of the builtins anyway).

But, hey, Clang can use TTI too :-) [Unfortunately, I'm not kidding really].

Hal and I discussed this on IRC and here's the synopsis (mostly in Hal's words):

Many of these builtins turn into libc calls, and those do set errno. Marking them as readnone is a problem, because we can reorder/eliminate writes to errno. For example, if we assume readnone for something like builtin_sqrt(); write(); perror(); it may be reordered to write(); builtin_sqrt(); perror(); and perror may provide junk. However, if we don't mark the builtins as readnone, then they won't get lowered into ISD nodes (for those nodes that are relevant), and that means that they won't get instruction selected into native instructions on targets where that's possible. So the __builtin_sqrt is kind of an escape hatch to force the direct lowering behavior even when we can't do it for sqrt because sqrt might set errno. But, as Hal said, this behavior *is* broken, technically. And, in fact, it is not hard to create test cases where you can see buggy behavior. Hak's just not sure what the right way of handling it is. Hal thinks we really need some kind of target input, so that we can know ahead of time whether the builtin will *really* set errno or not after isel. Hal thinks there are two solutions possible.

  1. Like with inline asm registers/constraints; each target will provide a table describing what builtins will really not set errno.
  2. Let Clang essentially query TTI for those builtins that are relevant. This second method will require a bit of infrastructure work, but is probably not too difficult.

I'm not going to push this patch further because it's not the right solution. In theory, I believe it is correct, but it breaks the __buitlin escape hatch, which would likely result in serious performance degradations.

include/clang/Basic/Builtins.def
165

That's must have slipped in. Thanks, Hal.

mcrosier abandoned this revision.Jul 23 2014, 9:09 AM

We should perhaps reopen this in light of PR21635 (or consider some variant of it). The problem, as pointed out in the bug report, is that libstdc++ implements std::X in terms of __builtin_X:

inline float
exp(float __x)
{ return __builtin_expf(__x); }

and these C++ functions are *required* to follow the same math error handling protocol as the underlying C functions. So we actually don't have much of a choice here (at least for those functions used to implement libstdc++) as far as I can tell. As a result, this seems like the right approach after all (although this is somewhat unfortunate).

mcrosier reclaimed this revision.Nov 24 2014, 12:30 PM
rsmith edited edge metadata.Nov 24 2014, 12:46 PM
In D3806#12, @hfinkel wrote:

The problem, as pointed out in the bug report, is that libstdc++ implements std::X in terms of __builtin_X

This is only non-conforming because math_errhandling is defined to a value that includes MATH_ERRNO. Another possible approach would be to subvert that macro instead of providing an errno guarantee in our builtins. Is there some reason we want to provide a guarantee that we set errno on domain error if the underlying libc does?

In D3806#14, @rsmith wrote:
In D3806#12, @hfinkel wrote:

The problem, as pointed out in the bug report, is that libstdc++ implements std::X in terms of __builtin_X

This is only non-conforming because math_errhandling is defined to a value that includes MATH_ERRNO.

Correct.

Another possible approach would be to subvert that macro instead of providing an errno guarantee in our builtins. Is there some reason we want to provide a guarantee that we set errno on domain error if the underlying libc does?

Interesting idea. Are the calls allowed to set errno if math_errhandling does not include MATH_ERRNO? And do we introduce a usability problem by having the behavior differ from the C functions?

Part of the problem here is that we don't really track errno, and if we think the calls don't set errno, we mark them as const. In doing so, they can be reordered with calls that do set errno (like open()), and can corrupt the errno value coming from those calls. That having been said, we have this problem in fast-math mode too.

mcrosier updated this revision to Diff 16775.Dec 1 2014, 11:17 AM
mcrosier edited edge metadata.

Rebase, per Hal's request.

Richard, as much as I don't like this, I'd like to move forward with this. While intercepting math_errhandling might allow us to pretend that the functions don't set errno (and it is not clear to me that we can really make the "C++" math_errhandling differ from the "C" math_errhandling), it does not address the fact that they actually do (and, thus, can cause miscompiles if they're reordered with calls to open(), etc.). I also recognize that this is somewhat self-inducing, because if we mark the functions as non-const, then they'll become real functions which are non-const, but if we mark them as const, then some of them are directly lowered, and thus really are const. The problem is marking calls as const which really aren't -- and some of these functions are never directly handled by the backends.

In D3806#16, @mcrosier wrote:

Rebase, per Hal's request.

Thanks! We, however, need a test case.

rsmith added a comment.Dec 9 2014, 5:58 PM

A few comments, then this seems OK with accompanying testcases. All the functions other than the ones I commented on are lowered directly to libm calls, so it's correct to mark them "e".

(Separately, I wonder why we lower these directly to libcalls rather than to intrinsic calls, and conversely why LLVM has intrinsics that are exactly equivalent to libm functions.)

include/clang/Basic/Builtins.def
111–113

This is wrong. We lower these to an frem instruction, which does not set errno.

140–142

Is this correct? We lower this to an intrinsic, and http://llvm.org/docs/LangRef.html#llvm-powi-intrinsic doesn't mention the possibility of the intrinsic setting errno.

mcrosier abandoned this revision.Oct 6 2015, 6:50 AM