[comp.lang.c] C optimizer

jwp@larry.UUCP (Jeffrey W Percival) (02/14/89)

I have a question about how much optimizing I should worry about when
writing programs in C.  Suppose I have this code fragment:

	x = (1 + cos(r)) / (cos(r) * sin(r));
	y = (cos(r) - sin(r)) / (1 + sin(r));

I made this up, but the point is the re-use of the sin() and cos()
calls.  Now, can I expect the compiler to form only one call to sin and
cos?  Specifically, I am using ULTRIX 2.1 on a VS2000, but my real
question is, would *most* compilers optimize this as I'd hope, or
*some* compilers, or hardly any?  I guess I'm looking for a gut feeling
on the general situation rather than in this specific example.  If a
function is used several times in a routine with an argument that
doesn't get assigned to, is the result generally stashed somewhere?
And what are the types of operations that interfere with an ongoing
optimization, causing calls to be re-made rather than pulled from some
stash?
-- 
Jeff Percival (jwp@larry.sal.wisc.edu)

earleh@eleazar.dartmouth.edu (Earle R. Horton) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>I have a question about how much optimizing I should worry about when
>writing programs in C.  Suppose I have this code fragment:
>
>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));
>
>I made this up, but the point is the re-use of the sin() and cos()
>calls.  Now, can I expect the compiler to form only one call to sin and
>cos?

     No.  The compiler has no way of knowing what sin() and cos() do,
besides obviously returning a result.  Either or both might have side
effects, and optimizing out any of the calls prevents the side effects
from happening.

Earle R. Horton. 23 Fletcher Circle, Hanover, NH 03755--Graduate student.
He who puts his hand to the plow and looks back is not fit for the kingdom
of winners.  In any case, 'BACK' doesn't work.

hascall@atanasoff.cs.iastate.edu (John Hascall) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>I have a question about how much optimizing I should worry about when
>writing programs in C.  Suppose I have this code fragment:

>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));

>I made this up, but the point is the re-use of the sin() and cos()
>calls.  Now, can I expect the compiler to form only one call to sin and
>cos?  ...

   I think it will/should call the function each time it appears as
   the compiler has no way of knowing the "side-effects" of a
   function call (i.e., file operations, accessing global variables, etc).

   Imagine:

	   x = (1 + printf(s)) / printf(s);
	   y = printf(s) / (1 - printf(s));

   (admittedly a hokey example--but you get the point...)

   John Hascall
   ISU Comp Center

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));
>Now, can I expect the compiler to form only one call to sin and cos?

This is an interesting issue.  In traditional C implementations, since
there was no guarantee that the functions wouldn't have side effects
(after all, you might have provided your own definitions for them),
the functions had to be called multiple times to make sure that any
side effects were properly performed.  In a (proposed) ANSI C hosted
environment, these functions are known to be provided by the
implementation and to conform to the Standard; therefore a sufficiently
clever optimizer could take advantage of the fact that they're known to
be so-called "pure" functions to avoid calling them multiple times with
the same argument.  I don't know of any implementations that perform
this particular optimization, but I suspect the supercomputer folks
will be doing it.

There is no standard mechanism for an application to declare its own
functions as "pure"; however, if the compiler has access to all files
it may be able to make such a determination itself.

Note also that ANSI C implementations can
	#define	cos(x)	__intrinsic_cos(x)
or something along those lines, to allow the compiler to generate
in-line code rather than an actual function call for standard
functions.  There actually is floating-point hardware with SQRT
support; I don't know about COS.

karl@haddock.ima.isc.com (Karl Heuer) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));

Most compilers will *not* optimize this, because they don't recognize that
sin() and cos() are pure functions.  (Clearly it would be an invalid
optimization to do this in general, because of the existence of impure
functions such as getchar() and rand().)

In this case, the optimization would be legal, if the compiler takes the
trouble to observe that the library functions sin() and cos() are pure.  It
could do the same thing with any user-defined functions that are visible and
whose purity can be proven at compile-time (or, perhaps, whose purity is
hinted at with a #pragma).  I don't know of any compilers that are this smart.

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

tim@crackle.amd.com (Tim Olson) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
| 
| 	x = (1 + cos(r)) / (cos(r) * sin(r));
| 	y = (cos(r) - sin(r)) / (1 + sin(r));
| 
| I made this up, but the point is the re-use of the sin() and cos()
| calls.  Now, can I expect the compiler to form only one call to sin and
| cos?  Specifically, I am using ULTRIX 2.1 on a VS2000, but my real
| question is, would *most* compilers optimize this as I'd hope, or
| *some* compilers, or hardly any?

I think that very few compilers currently perform this optimization. 
The problem is that (pre-ANSI) compilers usually have no knowledge about
standard library routines -- they look just like other function calls. 
Therefore, the compiler does not know that sin() or cos() are "true
functions", in that they will return the same values for the same
inputs, and have no side-effects.

ANSI-conforming C compilers will be able to perform this optimization,
since the standard library routine names are reserved, and the compiler
can have specific knowledge about them.

I know of at least one C compiler that is tracking the proposed ANSI
standard that will perform this optimization.

	-- Tim Olson
	Advanced Micro Devices
	(tim@crackle.amd.com)

mball@cod.NOSC.MIL (Michael S. Ball) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));
>Now, can I expect the compiler to form only one call to sin and cos?

In K&R C this is impossible, as many have mentioned.  The draft ANSI standard
makes it possible, though not too many take advantage of it.  The Oregon
Software C & C++ compiler uses the trick proposed in the standard and uses
a macro to convert the math functions to built-in functions.  These are known
to be pure and are so optimized.  In fact, if the target machine has a
math chip the simple math functions are compiled inline as single instructions.

