Patchwork trans-mem: Support noexcept specifications for transaction statements and expressions.

login
register
mail settings
Submitter Torvald Riegel
Date Nov. 8, 2011, 3:42 a.m.
Message ID <1320723774.32515.25.camel@triegel.csb>
Download mbox | patch
Permalink /patch/124258/
State New
Headers show

Comments

Torvald Riegel - Nov. 8, 2011, 3:42 a.m.
The patch seems to work fine, except that MUST_NOT_THROW_EXPR isn't
working as expected. When used inside an expression, it isn't correctly
transformed to Gimple. Second, we loose terminate calls for some reason
(see noexcept-5.C). Those two things cause the failures with those test
cases.

The noexcept specifications are a recent addition in the C++ TM
specification and they triggered a couple of other bugs along the way,
that's why this patch comes in rather late. I'd hope that it would be
accepted nonetheless (after the remaining issues have been fixed).
commit 74201384abf072d9ef95ebeb333cf4032e46886c
Author: Torvald Riegel <triegel@redhat.com>
Date:   Fri Nov 4 11:39:25 2011 +0100

    Support noexcept specifications for transaction statements and expressions.
Jason Merrill - Nov. 8, 2011, 4:12 a.m.
On 11/07/2011 10:42 PM, Torvald Riegel wrote:
> +		noex = tsubst_copy_and_build (noex, args, complain, in_decl,
> +					      /*function_p=*/false,
> +					      /*integral_const_expr_p=*/true);
> +		noex = build_noexcept_spec (TREE_PURPOSE (noex),
> +					    tf_warning_or_error);

If you're going to pull the TREE_PURPOSE out, you might as well do that 
before tsubsting rather than after.

> +      SET_EXPR_LOCATION(body, EXPR_LOCATION (TRANSACTION_EXPR_BODY (stmt)));

