[llvm-dev] Calling function from non-default floating-point environment

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[llvm-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev
Hi all,

Implementation of #pragma STDC FENV_ACCESS raises a problem: what to do if a function is called inside a region where FP environment differs from the default? If the function expects default FP mode it may work incorrectly in such case.

The C2x standard draft (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf) states (7.6p4):

Certain programming conventions support the intended model of use for the dynamic floating-point environment:*)
— a function call does not alter its caller’s floating-point control modes, clear its caller’s floating point status flags, nor depend on the state of its caller’s floating-point status flags unless the function is so documented;
— a function call is assumed to require default floating-point control modes, unless its documentation promises otherwise;
— a function call is assumed to have the potential for raising floating-point exceptions, unless its documentation promises otherwise.
*) With these conventions, a programmer can safely assume default floating-point control modes (or be unaware of them). The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.

It looks like that the standard requires to call functions in default FP mode, so inside a block where #pragma STDC FENV_ACCESS acts, each function call should be converted into sequence:
 - store FP state,
 - set default FP state,
 - call the function,
 - restore FP state.
These save/restore instructions could be inserted by compiler. This could be the safest solution but it complicates implementation and may impact performance. There is also another viewpoint: it is user responsibility to provide necessary environment and save/restore operations must be inserted manually.

Choosing the proper way we need to take into account:
- generally it is hard for a user to be sure that a function do not depend on FP environment. Functions that apparently do not use FP numbers (like addition to hash table) may actually involve FP operations internally.
- function inlining occurs in IR level and the chosen solution may potentially affect semantics of other languages (maybe Fortran?).

So the first question is: should the compiler set default FP state prior to function calls?

The next question is: should the compiler support some frontend attribute to mark functions that do not require default FP mode? These are functions that:
- do not involve FP operations,
- work correctly in any FP mode,
- expects particular FP mode,
- modifies FP mode,
- probably something else.
For such functions compiler would not generate save/restore operations. We also could have several attributes if we need to distinguish between these cases.


Thanks,
--Serge

_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev

 

 

From: Serge Pavlov <[hidden email]>
Sent: Tuesday, January 07, 2020 1:02 PM
To: LLVM Developers <[hidden email]>; Clang Dev <[hidden email]>
Cc: Kevin Neal <[hidden email]>
Subject: Calling function from non-default floating-point environment

 

EXTERNAL

Hi all,

Implementation of #pragma STDC FENV_ACCESS raises a problem: what to do if a function is called inside a region where FP environment differs from the default? If the function expects default FP mode it may work incorrectly in such case.

The C2x standard draft (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf) states (7.6p4):

Certain programming conventions support the intended model of use for the dynamic floating-point environment:*)
— a function call does not alter its caller’s floating-point control modes, clear its caller’s floating point status flags, nor depend on the state of its caller’s floating-point status flags unless the function is so documented;
— a function call is assumed to require default floating-point control modes, unless its documentation promises otherwise;
— a function call is assumed to have the potential for raising floating-point exceptions, unless its documentation promises otherwise.
*) With these conventions, a programmer can safely assume default floating-point control modes (or be unaware of them). The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.

 

Right here it says that dealing with non-default modes is the job of the program or programmer: “The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.” It doesn’t say compiler. It also hedges with words like “certain ... conventions”. If only some conventions then that implies that there are other conventions that do things differently and that’s OK.

 

It explicitly calls out the programmer to solve these issues when opting in to non-default FP behavior.

 

And I say this from a company that always runs with traps enabled and therefore has to deal with these FP issues. Sometimes we work around traps in third party, default FP environment software. Sometimes we _want_ that trap from default FP environment software because it indicates a bug somewhere. We have to examine these cases individually and determine what we need. It’s not the compiler’s job to protect us from ourselves.

 

Can we get a language lawyer to settle this once and for all?


It looks like that the standard requires to call functions in default FP mode, so inside a block where #pragma STDC FENV_ACCESS acts, each function call should be converted into sequence:
 - store FP state,
 - set default FP state,
 - call the function,
 - restore FP state.