-Mike Ball-
TauMetric Corporation
1094 Cudahy Pl. Ste 302
San Diego, CA 92110
(619)275-6381
mball@cod.nosc.mil

leo@philmds.UUCP (Leo de Wit) (02/14/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
|I have a question about how much optimizing I should worry about when
|writing programs in C.  Suppose I have this code fragment:
|
|	x = (1 + cos(r)) / (cos(r) * sin(r));
|	y = (cos(r) - sin(r)) / (1 + sin(r));
|
|I made this up, but the point is the re-use of the sin() and cos()
|calls.  Now, can I expect the compiler to form only one call to sin and
|cos?

Try rand() instead of sin(), cos(). Now, you wouldn't want the compiler
to form only one call to rand(), would you?

    Leo.

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (02/14/89)

In article <9648@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:

| This is an interesting issue.  In traditional C implementations, since
| there was no guarantee that the functions wouldn't have side effects
	...

| ...						    ... a sufficiently
| clever optimizer could take advantage of the fact that they're known to
| be so-called "pure" functions to avoid calling them multiple times with
| the same argument.  I don't know of any implementations that perform
| this particular optimization, but I suspect the supercomputer folks
| will be doing it.

  I agree. One solution would be to provide a keyword, perhaps something
like 'pure' or 'deterministic', which would indicate that a procedure
always returns the same value for a set of given arguments. Note that
this is not the same thing a "no side effects," my intent is only that a
second call with the same arguments would not change the side effects
(such as saving one of the arguments, etc).

  There must be a good mathmatical term for this, 'pure' has too many
overloaded meanings, and 'deterministic' is far too long. I am not sure
that having the compiler "know about" library functions is a good idea,
since the user could provide functions with identical names if the
compile were to be used in a non-hosted application.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

mcdonald@uxe.cso.uiuc.edu (02/14/89)

>I have a question about how much optimizing I should worry about when
>writing programs in C.  Suppose I have this code fragment:

>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));

>I made this up, but the point is the re-use of the sin() and cos()
>calls.  Now, can I expect the compiler to form only one call to sin and
>cos? 

Doug Gwyn has already posted the answer to part of this: according
to ANSI C, the compiler "can" do it to save time. (In general
we can assume his answers supercede most others, at least for 
ANSI compilers).
 
According to my just-performed test, the Microsoft C 5.1 DOES do it. 

In fact, if the compiler had an 80386/80387 mode 
it probably should just inline them, as there are machine
instructions for sin and cosine, as well as sin AND cos (in one 
instruction) in an 80387 (but NOT 8087, which does have tan). The
book says FSINCOS may not be as accurate as FSIN or FCOS, though.
This answers the query in Gwyn's posting.

Doug McDonald

henry@utzoo.uucp (Henry Spencer) (02/14/89)

In article <9648@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>... to allow the compiler to generate
>in-line code rather than an actual function call for standard
>functions.  There actually is floating-point hardware with SQRT
>support; I don't know about COS.

You betcha; the 80x87 and the 6888x implement everything including the
kitchen sink, notably the trig functions.  Never mind the mundane stuff
like cos():  the 68881 has a hyperbolic-arctangent instruction!

Of course, the newer and faster FPUs like the MIPS one don't have all
this gingerbread, so who knows what next year's FPUs will look like...
-- 
The Earth is our mother;       |     Henry Spencer at U of Toronto Zoology
our nine months are up.        | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

bowles@eris.berkeley.edu (Jeff A. Bowles) (02/15/89)

In article <13134@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>  I agree. One solution would be to provide a keyword, perhaps something
>like 'pure' or 'deterministic', which would indicate that a procedure
>always returns the same value for a set of given arguments. Note that
>this is not the same thing a "no side effects," my intent is only that a
>second call with the same arguments would not change the side effects
>(such as saving one of the arguments, etc).

One language I read about in ages past had the notion of function versus
procedure as defined below:
	function: Like a mathematical function, is strictly a function of
		  its arguments. (Perhaps the arguments might be put through
		  a "known" transformation, like a table lookup, but this
		  idea tends to support the notion that a set of inputs gives
		  the same output, consistently.)
	subroutine: fair game.

Now, in C, it's harder than you think - because if you pass a pointer to
a procedure, it's difficult to keep the procedure from modifying data to
which the argument points. Notions like "const" and function prototypes
make it a little easier to decide what a procedure DOESN'T do, but it's
pretty hard to tell if a procedure will fit the definition of a "function"
above.

	Jeff Bowles
case 

evil@arcturus.UUCP (Wade Guthrie) (02/15/89)

In article <515@larry.UUCP>, jwp@larry.UUCP (Jeffrey W Percival) writes:
 
> my real question is, would *most* compilers optimize this . . .

While we're talking about optimizers (and, since all the furor about
optimizing one's code has died down :->), what do most optimizers on
most C compilers do?  Where is TFM that I should be 'R'ing (and in which
FM should I look?)  I would expect the following optimizations:

	1) constant expression reduction
	2) loop inversion
	3) reducing things like intval/2 == intval >> 1 when they are
	   faster.  I actually saw a programmer write something
	   LAST YEAR that used intval >> n instead of dividing.  Scary.

What else should I expect?  From what compilers?  On what machines?
In short, what should I do myself and what should I let the *general*
optimizer/compiler do?


Wade Guthrie
evil@arcturus.UUCP
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

karl@haddock.ima.isc.com (Karl Heuer) (02/15/89)

In article <13134@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>One solution would be to provide a keyword ... which would indicate that a
>procedure always returns the same value for a set of given arguments.  Note
>that this is not the same thing a "no side effects," my intent is only that a
>second call with the same arguments would not change the side effects (such
>as saving one of the arguments, etc).

