This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Volatile MEMs in statement expressions and functions inlinedastrees


[relayed -aoliva]

(last one, thanks for your tolerance of my 2-cents):

Fundamentally, unless I misunderstand the basis of the debate, sequencing
of volatile accesses must be defined as being predictably controllable,
otherwise attempting to write functionally portable sw interacting with
often idiosyncratic hw interfaces becomes futile.

If it is agreed that language's expression semantics are not modified by
volatile use, but they simply restrict subsequent optimization transforms
available to the compiler, such that the type and sequence of volatile
access defined in the originating source code are preserved.

And it is agreed that:

 volatile q
 (q = 10) == 10

Then the following must also be true:

- the terminal lvalue is not semantically defined being accessed.
  (otherwise it would create an semantic inconsistency between volatile
  and non-volatile lvalue use, which is not the intent, or likely desirable)

- a temporary must be implied as being semantically defined to hold the
  rvalue being assigned to the lvalue, which may be optimized away in the
  case of the lvalue being non-volatile, but not otherwise.

- the final terminating lvalue of an expression is also correspondingly
  not semantically defined as being read-accessed (i.e. q; is semantically
  void, as no value is being assigned to it, therefore "value assigned" is
  void, nor is it's previous value semantically required for subsequent use
  as an rvalue).

- "value stored" must mean "equivalent to the value stored", in order for
  simple expressions to semantically consistent regardless of potentially
  enclosed sub-term volatile lvalues; again the compiler has the liberty to
  optimize away any semantically defined temporary intermediates as it sees
  fit as it pertains to non-volatile uses, including but not limited to any
  number of repeated load and/or stores to non-volatile variables (therefore
  a literal temporary is not required for subsequent access to earlier
  non-volatile "value stored" assignments, but is otherwise).

-paul-