These save/restore instructions could be inserted by compiler. This could be the safest solution but it complicates implementation and may impact performance. There is also another viewpoint: it is user responsibility to provide necessary environment and save/restore operations must be inserted manually.

Choosing the proper way we need to take into account:
- generally it is hard for a user to be sure that a function do not depend on FP environment. Functions that apparently do not use FP numbers (like addition to hash table) may actually involve FP operations internally.
- function inlining occurs in IR level and the chosen solution may potentially affect semantics of other languages (maybe Fortran?).

So the first question is: should the compiler set default FP state prior to function calls?

No.


The next question is: should the compiler support some frontend attribute to mark functions that do not require default FP mode? These are functions that:
- do not involve FP operations,
- work correctly in any FP mode,
- expects particular FP mode,
- modifies FP mode,
- probably something else.
For such functions compiler would not generate save/restore operations. We also could have several attributes if we need to distinguish between these cases.

 

No, the compiler should not be inserting save/restore operations. If this was intended then attributes or keywords would have been introduced a long time ago I bet.

 

Thanks,
--Serge


_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev
In reply to this post by Chris Lattner via llvm-dev
Hey Serge,

Before I comment, I should mention that we've discussed this before
and I think the C Standard is frustrating and vague w.r.t. to FPEnv.
We should probably work on tightening up this part of the standard.

Comments inline...

On Tue, Jan 7, 2020 at 1:02 PM Serge Pavlov via llvm-dev
<[hidden email]> wrote:
>
> Hi all,
>
> Implementation of #pragma STDC FENV_ACCESS raises a problem: what to do if a function is called inside a region where FP environment differs from the default?
> If the function expects default FP mode it may work incorrectly in such case.

The standard draft linked below says:

"If part of a program tests floating-point status flags or establishes
non-default floating-point mode settings using any means other than
the FENV_ROUND pragmas, but was translated with the state for the
FENV_ACCESS pragma "off", the behavior is undefined."

I think that covers your question. If the caller sets `FENV_ACCESS=ON`
and the callee is translated with `FENV_ACCESS=OFF`, then the behavior
is undefined.

Otherwise, if the caller sets `FENV_ACCESS=OFF` and the callee is
translated with `FENV_ACCESS=ON`, then there is no danger there since
the callee explicitly manages the FPEnv. I.e.:

"When execution passes from a part of the program translated with
FENV_ACCESS "off" to a part translated with FENV_ACCESS "on", the
state of the floating-point status flags is unspecified and the
floating-point control modes have their default settings." (1)

And, of course, the cases where `FENV_ACCESS` is the same for both
caller and callee are trivial.

> The C2x standard draft (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf) states (7.6p4):
>
> Certain programming conventions support the intended model of use for the dynamic floating-point environment:*)
> — a function call does not alter its caller’s floating-point control modes, clear its caller’s floating point status flags, nor depend on the state of its caller’s floating-point status flags unless the function is so documented;
> — a function call is assumed to require default floating-point control modes, unless its documentation promises otherwise;
> — a function call is assumed to have the potential for raising floating-point exceptions, unless its documentation promises otherwise.
> *) With these conventions, a programmer can safely assume default floating-point control modes (or be unaware of them). The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.
>
>
> It looks like that the standard requires to call functions in default FP mode, so inside a block where #pragma STDC FENV_ACCESS acts, each function call should be converted into sequence:
>  - store FP state,
>  - set default FP state,
>  - call the function,
>  - restore FP state.
> These save/restore instructions could be inserted by compiler. This could be the safest solution but it complicates implementation and may impact performance. There is also another viewpoint: it is user responsibility to provide necessary environment and save/restore operations must be inserted manually.

I think this is overkill, especially the save/restore steps. The only
case that the compiler ***may*** need to reset the default FPEnv state
is the `FENV_ACCESS=ON` case [see (1) above]. It's not clear to me
whether (1) implies that the compiler should explicitly set the
control modes to their default settings OR assumes that the user will
reset the control modes to their default settings. I'd like to hear
other interpretations though. IIRC, the group decided that it was the
user's responsibility to reset the default state.