If the concept is to be useful, it had better mean "no observable side effects
AT ALL".  The (IMHO) useful optimization of
	extern int __pure abs();
	x = abs(t);  y = abs(u);  z = abs(t);  return (z-x);
into
	return (0);
seems to be forbidden by your weaker form.

>I am not sure that having the compiler "know about" library functions is a
>good idea, since the user could provide functions with identical names if the
>compile were to be used in a non-hosted application.

No problem; such a compiler, when used in freestanding mode, simply disables
such knowledge.  This is automatic if the "magic" is embedded in the standard
header files, via a keyword.

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

mesard@bbn.com (Wayne Mesard) (02/15/89)

In article <9648@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>There is no standard mechanism for an application to declare its own
>functions as "pure"; however, if the compiler has access to all files
>it may be able to make such a determination itself.

Anyone want to challenge (by way of counter-example) the hypothesis that

  A function which is entirely composed of known "pure" operations*
  is, itself, pure.

* Where pure operations is defined as functions that produce no side
effects and return deterministic values, and most operators (excluding
assignment to dereferenced pointers and globals).  Hint: Sleep(3) would,
I believe, be labelled "pure" under this definition, so something's
still missing.

-- 
unsigned *Wayne_Mesard();
MESARD@BBN.COM           
BBN, Cambridge, MA       

jnh@ece-csc.UUCP (Joseph Nathan Hall) (02/16/89)

In article <954@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>|I have a question about how much optimizing I should worry about when
>|writing programs in C.  Suppose I have this code fragment:
>|
>|	x = (1 + cos(r)) / (cos(r) * sin(r));
>|	y = (cos(r) - sin(r)) / (1 + sin(r));
>|
>|I made this up, but the point is the re-use of the sin() and cos()
>|calls.  Now, can I expect the compiler to form only one call to sin and
>|cos?
>
>Try rand() instead of sin(), cos(). Now, you wouldn't want the compiler
>to form only one call to rand(), would you?
>
Now, you know, this is an interesting point.  The functions (call them
"pure" or "mathematical") that can be optimized in the fashion of redundant
subexpressions have to depend SOLELY on their inputs, and furthermore
(this is the point) can't have any "memory" of previous state.

I think language support for pure functions would be very useful, personally.
No static variables, no references to external variables (other than other
pure functions).

The function rand() would be OK if it required a seed value as a parameter.





-- 
v   v sssss|| joseph hall                      || 201-1D Hampton Lee Court
 v v s   s || jnh@ece-csc.ncsu.edu (Internet)  || Cary, NC  27511
  v   sss  || the opinions expressed herein are not necessarily those of my
-----------|| employer, north carolina state university . . . . . . . . . . . 

rob@kaa.eng.ohio-state.edu (Rob Carriere) (02/16/89)

In article <36034@bbn.COM> mesard@BBN.COM (Wayne Mesard) writes:
>  A function which is entirely composed of known "pure" operations*
>  is, itself, pure.
>
>* Where pure operations is defined as functions that produce no side
>effects and return deterministic values, and most operators (excluding
>assignment to dereferenced pointers and globals).  Hint: Sleep(3) would,
>I believe, be labelled "pure" under this definition, so something's
>still missing.

What seems to be missing is the idea that sleep *does* modify
something, namely time.  So formally speaking, your compiler should
consider sleep to have a side effect on a variable called __time.  If
you do that, there's no problem.

SR

bright@Data-IO.COM (Walter Bright) (02/16/89)

In article <515@larry.UUCP> jwp@larry.UUCP (Jeffrey W Percival) writes:
>I have a question about how much optimizing I should worry about when
>writing programs in C.
>	x = (1 + cos(r)) / (cos(r) * sin(r));
>	y = (cos(r) - sin(r)) / (1 + sin(r));
>Now, can I expect the compiler to form only one call to sin and
>cos?

In general, function calls are *not* regarded as common subexpressions,
since the compiler usually knows nothing about the function, and it
may have side effects.

The cases where func() *might* be done only once are:
1) If func() is an inline function, like in C++, and the compiler
   recognizes the expansions as being common.
2) If func() is a compiler built-in function, so the compiler knows that
   there are no side effects, and so it's common.
3) If func() is a compiler built-in function, and the
   target machine implements func() as a small number of machine instructions,
   and a peephole optimizer in the code generator commons it.
Obviously, if func() is a user-defined function, and its implementation is
in another file, there is no way that the compiler can recognize it as
common.

Of course, the best way to determine if the compiler makes it common
is to compile it and see!

thor@stout.ucar.edu (Rich Neitzel) (02/16/89)

In article <3684@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>While we're talking about optimizers (and, since all the furor about
>optimizing one's code has died down :->), what do most optimizers on
>most C compilers do?  Where is TFM that I should be 'R'ing (and in which
>FM should I look?)...
>
>What else should I expect?  From what compilers?  On what machines?
>In short, what should I do myself and what should I let the *general*
>optimizer/compiler do?
>
In addition, should one let the compiler do optimization? This may not
be important in most cases, but for others it can destroy the
functioning of the code. For example, many external devices have
control/status registers that are cleared only when read. An optimizer
might see code:

	junk = *csr;

and since junk is never used again, remove that line. Or how about this:

	while (i < length)
		{
		while (!(*csr & 0x80));
		*storage++ = *data_reg;
		}

This loop polls on a done bit and then reads the new data in the data
register. An optimizer might decide that since data_reg does not SEEM
to change, the value it points to can be put into a register and
copied from there on each pass. 