Space before (.

> +      SET_EXPR_LOCATION(expr, loc);

Here too.

Otherwise, looks OK.

Jason

Patch

diff --git a/gcc/c-parser.c b/gcc/c-parser.c
index e6a7fdf..e707ba3 100644
--- a/gcc/c-parser.c
+++ b/gcc/c-parser.c
@@ -10637,7 +10637,8 @@  c_parser_transaction_expression (c_parser *parser, enum rid keyword)
     {
       tree expr = c_parser_expression (parser).value;
       ret.original_type = TREE_TYPE (expr);
-      ret.value = build1 (TRANSACTION_EXPR, ret.original_type, expr);
+      ret.value = build2 (TRANSACTION_EXPR, ret.original_type, expr,
+			  NULL_TREE);
       if (this_in & TM_STMT_ATTR_RELAXED)
 	TRANSACTION_EXPR_RELAXED (ret.value) = 1;
       SET_EXPR_LOCATION (ret.value, loc);
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index c4e8930..8db0906 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -10929,7 +10929,7 @@  c_finish_omp_clauses (tree clauses)
 tree
 c_finish_transaction (location_t loc, tree block, int flags)
 {
-  tree stmt = build_stmt (loc, TRANSACTION_EXPR, block);
+  tree stmt = build_stmt (loc, TRANSACTION_EXPR, block, NULL_TREE);
   if (flags & TM_STMT_ATTR_OUTER)
     TRANSACTION_EXPR_OUTER (stmt) = 1;
   if (flags & TM_STMT_ATTR_RELAXED)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1bb6d98..d63644f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5531,8 +5531,8 @@  extern void finish_omp_barrier			(void);
 extern void finish_omp_flush			(void);
 extern void finish_omp_taskwait			(void);
 extern tree begin_transaction_stmt		(location_t, tree *, int);
-extern void finish_transaction_stmt		(tree, tree, int);
-extern tree build_transaction_expr		(location_t, tree, int);
+extern void finish_transaction_stmt		(tree, tree, int, tree);
+extern tree build_transaction_expr		(location_t, tree, int, tree);
 extern void finish_omp_taskyield		(void);
 extern bool cxx_omp_create_clause_info		(tree, tree, bool, bool, bool);
 extern tree baselink_for_fns                    (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7242fbf..5d5e276 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -19333,19 +19333,17 @@  cp_parser_base_specifier (cp_parser* parser)
 
 /* Exception handling [gram.exception] */
 
-/* Parse an (optional) exception-specification.
+/* Parse an (optional) noexcept-specification.
 
-   exception-specification:
-     throw ( type-id-list [opt] )
+   noexcept-specification:
+     noexcept ( constant-expression ) [opt]
 
-   Returns a TREE_LIST representing the exception-specification.  The
-   TREE_VALUE of each node is a type.  */
+   ??? What does it return precisely?  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_noexcept_specification_opt (cp_parser* parser)
 {
   cp_token *token;
-  tree type_id_list;
   const char *saved_message;
 
   /* Peek at the next token.  */
@@ -19378,6 +19376,32 @@  cp_parser_exception_specification_opt (cp_parser* parser)
 
       return build_noexcept_spec (expr, tf_warning_or_error);
     }
+  else
+    return NULL_TREE;
+}
+
+/* Parse an (optional) exception-specification.
+
+   exception-specification:
+     throw ( type-id-list [opt] )
+
+   Returns a TREE_LIST representing the exception-specification.  The
+   TREE_VALUE of each node is a type.  */
+
+static tree
+cp_parser_exception_specification_opt (cp_parser* parser)
+{
+  cp_token *token;
+  tree type_id_list;
+  const char *saved_message;
+
+  /* Peek at the next token.  */
+  token = cp_lexer_peek_token (parser->lexer);
+
+  /* Is it a noexcept-specification?  */
+  type_id_list = cp_parser_noexcept_specification_opt(parser);
+  if (type_id_list != NULL_TREE)
+    return type_id_list;
 
   /* If it's not `throw', then there's no exception-specification.  */
   if (!cp_parser_is_keyword (token, RID_THROW))
@@ -26598,11 +26622,9 @@  cp_parser_txn_attribute_opt (cp_parser *parser)
 /* Parse a __transaction_atomic or __transaction_relaxed statement.
 
    transaction-statement:
-     __transaction_atomic txn-attribute[opt] txn-exception-spec[opt]
+     __transaction_atomic txn-attribute[opt] txn-noexcept-spec[opt]
        compound-statement
-     __transaction_relaxed txn-exception-spec[opt] compound-statement
-
-   ??? The exception specification is not yet implemented.
+     __transaction_relaxed txn-noexcept-spec[opt] compound-statement
 */
 
 static tree
@@ -26611,7 +26633,7 @@  cp_parser_transaction (cp_parser *parser, enum rid keyword)
   unsigned char old_in = parser->in_transaction;
   unsigned char this_in = 1, new_in;
   cp_token *token;
-  tree stmt, attrs;
+  tree stmt, attrs, noex;
 
   gcc_assert (keyword == RID_TRANSACTION_ATOMIC
       || keyword == RID_TRANSACTION_RELAXED);
@@ -26629,6 +26651,9 @@  cp_parser_transaction (cp_parser *parser, enum rid keyword)
 	this_in |= parse_tm_stmt_attr (attrs, TM_STMT_ATTR_OUTER);
     }
 
+  /* Parse a noexcept specification.  */
+  noex = cp_parser_noexcept_specification_opt(parser);
+
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
 
@@ -26638,7 +26663,7 @@  cp_parser_transaction (cp_parser *parser, enum rid keyword)
   cp_parser_compound_statement (parser, NULL, false, false);
   parser->in_transaction = old_in;
 
-  finish_transaction_stmt (stmt, NULL, this_in);
+  finish_transaction_stmt (stmt, NULL, this_in, noex);
 
   return stmt;
 }
@@ -26646,10 +26671,8 @@  cp_parser_transaction (cp_parser *parser, enum rid keyword)
 /* Parse a __transaction_atomic or __transaction_relaxed expression.
 
    transaction-expression:
-     __transaction_atomic txn-exception-spec[opt] ( expression )
-     __transaction_relaxed txn-exception-spec[opt] ( expression )
-
-   ??? The exception specification is not yet implemented.
+     __transaction_atomic txn-noexcept-spec[opt] ( expression )
+     __transaction_relaxed txn-noexcept-spec[opt] ( expression )
 */
 
 static tree
@@ -26658,7 +26681,7 @@  cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   unsigned char old_in = parser->in_transaction;
   unsigned char this_in = 1;
   cp_token *token;
-  tree expr;
+  tree expr, noex;
 
   gcc_assert (keyword == RID_TRANSACTION_ATOMIC
       || keyword == RID_TRANSACTION_RELAXED);
@@ -26678,12 +26701,15 @@  cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   if (keyword == RID_TRANSACTION_RELAXED)
     this_in |= TM_STMT_ATTR_RELAXED;
 
+  /* Parse a noexcept specification.  */
+  noex = cp_parser_noexcept_specification_opt(parser);
+
   parser->in_transaction = this_in;
   cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 
   expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
   finish_parenthesized_expr (expr);
-  expr = build_transaction_expr (token->location, expr, this_in);
+  expr = build_transaction_expr (token->location, expr, this_in, noex);
 
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
   parser->in_transaction = old_in;
@@ -26741,7 +26767,7 @@  cp_parser_function_transaction (cp_parser *parser, enum rid keyword)
 
   parser->in_transaction = old_in;
 
-  finish_transaction_stmt (stmt, compound_stmt, new_in);
+  finish_transaction_stmt (stmt, compound_stmt, new_in, NULL_TREE);
 
   return ctor_initializer_p;
 }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3196d12..bd99f44 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12955,22 +12955,41 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
     case TRANSACTION_EXPR:
       {
 	int flags = 0;
+	tree noex;
 	flags |= (TRANSACTION_EXPR_OUTER (t) ? TM_STMT_ATTR_OUTER : 0);
 	flags |= (TRANSACTION_EXPR_RELAXED (t) ? TM_STMT_ATTR_RELAXED : 0);
+	noex = TRANSACTION_EXPR_NOEX (t);
+	if (noex)
+	  {
+	    if (noex != noexcept_false_spec && noex != noexcept_true_spec)
+	      {
+		/* Check the expression again.  */
+		noex = tsubst_copy_and_build (noex, args, complain, in_decl,
+					      /*function_p=*/false,
+					      /*integral_const_expr_p=*/true);
+		noex = build_noexcept_spec (TREE_PURPOSE (noex),
+					    tf_warning_or_error);
+		if (noex == error_mark_node)
+		  noex = noexcept_false_spec;
+	      }
+	    gcc_assert (noex == noexcept_false_spec
+			|| noex == noexcept_true_spec);
+	    TRANSACTION_EXPR_NOEX (t) = NULL_TREE;
+	  }
 
-        if (TRANSACTION_EXPR_IS_STMT (t))
-          {
-            stmt = begin_transaction_stmt (input_location, NULL, flags);
-            RECUR (TRANSACTION_EXPR_BODY (t));
-            finish_transaction_stmt (stmt, NULL, flags);
-          }
-        else
-          {
-            stmt = build_transaction_expr (EXPR_LOCATION (t),
+	if (TRANSACTION_EXPR_IS_STMT (t))
+	  {
+	    stmt = begin_transaction_stmt (input_location, NULL, flags);
+	    RECUR (TRANSACTION_EXPR_BODY (t));
+	    finish_transaction_stmt (stmt, NULL, flags, noex);
+	  }
+	else
+	  {
+	    stmt = build_transaction_expr (EXPR_LOCATION (t),
 					   RECUR (TRANSACTION_EXPR_BODY (t)),
-					   flags);
-            return stmt;
-          }
+					   flags, noex);
+	    return stmt;
+	  }
       }
       break;
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 068754a..a5be1b1 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4977,7 +4977,7 @@  begin_transaction_stmt (location_t loc, tree *pcompound, int flags)
   if (pcompound)
     *pcompound = begin_compound_stmt (0);
 
-  r = build_stmt (loc, TRANSACTION_EXPR, NULL_TREE);
+  r = build_stmt (loc, TRANSACTION_EXPR, NULL_TREE, NULL_TREE);
 
   /* Only add the statement to the function if support enabled.  */
   if (flag_tm)
@@ -4995,28 +4995,52 @@  begin_transaction_stmt (location_t loc, tree *pcompound, int flags)
 
 /* End a __transaction_atomic or __transaction_relaxed statement.
    If COMPOUND_STMT is non-null, this is for a function-transaction-block,
-   and we should end the compound.  */
+   and we should end the compound.  If NOEX is NOEXCEPT_TRUE_SPEC, we wrap
+   the body in a MUST_NOT_THROW_EXPR.  */
 
 void
-finish_transaction_stmt (tree stmt, tree compound_stmt, int flags)
+finish_transaction_stmt (tree stmt, tree compound_stmt, int flags, tree noex)
 {
   TRANSACTION_EXPR_BODY (stmt) = pop_stmt_list (TRANSACTION_EXPR_BODY (stmt));
   TRANSACTION_EXPR_OUTER (stmt) = (flags & TM_STMT_ATTR_OUTER) != 0;
   TRANSACTION_EXPR_RELAXED (stmt) = (flags & TM_STMT_ATTR_RELAXED) != 0;
   TRANSACTION_EXPR_IS_STMT (stmt) = 1;
 
+  /* noexcept specifications are not allowed for function transactions.  */
+  gcc_assert (!(noex && compound_stmt));
+  if (noex == noexcept_true_spec)
+    {
+      tree body = TRANSACTION_EXPR_BODY (stmt);
+      body = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (body), body);
+      SET_EXPR_LOCATION(body, EXPR_LOCATION (TRANSACTION_EXPR_BODY (stmt)));
+      TREE_SIDE_EFFECTS (body) = 1;
+      TRANSACTION_EXPR_BODY (stmt) = body;
+    }
+  else if (noex != noexcept_false_spec && noex != error_mark_node)
+    TRANSACTION_EXPR_NOEX (stmt) = noex;
+
   if (compound_stmt)
     finish_compound_stmt (compound_stmt);
   finish_stmt ();
 }
 
-/* Build a __transaction_atomic or __transaction_relaxed expression.  */
+/* Build a __transaction_atomic or __transaction_relaxed expression.  If
+   NOEX is NOEXCEPT_TRUE_SPEC, we wrap the body in a MUST_NOT_THROW_EXPR.  */
 
 tree
-build_transaction_expr (location_t loc, tree expr, int flags)
+build_transaction_expr (location_t loc, tree expr, int flags, tree noex)
 {
   tree ret;
-  ret = build1 (TRANSACTION_EXPR, TREE_TYPE (expr), expr);
+  if (noex == noexcept_true_spec)
+    {
+      expr = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (expr), expr);
+      SET_EXPR_LOCATION(expr, loc);
+      TREE_SIDE_EFFECTS (expr) = 1;
+      noex = NULL_TREE;
+    }
+  else if (noex == noexcept_false_spec || noex == error_mark_node)
+    noex = NULL_TREE;
+  ret = build2 (TRANSACTION_EXPR, TREE_TYPE (expr), expr, noex);
   if (flags & TM_STMT_ATTR_RELAXED)
 	TRANSACTION_EXPR_RELAXED (ret) = 1;
   SET_EXPR_LOCATION (ret, loc);
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-1.C b/gcc/testsuite/g++.dg/tm/noexcept-1.C
new file mode 100644
index 0000000..86940d2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-1.C
@@ -0,0 +1,38 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+struct TrueFalse
+{
+  static constexpr bool v() { return true; }
+};
+
+int global;
+
+template<typename T> int foo()
+{
+  __transaction_atomic noexcept(T::v()) { global += 1; }
+  return __transaction_atomic noexcept(T::v()) (global + 2);
+}
+
+int f1()
+{
+  return foo<TrueFalse>();
+}
+
+int f2()
+{
+  return __transaction_atomic noexcept(true) (global + 3)
+         + __transaction_atomic noexcept(TrueFalse::v()) (global + 4);
+}
+
+int f3()
+{
+  __transaction_atomic noexcept(true) { global += 5; }
+  __transaction_atomic noexcept(TrueFalse::v()) { global += 6; }
+  return global;
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 6 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU4\\s*\\(&global" 6 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-2.C b/gcc/testsuite/g++.dg/tm/noexcept-2.C
new file mode 100644
index 0000000..f9cbbb6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-2.C
@@ -0,0 +1,20 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm -std=c++0x" }
+
+// All of these must fail, because they are not constant expressions.
+template<typename T> int foo(int x, T t)
+{
+  __transaction_atomic noexcept(t) { x++; }      /* { dg-error "not a constant" } */
+  return __transaction_atomic noexcept(t) (x+1); /* { dg-error "not a constant" } */
+}
+
+int bar(int x)
+{
+  __transaction_atomic noexcept(x == 23) { x++; }      /* { dg-error "not a constant" } */
+  return __transaction_atomic noexcept(x == 42) (x+1); /* { dg-error "not a constant" } */
+}
+
+int f(int x)
+{
+  return foo<bool>(x, true);
+}
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-3.C b/gcc/testsuite/g++.dg/tm/noexcept-3.C
new file mode 100644
index 0000000..c2cb7bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-3.C
@@ -0,0 +1,40 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+// Same as noexcept-1.C but no noexcept at all.
+
+struct TrueFalse
+{
+  static constexpr bool v() { return false; }
+};
+
+int global;
+
+template<typename T> int foo()
+{
+  __transaction_atomic noexcept(T::v()) { global += 1; }
+  return __transaction_atomic noexcept(T::v()) (global + 2);
+}
+
+int f1()
+{
+  return foo<TrueFalse>();
+}
+
+int f2()
+{
+  return __transaction_atomic noexcept(false) (global + 3)
+         + __transaction_atomic noexcept(TrueFalse::v()) (global + 4);
+}
+
+int f3()
+{
+  __transaction_atomic noexcept(false) { global += 5; }
+  __transaction_atomic noexcept(TrueFalse::v()) { global += 6; }
+  return global;
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 0 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU4\\s*\\(&global" 6 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-4.C b/gcc/testsuite/g++.dg/tm/noexcept-4.C
new file mode 100644
index 0000000..a54f482
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-4.C
@@ -0,0 +1,14 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+int global;
+
+int f2()
+{
+  return __transaction_atomic noexcept(true) (global + 3);
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 1 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU4\\s*\\(&global" 1 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-5.C b/gcc/testsuite/g++.dg/tm/noexcept-5.C
new file mode 100644
index 0000000..f86bbb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-5.C
@@ -0,0 +1,23 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+int global;
+
+void f2(int x)
+{
+  __transaction_atomic
+    {
+      __transaction_atomic noexcept(true)
+        {
+	  global += 1;
+	  if (x)
+	      throw 23;
+        }
+    }
+}
+// FIXME: check that there actually is a call to terminate!
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 1 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU4\\s*\\(&global" 1 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/tree.def b/gcc/tree.def
index 2a2363e..e87fc61 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1077,8 +1077,9 @@  DEFTREECODE (OMP_ATOMIC_CAPTURE_NEW, "omp_atomic_capture_new", tcc_statement, 2)
 DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 0)
 
 /* TRANSACTION_EXPR tree code.
-   Operand 0: BODY: contains body of the transaction.  */
-DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 1)
+   Operand 0: BODY: contains body of the transaction.
+   Operand 1: contains noexcept argument, if noexcept has been specified.  */
+DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 2)
 
 /* Reduction operations.
    Operations that take a vector of elements and "reduce" it to a scalar
diff --git a/gcc/tree.h b/gcc/tree.h
index 2310801..9c5aada 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1817,6 +1817,8 @@  extern void protected_set_expr_location (tree, location_t);
 /* TM directives and accessors.  */
 #define TRANSACTION_EXPR_BODY(NODE) \
   TREE_OPERAND (TRANSACTION_EXPR_CHECK (NODE), 0)
+#define TRANSACTION_EXPR_NOEX(NODE) \
+  TREE_OPERAND (TRANSACTION_EXPR_CHECK (NODE), 1)
 #define TRANSACTION_EXPR_OUTER(NODE) \
   (TRANSACTION_EXPR_CHECK (NODE)->base.static_flag)
 #define TRANSACTION_EXPR_RELAXED(NODE) \