[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <alpine.LSU.2.20.1602291837100.20277@wotan.suse.de>
Date: Mon, 29 Feb 2016 19:17:55 +0100 (CET)
From: Michael Matz <matz@...e.de>
To: "Paul E. McKenney" <paulmck@...ux.vnet.ibm.com>
cc: parallel@...ts.isocpp.org, linux-arch@...r.kernel.org,
gcc@....gnu.org, p796231 <Peter.Sewell@...cam.ac.uk>,
llvm-dev@...ts.llvm.org, will.deacon@....com,
linux-kernel@...r.kernel.org, dhowells@...hat.com,
peterz@...radead.org, Ramana.Radhakrishnan@....com,
Luc Maranget <luc.maranget@...ia.fr>,
akpm@...ux-foundation.org, Jade Alglave <j.alglave@....ac.uk>,
torvalds@...ux-foundation.org, mingo@...nel.org
Subject: Re: [isocpp-parallel] Proposal for new memory_order_consume
definition
Hi,
On Sat, 27 Feb 2016, Paul E. McKenney wrote:
> But we do already have something very similar with signed integer
> overflow. If the compiler can see a way to generate faster code that
> does not handle the overflow case, then the semantics suddenly change
> from twos-complement arithmetic to something very strange. The standard
> does not specify all the ways that the implementation might deduce that
> faster code can be generated by ignoring the overflow case, it instead
> simply says that signed integer overflow invoked undefined behavior.
>
> And if that is a problem, you use unsigned integers instead of signed
> integers.
>
> So it seems that we should be able to do something very similar here.
For this case the important pice of information to convey one or the other
meaning in source code is the _type_ of involved entities, not annotations
on the operations. signed type -> undefined overflow, unsigned type ->
modulo arithmetic; easy, and it nicely carries automatically through
operation chains (and pointers) without any annotations.
I feel much of the complexity in the memory order specifications, also
with your recent (much better) wording to explain dependency chains, would
be much easier if the 'carries-dependency' would be encoded into the types
of operands. For purpose of example, let's call the marker "blaeh" (not
atomic to not confuse with existing use :) ):
int foo;
blaeh int global;
int *somep;
blae int *blaehp;
f () {
blaehp = &foo; // might be okay, adds restrictions on accesses through
// blaehp, but not through 'foo' directly
blaehp = &global;
if (somep == blaehp)
{
/* Even though the value is equal ... */
... *blaehp ... /* ... a compiler can't rewrite this into *somep */
}
}
A "carries-dependency" on some operation (e.g. a call) would be added by
using a properly typed pointer at those arguments (or return type) where
it matters. You can't give a blaeh pointer to something only accepting
non-blaeh pointers (without cast).
Pointer addition and similar transformations involving a blaeh pointer and
some integer would still give a blaeh pointer, and hence by default also
solve the problem of cancellations.
Such marking via types would not solve all problems in an optimal way if
you had two overlapping but independend dependency chains (all of them
would collapse to one chain and hence made dependend, which still is
conservatively correct).
OTOH introducing new type qualifiers is a much larger undertaking, so I
can understand one wants to avoid this. I think it'd ultimately be
clearer, though.
Ciao,
Michael.
Powered by blists - more mailing lists