Optimizing is an uncertain tool unless one knows what the optimizer is
doing. Since RISC cpus use heavily optimizing compilers and have
assemblers that are nearly impossible to follow, how does one make
certain that the optimizer hasn't shafted you? (On my 68020 I can peek
at the assembler output.) Even on CISC machines, perhaps compiler
vendors should supply a OPT_ON and OPT_OFF set of directives.
-------------------------------------------------------------------------------

			Richard Neitzel
			National Center For Atmospheric Research
			Box 3000
			Boulder, CO 80307-3000
			303-497-2057

			thor@thor.ucar.edu

    	Torren med sitt skjegg		Thor with the beard
    	lokkar borni under sole-vegg	calls the children to the sunny wall
    	Gjo'i med sitt shinn		Gjo with the pelts
    	jagar borni inn.		chases the children in.



-------------------------------------------------------------------------------

chris@mimsy.UUCP (Chris Torek) (02/16/89)

In article <1420@ncar.ucar.edu> thor@stout.ucar.edu (Rich Neitzel) writes:
>In addition, should one let the compiler do optimization? This may not
>be important in most cases, but for others it can destroy the
>functioning of the code. For example, many external devices have
>control/status registers that are cleared only when read.

This sort of thing is why `volatile' is in the pANS.  I suppose I
should put out another rerun:

Path: mimsy!chris
From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.lang.c
Subject: volatile: a summary
Message-ID: <11837@mimsy.UUCP>
Date: 7 Jun 88 06:58:00 GMT
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742
Lines: 87

With any luck, this will be my last article on this topic.

READ THE WHOLE THING BEFORE YOU FLAME.

	What is `volatile'?

Two things.  It is a keyword in the draft proposed ANSI C standard, and
it is an attribute of some kinds of variables.  As an attribute, it can
be applied to objects such as device registers or shared memory
regions, but not to `ordinary memory'.  The attribute basically means
that there is a hidden entity that may either change values stored in
what looks like ordinary memory, or take special actions when values
are stored, or both.  As a keyword, it can be applied in various places
within declarations to label variables (including pointer targets) as
having the volatile attribute.

	Is volatile necessary?

This question must be broken down to parts before it can be answered.
Is the concept necessary?  Is the keyword necessary?  Is the keyword
useful?  Is the keyword a `good thing'?

	Is the concept necessary?  That is, is the `volatility attribute'
	a real thing?

Not always.  Many applications have no need for the concept.  Programs
that compute Fibonacci numbers, or that list files, or do any number
of useful things may never care about it.  It can be vital to others,
such as device drivers.

	Is the keyword necessary?

No.  (As an existence proof, consider compilers that do not now have
the keyword.  Most of them work by not doing much in the way of
optimisation.  There is more to it, though.)

A perfect compiler would know as much as all its programmers combined,
and could use whatever rules those programmers would use to detect
`real volatility'.  Such a compiler would always know when a variable
should have the volatility attribute.  Of course, no perfect compilers
exist, nor are any likely to be written in the foreseeable future.  But
good compilers do exist, and better ones are conceivable; a very good
compiler could use external information to find most (but not all)
variables that are certain *not* to have the attribute, and could then
assume that all others do, and this would suffice.

(Note that computing `real volatility' can be as hard as solving the
halting problem.  I claim only that the compiler can use the same rules
that a programmer would use.  If the compiler errs only on the side of
caution---attaching `real volatility' where it is unnecessary---this is
safe, if not perfectly optimal.)

	Given that the keyword is not absolutely necessary,
	is it of any use?

Yes.  If the keyword exists, compilers are free to assume that any
variable not tagged by the keyword is truly not volatile.  This is a
very easy way to discover which variables should be considered to have
the attribute, and, knowing which variables are not volatile, the
compiler can make a number of optimisations that might otherwise be
unsafe.  This is not without its cost:  With such a compiler, the
programmer must correctly label any variables which do have the
volatility attribute.  But because the scheme is simple, compilers that
use it can be faster than, and need not be as complex as, compilers
that attempt to find `real volatility' with less help from the
programmer.  Being less complex, such compilers are more likely to
be correct; being faster, they are more pleasant to use.

	Is the keyword a `good thing', even given a near-perfect
	compiler, one that can optimise correctly even without the
	keyword?

I think it is.  A certain amount of redundancy helps to avoid errors.
A good-but-not-quite-perfect compiler might compute its set of volatile
declarations, then compare it with the programmer's, and warn of any
mismatch.  This would help find errors in both the compiler's rules and
the programmer's.  Too much redundancy is obscuring, but real
volatility is rare enough that I believe this will not be a problem.

    Summary of the summary:
	Is the concept necessary?	Yes.
	Is the keyword necessary?	No.
	Is the keyword useful?		Yes.
	Is the keyword a `good thing'?	Opinion: yes.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

w-colinp@microsoft.UUCP (Colin Plumb) (02/16/89)

karl@haddock.ima.isc.com (Karl Heuer) wrote:
> In article <13134@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
> >One solution would be to provide a keyword ... which would indicate that a
> >procedure always returns the same value for a set of given arguments.
> 
> If the concept is to be useful, it had better mean "no observable side effects
> AT ALL".

> This is automatic if the "magic" is embedded in the standard
> header files, via a keyword.

Um... Karl, what strange disease has come over your brain to make it suddenly
like adding keywords to C? :-)

This is a perfect example of a good use for #pragma.  In <math.h>, I could
just include:

#pragma functional
double sin(double), cos(double);

And the compiler could take that as a hint to use common subexpression
elimination (a pretty common optimisation) on sin and cos.  A good
compiler already knows that things like + and ~ are purely functional,
and need only generalise the common subexpression and dead-code eliminators
to handle more conventional function calls.

This is also an example of the point of having a compiler ignore an
unrecognised #pragma.  Without this #pragma, the code would compute the
same result, just more wastefully.
-- 
	-Colin (uunet!microsoft!w-colinp)

"Don't listen to me.  I never do."

karl@haddock.ima.isc.com (Karl Heuer) (02/16/89)

In article <3684@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>I would expect the following optimizations: ... 3) reducing things like
>intval/2 == intval >> 1 when they are faster.

On most implementations these are not equivalent, unless the compiler can
prove that intval >= 0.

(Ah, many's the time I've wished I could declare a type `nonnegative', which
would be the intersection of signed and unsigned, so the compiler could use
whichever happened to be faster...)

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

