Topic: operators without side effects


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Fri, 18 Mar 1994 17:32:56 GMT
Raw View
hopps@mmm.com (Kevin J. Hopps) writes:

>Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
>> kanze@us-es.sel.de (James Kanze) writes:
>>    jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:
>
>>    |> is there any possibilty to tell the compiler that an operator has no
>>    |> side effects so that it can do common subexpression optimization.
>
>> The 'const' modifier at the end of the member function declaration
>> tells the compiler that this member function does not modify the
>> state of the object.
>
>Is the compiler really allowed to assume this for optimization purposes?

No.  For starters, at best the compiler could only assume that the
member function does not modify the state of the object via the `this'
pointer.  The member function _could_ modify the state of the object
via a global pointer, for example.

>This seems problematic given that const-ness can be removed via casting
>within the member function.

Yes, cast-away-const does cause problems for optimization.
Under the committee's new rules, cast-away-const is definitely
not legal if the object being modified was declared const.
For example, with the committee's new rules, after

 const Object foo;
 foo.const_member_func();

the compiler can be sure that const_member_func() didn't modify `foo',
because `foo' was declared as const, and because foo's address couldn't
have been taken.  But the important const here is the const on the
object, no the const on the member function.  [Under the old rules in
the ARM, cast-away-const in this situation was legal unless the class
in question didn't have any constructors or destructors.]

I think that under the committee's new rules, cast-away-const will
still be legal so long as the object being modified wasn't originally
declared as const, although I'm not sure on this point.  If I'm right,
it would mean that `const' on a member function wouldn't be helpful
to the optimizer at all unless the object was also declared const.

>I know that there are changes afoot to make members modifiable even within
>const functions (is "mutable" the right term here?).

Yes.  Members which are declared `mutable' can be modified even within
const functions.  This removes most of the need for cast-away-const.

>Between now and when
>this keyword is understood by a compiler, what is the compiler really allowed
>to assume based on the presence of "const" on a member function?

Almost nothing.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 19 Mar 1994 17:34:02 GMT
Raw View
In article <9407803.7389@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>
>Yes, cast-away-const does cause problems for optimization.
>Under the committee's new rules, cast-away-const is definitely
>not legal if the object being modified was declared const.

 What you mean is that writing to const memory is now
not permitted, whereas it used to be if the memory was on the heap.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 19 Mar 1994 06:34:21 GMT
Raw View
esamy@supermulti.oracle.com (Patteaswaran Karivaradasamy) writes:

>hopps@mmm.com (Kevin J. Hopps) writes:
>>   Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
>>   > kanze@us-es.sel.de (James Kanze) writes:
>>   >  jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:
>
>>   >  |> is there any possibilty to tell the compiler that an operator has no
>>   >  |> side effects so that it can do common subexpression optimization.
>
>>   > The 'const' modifier at the end of the member function declaration
>>   > tells the compiler that this member function does not modify the
>>   > state of the object.
>
>>   Is the compiler really allowed to assume this for optimization purposes?
>>   This seems problematic given that const-ness can be removed via casting
>>   within the member function.
>
>You cannot remove the const-ness via casting.  That is illegal.
>When you are within a const member function, the 'this' pointer has
>the following type:
>
>  const A * const this;  // Assume A is the class name.
>  -----                  // Note the const before 'A'.
>
>Whatever 'this' is pointing to at the time, is a constant.

Wrong.  The only thing that is implied by first `const' in the type of `this'
is that what it points to can't be modified *via that pointer*,
*unless a cast is used*.  The object being pointed to does NOT need
to be a constant.

>There is
>no way to modify it through any casting or anything.

Yes there is.  The object being pointed to can be modifed
via another pointer, or it can be modified by casting `this'.

 class A *global_ptr;

 void foo(A& a) { global_ptr = &a; }

 class A {
 public:
  int i;
  void const_member_func() const {
   global_ptr->i++; // modify via another pointer
   ((A *)this)->i++; // modify using cast
  }
 };

 int main() {
  A a;
  foo(a);
  a.i = 1;
  cout << "a.i = " << a.i << endl; // outputs "a.i = 1"
  a.const_member_func();
  cout << "a.i = " << a.i << endl; // outputs "a.i = 3"
  return 0;
 }

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: hopps@mmm.com (Kevin J. Hopps)
Date: 14 Mar 1994 20:20:05 GMT
Raw View
Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
> In article <2lq613$jhe@dawn.mmm.com> hopps@mmm.com (Kevin J. Hopps) writes:
> >   Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
PK>     The 'const' modifier at the end of the member function declaration
PK>     tells the compiler that this member function does not modify the
PK>     state of the object.

KH>   Is the compiler really allowed to assume this for optimization purposes?
KH>   This seems problematic given that const-ness can be removed via casting
KH>   within the member function.

PK> You cannot remove the const-ness via casting.  That is illegal.
PK> When you are within a const member function, the 'this' pointer has
PK> the following type:
PK>   const A * const this;  // Assume A is the class name.
PK>   -----                  // Note the const before 'A'.
PK> Whatever 'this' is pointing to at the time, is a constant.  There is
PK> no way to modify it through any casting or anything.

Perhaps I don't understand what you are saying.  But from the ARM 5.4,
pp 70-71:
    A pointer to an object of a const type can be cast into a pointer to
    a non-const type.  The resulting pointer will refer to the original
    object.  An object of a const type or a reference to an object of a
    const type can be cast into a reference to a non-const type.  The
    resulting reference will refer to the original object.  The result
    of attempting to modify that object through such a pointer or reference
    will either cause an addressing exception or be the same as if the
    original pointer or reference had  referred to a non-const object.
    It is implementation dependent whether the addressing exception
    occurs.

And the annotation says:

    That is, unless the object has been stored in readonly memory the
    result of having cast away const is exactly the same as if the object
    had never been specified const.

I interpret this to mean that if I can legally write
    void A::f() const
    {
        ...
        ((A*)this)->member = new_value;
        ...
    }
and if the function returns, the member was modified and the "A" object
changed despite the "const" on f().  Further, the only way that this
function can possibly fail is if the original object was defined const
and if the compiler placed it in readonly memory.

This allows me to implement classes which have const member functions
which are "logically" const versus "physically" const.  (At times, much
more useful semantics.)  For example, I can write a File class which
caches data and maintains offsets and still have a function
    File::Read(void* buf, size_t len) const
which modifies the _object_ heavily but does not alter the _data_ within
the file that the object refers to.

KH>   I know that there are changes afoot to make members modifiable even within
KH>   const functions (is "mutable" the right term here?).  Between now and when
KH>   this keyword is understood by a compiler, what is the compiler really allowed
KH>   to assume based on the presence of "const" on a member function?

PK> The entire idea of allowing a 'const' member function is to say that
PK> it WILL NOT modify the object on which it is invoked.  This is sufficient
PK> information for the compiler to do optimizations on.
PK> I don't think anybody is trying to change the meaning of 'const' member
PK> functions.

I don't think anybody is trying to change the meaning either.  But perhaps
we each have a different understanding about what const really means.

PK> Looking back, at the history, the reason why 'const' member functions were
PK> introduced in the first place, is that they are the only member functions
PK> that you can invoke on a 'const' object itself.  i.e, you declare an object
PK> itself as a 'const' and then the only member functions you can invoke on
PK> that object are the const member functions, because they are guaranteed
PK> not to modify the state of the object.

You are correct I think.  But having "const" on a member function is a
necessary condition for allowing a call on a const object, but it is not
sufficient for knowing that the call will work.  (I think.)

PK> PS:
PK> If you strongly disagree with any of these, we can talk about it over e-mail.

Perhaps this is generally useful discussion.  I prefer to keep it here.




Author: <NKEW@ESRIN.BITNET>
Date: Tue, 22 Mar 1994 11:53:54 CET
Raw View
If I may paraphrase, the gist of this thread is "how much can a compiler
assume for the purposes of optimisation?" - answer: "const" is a weaker
condition than "no side effects".

This argues the need for a "no_side_effects" or equivalent keyword
programmers CAN use for the benefit of optimising compilers.
Maybe CONST (caps) or less confusingly FORTRAN (since this is
traditionally FORTRAN's province in numeric computation).

I am not au fait with the ARM, so perhaps this is nonsense - if so
perhaps someone would care to explain.

Nick Kew.
nick@mail.esrin.esa.it




Author: dak@messua.informatik.rwth-aachen.de (David Kastrup)
Date: 9 Mar 1994 11:19:39 GMT
Raw View
kanze@us-es.sel.de (James Kanze) writes:

>In article <2l1ki3$808@urmel.informatik.rwth-aachen.de>
>jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

>|> Dear experts,
>|> is there any possibilty to tell the compiler that an operator has no
>|> side effects so that it can do common subexpression optimization.

>There are three possible solutions for an implementation to do this:

>1. Define and use a pragma.

>I know of no compiler which has implemented 1, although it is obvious
>and simple.

Gnu C++ has this.
--
 David Kastrup        dak@pool.informatik.rwth-aachen.de
 Tel: +49-241-72419 Fax: +49-241-79502
 Goethestr. 20, D-52064 Aachen




Author: esamy@supermulti.oracle.com (Patteaswaran Karivaradasamy)
Date: Fri, 11 Mar 1994 01:17:18 GMT
Raw View
In article <KANZE.94Mar8210739@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:

   In article <2l1ki3$808@urmel.informatik.rwth-aachen.de>
   jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

   |> Dear experts,
   |> is there any possibilty to tell the compiler that an operator has no
   |> side effects so that it can do common subexpression optimization.
   |> A very simple example

   |> complex  a,b,c,d,r;
   |> r = (a*b)*c + (a*b)*d;

   |> for a compiler with builtin type complex this would be handled as
   |> complex temp= a*b;
   |> r= temp*c + temp*d;

   |> but since I cannot tell the compiler that complex::operator* has no
   |> side effects it must not do such an optimization.
   |> So, is there a way to make user defined type as efficient as builtin ones?
   |> The problem is even more urgent for classes with more expensive operators
   |> like a matrix class.


One way to solve your problem is to use a const member function.
In your case, you could define 'operator *' for the complex class
as following:

class comples... {
 public:
 complex operator * (const complex) const; //Note the 'const' at the end.
 ...
};

The 'const' modifier at the end of the member function declaration
tells the compiler that this member function does not modify the
state of the object.

Also in this case, the state of the parameter to this funciton is
also not affected and so the parameter is declared as a 'const' too.
In evaluating the example above, (a*b)*c + (a*b)*d, the value of 'a'
and 'b' are not be modified by the evaluation of the first
subexpression, and so the compiler can take out the common subexpression.

The 'const' function specification is already part of C++ and you
can use it with any compiler.  I guess this is sufficient to solve
your problem.

Thanks,

Easwar
esamy@us.oracle.com





Author: hopps@mmm.com (Kevin J. Hopps)
Date: 11 Mar 1994 16:25:07 GMT
Raw View
Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
> In article <KANZE.94Mar8210739@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:

>    In article <2l1ki3$808@urmel.informatik.rwth-aachen.de>
>    jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

>    |> Dear experts,
>    |> is there any possibilty to tell the compiler that an operator has no
>    |> side effects so that it can do common subexpression optimization.
>    |> A very simple example

[ munch ]

> One way to solve your problem is to use a const member function.

[ munch ]

> The 'const' modifier at the end of the member function declaration
> tells the compiler that this member function does not modify the
> state of the object.

Is the compiler really allowed to assume this for optimization purposes?
This seems problematic given that const-ness can be removed via casting
within the member function.

I know that there are changes afoot to make members modifiable even within
const functions (is "mutable" the right term here?).  Between now and when
this keyword is understood by a compiler, what is the compiler really allowed
to assume based on the presence of "const" on a member function?




Author: esamy@supermulti.oracle.com (Patteaswaran Karivaradasamy)
Date: Fri, 11 Mar 1994 20:53:29 GMT
Raw View
In article <2lq613$jhe@dawn.mmm.com> hopps@mmm.com (Kevin J. Hopps) writes:

>   Patteaswaran Karivaradasamy (esamy@supermulti.oracle.com) wrote:
>   > In article <KANZE.94Mar8210739@slsvhdt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:

>   >    In article <2l1ki3$808@urmel.informatik.rwth-aachen.de>
>   >    jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

>   >    |> Dear experts,
>   >    |> is there any possibilty to tell the compiler that an operator has no
>   >    |> side effects so that it can do common subexpression optimization.
>   >    |> A very simple example

>   [ munch ]

>   > One way to solve your problem is to use a const member function.

>   [ munch ]

>   > The 'const' modifier at the end of the member function declaration
>   > tells the compiler that this member function does not modify the
>   > state of the object.

>   Is the compiler really allowed to assume this for optimization purposes?
>   This seems problematic given that const-ness can be removed via casting
>   within the member function.

You cannot remove the const-ness via casting.  That is illegal.
When you are within a const member function, the 'this' pointer has
the following type:

  const A * const this;  // Assume A is the class name.
  -----                  // Note the const before 'A'.

Whatever 'this' is pointing to at the time, is a constant.  There is
no way to modify it through any casting or anything.

>   I know that there are changes afoot to make members modifiable even within
>   const functions (is "mutable" the right term here?).  Between now and when
>   this keyword is understood by a compiler, what is the compiler really allowed
>   to assume based on the presence of "const" on a member function?

The entire idea of allowing a 'const' member function is to say that
it WILL NOT modify the object on which it is invoked.  This is sufficient
information for the compiler to do optimizations on.
I don't think anybody is trying to change the meaning of 'const' member
functions.

Looking back, at the history, the reason why 'const' member functions were
introduced in the first place, is that they are the only member functions
that you can invoke on a 'const' object itself.  i.e, you declare an object
itself as a 'const' and then the only member functions you can invoke on
that object are the const member functions, because they are guaranteed
not to modify the state of the object.

Thanks,

Easwar
esamy@oracle.com

PS:
If you strongly disagree with any of these, we can talk about it over e-mail.




Author: bill@amber.csd.harris.com (Bill Leonard)
Date: 11 Mar 1994 22:06:57 GMT
Raw View
In article <ESAMY.94Mar10171718@supermulti.oracle.com>, esamy@supermulti.oracle.com (Patteaswaran Karivaradasamy) writes:
>
> One way to solve your problem is to use a const member function.
> In your case, you could define 'operator *' for the complex class
> as following:
>
> class comples... {
>  public:
>  complex operator * (const complex) const; //Note the 'const' at the end.
>  ...
> };
>
> The 'const' modifier at the end of the member function declaration
> tells the compiler that this member function does not modify the
> state of the object.

This is not sufficient, because the function could update some *other*
object that is neither "this" nor an argument.  For instance, suppose the
object identified by "this" contains a pointer to another object.  There is
nothing to stop a const member function from modifying that other object.

And then there's I/O, which is also a side effect...

--
Bill Leonard
Harris Computer Systems Division
2101 W. Cypress Creek Road
Fort Lauderdale, FL  33309
bill@ssd.csd.harris.com

These opinions and statements are my own and do not necessarily reflect the
opinions or positions of Harris Corporation.

------------------------------------------------------------------------------
"I can do trivial things pretty trivially."
                                                  Tom Horsley
------------------------------------------------------------------------------




Author: jss@lucid.com (Jerry Schwarz)
Date: 17 Mar 1994 23:55:54 GMT
Raw View

>   Perhaps I don't understand what you are saying.  But from the ARM 5.4,
>   pp 70-71:

...

A few meetings back the committee accepted my "mutable" proposal.

The change can be summarized as follows:

        The "constness" of an object's storage is determined by
        whether the object is constructed as const.

        An attempt to modify the contents of const storage (via
        casting of pointers or other tricks) results in undefined
        behavior.

        If a member is declared with the (newly invented) storage
        class "mutable", then that member is modifiable even
        if the containing object is const.

        Thus

        class X {
                mutable int i ;
                int j ;
            public:
                X(int a, int b) : i(a), j(b) { }
        } ;

        const X x ;

        // the following must be written in a context with
        // access rights to X::i and X::j.

        x.i = 5 ;           // well formed and defined.
        x.j = 5 ;           // not well-formed.
        *(int*)(&x.j) = 5 ; // well-formed but undefined behavior
        *const_cast<int*>(&x.j) = 5 ;
                            // better style, but still undefined behavior

        A consequence of this is that a object is ROMable if

                a) its class doesn't have any mutable members
                b) the compiler can figure out its contents after
                   construction at compile time.
                c) The compiler can cope with any side effects
                   of the constructor and destructor. (Or can
                   determine that there aren't any)

        no specific wording is requried and "ROMable" no longer
        appears in the working paper.

  -- Jerry Schwarz(jss@lucid.com)




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 18 Mar 1994 05:46:02 GMT
Raw View
In article <JSS.94Mar17155554@summit.lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>
>A few meetings back the committee accepted my "mutable" proposal.

 Unfortunately, there is no corresponding "nonvolatile" keyword.

>The change can be summarized as follows:
>
>        The "constness" of an object's storage is determined by
>        whether the object is constructed as const.
>
[]
>        A consequence of this is that a object is ROMable if
>
>                a) its class doesn't have any mutable members

 Actually, you need to add "recursively" here.
Better:

 Standard types are ROMable.
 A struct is ROMable if no member is mutable and
    each member and base is romable.
 (Smly for unions and arrays I guess)

>                b) the compiler can figure out its contents after
>                   construction at compile time.
>                c) The compiler can cope with any side effects
>                   of the constructor and destructor. (Or can
>                   determine that there aren't any)
>
>        no specific wording is requried and "ROMable" no longer
>        appears in the working paper.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 7 Mar 1994 11:02:07 GMT
Raw View
jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

>Dear experts,
>is there any possibilty to tell the compiler that an operator has no
>side effects so that it can do common subexpression optimization.

In general, the only way to do this is to make the operator inline.

(Note that GNU C has an extension which allows you specify that
a function has no side effects, but it doesn't work if the
function takes pointer or reference arguments.)

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: kanze@us-es.sel.de (James Kanze)
Date: 08 Mar 1994 20:07:39 GMT
Raw View
In article <2l1ki3$808@urmel.informatik.rwth-aachen.de>
jarausch@igpm.rwth-aachen.de (Helmut Jarausch) writes:

|> Dear experts,
|> is there any possibilty to tell the compiler that an operator has no
|> side effects so that it can do common subexpression optimization.
|> A very simple example

|> complex  a,b,c,d,r;
|> r = (a*b)*c + (a*b)*d;

|> for a compiler with builtin type complex this would be handled as
|> complex temp= a*b;
|> r= temp*c + temp*d;

|> but since I cannot tell the compiler that complex::operator* has no
|> side effects it must not do such an optimization.
|> So, is there a way to make user defined type as efficient as builtin ones?
|> The problem is even more urgent for classes with more expensive operators
|> like a matrix class.

There are three possible solutions for an implementation to do this:

1. Define and use a pragma.

2. Make all of the operators, constructors, etc. inline, and let the
optimizer figure it out for itself.

3. Have intermodule global optimization, and let the compiler figure
it out for itself even when the operators, constructors, etc. are not
inline.

I know of no compiler which has implemented 1, although it is obvious
and simple.

Very few compilers will be able to do sufficient analyse to eliminate
all of the excess overhead with 2.

And I know of only one compiler (and only second hand, at that) which
will do 3.

Note that the real problem is *not* common sub-routine elimination.
That can be done by hand, once profiling has picked out the hot spots.
The real problem is eliminating unnecessary temporaries by
interleaving the loops in the operators of a matrix class, and
executing the resulting operations in parallel.
--
James Kanze                       email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: jarausch@igpm.rwth-aachen.de (Helmut Jarausch)
Date: 2 Mar 1994 08:59:47 GMT
Raw View
Dear experts,
is there any possibilty to tell the compiler that an operator has no
side effects so that it can do common subexpression optimization.
A very simple example

complex  a,b,c,d,r;
r = (a*b)*c + (a*b)*d;

for a compiler with builtin type complex this would be handled as
complex temp= a*b;
r= temp*c + temp*d;

but since I cannot tell the compiler that complex::operator* has no
side effects it must not do such an optimization.
So, is there a way to make user defined type as efficient as builtin ones?
The problem is even more urgent for classes with more expensive operators
like a matrix class.

Thank you for any comments,

Helmut Jarausch
Institute of Technology
RWTH Aachen
Germany