[comp.lang.c] optimizing assert

karl@haddock.ISC.COM (Karl Heuer) (01/15/88)

In article <531@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>Is there any reason why C compilers should not be allowed to assume the
>truth of assertions even in the presence of NDEBUG?

I like the idea.  There are a few problems; let's formalize.

Subproposal A: Whether or not NDEBUG is set, assert() shall always evaluate
its argument for side effects.

Rationale: Although current implementations do not conform to it, the proposal
is easy to implement ("#define assert(e) ((void)(e))") and makes assert() more
useful ("assert(getchar() == '\n')").  This should not break any existing
code, because anything which depends on the argument *not* being evaluated
will behave differently depending on whether NDEBUG is set or not.

Subproposal B: The macro shall expand into a void expression.

Rationale: This would guarantee that it is legal in an expression context,
e.g. "y = 1.0 / (assert(x!=0.0), x)".  This isn't critical, but it helps
prevent surprising behavior in some contexts.

Subproposal C: If the argument of assert() is true (compares unequal to zero),
then the macro returns no value.  Else, if the NDEBUG macro is not defined,
then a message is printed and the abort() function is invoked.  (At this point
we still agree with the dpANS; the next statement is the proposal.)  Else, the
behavior is undefined.

Rationale: One normally defines NDEBUG for optimization.  The code has been
tested without NDEBUG and verified to be correct; the programmer no longer
wants the overhead of an explicit run-time check.  If the behavior in the
third case is undefined, the compiler may be able to generate better code by
assuming that the assertion is true.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
Note: according to Walter Bright's article <1459@dataio.Data-IO.COM>, it
seems that current optimizer technology won't handle anything much more
complex than "assert(x == 3)" or "assert(x == y)".

dsill@NSWC-OAS.arpa (Dave Sill) (01/16/88)

In article <2276@haddock.ISC.COM> Karl Heuer <karl@haddock.ISC.COM> writes:
>Subproposal A: Whether or not NDEBUG is set, assert() shall always evaluate
>its argument for side effects.

Doesn't this defeat the purpose of NDEBUG?  Granted, the
diagnostic-generating code would be left out, but the expression
evaluation code would remain.

>Subproposal B: The macro shall expand into a void expression.
>
>Rationale: This would guarantee that it is legal in an expression context,
>e.g. "y = 1.0 / (assert(x!=0.0), x)".  This isn't critical, but it helps
>prevent surprising behavior in some contexts.

I may be missing something here, but I don't see where it makes much
difference what type `assert' expands to.  The example would work if
it expanded to an `int'.

>Subproposal C: If the argument of assert() is true (compares unequal to zero),
>then the macro returns no value.  Else, if the NDEBUG macro is not defined,
>then a message is printed and the abort() function is invoked.  (At this point
>we still agree with the dpANS; the next statement is the proposal.)
>Else, [assertion is false and NDEBUG is defined,] behavior is undefined.

I like this.  After all, the assertion is false...

========
The opinions expressed above are mine.

"Damn it, wine is liquid food!"
					-- Robert Mondavi

karl@haddock.ISC.COM (Karl Heuer) (01/18/88)

In article <11295@brl-adm.ARPA> dsill@NSWC-OAS.arpa (Dave Sill) writes:
>In article <2276@haddock.ISC.COM> Karl Heuer <karl@haddock.ISC.COM> writes:
>>Subproposal A: ... assert() shall always evaluate its argument ...
>
>Doesn't this defeat the purpose of NDEBUG?  ... The expression evaluation
>code would [still be generated].

If the expression has side effects, I think it's clear that the user does want
them to be evaluated.   If the expression has no side effects, the compiler is
free to generate zero instructions.  (I realize that there are some pretty bad
nonoptimizing compilers out there.  I wish there were fewer.)

>>Subproposal B: The macro shall expand into a void expression.  [To guarantee
>>that it is legal in an expression context.]
>
>I may be missing something here, but I don't see where it makes much
>difference what type `assert' expands to.

The intended emphasis was on "expression", not "void".  Some implementations
currently expand assert() into nothing at all (when NDEBUG is set) and/or a
non-expression statement (when NDEBUG is unset).  As for the type, since the
synopsis says "void assert(int)", why not require the implementation to
enforce it?  All it takes is a (void) cast, and it catches more errors.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

franka@mmintl.UUCP (Frank Adams) (01/19/88)

In article <2300@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:
>If the expression has side effects, I think it's clear that the user does want
>them to be evaluated.   If the expression has no side effects, the compiler is
>free to generate zero instructions.

This is fine as long as the assert expression calls no functions.  But
unless you in an environment which permits global optimization, which most
of us aren't, the compiler will have to make any function calls which are
used in the expression.

This would include, for example, assert(pow(x, 2) < y) -- to touch on
another recent topic of discussion.
-- 

Frank Adams                           ihnp4!philabs!pwa-b!mmintl!franka
Ashton-Tate          52 Oakland Ave North         E. Hartford, CT 06108

karl@haddock.ISC.COM (Karl Heuer) (01/21/88)

In article <2662@mmintl.UUCP> franka@mmintl.UUCP (Frank Adams) writes:
>In article <2300@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>If the expression has side effects, I think it's clear that the user does
>>want them to be evaluated.
>
>This is fine as long as the assert expression calls no functions.  ...
>[But what about] assert(pow(x, 2) < y)

Good point.  I was thinking in terms of functions with desirable side effects,
in particular things like assert(getchar() == '\n') .

Okay, world.  Do you use <assert.h>?  Do you have any/much existing code that
uses benign function calls within assert?  Is it worth "breaking"% that code
in exchange for the added benefit of being able to use arbitrary expressions
with impunity?

Send mail; I'll post a summary.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
%The proposed change wouldn't invalidate such a program, but it would probably
cause extra code to be generated/executed.

ado@elsie.UUCP (Arthur David Olson) (01/22/88)

Up to a year ago, I believed that the ANSI standard should set things up so
that the "argument" of assert was always evaluated--even when NDEBUG was
defined.  Now that I worry more about "following existing practice," I
believe that assert's argument should *not* be evaluated when NDEBUG is
defined, since most existing implementations don't evaluate it.

Which needn't stop the committee from tossing the lines

	#if defined NDEBUG
	#define demand(x)	((void) (x))
	#else /* !defined NDEBUG */
	#define demand(x)	assert(x)
	#endif /* !defined NDEBUG */

into "assert.h" (with suitable documentation).  (Failing that, you can toss
the lines into your own code.)
-- 
ado@vax2.nlm.nih.gov		ADO, VAX, and NIH are Ampex and DEC trademarks