djones@megatest.UUCP (Dave Jones) (02/16/89)

From article <1398@quanta.eng.ohio-state.edu>, by rob@kaa.eng.ohio-state.edu (Rob Carriere):

> 
> What seems to be missing is the idea that sleep *does* modify
> something, namely time.  So formally speaking, your compiler should
> consider sleep to have a side effect on a variable called __time.  If
> you do that, there's no problem.
> 


Huh?? 

% nm a.out | grep __time
%

Know what I mean?

On a time-shared system (such as Unix), does every instruction have
a side effect on a variable called __time?

Gimme a break.  (Or at least a sleep(300).)

peter@ficc.uu.net (Peter da Silva) (02/17/89)

In article <36034@bbn.COM>, mesard@bbn.com (Wayne Mesard) writes:
>   A function which is entirely composed of known "pure" operations*
>   is, itself, pure.

> * Where pure operations is defined as functions that produce no side
> effects and return deterministic values, and most operators (excluding
> assignment to dereferenced pointers and globals).

Sounds good.

> Hint: Sleep(3) would,
> I believe, be labelled "pure" under this definition, so something's
> still missing.

No, since I don't think *any* system call is definable as a pure function.
In this case alarm() returns a nondeterministic value *and* produces the
side effect of setting a timer in your process table entry.

Can anyone think of any pure system calls? ioctl(fd, TCGETA, tbuf)?
No, I think a system call should be considered not only a global ref
but a volatile one!
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.   `-_-'
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.                 'U`
People have opinions. Companies have policy. And typos are my own business.

rob@kaa.eng.ohio-state.edu (Rob Carriere) (02/17/89)

In article <2008@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>From article <1398@quanta.eng.ohio-state.edu>, by rob@kaa.eng.ohio-state.edu 
(Rob Carriere):
>> What seems to be missing is the idea that sleep *does* modify
>> something, namely time.  So formally speaking, your compiler should
>> consider sleep to have a side effect on a variable called __time.  If
>> you do that, there's no problem.
>Huh?? 
>% nm a.out | grep __time
>Know what I mean?

Perfectly.  Do you know what I mean?  Note the phrase ``formally
speqaking'' in the above and insert ``for example'' after ``called''.

>On a time-shared system (such as Unix), does every instruction have
>a side effect on a variable called __time?

Of course.  And on any non-time shared system as well.  The poster to
whom I was replying considered the passage of time a side effect.  If
that is the case, you'd bloody well better model it as part of the
system state.

>Gimme a break.  (Or at least a sleep(300).)

Breaks are free() this week :-)

SR

msb@sq.com (Mark Brader) (02/19/89)

> >I would expect the following optimizations: ... 3) reducing things like
> >intval/2 == intval >> 1 when they are faster.
> 
> On most implementations these are not equivalent, unless the compiler can
> prove that intval >= 0.

Quite right, except that it also generally works if the compiler can prove
that intval is a multiple of the divisor.

However, I have met one compiler that always did the optimization anyway!
Accordingly, the expressions a/2 and a/b produced *different answers*
when a was negative and b was equal to 2!

(No, I don't remember which compiler it was.  It was a machine we were
porting product to, but I wasn't doing the port myself, I just came in to
try to identify why my code was producing wrong answers.)

It may be noted that the values produced by / and % on negative numbers,
where the dividend is not a multiple of the divisor, are not fully
defined by the language, neither in the K&R 1 nor the proposed ANSI versions.
This is because there are three identities that are desirable in different
situations, namely

	[1]	(-a)/b, a/(-b), -(a/b) are all equal
	[2]	a%b ranges from 0 to b-1 for positive b
	[3]	(a/b) * b  equals  a - a%b

But it isn't possible to satisfy all three.  The language does guarantee
[3].  Of the others, most implementations choose [1], to the annoyance
of mathematical users to whom [2] would be much more preferred; that's
because it's the way the underlying hardware typically behaves.

Now, it turns out that on a 2's complement machine that chooses to sign-
extend on >> (also implementation-defined) *and* which elects to support
identity [2], *then* the optimization described above *is* possible.

Oh -- I should also mention that in proposed ANSI C the library includes
two functions div() and ldiv(), which implement (for ints and longs
respectively) a form of / and % that is guaranteed to support identities
[1] and [3].  In ANSI C the compiler is allowed to optimize these so that
they do not become actual function calls.

Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com
	A standard is established on sure bases, not capriciously but with
	the surety of something intentional and of a logic controlled by
	analysis and experiment. ... A standard is necessary for order
	in human effort.				-- Le Corbusier

scs@adam.pika.mit.edu (Steve Summit) (02/19/89)

In article <13134@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>  I agree. One solution would be to provide a keyword, perhaps something
>like 'pure' or 'deterministic', which would indicate...

Just Say No to new keywords.  This sort of optimization is a fine
idea, and it does require a hint to the compiler, for the reasons
already discussed.  However, outside-the-language mucking around
is exactly what #pragmas are for:

	#pragma pure(sin)