> Choosing the proper way we need to take into account:
> - generally it is hard for a user to be sure that a function do not depend on FP environment. Functions that apparently do not use FP numbers (like addition to hash table) may actually involve FP operations internally.
> - function inlining occurs in IR level and the chosen solution may potentially affect semantics of other languages (maybe Fortran?).
>
> So the first question is: should the compiler set default FP state prior to function calls?

I'm leaning towards agreeing with Kevin. It's up to the user to manage
the FPEnv state (except maybe at the end of compound statements, which
we punted on). I think the footnote mentioned above is significant:
"The responsibilities associated with accessing the floating-point
environment fall on the programmer or program that does so
explicitly."


Digressing a bit, this clause brings up an interesting (and
controversial) topic:

  — a function call is assumed to have the potential for raising
floating-point exceptions, unless its documentation promises
otherwise.

I read this as we should be compiling system/3rd party libraries, e.g.
libm, in a trap-safe mode. IINM, the current plan is to not do that.
_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] [cfe-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev
In reply to this post by Chris Lattner via llvm-dev

On 7 Jan 2020, at 14:00, Kevin Neal via cfe-dev wrote:

Right here it says that dealing with non-default modes is the job of the program or programmer: “The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.” It doesn’t say compiler. It also hedges with words like “certain ... conventions”. If only some conventions then that implies that there are other conventions that do things differently and that’s OK.

It explicitly calls out the programmer to solve these issues when opting in to non-default FP behavior.

And I say this from a company that always runs with traps enabled and therefore has to deal with these FP issues. Sometimes we work around traps in third party, default FP environment software. Sometimes we _want_ that trap from default FP environment software because it indicates a bug somewhere. We have to examine these cases individually and determine what we need. It’s not the compiler’s job to protect us from ourselves.

Can we get a language lawyer to settle this once and for all?

I agree with your reading. The standard is quite clear that functions translated under #pragma FENV_ACCESS OFF may assume that the control modes have their default settings, but it also plainly describes functions that are sensitive to their caller’s control modes. If the control modes were meant to be implicitly managed by the implementation, there would have to be some way to declare the difference, but the standard provides no such mechanism. Instead, the standard describes conventions which programmers may follow explicitly to satisfy these control-mode expectations. This all leads me to the straightforward conclusion that it is meant to be undefined behavior to allow control to enter code translated under #pragma FENV_ACCESS OFF with non-default settings for the control modes (at least if that code performs any floating-point operations). It would even be somewhat reasonable to argue that this is an intended implication of the sentence in 7.6.1p2 beginning “If part of a program…”, although really I think it’s just an oversight in the drafting.

I’m not sure I like this language design, but it’s definitely the intended design.

The next question is: should the compiler support some frontend attribute to mark functions that do not require default FP mode? These are functions that:
- do not involve FP operations,
- work correctly in any FP mode,
- expects particular FP mode,
- modifies FP mode,
- probably something else.
For such functions compiler would not generate save/restore operations. We also could have several attributes if we need to distinguish between these cases.

This isn’t quite what you’re asking, but:

If we’re going to do serious optimization work with code translated under #pragma FENV_ACCESS ON, we’ll want to be able to declare the readonly/readnone equivalents for the FP environment:

  • the function will not access the FP environment at all (essentially meaning that it won’t perform any FP operations) or
  • the function may read the FP environment, and it may change the status flags, but it at least won’t change the control modes.

It’s possible that the intermediate positions of “may read the FP environment but will not change the status flags” and “may set status flags, but will not read control modes” may be useful. (I think the latter can only happen with calls to intrinsics like fesetexcept, but, well, those functions exist, and optimizing around them might matter.)

Anyway, we’ll want to do that at the IR level at least, and we’ll probably want to do it at the user level eventually. But it’ll be awhile before we get there. And these attributes would be quite different from the “allows non-default control modes” attribute that a different language design might need.

John.


_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] [cfe-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev
Thank you very much for the discussion.

This viewpoint on mixed FP environment simplifies development. Of course, leaving user without compiler support is not good thing. But in future we can invent mechanisms to alleviate the problem. For instance, a command-line option that put save/restore call around functions, for which FPEnv requirements are unknown. It depends on what problems will actually arise in practical use of this feature.

Thanks,
--Serge


On Thu, Jan 9, 2020 at 1:31 PM John McCall <[hidden email]> wrote:

On 7 Jan 2020, at 14:00, Kevin Neal via cfe-dev wrote:

Right here it says that dealing with non-default modes is the job of the program or programmer: “The responsibilities associated with accessing the floating-point environment fall on the programmer or program that does so explicitly.” It doesn’t say compiler. It also hedges with words like “certain ... conventions”. If only some conventions then that implies that there are other conventions that do things differently and that’s OK.

It explicitly calls out the programmer to solve these issues when opting in to non-default FP behavior.

And I say this from a company that always runs with traps enabled and therefore has to deal with these FP issues. Sometimes we work around traps in third party, default FP environment software. Sometimes we _want_ that trap from default FP environment software because it indicates a bug somewhere. We have to examine these cases individually and determine what we need. It’s not the compiler’s job to protect us from ourselves.

Can we get a language lawyer to settle this once and for all?

I agree with your reading. The standard is quite clear that functions translated under #pragma FENV_ACCESS OFF may assume that the control modes have their default settings, but it also plainly describes functions that are sensitive to their caller’s control modes. If the control modes were meant to be implicitly managed by the implementation, there would have to be some way to declare the difference, but the standard provides no such mechanism. Instead, the standard describes conventions which programmers may follow explicitly to satisfy these control-mode expectations. This all leads me to the straightforward conclusion that it is meant to be undefined behavior to allow control to enter code translated under #pragma FENV_ACCESS OFF with non-default settings for the control modes (at least if that code performs any floating-point operations). It would even be somewhat reasonable to argue that this is an intended implication of the sentence in 7.6.1p2 beginning “If part of a program…”, although really I think it’s just an oversight in the drafting.

I’m not sure I like this language design, but it’s definitely the intended design.

The next question is: should the compiler support some frontend attribute to mark functions that do not require default FP mode? These are functions that:
- do not involve FP operations,
- work correctly in any FP mode,
- expects particular FP mode,
- modifies FP mode,
- probably something else.
For such functions compiler would not generate save/restore operations. We also could have several attributes if we need to distinguish between these cases.

This isn’t quite what you’re asking, but:

If we’re going to do serious optimization work with code translated under #pragma FENV_ACCESS ON, we’ll want to be able to declare the readonly/readnone equivalents for the FP environment:

  • the function will not access the FP environment at all (essentially meaning that it won’t perform any FP operations) or
  • the function may read the FP environment, and it may change the status flags, but it at least won’t change the control modes.

It’s possible that the intermediate positions of “may read the FP environment but will not change the status flags” and “may set status flags, but will not read control modes” may be useful. (I think the latter can only happen with calls to intrinsics like fesetexcept, but, well, those functions exist, and optimizing around them might matter.)

Anyway, we’ll want to do that at the IR level at least, and we’ll probably want to do it at the user level eventually. But it’ll be awhile before we get there. And these attributes would be quite different from the “allows non-default control modes” attribute that a different language design might need.

John.


_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] [cfe-dev] Calling function from non-default floating-point environment

Chris Lattner via llvm-dev
On 9 Jan 2020, at 11:11, Serge Pavlov wrote:
> Thank you very much for the discussion.
>
> This viewpoint on mixed FP environment simplifies development. Of course,
> leaving user without compiler support is not good thing. But in future we
> can invent mechanisms to alleviate the problem. For instance, a
> command-line option that put save/restore call around functions, for which
> FPEnv requirements are unknown. It depends on what problems will actually
> arise in practical use of this feature.

To do this, we would need a new pragma with its own semantics.
I find it unlikely that we would pursue that.  I think it’d be
much more palatable to add a static analysis that warned about
code that appears to not be following the standard recommended
conventions.

John.
_______________________________________________
LLVM Developers mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev