diff mbox

[rfc] Fix PR52770 (supoport throwing asms)

Message ID Pine.LNX.4.64.1203301816260.25409@wotan.suse.de
State New
Headers show

Commit Message

Michael Matz March 30, 2012, 4:37 p.m. UTC
Hi,

So here's an extended variant of my hack that implements throwing asms.  
Like rth proposed I've added a new pseudo clobber, "throw":

int f (void)
{
  int x, y;
  x = 1;
  y = 2;
  try {
    __asm__ ("" : "=r"(x), "=r"(y) : : "throw");
  } catch (...) {
    return 2+x+y;
  }
  return x+y;
}

The patch handles multiple output arguments by doing the same we do for 
calls, i.e. introducing new temporaries.  For gimple this clobber is 
retained and hence a throwing asm can be simply recognized by searching 
for it.  For RTL I've added a new flag to mark the ASM_OPERANDS rtx.  
Without such marking we would have to treat every asm as potentially 
throwing in insn_could_throw_p, and rely on the REG_EH_REGION notes to 
mark them as non-throwing.  Instead of auditing all MEM_VOLATILE_P uses on 
asms to see if they require handling throwing I've settled on simply 
implying the volatile bit for throwing asms.

Not yet regstrapped, so no rfa, but does this seem sane?


Ciao,
Michael.

Comments

Richard Henderson March 30, 2012, 4:43 p.m. UTC | #1
On 03/30/2012 12:37 PM, Michael Matz wrote:
> Not yet regstrapped, so no rfa, but does this seem sane?

It definitely seems plausible.  I can't immediately think of
anything you might have forgotten.


r~
Richard Henderson March 30, 2012, 5:38 p.m. UTC | #2
On 03/30/2012 12:43 PM, Richard Henderson wrote:
> On 03/30/2012 12:37 PM, Michael Matz wrote:
>> Not yet regstrapped, so no rfa, but does this seem sane?
> 
> It definitely seems plausible.  I can't immediately think of
> anything you might have forgotten.

Stating the obvious, but you'd do well to add at least an
x86 test case that does a call to a local function which does throw.


r~
diff mbox

Patch

Index: tree-eh.c
===================================================================
--- tree-eh.c	(revision 183716)
+++ tree-eh.c	(working copy)
@@ -1990,6 +1990,44 @@  lower_eh_constructs_2 (struct leh_state
 	}
       break;
 
+    case GIMPLE_ASM:
+      /* Similar to normal LHS handling above, replace outputs
+         with new temporaries.  */
+      if (stmt_could_throw_p (stmt)
+	  && gimple_code (stmt) == GIMPLE_ASM)
+	{
+	  unsigned noutputs;
+	  unsigned i;
+
+	  noutputs = gimple_asm_noutputs (stmt);
+	  for (i = 0; i < noutputs; i++)
+	    {
+	      tree link, op;
+	      link = gimple_asm_output_op (stmt, i);
+	      op = TREE_VALUE (link);
+	      if (!tree_could_throw_p (op)
+		  && is_gimple_reg_type (TREE_TYPE (op)))
+		{
+		  tree tmp = create_tmp_var (TREE_TYPE (op), NULL);
+		  gimple s = gimple_build_assign (op, tmp);
+		  gimple_set_location (s, gimple_location (stmt));
+		  gimple_set_block (s, gimple_block (stmt));
+		  TREE_VALUE (link) = tmp;
+		  if (TREE_CODE (TREE_TYPE (tmp)) == COMPLEX_TYPE
+		      || TREE_CODE (TREE_TYPE (tmp)) == VECTOR_TYPE)
+		    DECL_GIMPLE_REG_P (tmp) = 1;
+		  gsi_insert_after (gsi, s, GSI_SAME_STMT);
+		}
+	    }
+	}
+      /* Look for things that can throw exceptions, and record them.  */
+      if (state->cur_region && stmt_could_throw_p (stmt))
+	{
+	  record_stmt_eh_region (state->cur_region, stmt);
+	  note_eh_region_may_contain_throw (state->cur_region);
+	}
+      break;
+
     case GIMPLE_COND:
     case GIMPLE_GOTO:
     case GIMPLE_RETURN:
@@ -2639,6 +2677,8 @@  stmt_could_throw_p (gimple stmt)
       return stmt_could_throw_1_p (stmt);
 
     case GIMPLE_ASM:
+      if (gimple_asm_can_throw_p (stmt))
+	return true;
       if (!cfun->can_throw_non_call_exceptions)
         return false;
       return gimple_asm_volatile_p (stmt);
Index: varasm.c
===================================================================
--- varasm.c	(revision 183716)
+++ varasm.c	(working copy)
@@ -834,9 +834,10 @@  set_user_assembler_name (tree decl, cons
 
 /* Decode an `asm' spec for a declaration as a register name.
    Return the register number, or -1 if nothing specified,
-   or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized,
+   or -2 if the ASMSPEC is not `cc', `memory' or `throw' and is not recognized,
    or -3 if ASMSPEC is `cc' and is not recognized,
-   or -4 if ASMSPEC is `memory' and is not recognized.
+   or -4 if ASMSPEC is `memory' and is not recognized,
+   or -5 if ASMSPEC is `throw' and is not recognized.
    Accept an exact spelling or a decimal number.
    Prefixes such as % are optional.  */
 
@@ -902,6 +903,9 @@  decode_reg_name_and_count (const char *a
       }
 #endif /* ADDITIONAL_REGISTER_NAMES */
 
+      if (!strcmp (asmspec, "throw"))
+	return -5;
+
       if (!strcmp (asmspec, "memory"))
 	return -4;
 
Index: rtl.h
===================================================================
--- rtl.h	(revision 183716)
+++ rtl.h	(working copy)
@@ -266,7 +266,8 @@  struct GTY((chain_next ("RTX_NEXT (&%h)"
      1 in a CALL_INSN if it is a sibling call.
      1 in a SET that is for a return.
      In a CODE_LABEL, part of the two-bit alternate entry field.
-     1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c.  */
+     1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c.
+     1 in an ASM_OPERANDS is ASM_OPERANDS_THROW_P.  */
   unsigned int jump : 1;
   /* In a CODE_LABEL, part of the two-bit alternate entry field.
      1 in a MEM if it cannot trap.
@@ -1317,6 +1318,8 @@  do {									\
 #define ASM_OPERANDS_LABEL_LENGTH(RTX) XCVECLEN (RTX, 5, ASM_OPERANDS)
 #define ASM_OPERANDS_LABEL(RTX, N) XCVECEXP (RTX, 5, N, ASM_OPERANDS)
 #define ASM_OPERANDS_SOURCE_LOCATION(RTX) XCUINT (RTX, 6, ASM_OPERANDS)
+#define ASM_OPERANDS_THROW_P(RTX) \
+  (RTL_FLAG_CHECK1("ASM_OPERANDS_THROW_P", (RTX), ASM_OPERANDS)->jump)
 #define ASM_INPUT_SOURCE_LOCATION(RTX) XCUINT (RTX, 1, ASM_INPUT)
 
 /* 1 if RTX is a mem that is statically allocated in read-only memory.  */
Index: gimple.c
===================================================================
--- gimple.c	(revision 183716)
+++ gimple.c	(working copy)
@@ -5513,4 +5513,21 @@  gimple_asm_clobbers_memory_p (const_gimp
 
   return false;
 }
+
+/* Return true if the GIMPLE_ASM STMT can throw.  */
+
+bool
+gimple_asm_can_throw_p (const_gimple stmt)
+{
+  unsigned i;
+
+  for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
+    {
+      tree op = gimple_asm_clobber_op (stmt, i);
+      if (strcmp (TREE_STRING_POINTER (TREE_VALUE (op)), "throw") == 0)
+	return true;
+    }
+
+  return false;
+}
 #include "gt-gimple.h"
Index: gimple.h
===================================================================
--- gimple.h	(revision 183716)
+++ gimple.h	(working copy)
@@ -1030,6 +1030,7 @@  extern bool walk_stmt_load_store_ops (gi
 extern bool gimple_ior_addresses_taken (bitmap, gimple);
 extern bool gimple_call_builtin_p (gimple, enum built_in_function);
 extern bool gimple_asm_clobbers_memory_p (const_gimple);
+extern bool gimple_asm_can_throw_p (const_gimple);
 
 /* In gimplify.c  */
 extern tree create_tmp_var_raw (tree, const char *);
Index: tree-cfg.c
===================================================================
--- tree-cfg.c	(revision 183716)
+++ tree-cfg.c	(working copy)
@@ -580,6 +580,8 @@  make_edges (void)
 
 	    case GIMPLE_ASM:
 	      make_gimple_asm_edges (bb);
+	      if (is_ctrl_altering_stmt (last))
+		make_eh_edges (last);
 	      fallthru = true;
 	      break;
 
Index: stmt.c
===================================================================
--- stmt.c	(revision 183716)
+++ stmt.c	(working copy)
@@ -657,6 +657,7 @@  expand_asm_operands (tree string, tree o
   enum machine_mode *inout_mode = XALLOCAVEC (enum machine_mode, noutputs);
   const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs);
   int old_generating_concat_p = generating_concat_p;
+  bool throw_p = false;
 
   /* An ASM with no outputs needs to be treated as volatile, for now.  */
   if (noutputs == 0)
@@ -696,6 +697,11 @@  expand_asm_operands (tree string, tree o
       i = decode_reg_name_and_count (regname, &nregs);
       if (i == -4)
 	++nclobbers;
+      else if (i == -5)
+	{
+	  throw_p = true;
+	  vol = 1;
+	}
       else if (i == -2)
 	error ("unknown register name %qs in %<asm%>", regname);
 
@@ -872,6 +878,7 @@  expand_asm_operands (tree string, tree o
 			       labelvec, locus);
 
   MEM_VOLATILE_P (body) = vol;
+  ASM_OPERANDS_THROW_P (body) = throw_p;
 
   /* Eval the inputs and put them into ARGVEC.
      Put their constraints into ASM_INPUTs and store in CONSTRAINTS.  */
@@ -1028,6 +1035,7 @@  expand_asm_operands (tree string, tree o
 			    i, argvec, constraintvec, labelvec, locus));
 
 	  MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
+	  ASM_OPERANDS_THROW_P (SET_SRC (XVECEXP (body, 0, i))) = throw_p;
 	}
 
       /* If there are no outputs (but there are some clobbers)
Index: except.c
===================================================================
--- except.c	(revision 183716)
+++ except.c	(working copy)
@@ -1622,12 +1622,16 @@  make_reg_eh_region_note_nothrow_nononloc
 bool
 insn_could_throw_p (const_rtx insn)
 {
+  rtx tmp;
   if (!flag_exceptions)
     return false;
   if (CALL_P (insn))
     return true;
   if (INSN_P (insn) && cfun->can_throw_non_call_exceptions)
     return may_trap_p (PATTERN (insn));
+  if (INSN_P (insn)
+      && (tmp = extract_asm_operands (PATTERN (insn))) != NULL)
+    return ASM_OPERANDS_THROW_P (tmp);
   return false;
 }
 
Index: cfgbuild.c
===================================================================
--- cfgbuild.c	(revision 183716)
+++ cfgbuild.c	(working copy)
@@ -82,6 +82,7 @@  inside_basic_block_p (const_rtx insn)
 bool
 control_flow_insn_p (const_rtx insn)
 {
+  rtx tmp;
   switch (GET_CODE (insn))
     {
     case NOTE:
@@ -112,6 +113,8 @@  control_flow_insn_p (const_rtx insn)
       if (GET_CODE (PATTERN (insn)) == TRAP_IF
 	  && XEXP (PATTERN (insn), 0) == const1_rtx)
 	return true;
+      if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
+	return can_throw_internal (insn);
       if (!cfun->can_throw_non_call_exceptions)
 	return false;
       break;
@@ -364,6 +367,8 @@  make_edges (basic_block min, basic_block
 		}
 	    }
 	}
+      else if (code == INSN && extract_asm_operands (PATTERN (insn)))
+	rtl_make_eh_edge (edge_cache, bb, insn);
 
       /* Find out if we can drop through to the next block.  */
       insn = NEXT_INSN (insn);
Index: testsuite/g++.dg/eh/pr52770.C
===================================================================
--- testsuite/g++.dg/eh/pr52770.C	(revision 0)
+++ testsuite/g++.dg/eh/pr52770.C	(revision 0)
@@ -0,0 +1,16 @@ 
+// PR c++/52770
+// { dg-do compile }
+// { dg-final { scan-assembler "EHB" } }
+// { dg-options "-O2" }
+int f (void)
+{
+  int x, y;
+  x = 1;
+  y = 2;
+  try {
+    __asm__ ("" : "=r"(x), "=r"(y) : : "throw");
+  } catch (...) {
+    return 2+x+y;
+  }
+  return x+y;
+}