(Someone, probably Karl Heuer, already made passing reference to
the use of #pragmas for this example.)  Extra keywords are messy,
even if they do follow the rules and use leading underscores.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

gateley@m2.csc.ti.com (John Gateley) (02/20/89)

In article <1416@quanta.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:
<In article <2008@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
<>On a time-shared system (such as Unix), does every instruction have
<>a side effect on a variable called __time?

<Of course.  And on any non-time shared system as well.  The poster to
<whom I was replying considered the passage of time a side effect.  If
<that is the case, you'd bloody well better model it as part of the
<system state.

The definition of sleep involves passage of time, but the definition
of everything else does not. So, even though sleep has a side effect,
if, while, for, etc. do not have the same side effect (though they
may have others).

John
gateley@tilde.csc.ti.com

karl@haddock.ima.isc.com (Karl Heuer) (02/22/89)

In article <667@microsoft.UUCP> w-colinp@microsoft.uucp (Colin Plumb) writes:
>karl@haddock.ima.isc.com (Karl Heuer) wrote:
>>This is automatic if the "magic" is embedded in the standard
>>header files, via a keyword.
>
>Um... Karl, what strange disease has come over your brain to make it suddenly
>like adding keywords to C? :-)

I meant this to include not only normal keywords (in the reserved namespace)
but also #pragmas.  However, using #pragma for this is subject to some of the
same objections as using it for volatile: does
	extern int foo(int, double __pure (*)(double));
have a #pragma equivalent?