> Re: (x ? (a = b) : (c = d)) += e;
> 
> ::  if (x != 0) {
>       (at)a = ( ((at)tmp1 = b, (at)tmp1)
>               + ((at)tmp2 = a, (at)tmp2) + ((at)tmp3 = e, (at)temp3) )
>   }else{
>       (ct)c = ( ((ct)tmp1 = d, (ct)tmp1)
>               + ((ct)tmp2 = c, (ct)tmp2) + ((ct)tmp3 = e, (ct)temp3) )
>   }
> 
> A proposed likely good abstract rule, to prevent ambiguous surprises:
> - no more, and no less, than a single semantically required load and/or
> store enforced reference may occur per explicit (i.e symbolic) reference
> in the originating source code.
> 
> Therefore, it is guaranteed that a single load and/or store will occur
> consistent with the expression semantics, per explicit volatile source
> reference; no more, no less. Ambiguous rules relating to treatment of volatile
> references are for all practical purposes useless, as volatile reference are
> defined as being potentially sequence sensitive, therefore their access
> sequence must be predictably controllable within the context
> of a procedural block of code.
> 
> -paul-
> 
>> (sorry for direct email, but my mail relay is rejected by gnu.org)
>> 
>> Please let me refine my earlier expansion, such that an lvalue expression
>> returns "the value stored" if referenced, and let lt/rt/tt represent
>> lvalue/rvalue/target-lvalue-type casts, and assume l/rvalue references are
>> appropriately implicitly de-referenced, which may be optimized such that:
>> - all volatile rv (rvalue) operand references are load-enforced,
>> - all volatile lv (lvalue) target references are store-enforced.
>> - each explicit/implicit volatile reference will yield one
>> and only one corresponding load/store-enforced operation.
>> 
>> Where since a statement doesn't return a value, therefore doesn't imply a
>> root/terminating r/lvalue reference, therefore:
>> 
>> (lt)lv :: <null-expression/operation>
>> 
>> (lt)lv = rv :: lv = ((lt)tmp = rv, (lt)tmp)
>> 
>> Where an (rvalue) referenced expression does imply a value reference:
>> 
>> (tt)(rv) :: ((tt)tmp = rv, (tt)tmp)
>> 
>> (tt)(lv = rv) :: (lv = ((lt)tmp = rv, (lt)tmp), (tt)((lt)tmp))
>> 
>> Thereby:
>> 
>> q = (p = (x))
>> 
>> :: q = ((qt)tmp2 = (p = ((pt)tmp1 = x, (pt)tmp1), (qt)((pt)tmp1)), (qt)tmp2)
>> 
>> Yielding:
>> - logically (pt)tmp = x, p = (pt)tmp, q = (qt)((pt)tmp);
>> - one potentially volatile non-optimizable load: (pt)tmp = x;
>> - two potentially volatile non-optimizable stores: p = (pt)tmp, q =
>> (qt)((pt)temp);
>> 
>> q++ :: q = ((q) + 1) :: q = ( ((qt)tmp = q, (qt)tmp) + (qt)1 )
>> 
>> Yielding:
>> - logically (qt)tmp = q, q = ((qt)tmp + (qt)1);
>> - one potentially volatile non-optimizable load: (qt)tmp = q;
>> - one potentially volatile non-optimizable store: q = ((qt)tmp + (qt)1);
>> 
>> For functions, a return value implies an assignment to the function value,
>> otherwise no return value reference is implied, therefore:
>> 
>> ft f(){ q; ... } :: <null-expression/operation>(related to q;)
>> 
>> Yielding:
>> - logically no operations related to q;
>> - zero potentially volatile non-optimizable load/stores related to q;
>> 
>> ft f(){ ... temp = q; } :: (tt)temp = ((tt)tmp = q, (tt)tmp)
>> 
>> Yielding:
>> - logically (tt)tmp = q, temp = (tt)tmp;
>> - one potentially volatile non-optimizable load: (tt)tmp = q;
>> - zero potentially volatile non-optimizable stores related to q;
>> 
>> ft f(){ ... return q; } :: fv = ((ft)tmp = q, (ft)tmp)
>> 
>> Yielding:
>> - logically (ft)tmp = q, fv = (ft)tmp;
>> - one potentially volatile non-optimizable load: (ft)tmp = q;
>> - zero potentially volatile non-optimizable stores related to (return q);
>> 
>> and so on...
>> 
>> -paul-
>> 
>> 
>>> would suspect that:
>>> 
>>> rvalue :: (temp = rvalue, temp)
>>> 
>>> therefor:
>>> 
>>> lvalue = rvalue :: lvalue = (temp = rvalue, temp)
>>> 
>>> therefor:
>>> 
>>> q = p = x :: q = (p = (x)) :: q = (temp2 = (p = (temp1 = x, temp1)), temp2)
>>> 
>>> equivalent to:
>>> 
>>> q = p = x :: q = (p = (temp = x, temp), temp)
>>> 
>>> which may be optimized such that:
>>> - all volatile rvalue operand references are load-enforced,
>>> - all volatile lvalue operand targets are store-enforced.
>>> - each explicit or implied volatile reference will yield one
>>> and only one corresponding load/store-enforced operation.
>>> 
>>> yielding:
>>> - one potentially volatile non-optimizable load: temp = x;
>>> - two potentially volatile non-optimizable stores: p = temp, q = temp;
>>> 
>>> correspondingly:
>>> 
>>> q += p :: q = ((q) + (p)) :: q = ((temp1 = q, temp1) + (temp2 = p, temp2))
>>> 
>>> yielding:
>>> - two potentially volatile non-optimizable loads: temp1 = q, temp2 = p;
>>> - one potentially volatile non-optimizable store: q = (temp1 + temp2);
>>> 
>>> q++ :: q = ((q) + (1)) :: q = ((temp = q, temp) + 1)
>>> 
>>> yielding:
>>> - one potentially volatile non-optimizable load: temp = q;
>>> - one potentially volatile non-optimizable store: q = (temp + 1)
>>> 
>>> q :: <null-lvalue-operation>
>>> 
>>> - zero potentially volatile non-optimizable loads, stores, or side-effects;
>>> 
>>> and so forth...
>>> 
>>> -paul-


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]