Date: Sat, 13 May 2017 03:40:39 -0700 From: Mark Millard <markmi@dsl-only.net> To: sgk@troutmask.apl.washington.edu Cc: Bruce Evans <brde@optusnet.com.au>, freebsd-hackers@freebsd.org, numerics@freebsd.org Subject: Re: catrig[fl].c and inexact Message-ID: <DC2DA938-6A07-4CB0-AFB6-038368971B77@dsl-only.net> In-Reply-To: <20170513060803.GA84399@troutmask.apl.washington.edu> References: <20170512215654.GA82545@troutmask.apl.washington.edu> <20170513103208.M845@besplex.bde.org> <20170513060803.GA84399@troutmask.apl.washington.edu>
next in thread | previous in thread | raw e-mail | index | archive | help
On 2017-May-12, at 11:08 PM, Steve Kargl <sgk at = troutmask.apl.washington.edu> wrote: > On Sat, May 13, 2017 at 11:35:49AM +1000, Bruce Evans wrote: >> On Fri, 12 May 2017, Steve Kargl wrote: >>=20 >>> So, I've been making improvements to my implementations of >>> the half-cycle trig functions. In doing so, I decide to >>> add WARNS=3D2 to msun/Makefile. clang 4.0.0 dies with an >>> error about an unused variable in raise_inexact() from >>> catrig[fl].c. >>>=20 >>> /usr/home/kargl/trunk/math/libm/msun/src/catrigl.c:195:2: error: = unused variable >>> 'junk' [-Werror,-Wunused-variable] >>> raise_inexact(); >>> ^ >>> /usr/home/kargl/trunk/math/libm/msun/src/catrigl.c:56:45: note: = expanded from >>> macro 'raise_inexact' >>> #define raise_inexact() do { volatile float junk =3D 1 + tiny; } = while(0) >>> ^ >>> Grepping catrig.o for the variable 'junk' suggests that 'junk' is >>> optimized out (with at least -O2). >>=20 >> Just another bug in clang. Volatile variables cannot be optimized = out >> (if they are accessed). >=20 > Does this depend on scope? 'junk' is local to the do {...} while(0); > construct. Can a compiler completely eliminate a do-nothing scoping > unit? I don't know C well enough to know. I do know what I have > observed in clang. [This note ignores other standards than C99/C11 that might place other constraints. And I've done no checking of compiler results, I've just looked at a couple of the C standards.] Note: I've not looking to tiny's declaration. It may contribute in a way not covered below. Unfortunately the declarator in an init-declarator that has an initializer is not part of an expression. The rules for volatile are tied to uses in expressions, not to the declarator. (Which is a hole in the language definition as far as I can tell.) There is one part of the wording that might mitigate this, tied to a full declarator having a sequence point at its end despite the declarator itself not being an expression, even if its initializer is one. There is another wording detail that might as well. Still, overall it would seem safer to be sure there is an expression that references the volatile object, not having only its declarator. But I would not take even that as a guarantee under the C standards. It may seem a silly difference but: do { volatile float junk=3D1; junk+=3Dtiny; } while(0) may well be a better way of writing the "must evaluate" part of the intent simply because junk is used in an expression. Also it has both read and write access, so is a little more "used". The sequence point before the assignment can help avoid compile-time evaluation as well. Details if you care. . . I used the C99 and C11 definitions here, I reference C11 section numbering but C99 agrees as I remember. 5.1.2.3 Program execution says: "Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression may produce side effects." Note that raising inexact does not fit in the definition of side effect as far as I can tell. So a compiler need not consider such a thing for side-effect issues if I understand right. [C11 specific wording:] "The presence of a sequence point between the evaluations of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value compuation and side effect associated with B." [C99 is similar but is before the detailed "sequenced before" definition.] "An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object)." Can a accessing a volatile object ever be classified as having "no needed side effects"? More on this later. [Remember what "side effect" excludes, as noted earlier. So some consequences need not be considered by the compiler, all in the name of optimizations.] 6.7.3 Type Qualifiers says: "An object that has volatile-qualified type . . . Therefore any expression referring to such as object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. What constitutes an access to an object that has volatile-qualified type is implementation-defined." This part is mixed: what the sequence point wording giveth the last sentence taketh away. (More later.) It also says in a note (134): "A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall, not be "optimized out" by an implementation or reordered except as permitted by the rules for evaluating expressions." Since rules for evaluating expressions are not rules for declarators (vs. initializers), this could be read as not allowing the "optimize out". (But the abstract machine's description is not explicit about declarators for such issues.) The C99 Rationale: The C99 Rationale was explicit about static volatile for a memory mapped I/O register, static const volatile for a memory mapped input port, const volatile and volatile for variables shared across processes. To some extent this identifies examples of contexts with "needed side effects" that have hardware details to take into account. For taking into account hardware details: ". . . Whatever decision are adopted on such issues must be documented, as volatile access is implementation-defined". For volatile use with no explicitly identified hardware details: volatile would appear to be no more than a potential hint for such a context, not an effective requirement. The implementation-defined status could allow lack of access. Overall, based on what I see in the C99 and C11 language definitions, I'd not be willing to declare clang wrong (if it did optimize out junk), even with my alternative formulation. C does not have an explicit Principle of Least Astonishment as a official guideline to its interpretation and the rules are very biased to allowing so-called optimizations. "junk" does not fit with being shared across processes (for example its address is not handed to anything) and is not static or even global. There is no known type of potential context for specific hardware details that would need to be taken into account for junk. That in turn leaves open not accessing it at all as far as I can tell. =3D=3D=3D Mark Millard markmi at dsl-only.net
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?DC2DA938-6A07-4CB0-AFB6-038368971B77>