Btw, it would also be possible to use the existing keyword `const' for this.
(I'm sure someone else will mention this if I don't.)  It remains unclear
whether this would be any less of a mistake than the overloading of `static'
and `break'.

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

john@frog.UUCP (John Woods) (02/23/89)

In article <9648@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> Note also that ANSI C implementations can
> 	#define	cos(x)	__intrinsic_cos(x)
> or something along those lines, to allow the compiler to generate
> in-line code rather than an actual function call for standard
> functions.  There actually is floating-point hardware with SQRT
> support; I don't know about COS.

The 68881 chip has FCOS, FSIN, and (gasp) FSINCOS which simultaneously
computes cos() and sin() of the same argument.  A sufficiently clever
compiler could potentially make great use of that.
 
-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"He should be put in stocks in Lafeyette Square across from the White House
 and pelted with dead cats."	- George F. Will

john@frog.UUCP (John Woods) (02/23/89)

In article <3121@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
> Can anyone think of any pure system calls? ioctl(fd, TCGETA, tbuf)?

I think that if

	(getpid() != getpid())

ever evaluated to 1, I would be severely astonished.

-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"He should be put in stocks in Lafeyette Square across from the White House
 and pelted with dead cats."	- George F. Will

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/23/89)

In article <1026@frog.UUCP> john@frog.UUCP (John Woods) writes:
>The 68881 chip has FCOS, FSIN, and (gasp) FSINCOS which simultaneously
>computes cos() and sin() of the same argument.  A sufficiently clever
>compiler could potentially make great use of that.

Well, hurray -- I've wanted that for years!  I do hope C implementors
will perform this optimization; it could help us computer-graphics types
quite a bit.

lvc@cbnews.ATT.COM (Lawrence V. Cipriani) (02/24/89)

In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>I think that if
>
>	(getpid() != getpid())
>
>ever evaluated to 1, I would be severely astonished.

How about if the left hand getpid() is called, and before the right hand
getpid() is called a signal comes in, causing a signal handler to be called,
then the signal handler does a fork.  Then, the interrupt handler returns
and the right hand getpid() is called.  Voila!
-- 
Larry Cipriani, att!cbnews!lvc or lvc@cbnews.att.com

karl@haddock.ima.isc.com (Karl Heuer) (02/24/89)

In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>In article <3121@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
>>Can anyone think of any pure system calls?
>
>[What about getpid()?]

Nope.
	register int i = getpid();
	if (fork() != 0) exit(0);
	if (getpid() != i) printf("impure!\n");

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

barmar@think.COM (Barry Margolin) (02/24/89)

In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>I think that if
>	(getpid() != getpid())
>ever evaluated to 1, I would be severely astonished.

Well, how about

	(pid = getpid(), (void) fork(), pid != getpid())

?  It will evaluate to 0 in the parent process, 1 in the child
process.

Since fork() changes the value of getpid(), getpid() isn't pure.  But
it would be wrong to make fork() always prevent common subexpression
elimination, since it doesn't modify any of the program's own
variables.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) (02/24/89)

In article <3121@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
> Can anyone think of any pure system calls? ioctl(fd, TCGETA, tbuf)?

trivial examples are getdtablesize() or getpagesize() from bsd,
at least in the absence of process migration for heterogenous
operating system versions (a safe assumption in 1989).
one might want to consider _exit() pure, as there can be no
second call, but it's largely irrelevant to optimization.

now, who cares?

khera@juliet.cs.duke.edu (Vick Khera) (02/25/89)

In article <9693@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <1026@frog.UUCP> john@frog.UUCP (John Woods) writes:
>>The 68881 chip has FCOS, FSIN, and (gasp) FSINCOS which simultaneously
>>computes cos() and sin() of the same argument.  A sufficiently clever
>>compiler could potentially make great use of that.
>
>Well, hurray -- I've wanted that for years!  I do hope C implementors
>will perform this optimization; it could help us computer-graphics types
>quite a bit.

The Sun 3 C compiler will use the intrinsic functions of the 68881 chip if
you tell it to compile with the f68881.il inline-expansion file. i beilieve
it is located in /usr/lib/f68881/libm.il under 4.0, and in
/usr/lib/f68881.il in 3.x.

example compile line:

cc -o prog prog.c /usr/lib/f68881.il

						vick.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ARPA:	khera@cs.duke.edu		Department of Computer Science
CSNET:	khera@duke        		Duke University
UUCP:	decvax!duke!khera		Durham, NC 27706

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/25/89)

In article <4276@cbnews.ATT.COM> lvc@cbnews.ATT.COM (Lawrence V. Cipriani) writes:
-How about if the left hand getpid() is called, and before the right hand
-getpid() is called a signal comes in, causing a signal handler to be called,
-then the signal handler does a fork.  Then, the interrupt handler returns
-and the right hand getpid() is called.  Voila!

Anyone who fork()s then returns inside a signal handler deserves to suffer.

lvc@cbnews.ATT.COM (Lawrence V. Cipriani) (02/26/89)

In article <9705@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
! In article <4276@cbnews.ATT.COM> lvc@cbnews.ATT.COM (Lawrence V. Cipriani) writes:
! -How about if the left hand getpid() is called, and before the right hand
! -getpid() is called a signal comes in, causing a signal handler to be called,
! -then the signal handler does a fork.  Then, the interrupt handler returns
! -and the right hand getpid() is called.  Voila!
! 
! Anyone who fork()s then returns inside a signal handler deserves to suffer.

Yes, but so what.  The question basically was "Is getpid() != getpid() ever
true."  It was not restricted to what was reasonable or common coding practice,
or sanctioned by the pANS, or your personal preference, or anything else.  It
isn't necessary to say something is a bad idea when that wasn't asked, so I
didn't.
-- 
Larry Cipriani, att!cbnews!lvc or lvc@cbnews.att.com

richard@aiai.ed.ac.uk (Richard Tobin) (02/28/89)

>one might want to consider _exit() pure

Yeah, that's right, the compiler can put in a call to it at the start
of the program, and save the value it returns :-)

-- Richard
-- 
Richard Tobin,                         JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,             ARPA:  R.Tobin%uk.ac.ed@nss.cs.ucl.ac.uk
Edinburgh University.                  UUCP:  ...!ukc!ed.ac.uk!R.Tobin

dg@lakart.UUCP (David Goodenough) (03/01/89)

From article <36662@think.UUCP>, by barmar@think.COM (Barry Margolin):
] In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
]>I think that if
]>	(getpid() != getpid())
]>ever evaluated to 1, I would be severely astonished.
] 
] Well, how about
] 
] 	(pid = getpid(), (void) fork(), pid != getpid())
] 
] ?  It will evaluate to 0 in the parent process, 1 in the child
] process.

True - but I don't see any references to fork() in Mr.Woods' posting.
What he is stating is that _IN THE ABSENCE_ of fork() calls, getpid()
had better return an unchanging value.
-- 
	dg@lakart.UUCP - David Goodenough		+---+
						IHS	| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@xait.xerox.com		  	  +---+

john@frog.UUCP (John Woods) (03/03/89)

In article <453@lakart.UUCP>, dg@lakart.UUCP (David Goodenough) writes:
O> From article <36662@think.UUCP>, by barmar@think.COM (Barry Margolin):
O> ] In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
P> ]>I think that if
S> ]>	(getpid() != getpid())
 > ]>ever evaluated to 1, I would be severely astonished.
O> ] Well, how about
O> ] 	(pid = getpid(), (void) fork(), pid != getpid())
P> True - but I don't see any references to fork() in Mr.Woods' posting.
S> What he is stating is that _IN THE ABSENCE_ of fork() calls, getpid()
 > had better return an unchanging value.

No, what I was stating was that getpid() was a pure function.  Pure functions
are supposed to be pure functions even in the presense of other function calls.
It is that property which allows them to be aggressively optimized.  It is
obvious that I was mistaken.  As I have said to the first two people to
point this out,


			oops.




-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"He should be put in stocks in Lafeyette Square across from the White House
 and pelted with dead cats."	- George F. Will

djones@megatest.UUCP (Dave Jones) (03/04/89)

From article <1056@frog.UUCP>, by john@frog.UUCP (John Woods):
>
> It is obvious that I was mistaken.
>

He says this even in response to a posting which tried to help him
weasel out, by saying, "What he *really* meant was.."

I am impressed!  


    Dave J.

mouse@mcgill-vision.UUCP (der Mouse) (03/07/89)

In article <1028@frog.UUCP>, john@frog.UUCP (John Woods) writes:
> In article <3121@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
>> Can anyone think of any pure system calls? ioctl(fd, TCGETA, tbuf)?

This really belongs in an OS-specific group.

Let's see....I just ran through the table of syscalls on this system
and getpagesize() and getdtablesize() are the only really pure syscalls
I see there.

> I think that if
> 	(getpid() != getpid())
> ever evaluated to 1, I would be severely astonished.

Signal handlers aside, that's not the point.  If I write

int pid1;
int pid2;

pid1 = getpid();
runinchild();
pid2 = getpid();

then I don't want the compiler to decide that it can rewrite the second
one to "pid2 = pid1;" because getpid() is pure - presumably
runinchild() will fork.

Here's a quick test: a function is pure if it can be replaced with a
memo-function wrapper without changing the semantics.  This is not true
of getpid() or sync(), to pick two examples that are tempting.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

mouse@mcgill-vision.UUCP (der Mouse) (03/15/89)

In article <453@lakart.UUCP>, dg@lakart.UUCP (David Goodenough) writes:
> From article <36662@think.UUCP>, by barmar@think.COM (Barry Margolin):
>> In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>>> (getpid() != getpid())  [should always be 1]
>> Well, how about
>> (pid = getpid(), (void) fork(), pid != getpid())
> True - but I don't see any references to fork() in Mr.Woods' posting.
> What he is stating is that _IN THE ABSENCE_ of fork() calls, getpid()
> had better return an unchanging value.

Sure - and in the absence of assignments, variables are constant and
can be optimized away.  The whole point of this was to find a pure
syscall, and a routine isn't pure if calling another routine can cause
it to change its return value for a given set of argument values.
Thus, fork()'s changing the return value of getpid() means that
getpid() can't be considered pure.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

bowles@eris.berkeley.edu (Jeff A. Bowles) (03/15/89)

>>> In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>>>> (getpid() != getpid())  [should always be 1]
>>> Well, how about
>>> (pid = getpid(), (void) fork(), pid != getpid())
>> True - but I don't see any references to fork() in Mr.Woods' posting.
>> What he is stating is that _IN THE ABSENCE_ of fork() calls, getpid()
>> had better return an unchanging value.
>
>Sure - and in the absence of assignments, variables are constant and
>can be optimized away.  The whole point of this was to find a pure
>syscall, and a routine isn't pure if calling another routine can cause
>it to change its return value for a given set of argument values.
>Thus, fork()'s changing the return value of getpid() means that
>getpid() can't be considered pure.

Are we still on this topic? By the bogosity above, nothing is "pure",
because you might write a function that changes the behaviour of another
function - for example,
	ftell(3S)	(tell you where your file pointer is, in file)
is pure, unless there's an fread/fwrite/fseek/fclose/freopen in the vicinity.
Now, those named functions are obviously impure, but aren't you pushing it
a little far? How about:

1. These functions are pure, i.e. the values returned will not change from
   one invocation to the next, given that the arguments didn't change:

    a function that returns the process's pid
    a function that returns the byte offset of a file pointer
    a function that returns the file descriptor corresponding to a file pointer
    (and so on)
    any function that performs arithmetic operations on its arguments and on
		the results of "pure" functions it invokes.

2. Any function that deviates by invoking an "impure" function, anywhere
   along the way, is an "impure" function. (Certain basic blocks might still
   be "pure" but the function itself isn't.

3. Any function that dereferences a pointer is best though of as "inpure"
   unless you decide how to describe "the argument passed didn't change
   in the second invocation of that function" - even if you passed a pointer
   to the same object, the pointer might be the same, but the object might
   not. This obviously limits the power within C, and makes a good argument
   for "const" and the like....


As a result,
	for (i = 0; i < 100000; i++)
		pid = getpid();

is a reasonable thing to compress to the single assignment "pid = getpid();"

	Jeff Bowles

desnoyer@Apple.COM (Peter Desnoyers) (03/21/89)

In article <21652@agate.BERKELEY.EDU> bowles@eris.berkeley.edu (Jeff A. Bowles) writes:
>>>> In article <1028@frog.UUCP> john@frog.UUCP (John Woods) writes:
>>> [various people arguing whether getpid() is pure]
>>
>Are we still on this topic? By the bogosity above, nothing is "pure",
>because you might write a function that changes the behaviour of another
>function - for example, [...]

I beg to differ. I would consider that any "pure" function that does
not take arguments to be a constant, as otherwise it cannot depend
solely on its arguments, but must return a value dependent on state
information external to the function.

How about these definitions:

(note that I am considering "implicit" results, e.g.
status = f(arguments,&result) style calls return two results.)

  pure - if f is pure and f takes arguments Ai and returns results Ri,
         then for any action B which does not modify A or R:
           R=f(A),B,R=f(A) is equiv. to R=f(A),B 
			   is equiv. to B,R=f(A)

  conditionally pure - if f is conditionally pure, then one can define
         a dependency set D such that if B is some action with does
         not modify A or R, and does not contain any elements of D,
         then R=f(A),B,R=f(A) is equiv. to R=f(A),B is equiv. to
         B,R=f(A). 

It is a lot simpler for a compiler to take advantage of pure functions
than conditionally pure ones, because it doesn't have to worry about
dependencies. 

				Peter Desnoyers

mouse@mcgill-vision.UUCP (der Mouse) (03/29/89)

In article <21652@agate.BERKELEY.EDU>, bowles@eris.berkeley.edu (Jeff A. Bowles) writes:
[many attribution lines seem to have been lost -dM]
>>>>> (getpid() != getpid())  [should always be 1]
>>>> [how about] (pid = getpid(), (void) fork(), pid != getpid())
>>> What he is stating is that _IN THE ABSENCE_ of fork() calls,
>>> getpid() had better return an unchanging value.
[this next comment was mine -dM]
>> Sure - and in the absence of assignments, variables are constant and
>> can be optimized away.  [... A] routine isn't pure if calling
>> another routine can cause it to change its return value for a given
>> set of argument values.

> Are we still on this topic? By the bogosity above, nothing is "pure",
> because you might write a function that changes the behaviour of
> another function - [...example with ftell...]

ftell() is of course not pure, as you noted, because there are
functions (fread, fseek, fwrite, fclose, etc) that can cause its return
value to change.

sin(), on the other hand, is: if I call sin() with some argument value,
and then call it again with the same value, the second call *will*
return the same value, regardless of what other routines I've called in
the meantime.  This is what I mean (and understand to be meant) by a
"pure" function.

> 1. These functions are pure, i.e. the values returned will not change
>    from one invocation to the next, given that the arguments didn't
>    change:

...and provided that certain other things don't happen between the two
invocations, generally calling certain other functions with certain
arguments, the set of forbidden functions and arguments varying with
the function being considered.

This is a much weaker property, but one which may still be useful in
some circumstances.  Which one should properly be called "pure" is a
different argument entirely (and one which I won't argue here except to
note that I'll stick with the stronger meaning).

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu