On 04/05/2012 05:14 AM, Richard Guenther wrote:

Ok.  Remember that you should use non-tree things if you can in GIMPLE
land.  This probably means that both the size and the memmodel "operands"
should be

+ struct GTY(()) gimple_statement_atomic
+ {
+   /* [ WORD 1-8 ]  */
+   struct gimple_statement_with_memory_ops_base membase;
+
+   /* [ WORD 9 ] */
+   enum gimple_atomic_kind kind;
+
      enum gimple_atomic_memmodel memmodel;

      unsigned size;

and not be trees in the ops array.  Even more, both kind and memmodel
should probably go in membase.base.subcode

I'm using subcode now for for the fetch_op and op_fetch operation when we actually need a tree code for plus, sub, and, etc... so I am utilizing that field. Since the 'kind' field is present in ALL node, and the tree code was only needed in some, it looked cleaner to utilize the subcode field for the 'sometimes' field so that it wouldnt be an obvious wasted field in all the non-fetch nodes. In the end it amounts to the same thing, but just looked cleaner :-) I could change if it you felt strongly about it and use subcode for the kind and create a tree_code field in the object for the operation.

Since integral atomics are always of an unsigned type , I could switch over and use 'unsigned size' instead of 'tree fntype' for them (I will rename it), but then things may be more complicated when dealing with generic atomics... those can be structure or array types and I was planning to allow leaving the type in case I discover something useful I can do with it. It may ultimately turn out that the real type isn't going to matter, in which case I will remove it and replace it with an unsigned int for size.

And the reason memmodel is a tree is because, as ridiculous as it seems, it can ultimately be a runtime value. Even barring that, it shows up as a variable after inlining before various propagation engines run, especially in the C++ templates. So it must be a tree.

I was actually thinking about doing it during gimplification... I hadnt
gotten as far as figuring out what to do with the functions from the front
end yet.  I dont know that code well, but I was in fact hoping there was a
way to 'recognize' the function names easily and avoid built in functions
completely...
Heh ... you'd still need a GENERIC representation then.  Possibly
a ATOMIC_OP tree may do.
possibly... or maybe a single generic atomic_builtin with a kind and a variable list of parameters.

well, the names must remain exposed and recognizable since they are 'out
there'.  Maybe under the covers I can just leave them as normal calls and
then during gimplification simply recognize the names and generate
GIMPLE_ATOMIC statements directly from the CALL_EXPR.  That would be ideal.
  That way there are no builtins any more.
I suppose my question was whether the frontends need to do sth about the
__atomic keyword or if that is simply translated to some type flag - or
is that keyword applying to operations, not to objects or types?

The _Atomic keyword is a type modifier like const or volatile. So during gimplification I'll also look for all occurrences of variables in normal expressions which have that bit set in the type, then translate the expression to utilize the new gimple atomic node. so

_Atomic int var = 0;
 var += 4;
 foo (var);

would become

 __atomic_add_fetch (&var, 4, SEQ_CST);
 D.123 = __atomic_load (&var, SEQ_CST);
foo (D.123);





So bottom line, a GIMPLE_ATOMIC statement is just an object that is much
easier to work with.
Yes, I see that it is easier to work with for you.  All other statements
will
see GIMPLE_ATOMICs as blockers for their work though, even if they
already deal with calls just fine - that's why I most of the time suggest
to use builtins (or internal fns) to do things (I bet you forgot to update
enough predicates ...).  Can GIMPLE_ATOMICs throw with
-fnon-call-exceptions?
I suppose yes.  One thing you missed at least ;)

Not that I am aware of, they are 'noexcept'.  But I'm sure I've missed more
than a few things so far.  Im pretty early in the process :-)
What about compare-exchange on a pointer dereference? The pointer
dereference surely can trap, so it can throw with -fnon-call-exceptions.  No?
in theory *any* gimple atomic operation could end up being expanded as a library call, so I will have to ensure they are treated as calls because of things like that.

These are all things I will focus on once all the basic functionality is there. This patch is not meant to be fully flushed out, I just wanted some eyes on it before I checked it into the branch so i dont carry this huge patch set around when making changes. When I get things functional in the branch I'll revisit all this and any of the other implementation comments I don't get to and then submit another patch for review.

+ /* Return the expression field of atomic operation GS.  */
+
+ static inline tree
+ gimple_atomic_expr (const_gimple gs)
+ {
+   GIMPLE_CHECK (gs, GIMPLE_ATOMIC);
+   gcc_assert (gimple_atomic_has_expr (gs));
+   return gimple_op (gs, 2);
+ }

err - what's "expression" in this context?  I hope it's not an arbitrary
tcc_expression tree?!

Its just the 'expression' parameter of atomic operations which have it, like store , fetch_add, or exchange. It would normally be an SSA_NAME or const. I suppose we coud call it 'value' or something if expression is confusing.



+ /* Return the arithmetic operation tree code for atomic operation GS.  */
+
+ static inline enum tree_code
+ gimple_atomic_op_code (const_gimple gs)
+ {
+   GIMPLE_CHECK (gs, GIMPLE_ATOMIC);
+   gcc_assert (gimple_atomic_kind (gs) == GIMPLE_ATOMIC_FETCH_OP ||
+             gimple_atomic_kind (gs) == GIMPLE_ATOMIC_OP_FETCH);
+   return (enum tree_code) gs->gsbase.subcode;
+ }

now, what was it with this "expression" thing again?  Btw, ||s go to the
next line.  subcode should be the atomic kind - it seems that you glob
too much into GIMPLE_ATOMIC and that you maybe should sub-class
GIMPLE_ATOMIC properly via the subcode field.  You'd have an
gimple_atomic_base which fences could use for example.

I was trying to make it simple and utilize the variable length ops array to handle the variable stuff :-) It took many attempts to arrive at this layout.

They don't really break down well into sub-components. I wanted to use a single routine to access a given field for all atomic kinds, so gimple_atomic_target(), gimple_atomic_lhs(), and gimple_atomic_expr() just work . The problem is that they don't break down into a tree structure, but more of a multiple-inheritance type thing.

LOAD has a LHS and a TARGET, no EXPR
STORE has no LHS, but has a TARGET and EXPR
EXCHANGE has a LHS, a TARGET and an EXPR
FENCE has no target or anything else.
COMPARE_EXCHANGE has 2 LHS, a TARGET, and an EXPR, not to mention an additional memorder

I set it up so that the variable length array always stores a given parameter at the same offset, and also allows for contiguous trees in the array to represent all the LHS or RHS parameters for easy generic operand scanning or whatever. And each statement type only allocated the right amount of memory required.

I planned to add a comment to the description showing the layout of the various nodes:
/*
  LOAD         | ORDER | TARGET | LHS  |
   STORE        | ORDER | TARGET | EXPR |
   EXCHANGE     | ORDER | TARGET | EXPR | LHS      |
COMPARE_EXCHG| ORDER | TARGET | EXPR | EXPECTED | FAIL_ORDER | LHS2 | LHS1 |
   FETCH        | ORDER | TARGET | EXPR | LHS      |
   TEST_AND_SET | ORDER | TARGET | LHS  |
   CLEAR        | ORDER | TARGET |
   FENCE        | ORDER |


This allows all the RHS values to be contiguous for bulk processing like operands, and located at the same index for an easy access routine. The LHS is viewed from right to left, and allows more than 1 value as required by compare_exchange. They also are contiguous for generic access, and a single LHS routine can also be used to access those values.
*/

Is that OK? it seemed better than trying to map it to some sort of hierarchical structure it doesn't really fit into.

Andrew



Reply via email to