x=r=y; // With r volatile. Is r read?

Discussion in 'C Programming' started by Francois Grieu, Mar 7, 2011.

  1. Hi, the subject sums it all.

    extern volatile unsigned r;
    unsigned x,y;

    void test(void)
    {
    x=r=y;
    }

    What happens:
    - y is read, written in r and in x.
    - y is read, written in r; then r is read, written in x.
    - any of the above.
    - other..

    TIA,
    Francois Grieu
    Francois Grieu, Mar 7, 2011
    #1
    1. Advertising

  2. Francois Grieu

    Mark Bluemel Guest

    On 03/07/2011 04:05 PM, Francois Grieu wrote:
    > Hi, the subject sums it all.
    >
    > extern volatile unsigned r;
    > unsigned x,y;
    >
    > void test(void)
    > {
    > x=r=y;
    > }
    >
    > What happens:
    > - y is read, written in r and in x.
    > - y is read, written in r; then r is read, written in x.
    > - any of the above.
    > - other..


    My reading of this is that your statement is exactly equivalent
    to "x=(r=y);".

    That is to say that the value of the expression "r=y" is assigned to x -
    so there is no need for r to be read.
    Mark Bluemel, Mar 7, 2011
    #2
    1. Advertising

  3. On 07/03/2011 17:33, Mark Bluemel wrote:
    > On 03/07/2011 04:05 PM, Francois Grieu
    >> Hi, the subject sums it all.
    >>
    >> extern volatile unsigned r;
    >> unsigned x,y;
    >>
    >> void test(void)
    >> {
    >> x=r=y;
    >> }
    >>
    >> What happens:
    >> - y is read, written in r and in x.
    >> - y is read, written in r; then r is read, written in x.
    >> - any of the above.
    >> - other..

    >
    > My reading of this is that your statement is exactly equivalent
    > to "x=(r=y);".


    Yes.

    > That is to say that the value of the expression "r=y" is assigned to x


    Yes.

    > so there is no need for r to be read.


    I'm unsure of that, and of if "no need" translates to "no read".
    My problem boils down to: is the value of (r=y)
    - the value read from y (that gets written into r).
    - the value read from r (after?) writing the value of y into r.
    - any of the above.
    - other..

    Francois Grieu
    Francois Grieu, Mar 7, 2011
    #3
  4. Francois Grieu

    Mark Bluemel Guest

    On 03/07/2011 04:41 PM, Francois Grieu wrote:
    > On 07/03/2011 17:33, Mark Bluemel wrote:
    >> On 03/07/2011 04:05 PM, Francois Grieu
    >>> Hi, the subject sums it all.
    >>>
    >>> extern volatile unsigned r;
    >>> unsigned x,y;
    >>>
    >>> void test(void)
    >>> {
    >>> x=r=y;
    >>> }
    >>>
    >>> What happens:
    >>> - y is read, written in r and in x.
    >>> - y is read, written in r; then r is read, written in x.
    >>> - any of the above.
    >>> - other..

    >>
    >> My reading of this is that your statement is exactly equivalent
    >> to "x=(r=y);".

    >
    > Yes.
    >
    >> That is to say that the value of the expression "r=y" is assigned to x

    >
    > Yes.
    >
    >> so there is no need for r to be read.

    >
    > I'm unsure of that, and of if "no need" translates to "no read".
    > My problem boils down to: is the value of (r=y)
    > - the value read from y (that gets written into r).
    > - the value read from r (after?) writing the value of y into r.
    > - any of the above.
    > - other..


    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    6.5.16 paragraph 3
    .... An assignment expression has the value of the left operand after the
    assignment ....

    I see no mention of other operations after the assignment.
    Mark Bluemel, Mar 7, 2011
    #4
  5. Francois Grieu

    Guest

    Francois Grieu <> wrote:
    >
    > My problem boils down to: is the value of (r=y)
    > - the value read from y (that gets written into r).
    > - the value read from r (after?) writing the value of y into r.
    > - any of the above.


    Any of the above. "What constitutes an access to an object that has
    volatile-qualified type is implementation-defined." (6.7.3p7) The
    implementation is permitted to read the value from r, but it is not
    required to.
    --
    Larry Jones

    I think grown-ups just ACT like they know what they're doing. -- Calvin
    , Mar 7, 2011
    #5
  6. Francois Grieu

    Jens Guest

    On 7 Mrz., 20:33, wrote:
    > Any of the above.  "What constitutes an access to an object that has
    > volatile-qualified type is implementation-defined." (6.7.3p7)  The
    > implementation is permitted to read the value from r, but it is not
    > required to.


    Exactly. The draft of C1X even has a footnote (in assignment
    operators) to make the situation in question explicit:

    C1X> The implementation is permitted to read the object to determine
    the value but is not
    C1X> required to, even when the object has volatile-qualified type.

    Jens
    Jens, Mar 7, 2011
    #6
  7. On 07/03/2011 22:42, Jens wote on my question <quote>
    >>>> My problem boils down to: is the value of (r=y)
    >>>> - the value read from y (that gets written into r).
    >>>> - the value read from r (after?) writing the value of y into r.
    >>>> - any of the above.
    >>>> - other.. </quote>

    >
    > On 7 Mrz., 20:33, wrote:
    >> Any of the above. "What constitutes an access to an object that has
    >> volatile-qualified type is implementation-defined." (6.7.3p7) The
    >> implementation is permitted to read the value from r, but it is not
    >> required to.

    >
    > Exactly. The draft of C1X even has a footnote (in assignment
    > operators) to make the situation in question explicit:
    >
    > C1X> The implementation is permitted to read the object to determine
    > the value but is not
    > C1X> required to, even when the object has volatile-qualified type.


    Yes. Quoting <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf>
    more in depth:

    " 6.5.16 Assignment operators (..)
    p3) An assignment operator stores a value in the object designated by
    the left operand. An assignment expression has the value of the
    left operand after the assignment, (note 111) but is not an lvalue.
    (..)

    (111) The implementation is permitted to read the object to determine
    the value but is not required to, even when the object has
    volatile-qualified type. "

    That settles (r=y) can have either
    - the value read from y that also gets written into r
    - the value read from r AFTER writing the value of y into r

    Thanks.

    Francois Grieu
    Francois Grieu, Mar 8, 2011
    #7
  8. Francois Grieu

    Noob Guest

    Francois Grieu wrote:

    > extern volatile unsigned r;
    > unsigned x,y;
    >
    > void test(void)
    > {
    > x=r=y;
    > }
    >
    > What happens:
    > - y is read, written in r and in x.
    > - y is read, written in r; then r is read, written in x.
    > - any of the above.
    > - other..


    On a related note, I'd like to point to an (IMHO) interesting paper.

    Volatiles Are Miscompiled, and What to Do about It
    http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf

    Regards.
    Noob, Mar 8, 2011
    #8
  9. Francois Grieu

    Chad Guest

    On Mar 8, 7:18 am, Kenneth Brody <> wrote:
    > On 3/7/2011 12:00 PM, Mark Bluemel wrote:
    >
    >
    >
    > > On 03/07/2011 04:41 PM, Francois Grieu wrote:
    > >> On 07/03/2011 17:33, Mark Bluemel wrote:
    > >>> On 03/07/2011 04:05 PM, Francois Grieu
    > >>>> Hi, the subject sums it all.

    >
    > >>>> extern volatile unsigned r;
    > >>>> unsigned x,y;

    >
    > >>>> void test(void)
    > >>>> {
    > >>>> x=r=y;
    > >>>> }

    > [...]
    > >>> so there is no need for r to be read.

    >
    > >> I'm unsure of that, and of if "no need" translates to "no read".
    > >> My problem boils down to: is the value of (r=y)
    > >> - the value read from y (that gets written into r).
    > >> - the value read from r (after?) writing the value of y into r.
    > >> - any of the above.
    > >> - other..

    >
    > >http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    > > 6.5.16 paragraph 3
    > > .... An assignment expression has the value of the left operand after the
    > > assignment ....

    >
    > > I see no mention of other operations after the assignment.

    >
    > Consider the fact that, with a volatile, the "value of the left operand
    > after the assignment" might not necessarily be the value that was just
    > assigned to it.
    >


    Would this be because the assignment produces a side effect?


    Chad
    Chad, Mar 8, 2011
    #9
  10. Francois Grieu

    Chad Guest

    On Mar 7, 8:33 am, Mark Bluemel <> wrote:
    > On 03/07/2011 04:05 PM, Francois Grieu wrote:
    >
    > > Hi, the subject sums it all.

    >
    > > extern volatile unsigned r;
    > > unsigned x,y;

    >
    > > void test(void)
    > >   {
    > >   x=r=y;
    > >   }

    >
    > > What happens:
    > > - y is read, written in r and in x.
    > > - y is read, written in r; then r is read, written in x.
    > > - any of the above.
    > > - other..

    >
    > My reading of this is that your statement is exactly equivalent
    > to "x=(r=y);".
    >
    > That is to say that the value of the expression "r=y" is assigned to x -
    > so there is no need for r to be read.


    I don't know about C, but some other programming languages would refer
    to this as "right associative".
    Chad, Mar 8, 2011
    #10
  11. Francois Grieu

    Guest

    Jens <> wrote:
    >
    > Exactly. The draft of C1X even has a footnote (in assignment
    > operators) to make the situation in question explicit:
    >
    > C1X> The implementation is permitted to read the object to determine
    > the value but is not
    > C1X> required to, even when the object has volatile-qualified type.


    Gee, what a coincidence! :)
    --
    Larry Jones

    Another casualty of applied metaphysics. -- Hobbes
    , Mar 8, 2011
    #11
  12. On 08/03/2011 17:23, Chad wrote:
    > On Mar 8, 7:18 am, Kenneth Brody <> wrote:
    >> On 3/7/2011 12:00 PM, Mark Bluemel wrote:
    >>
    >>
    >>
    >>> On 03/07/2011 04:41 PM, Francois Grieu wrote:
    >>>> On 07/03/2011 17:33, Mark Bluemel wrote:
    >>>>> On 03/07/2011 04:05 PM, Francois Grieu
    >>>>>> Hi, the subject sums it all.

    >>
    >>>>>> extern volatile unsigned r;
    >>>>>> unsigned x,y;

    >>
    >>>>>> void test(void)
    >>>>>> {
    >>>>>> x=r=y;
    >>>>>> }

    >> [...]
    >>>>> so there is no need for r to be read.

    >>
    >>>> I'm unsure of that, and of if "no need" translates to "no read".
    >>>> My problem boils down to: is the value of (r=y)
    >>>> - the value read from y (that gets written into r).
    >>>> - the value read from r (after?) writing the value of y into r.
    >>>> - any of the above.
    >>>> - other..

    >>
    >>> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    >>> 6.5.16 paragraph 3
    >>> .... An assignment expression has the value of the left operand after the
    >>> assignment ....

    >>
    >>> I see no mention of other operations after the assignment.

    >>
    >> Consider the fact that, with a volatile, the "value of the left operand
    >> after the assignment" might not necessarily be the value that was just
    >> assigned to it.
    >>

    >
    > Would this be because the assignment produces a side effect?


    A common example is a register r where some (or all) bits are always
    read as 0, regardless of what's written to.

    Francois Grieu
    Francois Grieu, Mar 8, 2011
    #12
  13. Francois Grieu

    Chad Guest

    On Mar 8, 9:00 am, Francois Grieu <> wrote:
    > On 08/03/2011 17:23, Chad wrote:
    >
    >
    >
    > > On Mar 8, 7:18 am, Kenneth Brody <> wrote:
    > >> On 3/7/2011 12:00 PM, Mark Bluemel wrote:

    >
    > >>> On 03/07/2011 04:41 PM, Francois Grieu wrote:
    > >>>> On 07/03/2011 17:33, Mark Bluemel wrote:
    > >>>>> On 03/07/2011 04:05 PM, Francois Grieu
    > >>>>>> Hi, the subject sums it all.

    >
    > >>>>>> extern volatile unsigned r;
    > >>>>>> unsigned x,y;

    >
    > >>>>>> void test(void)
    > >>>>>> {
    > >>>>>> x=r=y;
    > >>>>>> }
    > >> [...]
    > >>>>> so there is no need for r to be read.

    >
    > >>>> I'm unsure of that, and of if "no need" translates to "no read".
    > >>>> My problem boils down to: is the value of (r=y)
    > >>>> - the value read from y (that gets written into r).
    > >>>> - the value read from r (after?) writing the value of y into r.
    > >>>> - any of the above.
    > >>>> - other..

    >
    > >>>http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    > >>> 6.5.16 paragraph 3
    > >>> .... An assignment expression has the value of the left operand afterthe
    > >>> assignment ....

    >
    > >>> I see no mention of other operations after the assignment.

    >
    > >> Consider the fact that, with a volatile, the "value of the left operand
    > >> after the assignment" might not necessarily be the value that was just
    > >> assigned to it.

    >
    > > Would this be because the assignment produces a side effect?

    >
    > A common example is a register r where some (or all) bits are always
    > read as 0, regardless of what's written to.
    >


    I was think of something like the following....

    #include <stdio.h>

    int main(void)
    {
    int a = 5;
    int b = 9;

    int k = a++ + ++b;

    printf("The value of k is: %d\n", k);
    printf("a = %d and b = %d\n", a, b);
    return 0;
    }
    [cdalten@localhost oakland]$ gcc -Wall -Wextra -Wshadow side.c -o side
    [cdalten@localhost oakland]$ ./side
    The value of k is: 15
    a = 6 and b = 10
    [cdalten@localhost oakland]$

    This is how I would attempt to explain the example...

    In this case, the value of 'b' is incremented by 1. Then this value,
    which is now 10, is added the value of 'a' (which is still 5), to
    produce a value of 16. The side effect would be the result of applying
    the postincrement operator to 'a'. The result of this side effect is
    that the value of 'a' is increased by one. Which would happen after
    the sequence point?

    Or something along those lines. I'm pretty sure the regulars will
    correct me.

    Chad
    Chad, Mar 8, 2011
    #13
  14. Francois Grieu

    Chad Guest

    On Mar 8, 10:03 am, Chad <> wrote:
    > On Mar 8, 9:00 am, Francois Grieu <> wrote:
    >
    >
    >
    > > On 08/03/2011 17:23, Chad wrote:

    >
    > > > On Mar 8, 7:18 am, Kenneth Brody <> wrote:
    > > >> On 3/7/2011 12:00 PM, Mark Bluemel wrote:

    >
    > > >>> On 03/07/2011 04:41 PM, Francois Grieu wrote:
    > > >>>> On 07/03/2011 17:33, Mark Bluemel wrote:
    > > >>>>> On 03/07/2011 04:05 PM, Francois Grieu
    > > >>>>>> Hi, the subject sums it all.

    >
    > > >>>>>> extern volatile unsigned r;
    > > >>>>>> unsigned x,y;

    >
    > > >>>>>> void test(void)
    > > >>>>>> {
    > > >>>>>> x=r=y;
    > > >>>>>> }
    > > >> [...]
    > > >>>>> so there is no need for r to be read.

    >
    > > >>>> I'm unsure of that, and of if "no need" translates to "no read".
    > > >>>> My problem boils down to: is the value of (r=y)
    > > >>>> - the value read from y (that gets written into r).
    > > >>>> - the value read from r (after?) writing the value of y into r.
    > > >>>> - any of the above.
    > > >>>> - other..

    >
    > > >>>http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
    > > >>> 6.5.16 paragraph 3
    > > >>> .... An assignment expression has the value of the left operand after the
    > > >>> assignment ....

    >
    > > >>> I see no mention of other operations after the assignment.

    >
    > > >> Consider the fact that, with a volatile, the "value of the left operand
    > > >> after the assignment" might not necessarily be the value that was just
    > > >> assigned to it.

    >
    > > > Would this be because the assignment produces a side effect?

    >
    > > A common example is a register r where some (or all) bits are always
    > > read as 0, regardless of what's written to.

    >
    > I was think of something like the following....
    >
    > #include <stdio.h>
    >
    > int main(void)
    > {
    >   int a = 5;
    >   int b = 9;
    >
    >   int k = a++ + ++b;
    >
    >   printf("The value of k is: %d\n", k);
    >   printf("a = %d and b = %d\n", a, b);
    >   return 0;}
    >
    > [cdalten@localhost oakland]$ gcc -Wall -Wextra -Wshadow side.c -o side
    > [cdalten@localhost oakland]$ ./side
    > The value of k is: 15
    > a = 6 and b = 10
    > [cdalten@localhost oakland]$
    >
    > This is how I would attempt to explain the example...
    >
    > In this case, the value of 'b' is incremented by 1. Then this value,
    > which is now 10, is added the value of 'a' (which is still 5), to
    > produce a value of 16. The side effect would be the result of applying
    > the postincrement operator to 'a'. The result of this side effect is
    > that the value of 'a' is increased by one. Which would happen after
    > the sequence point?
    >
    > Or something along those lines. I'm pretty sure the regulars will
    > correct me.
    >


    The part I'm kind of fuzzy about is the ++b

    in

    int k = a++ + ++b;

    The result of the preincrement operator to (applied) b is also a side
    effect. However, I think this side effect is applied before the
    statement completes. Ie, both b in incremented by one and the side
    effect is applied to b. Whereas side effect of a++ is applied *after*
    the statement completes.

    Chad
    Chad, Mar 8, 2011
    #14
  15. Chad <> writes:
    [...]
    > The part I'm kind of fuzzy about is the ++b
    >
    > in
    >
    > int k = a++ + ++b;
    >
    > The result of the preincrement operator to (applied) b is also a side
    > effect. However, I think this side effect is applied before the
    > statement completes. Ie, both b in incremented by one and the side
    > effect is applied to b. Whereas side effect of a++ is applied *after*
    > the statement completes.


    The standard defines the behavior of ++b:

    The value of the operand of the prefix ++ operator is
    incremented. The result is the new value of the operand
    after incrementation. The expression ++E is equivalent to
    (E+=1). See the discussions of additive operators and compound
    assignment for information on constraints, types, side effects,
    and conversions and the effects of operations on pointers.

    For both a++ and ++b, the side effect occurs some time between the
    preceding and following sequence points.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 8, 2011
    #15
  16. On Tue, 8 Mar 2011 08:23:01 -0800 (PST)
    Chad <> wrote:
    > On Mar 8, 7:18 am, Kenneth Brody <> wrote:
    > > Consider the fact that, with a volatile, the "value of the left operand
    > > after the assignment" might not necessarily be the value that was just
    > > assigned to it.

    >
    > Would this be because the assignment produces a side effect?


    No , it would be because with a volatile , an outside agent outside
    the control of the C programme can modify the value of the variable.
    With non volatiles this is not allowed to happen.
    Spiros Bousbouras, Mar 8, 2011
    #16
  17. Re: pre/post-increment and sequence points (was Re: x=r=y; // With r volatile. Is r read?)

    Kenneth Brody <> writes:
    [...]
    > Despite the fact that we are using the value of "a++" after the increment,
    > and "++b" before the increment, the result is indistinguishable from the
    > other way around. (However, there may be a problem with this implementation
    > for values of a==INT_MAX and b<0 due to the overflow in "a++". I leave that
    > discussion to those who are more versed in the intricacies of the Standard
    > than I am.)


    If a==INT_MAX, then the behavior of "a++" is undefined, which makes
    the behavior of the entire statement undefined. To put it another
    way, the compiler is free to generate code based on the assumption
    that it *won't* overflow.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 8, 2011
    #17
  18. Re: pre/post-increment and sequence points (was Re: x=r=y; // With r

    On Tue, 08 Mar 2011 14:58:41 -0500
    Kenneth Brody <> wrote:
    > On 3/8/2011 1:09 PM, Chad wrote:
    > [...]
    > > The part I'm kind of fuzzy about is the ++b
    > >
    > > in
    > >
    > > int k = a++ + ++b;
    > >
    > > The result of the preincrement operator to (applied) b is also a side
    > > effect. However, I think this side effect is applied before the
    > > statement completes. Ie, both b in incremented by one and the side
    > > effect is applied to b. Whereas side effect of a++ is applied *after*
    > > the statement completes.

    >
    > No. Both the increments of a and of b are guaranteed to take place before
    > the statement completes. (Or, more technically, before the next "sequence
    > point".) When, exactly, during the execution of the statement, the
    > increments take place is not specified by the Standard.
    >
    > However, the meaning of "a++" is "the value of a before the increment", and
    > "++b" is "the value of b after the increment". However, that does not
    > necessarily mean that "b" actually has been incremented by the time the
    > value of "++b" is used, nor that "a" won't be incremented before the value
    > is read.
    >
    > In other words, this pseudo-code is a perfectly valid implementation of the
    > above code:
    >
    > ; extern int a,b;
    > ; int k = a++ + ++b;
    >
    > load k,a
    > add k,b
    > incr k
    > incr a
    > incr b
    >
    > In fact, I believe this implementation is just as valid:
    >
    > incr a
    > load k,a
    > add k,b
    > incr b
    >
    > Despite the fact that we are using the value of "a++" after the increment,
    > and "++b" before the increment, the result is indistinguishable from the
    > other way around. (However, there may be a problem with this implementation
    > for values of a==INT_MAX and b<0 due to the overflow in "a++".


    If a==INT_MAX then a++ produces undefined behavior so all
    implementations are valid.
    Spiros Bousbouras, Mar 8, 2011
    #18
  19. Francois Grieu

    Guest

    Kenneth Brody <> wrote:
    >
    > Consider the fact that, with a volatile, the "value of the left operand
    > after the assignment" might not necessarily be the value that was just
    > assigned to it.
    >
    > But, apparently that description was ambiguous, and the latest draft of the
    > Standard (see elsethread) make it clear that it's implementation defined.


    Unspecified, not implementation defined.
    --
    Larry Jones

    These child psychology books we bought were such a waste of money.
    -- Calvin's Mom
    , Mar 8, 2011
    #19
  20. Francois Grieu

    Tim Rentsch Guest

    Re: pre/post-increment and sequence points (was Re: x=r=y; // With r volatile. Is r read?)

    Keith Thompson <> writes:

    > Kenneth Brody <> writes:
    > [...]
    >> Despite the fact that we are using the value of "a++" after the increment,
    >> and "++b" before the increment, the result is indistinguishable from the
    >> other way around. (However, there may be a problem with this implementation
    >> for values of a==INT_MAX and b<0 due to the overflow in "a++". I leave that
    >> discussion to those who are more versed in the intricacies of the Standard
    >> than I am.)

    >
    > If a==INT_MAX, then the behavior of "a++" is undefined, which makes
    > the behavior of the entire statement undefined. To put it another
    > way, the compiler is free to generate code based on the assumption
    > that it *won't* overflow.


    The compiler _might_ be free to do that, depending on if and how
    the implementation chooses to define the behavior on overflow,
    which the implementation is at liberty to do however it wishes.
    Tim Rentsch, Mar 11, 2011
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Knute Johnson

    Re: Volatile?

    Knute Johnson, Jul 1, 2003, in forum: Java
    Replies:
    17
    Views:
    1,434
    Knute Johnson
    Jul 3, 2003
  2. Doug Pardee

    Re: Volatile?

    Doug Pardee, Jul 1, 2003, in forum: Java
    Replies:
    0
    Views:
    881
    Doug Pardee
    Jul 1, 2003
  3. Replies:
    1
    Views:
    901
    Knute Johnson
    Aug 29, 2003
  4. Daniel

    Can volatile be trusted?

    Daniel, Sep 11, 2003, in forum: Java
    Replies:
    7
    Views:
    465
    Chris Uppal
    Sep 12, 2003
  5. ben
    Replies:
    5
    Views:
    579
    Ulrich Eckhardt
    Jan 11, 2005
Loading...

Share This Page