diff mbox

[4/6] Convert to md_asm_adjust

Message ID 1431034740-5375-5-git-send-email-rth@redhat.com
State New
Headers show

Commit Message

Richard Henderson May 7, 2015, 9:38 p.m. UTC
Using proper vectors instead of lists of trees.
---
 gcc/cfgexpand.c              | 614 ++++++++++++++++++++-----------------------
 gcc/config/cris/cris.c       |  88 +++----
 gcc/config/i386/i386.c       |  24 +-
 gcc/config/mn10300/mn10300.c |  20 +-
 gcc/config/rs6000/rs6000.c   |  17 +-
 gcc/config/visium/visium.c   |  21 +-
 gcc/config/vxworks.c         |   2 +-
 gcc/doc/tm.texi              |  18 +-
 gcc/doc/tm.texi.in           |   2 +-
 gcc/gimple.c                 |   2 +-
 gcc/hooks.c                  |   8 -
 gcc/hooks.h                  |   1 -
 gcc/incpath.c                |   1 +
 gcc/mode-switching.c         |   2 +-
 gcc/system.h                 |   1 +
 gcc/target.def               |  30 ++-
 16 files changed, 401 insertions(+), 450 deletions(-)

Comments

Jeff Law May 8, 2015, 7:41 p.m. UTC | #1
On 05/07/2015 03:38 PM, Richard Henderson wrote:
> Using proper vectors instead of lists of trees.
> ---
>   gcc/cfgexpand.c              | 614 ++++++++++++++++++++-----------------------
>   gcc/config/cris/cris.c       |  88 +++----
>   gcc/config/i386/i386.c       |  24 +-
>   gcc/config/mn10300/mn10300.c |  20 +-
>   gcc/config/rs6000/rs6000.c   |  17 +-
>   gcc/config/visium/visium.c   |  21 +-
>   gcc/config/vxworks.c         |   2 +-
>   gcc/doc/tm.texi              |  18 +-
>   gcc/doc/tm.texi.in           |   2 +-
>   gcc/gimple.c                 |   2 +-
>   gcc/hooks.c                  |   8 -
>   gcc/hooks.h                  |   1 -
>   gcc/incpath.c                |   1 +
>   gcc/mode-switching.c         |   2 +-
>   gcc/system.h                 |   1 +
>   gcc/target.def               |  30 ++-
>   16 files changed, 401 insertions(+), 450 deletions(-)
ChangeLog and the usual testing.

One less use of TREE_LIST, is goodness.

I'm going to assume the include header order juggling/additions were all 
necessary.

Presumably we don't have a symbolic return value from decode_reg_name. 
   That'd be a good cleanup for someone.


Much of the cfgexpand bits were painful to read.  I did my best, things 
look reasonable, but that's largely from reading the comments and 
scanning the relevant code -- trying to match up old behaviour and new 
behaviour across the board was tough.  I realize this mess isn't of your 
making :-)

With the usual testing, this is fine.

jeff
Richard Henderson May 8, 2015, 7:56 p.m. UTC | #2
On 05/08/2015 12:41 PM, Jeff Law wrote:
> I'm going to assume the include header order juggling/additions were all
> necessary.

Correct -- target.h now uses vec<>, which means that vec.h must be included
first.  In the olden days I'd have just put the vec.h include at the top of
target.h.  ;-)

> Presumably we don't have a symbolic return value from decode_reg_name.   That'd
> be a good cleanup for someone.

Correct.  Since the beginning of time, it would seem.

> Much of the cfgexpand bits were painful to read.  I did my best, things look
> reasonable, but that's largely from reading the comments and scanning the
> relevant code -- trying to match up old behaviour and new behaviour across the
> board was tough.  I realize this mess isn't of your making :-)

Yeah.  Sadly there was no smaller step; you can't half-covert a variable from
lists to vectors.  And since the existing target hook used the lists, they had
to be done at the same time.



r~
diff mbox

Patch

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index fbd2101..d73678c 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2438,14 +2438,12 @@  n_occurrences (int c, const char *s)
    the same number of alternatives.  Return true if so.  */
 
 static bool
-check_operand_nalternatives (tree outputs, tree inputs)
+check_operand_nalternatives (const vec<const char *> &constraints)
 {
-  if (outputs || inputs)
+  unsigned len = constraints.length();
+  if (len > 0)
     {
-      tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
-      int nalternatives
-	= n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp)));
-      tree next = inputs;
+      int nalternatives = n_occurrences (',', constraints[0]);
 
       if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
 	{
@@ -2453,26 +2451,14 @@  check_operand_nalternatives (tree outputs, tree inputs)
 	  return false;
 	}
 
-      tmp = outputs;
-      while (tmp)
-	{
-	  const char *constraint
-	    = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp)));
-
-	  if (n_occurrences (',', constraint) != nalternatives)
-	    {
-	      error ("operand constraints for %<asm%> differ "
-		     "in number of alternatives");
-	      return false;
-	    }
-
-	  if (TREE_CHAIN (tmp))
-	    tmp = TREE_CHAIN (tmp);
-	  else
-	    tmp = next, next = 0;
-	}
+      for (unsigned i = 1; i < len; ++i)
+	if (n_occurrences (',', constraints[i]) != nalternatives)
+	  {
+	    error ("operand constraints for %<asm%> differ "
+		   "in number of alternatives");
+	    return false;
+	  }
     }
-
   return true;
 }
 
@@ -2524,156 +2510,145 @@  tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
 static void
 expand_asm_stmt (gasm *stmt)
 {
-  int noutputs, ninputs, nclobbers, nlabels, i;
-  tree string, outputs, inputs, clobbers, labels, tail, t;
-  location_t locus = gimple_location (stmt);
-  basic_block fallthru_bb = NULL;
-
-  /* Meh... convert the gimple asm operands into real tree lists.
-     Eventually we should make all routines work on the vectors instead
-     of relying on TREE_CHAIN.  */
-  outputs = NULL_TREE;
-  noutputs = gimple_asm_noutputs (stmt);
-  if (noutputs > 0)
-    {
-      t = outputs = gimple_asm_output_op (stmt, 0);
-      for (i = 1; i < noutputs; i++)
-	t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
-    }
+  class save_input_location
+  {
+    location_t old;
 
-  inputs = NULL_TREE;
-  ninputs = gimple_asm_ninputs (stmt);
-  if (ninputs > 0)
+  public:
+    explicit save_input_location(location_t where)
     {
-      t = inputs = gimple_asm_input_op (stmt, 0);
-      for (i = 1; i < ninputs; i++)
-	t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
+      old = input_location;
+      input_location = where;
     }
 
-  clobbers = NULL_TREE;
-  nclobbers = gimple_asm_nclobbers (stmt);
-  if (nclobbers > 0)
+    ~save_input_location()
     {
-      t = clobbers = gimple_asm_clobber_op (stmt, 0);
-      for (i = 1; i < nclobbers; i++)
-	t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
+      input_location = old;
     }
+  };
 
-  labels = NULL_TREE;
-  nlabels = gimple_asm_nlabels (stmt);
-  if (nlabels > 0)
+  location_t locus = gimple_location (stmt);
+
+  if (gimple_asm_input_p (stmt))
     {
-      edge fallthru = find_fallthru_edge (gimple_bb (stmt)->succs);
-      if (fallthru)
-	fallthru_bb = fallthru->dest;
-      t = labels = gimple_asm_label_op (stmt, 0);
-      for (i = 1; i < nlabels; i++)
-	t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
+      const char *s = gimple_asm_string (stmt);
+      tree string = build_string (strlen (s), s);
+      expand_asm_loc (string, gimple_asm_volatile_p (stmt), locus);
+      return;
     }
 
-  {
-    const char *s = gimple_asm_string (stmt);
-    string = build_string (strlen (s), s);
-  }
+  /* There are some legacy diagnostics in here, and also avoids a
+     sixth parameger to targetm.md_asm_adjust.  */
+  save_input_location s_i_l(locus);
 
-  if (gimple_asm_input_p (stmt))
+  unsigned noutputs = gimple_asm_noutputs (stmt);
+  unsigned ninputs = gimple_asm_ninputs (stmt);
+  unsigned nlabels = gimple_asm_nlabels (stmt);
+  unsigned i;
+
+  /* ??? Diagnose during gimplification?  */
+  if (ninputs + noutputs + nlabels > MAX_RECOG_OPERANDS)
     {
-      expand_asm_loc (string, gimple_asm_volatile_p (stmt), locus);
+      error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
       return;
     }
 
-  /* Record the contents of OUTPUTS before it is modified.  */
-  tree *orig_outputs = XALLOCAVEC (tree, noutputs);
-  for (i = 0; i < noutputs; ++i)
-    orig_outputs[i] = TREE_VALUE (gimple_asm_output_op (stmt, i));
+  auto_vec<tree, MAX_RECOG_OPERANDS> output_tvec;
+  auto_vec<tree, MAX_RECOG_OPERANDS> input_tvec;
+  auto_vec<const char *, MAX_RECOG_OPERANDS> constraints;
 
-  rtvec argvec, constraintvec, labelvec;
-  rtx body;
-  int ninout;
-  HARD_REG_SET clobbered_regs;
-  int clobber_conflict_found = 0;
-  /* Vector of RTX's of evaluated output operands.  */
-  rtx *output_rtx = XALLOCAVEC (rtx, noutputs);
-  int *inout_opnum = XALLOCAVEC (int, noutputs);
-  rtx *real_output_rtx = XALLOCAVEC (rtx, noutputs);
-  machine_mode *inout_mode = XALLOCAVEC (machine_mode, noutputs);
-  const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs);
-  int old_generating_concat_p = generating_concat_p;
-  rtx_code_label *fallthru_label = NULL;
+  /* Copy the gimple vectors into new vectors that we can manipulate.  */
 
-  if (! check_operand_nalternatives (outputs, inputs))
-    return;
+  output_tvec.safe_grow (noutputs);
+  input_tvec.safe_grow (ninputs);
+  constraints.safe_grow (noutputs + ninputs);
 
-  /* Collect constraints.  */
-  i = 0;
-  for (t = outputs; t ; t = TREE_CHAIN (t), i++)
-    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
-  for (t = inputs; t ; t = TREE_CHAIN (t), i++)
-    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+  for (i = 0; i < noutputs; ++i)
+    {
+      tree t = gimple_asm_output_op (stmt, i);
+      output_tvec[i] = TREE_VALUE (t);
+      constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+    }
+  for (i = 0; i < ninputs; i++)
+    {
+      tree t = gimple_asm_input_op (stmt, i);
+      input_tvec[i] = TREE_VALUE (t);
+      constraints[i + noutputs]
+	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+    }
 
-  /* Sometimes we wish to automatically clobber registers across an asm.
-     Case in point is when the i386 backend moved from cc0 to a hard reg --
-     maintaining source-level compatibility means automatically clobbering
-     the flags register.  */
-  clobbers = targetm.md_asm_clobbers (outputs, inputs, clobbers);
+  /* ??? Diagnose during gimplification?  */
+  if (! check_operand_nalternatives (constraints))
+    return;
 
   /* Count the number of meaningful clobbered registers, ignoring what
      we would ignore later.  */
-  nclobbers = 0;
+  auto_vec<rtx> clobber_rvec;
+  HARD_REG_SET clobbered_regs;
   CLEAR_HARD_REG_SET (clobbered_regs);
-  for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
-    {
-      const char *regname;
-      int nregs;
-
-      if (TREE_VALUE (tail) == error_mark_node)
-	return;
-      regname = TREE_STRING_POINTER (TREE_VALUE (tail));
-
-      i = decode_reg_name_and_count (regname, &nregs);
-      if (i == -4)
-	++nclobbers;
-      else if (i == -2)
-	error ("unknown register name %qs in %<asm%>", regname);
 
-      /* Mark clobbered registers.  */
-      if (i >= 0)
-        {
-	  int reg;
+  if (unsigned n = gimple_asm_nclobbers (stmt))
+    {
+      clobber_rvec.reserve (n);
+      for (i = 0; i < n; i++)
+	{
+	  tree t = gimple_asm_clobber_op (stmt, i);
+          const char *regname = TREE_STRING_POINTER (TREE_VALUE (t));
+	  int nregs, j;
 
-	  for (reg = i; reg < i + nregs; reg++)
+	  j = decode_reg_name_and_count (regname, &nregs);
+	  if (j < 0)
 	    {
-	      ++nclobbers;
-
-	      /* Clobbering the PIC register is an error.  */
-	      if (reg == (int) PIC_OFFSET_TABLE_REGNUM)
+	      if (j == -2)
 		{
-		  error ("PIC register clobbered by %qs in %<asm%>", regname);
-		  return;
+		  /* ??? Diagnose during gimplification?  */
+		  error ("unknown register name %qs in %<asm%>", regname);
+		}
+	      else if (j == -4)
+		{
+		  rtx x = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+		  clobber_rvec.safe_push (x);
+		}
+	      else
+		{
+		  /* Otherwise we should have -1 == empty string
+		     or -3 == cc, which is not a register.  */
+		  gcc_assert (j == -1 || j == -3);
 		}
-
-	      SET_HARD_REG_BIT (clobbered_regs, reg);
 	    }
+	  else
+	    for (int reg = j; reg < j + nregs; reg++)
+	      {
+		/* Clobbering the PIC register is an error.  */
+		if (reg == (int) PIC_OFFSET_TABLE_REGNUM)
+		  {
+		    /* ??? Diagnose during gimplification?  */
+		    error ("PIC register clobbered by %qs in %<asm%>",
+			   regname);
+		    return;
+		  }
+
+	        SET_HARD_REG_BIT (clobbered_regs, reg);
+	        rtx x = gen_rtx_REG (reg_raw_mode[reg], reg);
+		clobber_rvec.safe_push (x);
+	      }
 	}
     }
+  unsigned nclobbers = clobber_rvec.length();
 
   /* First pass over inputs and outputs checks validity and sets
      mark_addressable if needed.  */
+  /* ??? Diagnose during gimplification?  */
 
-  ninout = 0;
-  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+  for (i = 0; i < noutputs; ++i)
     {
-      tree val = TREE_VALUE (tail);
+      tree val = output_tvec[i];
       tree type = TREE_TYPE (val);
       const char *constraint;
       bool is_inout;
       bool allows_reg;
       bool allows_mem;
 
-      /* If there's an erroneous arg, emit no insn.  */
-      if (type == error_mark_node)
-	return;
-
       /* Try to parse the output constraint.  If that fails, there's
 	 no point in going further.  */
       constraint = constraints[i];
@@ -2688,35 +2663,21 @@  expand_asm_stmt (gasm *stmt)
 		  && REG_P (DECL_RTL (val))
 		  && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
 	mark_addressable (val);
-
-      if (is_inout)
-	ninout++;
-    }
-
-  ninputs += ninout;
-  if (ninputs + noutputs + nlabels > MAX_RECOG_OPERANDS)
-    {
-      error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
-      return;
     }
 
-  for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail))
+  for (i = 0; i < ninputs; ++i)
     {
       bool allows_reg, allows_mem;
       const char *constraint;
 
-      /* If there's an erroneous arg, emit no insn, because the ASM_INPUT
-	 would get VOIDmode and that could cause a crash in reload.  */
-      if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
-	return;
-
       constraint = constraints[i + noutputs];
-      if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
-				    constraints, &allows_mem, &allows_reg))
+      if (! parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
+				    constraints.address (),
+				    &allows_mem, &allows_reg))
 	return;
 
       if (! allows_reg && allows_mem)
-	mark_addressable (TREE_VALUE (tail));
+	mark_addressable (input_tvec[i]);
     }
 
   /* Second pass evaluates arguments.  */
@@ -2724,17 +2685,21 @@  expand_asm_stmt (gasm *stmt)
   /* Make sure stack is consistent for asm goto.  */
   if (nlabels > 0)
     do_pending_stack_adjust ();
+  int old_generating_concat_p = generating_concat_p;
+
+  /* Vector of RTX's of evaluated output operands.  */
+  auto_vec<rtx, MAX_RECOG_OPERANDS> output_rvec;
+  auto_vec<int, MAX_RECOG_OPERANDS> inout_opnum;
+  rtx_insn *after_rtl_seq = NULL, *after_rtl_end = NULL;
+
+  output_rvec.safe_grow (noutputs);
 
-  ninout = 0;
-  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+  for (i = 0; i < noutputs; ++i)
     {
-      tree val = TREE_VALUE (tail);
+      tree val = output_tvec[i];
       tree type = TREE_TYPE (val);
-      bool is_inout;
-      bool allows_reg;
-      bool allows_mem;
+      bool is_inout, allows_reg, allows_mem, ok;
       rtx op;
-      bool ok;
 
       ok = parse_output_constraint (&constraints[i], i, ninputs,
 				    noutputs, &allows_mem, &allows_reg,
@@ -2743,12 +2708,11 @@  expand_asm_stmt (gasm *stmt)
 
       /* If an output operand is not a decl or indirect ref and our constraint
 	 allows a register, make a temporary to act as an intermediate.
-	 Make the asm insn write into that, then our caller will copy it to
+	 Make the asm insn write into that, then we will copy it to
 	 the real output operand.  Likewise for promoted variables.  */
 
       generating_concat_p = 0;
 
-      real_output_rtx[i] = NULL_RTX;
       if ((TREE_CODE (val) == INDIRECT_REF
 	   && allows_mem)
 	  || (DECL_P (val)
@@ -2768,69 +2732,64 @@  expand_asm_stmt (gasm *stmt)
 	  if ((! allows_mem && MEM_P (op))
 	      || GET_CODE (op) == CONCAT)
 	    {
-	      real_output_rtx[i] = op;
+	      rtx old_op = op;
 	      op = gen_reg_rtx (GET_MODE (op));
+
+	      generating_concat_p = old_generating_concat_p;
+
 	      if (is_inout)
-		emit_move_insn (op, real_output_rtx[i]);
+		emit_move_insn (op, old_op);
+
+	      push_to_sequence2 (after_rtl_seq, after_rtl_end);
+	      emit_move_insn (old_op, op);
+	      after_rtl_seq = get_insns ();
+	      after_rtl_end = get_last_insn ();
+	      end_sequence ();
 	    }
 	}
       else
 	{
 	  op = assign_temp (type, 0, 1);
 	  op = validize_mem (op);
-	  if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME)
-	    set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op);
-	  TREE_VALUE (tail) = make_tree (type, op);
-	}
-      output_rtx[i] = op;
+	  if (!MEM_P (op) && TREE_CODE (val) == SSA_NAME)
+	    set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (val), op);
 
-      generating_concat_p = old_generating_concat_p;
+	  generating_concat_p = old_generating_concat_p;
 
-      if (is_inout)
-	{
-	  inout_mode[ninout] = TYPE_MODE (type);
-	  inout_opnum[ninout++] = i;
+	  push_to_sequence2 (after_rtl_seq, after_rtl_end);
+	  expand_assignment (val, make_tree (type, op), false);
+	  after_rtl_seq = get_insns ();
+	  after_rtl_end = get_last_insn ();
+	  end_sequence ();
 	}
+      output_rvec[i] = op;
 
-      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
-	clobber_conflict_found = 1;
+      if (is_inout)
+	inout_opnum.safe_push (i);
     }
 
-  /* Make vectors for the expression-rtx, constraint strings,
-     and named operands.  */
-
-  argvec = rtvec_alloc (ninputs);
-  constraintvec = rtvec_alloc (ninputs);
-  labelvec = rtvec_alloc (nlabels);
+  auto_vec<rtx, MAX_RECOG_OPERANDS> input_rvec;
+  auto_vec<machine_mode, MAX_RECOG_OPERANDS> input_mode;
 
-  body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
-				: GET_MODE (output_rtx[0])),
-			       ggc_strdup (TREE_STRING_POINTER (string)),
-			       empty_string, 0, argvec, constraintvec,
-			       labelvec, locus);
+  input_rvec.safe_grow (ninputs);
+  input_mode.safe_grow (ninputs);
 
-  MEM_VOLATILE_P (body) = gimple_asm_volatile_p (stmt);
-
-  /* Eval the inputs and put them into ARGVEC.
-     Put their constraints into ASM_INPUTs and store in CONSTRAINTS.  */
+  generating_concat_p = 0;
 
-  for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
+  for (i = 0; i < ninputs; ++i)
     {
-      bool allows_reg, allows_mem;
+      tree val = input_tvec[i];
+      tree type = TREE_TYPE (val);
+      bool allows_reg, allows_mem, ok;
       const char *constraint;
-      tree val, type;
       rtx op;
-      bool ok;
 
       constraint = constraints[i + noutputs];
-      ok = parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
-				   constraints, &allows_mem, &allows_reg);
+      ok = parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
+				   constraints.address (),
+				   &allows_mem, &allows_reg);
       gcc_assert (ok);
 
-      generating_concat_p = 0;
-
-      val = TREE_VALUE (tail);
-      type = TREE_TYPE (val);
       /* EXPAND_INITIALIZER will not generate code for valid initializer
 	 constants, but will still generate code for other types of operand.
 	 This is the behavior we want for constant constraints.  */
@@ -2861,61 +2820,109 @@  expand_asm_stmt (gasm *stmt)
 	  else
 	    gcc_unreachable ();
 	}
-
-      generating_concat_p = old_generating_concat_p;
-      ASM_OPERANDS_INPUT (body, i) = op;
-
-      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
-	= gen_rtx_ASM_INPUT_loc (TYPE_MODE (type),
-				 ggc_strdup (constraints[i + noutputs]),
-				 locus);
-
-      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
-	clobber_conflict_found = 1;
+      input_rvec[i] = op;
+      input_mode[i] = TYPE_MODE (type);
     }
 
-  /* Protect all the operands from the queue now that they have all been
-     evaluated.  */
-
-  generating_concat_p = 0;
-
   /* For in-out operands, copy output rtx to input rtx.  */
+  unsigned ninout = inout_opnum.length();
   for (i = 0; i < ninout; i++)
     {
       int j = inout_opnum[i];
-      char buffer[16];
+      rtx o = output_rvec[j];
 
-      ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
-	= output_rtx[j];
+      input_rvec.safe_push (o);
+      input_mode.safe_push (GET_MODE (o));
 
+      char buffer[16];
       sprintf (buffer, "%d", j);
-      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
-	= gen_rtx_ASM_INPUT_loc (inout_mode[i], ggc_strdup (buffer), locus);
+      constraints.safe_push (ggc_strdup (buffer));
+    }
+  ninputs += ninout;
+
+  /* Sometimes we wish to automatically clobber registers across an asm.
+     Case in point is when the i386 backend moved from cc0 to a hard reg --
+     maintaining source-level compatibility means automatically clobbering
+     the flags register.  */
+  rtx_insn *after_md_seq = NULL;
+  if (targetm.md_asm_adjust)
+    after_md_seq = targetm.md_asm_adjust (output_rvec, input_rvec,
+					  constraints, clobber_rvec,
+					  clobbered_regs);
+
+  /* Do not allow the hook to change the output and input count,
+     lest it mess up the operand numbering.  */
+  gcc_assert (output_rvec.length() == noutputs);
+  gcc_assert (input_rvec.length() == ninputs);
+  gcc_assert (constraints.length() == noutputs + ninputs);
+
+  /* But it certainly can adjust the clobbers.  */
+  nclobbers = clobber_rvec.length();
+
+  /* Third pass checks for easy conflicts.  */
+  /* ??? Why are we doing this on trees instead of rtx.  */
+
+  bool clobber_conflict_found = 0;
+  for (i = 0; i < noutputs; ++i)
+    if (tree_conflicts_with_clobbers_p (output_tvec[i], &clobbered_regs))
+	clobber_conflict_found = 1;
+  for (i = 0; i < ninputs - ninout; ++i)
+    if (tree_conflicts_with_clobbers_p (input_tvec[i], &clobbered_regs))
+	clobber_conflict_found = 1;
+
+  /* Make vectors for the expression-rtx, constraint strings,
+     and named operands.  */
+
+  rtvec argvec = rtvec_alloc (ninputs);
+  rtvec constraintvec = rtvec_alloc (ninputs);
+  rtvec labelvec = rtvec_alloc (nlabels);
+
+  rtx body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
+				    : GET_MODE (output_rvec[0])),
+				   ggc_strdup (gimple_asm_string (stmt)),
+				   empty_string, 0, argvec, constraintvec,
+				   labelvec, locus);
+  MEM_VOLATILE_P (body) = gimple_asm_volatile_p (stmt);
+
+  for (i = 0; i < ninputs; ++i)
+    {
+      ASM_OPERANDS_INPUT (body, i) = input_rvec[i];
+      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
+	= gen_rtx_ASM_INPUT_loc (input_mode[i],
+				 constraints[i + noutputs],
+				 locus);
     }
 
   /* Copy labels to the vector.  */
-  for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
+  rtx_code_label *fallthru_label = NULL;
+  if (nlabels > 0)
     {
-      rtx r;
-      /* If asm goto has any labels in the fallthru basic block, use
-	 a label that we emit immediately after the asm goto.  Expansion
-	 may insert further instructions into the same basic block after
-	 asm goto and if we don't do this, insertion of instructions on
-	 the fallthru edge might misbehave.  See PR58670.  */
-      if (fallthru_bb
-	  && label_to_block_fn (cfun, TREE_VALUE (tail)) == fallthru_bb)
+      basic_block fallthru_bb = NULL;
+      edge fallthru = find_fallthru_edge (gimple_bb (stmt)->succs);
+      if (fallthru)
+	fallthru_bb = fallthru->dest;
+
+      for (i = 0; i < nlabels; ++i)
 	{
-	  if (fallthru_label == NULL_RTX)
-	    fallthru_label = gen_label_rtx ();
-	  r = fallthru_label;
+	  tree label = TREE_VALUE (gimple_asm_label_op (stmt, i));
+	  rtx r;
+	  /* If asm goto has any labels in the fallthru basic block, use
+	     a label that we emit immediately after the asm goto.  Expansion
+	     may insert further instructions into the same basic block after
+	     asm goto and if we don't do this, insertion of instructions on
+	     the fallthru edge might misbehave.  See PR58670.  */
+	  if (fallthru_bb && label_to_block_fn (cfun, label) == fallthru_bb)
+	    {
+	      if (fallthru_label == NULL_RTX)
+	        fallthru_label = gen_label_rtx ();
+	      r = fallthru_label;
+	    }
+	  else
+	    r = label_rtx (label);
+	  ASM_OPERANDS_LABEL (body, i) = gen_rtx_LABEL_REF (Pmode, r);
 	}
-      else
-	r = label_rtx (TREE_VALUE (tail));
-      ASM_OPERANDS_LABEL (body, i) = gen_rtx_LABEL_REF (Pmode, r);
     }
 
-  generating_concat_p = old_generating_concat_p;
-
   /* Now, for each output, construct an rtx
      (set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER
 			       ARGVEC CONSTRAINTS OPNAMES))
@@ -2933,8 +2940,8 @@  expand_asm_stmt (gasm *stmt)
     }
   else if (noutputs == 1 && nclobbers == 0)
     {
-      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
-      emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = constraints[0];
+      emit_insn (gen_rtx_SET (VOIDmode, output_rvec[0], body));
     }
   else
     {
@@ -2947,88 +2954,52 @@  expand_asm_stmt (gasm *stmt)
       body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
 
       /* For each output operand, store a SET.  */
-      for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+      for (i = 0; i < noutputs; ++i)
 	{
-	  XVECEXP (body, 0, i)
-	    = gen_rtx_SET (VOIDmode,
-			   output_rtx[i],
-			   gen_rtx_ASM_OPERANDS
-			   (GET_MODE (output_rtx[i]),
-			    ggc_strdup (TREE_STRING_POINTER (string)),
-			    ggc_strdup (constraints[i]),
-			    i, argvec, constraintvec, labelvec, locus));
-
-	  MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i)))
-	    = gimple_asm_volatile_p (stmt);
+	  rtx src, o = output_rvec[i];
+	  if (i == 0)
+	    {
+	      ASM_OPERANDS_OUTPUT_CONSTRAINT (obody) = constraints[0];
+	      src = obody;
+	    }
+	  else
+	    {
+	      src = gen_rtx_ASM_OPERANDS (GET_MODE (o),
+					  ASM_OPERANDS_TEMPLATE (obody),
+					  constraints[i], i, argvec,
+					  constraintvec, labelvec, locus);
+	      MEM_VOLATILE_P (src) = gimple_asm_volatile_p (stmt);
+	    }
+	  XVECEXP (body, 0, i) = gen_rtx_SET (VOIDmode, o, src);
 	}
 
       /* If there are no outputs (but there are some clobbers)
 	 store the bare ASM_OPERANDS into the PARALLEL.  */
-
       if (i == 0)
 	XVECEXP (body, 0, i++) = obody;
 
       /* Store (clobber REG) for each clobbered register specified.  */
-
-      for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
+      for (unsigned j = 0; j < nclobbers; ++j)
 	{
-	  const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
-	  int reg, nregs;
-	  int j = decode_reg_name_and_count (regname, &nregs);
-	  rtx clobbered_reg;
+	  rtx clobbered_reg = clobber_rvec[j];
 
-	  if (j < 0)
+	  /* Do sanity check for overlap between clobbers and respectively
+	     input and outputs that hasn't been handled.  Such overlap
+	     should have been detected and reported above.  */
+	  if (!clobber_conflict_found && REG_P (clobbered_reg))
 	    {
-	      if (j == -3)	/* `cc', which is not a register */
-		continue;
-
-	      if (j == -4)	/* `memory', don't cache memory across asm */
-		{
-		  XVECEXP (body, 0, i++)
-		    = gen_rtx_CLOBBER (VOIDmode,
-				       gen_rtx_MEM
-				       (BLKmode,
-					gen_rtx_SCRATCH (VOIDmode)));
-		  continue;
-		}
-
-	      /* Ignore unknown register, error already signaled.  */
-	      continue;
+	      /* We test the old body (obody) contents to avoid
+		 tripping over the under-construction body.  */
+	      for (unsigned k = 0; k < noutputs; ++k)
+		if (reg_overlap_mentioned_p (clobbered_reg, output_rvec[k]))
+		  internal_error ("asm clobber conflict with output operand");
+
+	      for (unsigned k = 0; k < ninputs - ninout; ++k)
+		if (reg_overlap_mentioned_p (clobbered_reg, input_rvec[k]))
+		  internal_error ("asm clobber conflict with input operand");
 	    }
 
-	  for (reg = j; reg < j + nregs; reg++)
-	    {
-	      /* Use QImode since that's guaranteed to clobber just
-	       * one reg.  */
-	      clobbered_reg = gen_rtx_REG (QImode, reg);
-
-	      /* Do sanity check for overlap between clobbers and
-		 respectively input and outputs that hasn't been
-		 handled.  Such overlap should have been detected and
-		 reported above.  */
-	      if (!clobber_conflict_found)
-		{
-		  int opno;
-
-		  /* We test the old body (obody) contents to avoid
-		     tripping over the under-construction body.  */
-		  for (opno = 0; opno < noutputs; opno++)
-		    if (reg_overlap_mentioned_p (clobbered_reg,
-						 output_rtx[opno]))
-		      internal_error
-			("asm clobber conflict with output operand");
-
-		  for (opno = 0; opno < ninputs - ninout; opno++)
-		    if (reg_overlap_mentioned_p (clobbered_reg,
-						 ASM_OPERANDS_INPUT (obody,
-								     opno)))
-		      internal_error
-			("asm clobber conflict with input operand");
-		}
-
-	      XVECEXP (body, 0, i++)
-		= gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
-	    }
+	  XVECEXP (body, 0, i++) = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
 	}
 
       if (nlabels > 0)
@@ -3037,31 +3008,18 @@  expand_asm_stmt (gasm *stmt)
 	emit_insn (body);
     }
 
+  generating_concat_p = old_generating_concat_p;
+
   if (fallthru_label)
     emit_label (fallthru_label);
 
-  /* For any outputs that needed reloading into registers, spill them
-     back to where they belong.  */
-  for (i = 0; i < noutputs; ++i)
-    if (real_output_rtx[i])
-      emit_move_insn (real_output_rtx[i], output_rtx[i]);
-
-  /* Copy all the intermediate outputs into the specified outputs.  */
-  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
-    {
-      if (orig_outputs[i] != TREE_VALUE (tail))
-	{
-	  expand_assignment (orig_outputs[i], TREE_VALUE (tail), false);
-	  free_temp_slots ();
+  if (after_md_seq)
+    emit_insn (after_md_seq);
+  if (after_rtl_seq)
+    emit_insn (after_rtl_seq);
 
-	  /* Restore the original value so that it's correct the next
-	     time we expand this function.  */
-	  TREE_VALUE (tail) = orig_outputs[i];
-	}
-    }
-
-  crtl->has_asm_statement = 1;
   free_temp_slots ();
+  crtl->has_asm_statement = 1;
 }
 
 /* Emit code to jump to the address
diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c
index 650402e..7d88a22 100644
--- a/gcc/config/cris/cris.c
+++ b/gcc/config/cris/cris.c
@@ -179,7 +179,9 @@  static rtx cris_function_incoming_arg (cumulative_args_t,
 				       machine_mode, const_tree, bool);
 static void cris_function_arg_advance (cumulative_args_t, machine_mode,
 				       const_tree, bool);
-static tree cris_md_asm_clobbers (tree, tree, tree);
+static rtx_insn *cris_md_asm_adjust (vec<rtx> &, vec<rtx> &,
+				     vec<const char *> &,
+				     vec<rtx> &, HARD_REG_SET &);
 static bool cris_cannot_force_const_mem (machine_mode, rtx);
 
 static void cris_option_override (void);
@@ -283,8 +285,8 @@  int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
 #define TARGET_FUNCTION_INCOMING_ARG cris_function_incoming_arg
 #undef TARGET_FUNCTION_ARG_ADVANCE
 #define TARGET_FUNCTION_ARG_ADVANCE cris_function_arg_advance
-#undef TARGET_MD_ASM_CLOBBERS
-#define TARGET_MD_ASM_CLOBBERS cris_md_asm_clobbers
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST cris_md_asm_adjust
 
 #undef TARGET_CANNOT_FORCE_CONST_MEM
 #define TARGET_CANNOT_FORCE_CONST_MEM cris_cannot_force_const_mem
@@ -4219,55 +4221,41 @@  cris_function_arg_advance (cumulative_args_t ca_v, machine_mode mode,
   ca->regs += (3 + CRIS_FUNCTION_ARG_SIZE (mode, type)) / 4;
 }
 
-/* Worker function for TARGET_MD_ASM_CLOBBERS.  */
+/* Worker function for TARGET_MD_ASM_ADJUST.  */
 
-static tree
-cris_md_asm_clobbers (tree outputs, tree inputs, tree in_clobbers)
+static rtx_insn *
+cris_md_asm_adjust (vec<rtx> &outputs, vec<rtx> &inputs,
+		    vec<const char *> &constraints,
+		    vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
 {
-  HARD_REG_SET mof_set;
-  tree clobbers;
-  tree t;
-
-  CLEAR_HARD_REG_SET (mof_set);
-  SET_HARD_REG_BIT (mof_set, CRIS_MOF_REGNUM);
-
-  /* For the time being, all asms clobber condition codes.  Revisit when
-     there's a reasonable use for inputs/outputs that mention condition
-     codes.  */
-  clobbers
-    = tree_cons (NULL_TREE,
-		 build_string (strlen (reg_names[CRIS_CC0_REGNUM]),
-			       reg_names[CRIS_CC0_REGNUM]),
-		 in_clobbers);
-
-  for (t = outputs; t != NULL; t = TREE_CHAIN (t))
-    {
-      tree val = TREE_VALUE (t);
-
-      /* The constraint letter for the singleton register class of MOF
-	 is 'h'.  If it's mentioned in the constraints, the asm is
-	 MOF-aware and adding it to the clobbers would cause it to have
-	 impossible constraints.  */
-      if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
-		  'h') != NULL
-	  || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
-	return clobbers;
-    }
-
-  for (t = inputs; t != NULL; t = TREE_CHAIN (t))
-    {
-      tree val = TREE_VALUE (t);
-
-      if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
-		  'h') != NULL
-	  || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
-	return clobbers;
-    }
-
-  return tree_cons (NULL_TREE,
-		    build_string (strlen (reg_names[CRIS_MOF_REGNUM]),
-				  reg_names[CRIS_MOF_REGNUM]),
-		    clobbers);
+  /* For the time being, all asms clobber condition codes.
+     Revisit when there's a reasonable use for inputs/outputs
+     that mention condition codes.  */
+  clobbers.safe_push (gen_rtx_REG (CCmode, CRIS_CC0_REGNUM));
+  SET_HARD_REG_BIT (clobbered_regs, CRIS_CC0_REGNUM);
+
+  /* Determine if the source using MOF.  If it is, automatically
+     clobbering MOF would cause it to have impossible constraints.  */
+
+  /* Look for a use of the MOF constraint letter: h.  */
+  for (unsigned i = 0, n = constraints.length(); i < n; ++i)
+    if (strchr (constraints[i], 'h') != NULL)
+      return NULL;
+
+  /* Look for an output or an input that touches MOF.  */
+  rtx mof_reg = gen_rtx_REG (SImode, CRIS_MOF_REGNUM);
+  for (unsigned i = 0, n = outputs.length(); i < n; ++i)
+    if (reg_overlap_mentioned_p (mof_reg, outputs[i]))
+      return NULL;
+  for (unsigned i = 0, n = inputs.length(); i < n; ++i)
+    if (reg_overlap_mentioned_p (mof_reg, inputs[i]))
+      return NULL;
+
+  /* No direct reference to MOF or its constraint.
+     Clobber it for backward compatibility.  */
+  clobbers.safe_push (mof_reg);
+  SET_HARD_REG_BIT (clobbered_regs, CRIS_MOF_REGNUM);
+  return NULL;
 }
 
 /* Implement TARGET_FRAME_POINTER_REQUIRED.
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 627ef65..c47134e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -45428,19 +45428,23 @@  ix86_c_mode_for_suffix (char suffix)
   return VOIDmode;
 }
 
-/* Worker function for TARGET_MD_ASM_CLOBBERS.
+/* Worker function for TARGET_MD_ASM_ADJUST.
 
    We do this in the new i386 backend to maintain source compatibility
    with the old cc0-based compiler.  */
 
-static tree
-ix86_md_asm_clobbers (tree, tree, tree clobbers)
+static rtx_insn *
+ix86_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
+		    vec<const char *> &/*constraints*/,
+		    vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
 {
-  clobbers = tree_cons (NULL_TREE, build_string (5, "flags"),
-			clobbers);
-  clobbers = tree_cons (NULL_TREE, build_string (4, "fpsr"),
-			clobbers);
-  return clobbers;
+  clobbers.safe_push (gen_rtx_REG (CCmode, FLAGS_REG));
+  clobbers.safe_push (gen_rtx_REG (CCFPmode, FPSR_REG));
+
+  SET_HARD_REG_BIT (clobbered_regs, FLAGS_REG);
+  SET_HARD_REG_BIT (clobbered_regs, FPSR_REG);
+
+  return NULL;
 }
 
 /* Implements target vector targetm.asm.encode_section_info.  */
@@ -51995,8 +51999,8 @@  ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_EXPAND_BUILTIN_VA_START
 #define TARGET_EXPAND_BUILTIN_VA_START ix86_va_start
 
-#undef TARGET_MD_ASM_CLOBBERS
-#define TARGET_MD_ASM_CLOBBERS ix86_md_asm_clobbers
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST ix86_md_asm_adjust
 
 #undef TARGET_PROMOTE_PROTOTYPES
 #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c
index 58c97e8..34d577d 100644
--- a/gcc/config/mn10300/mn10300.c
+++ b/gcc/config/mn10300/mn10300.c
@@ -2881,18 +2881,18 @@  mn10300_conditional_register_usage (void)
     call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
 }
 
-/* Worker function for TARGET_MD_ASM_CLOBBERS.
+/* Worker function for TARGET_MD_ASM_ADJUST.
    We do this in the mn10300 backend to maintain source compatibility
    with the old cc0-based compiler.  */
 
-static tree
-mn10300_md_asm_clobbers (tree outputs ATTRIBUTE_UNUSED,
-                         tree inputs ATTRIBUTE_UNUSED,
-                         tree clobbers)
+static rtx_insn *
+mn10300_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
+		       vec<const char *> &/*constraints*/,
+		       vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
 {
-  clobbers = tree_cons (NULL_TREE, build_string (5, "EPSW"),
-                        clobbers);
-  return clobbers;
+  clobbers.safe_push (gen_rtx_REG (CCmode, CC_REG));
+  SET_HARD_REG_BIT (clobbered_regs, CC_REG);
+  return NULL;
 }
 
 /* A helper function for splitting cbranch patterns after reload.  */
@@ -3442,8 +3442,8 @@  mn10300_reorg (void)
 #undef  TARGET_CONDITIONAL_REGISTER_USAGE
 #define TARGET_CONDITIONAL_REGISTER_USAGE mn10300_conditional_register_usage
 
-#undef TARGET_MD_ASM_CLOBBERS
-#define TARGET_MD_ASM_CLOBBERS  mn10300_md_asm_clobbers
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST mn10300_md_asm_adjust
 
 #undef  TARGET_FLAGS_REGNUM
 #define TARGET_FLAGS_REGNUM  CC_REG
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index c74c7d1..79ff0ea 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -1597,8 +1597,8 @@  static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_ASM_LOOP_ALIGN_MAX_SKIP
 #define TARGET_ASM_LOOP_ALIGN_MAX_SKIP rs6000_loop_align_max_skip
 
-#undef TARGET_MD_ASM_CLOBBERS
-#define TARGET_MD_ASM_CLOBBERS rs6000_md_asm_clobbers
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST rs6000_md_asm_adjust
 
 #undef TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE rs6000_option_override
@@ -3209,17 +3209,20 @@  rs6000_builtin_mask_calculate (void)
 	  | ((TARGET_LONG_DOUBLE_128)	    ? RS6000_BTM_LDBL128 : 0));
 }
 
-/* Implement TARGET_MD_ASM_CLOBBERS.  All asm statements are considered
+/* Implement TARGET_MD_ASM_ADJUST.  All asm statements are considered
    to clobber the XER[CA] bit because clobbering that bit without telling
    the compiler worked just fine with versions of GCC before GCC 5, and
    breaking a lot of older code in ways that are hard to track down is
    not such a great idea.  */
 
-static tree
-rs6000_md_asm_clobbers (tree, tree, tree clobbers)
+static rtx_insn *
+rs6000_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
+		      vec<const char *> &/*constraints*/,
+		      vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
 {
-  tree s = build_string (strlen (reg_names[CA_REGNO]), reg_names[CA_REGNO]);
-  return tree_cons (NULL_TREE, s, clobbers);
+  clobbers.safe_push (gen_rtx_REG (SImode, CA_REGNO));
+  SET_HARD_REG_BIT (clobbered_regs, CA_REGNO);
+  return NULL;
 }
 
 /* Override command line options.  Mostly we process the processor type and
diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c
index e656af9..164d4d2 100644
--- a/gcc/config/visium/visium.c
+++ b/gcc/config/visium/visium.c
@@ -172,7 +172,9 @@  static bool visium_frame_pointer_required (void);
 
 static tree visium_build_builtin_va_list (void);
 
-static tree visium_md_asm_clobbers (tree, tree, tree);
+static rtx_insn *visium_md_asm_adjust (vec<rtx> &, vec<rtx> &,
+				       vec<const char *> &,
+				       vec<rtx> &, HARD_REG_SET &);
 
 static bool visium_legitimate_constant_p (enum machine_mode, rtx);
 
@@ -299,8 +301,8 @@  static unsigned int visium_reorg (void);
 #undef  TARGET_TRAMPOLINE_INIT
 #define TARGET_TRAMPOLINE_INIT visium_trampoline_init
 
-#undef TARGET_MD_ASM_CLOBBERS
-#define TARGET_MD_ASM_CLOBBERS visium_md_asm_clobbers
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST visium_md_asm_adjust
 
 #undef TARGET_FLAGS_REGNUM
 #define TARGET_FLAGS_REGNUM FLAGS_REGNUM
@@ -720,13 +722,14 @@  visium_conditional_register_usage (void)
    an asm   We do this for the FLAGS to maintain source compatibility with
    the original cc0-based compiler.  */
 
-static tree
-visium_md_asm_clobbers (tree outputs ATTRIBUTE_UNUSED,
-			tree inputs ATTRIBUTE_UNUSED,
-			tree clobbers)
+static rtx_insn *
+visium_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
+		      vec<const char *> &/*constraints*/,
+		      vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
 {
-  const char *flags = reg_names[FLAGS_REGNUM];
-  return tree_cons (NULL_TREE, build_string (strlen (flags), flags), clobbers);
+  clobbers.safe_push (gen_rtx_REG (CCmode, FLAGS_REGNUM));
+  SET_HARD_REG_BIT (clobbered_regs, FLAGS_REGNUM);
+  return NULL;
 }
 
 /* Return true if X is a legitimate constant for a MODE immediate operand.
diff --git a/gcc/config/vxworks.c b/gcc/config/vxworks.c
index 4e4cfc8..e29dc69 100644
--- a/gcc/config/vxworks.c
+++ b/gcc/config/vxworks.c
@@ -21,13 +21,13 @@  along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "vec.h"
 #include "target.h"
 #include "diagnostic-core.h"
 #include "output.h"
 #include "tm.h"
 #include "hash-set.h"
 #include "machmode.h"
-#include "vec.h"
 #include "double-int.h"
 #include "input.h"
 #include "alias.h"
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index dd1fd22..9886f4a 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -10835,15 +10835,15 @@  from shared libraries (DLLs).
 You need not define this macro if it would always evaluate to zero.
 @end defmac
 
-@deftypefn {Target Hook} tree TARGET_MD_ASM_CLOBBERS (tree @var{outputs}, tree @var{inputs}, tree @var{clobbers})
-This target hook should add to @var{clobbers} @code{STRING_CST} trees for
-any hard regs the port wishes to automatically clobber for an asm.
-It should return the result of the last @code{tree_cons} used to add a
-clobber.  The @var{outputs}, @var{inputs} and @var{clobber} lists are the
-corresponding parameters to the asm and may be inspected to avoid
-clobbering a register that is an input or output of the asm.  You can use
-@code{tree_overlaps_hard_reg_set}, declared in @file{tree.h}, to test
-for overlap with regards to asm-declared registers.
+@deftypefn {Target Hook} {rtx_insn *} TARGET_MD_ASM_ADJUST (vec<rtx>& @var{outputs}, vec<rtx>& @var{inputs}, vec<const char *>& @var{constraints}, vec<rtx>& @var{clobbers}, HARD_REG_SET& @var{clobbered_regs})
+This target hook may add @dfn{clobbers} to @var{clobbers} and
+@var{clobbered_regs} for any hard regs the port wishes to automatically
+clobber for an asm.  The @var{outputs} and @var{inputs} may be inspected
+to avoid clobbering a register that is already used by the asm.
+
+It may modify the @var{outputs}, @var{inputs}, and @var{constraints}
+as necessary for other pre-processing.  In this case the return value is
+a sequence of insns to emit after the asm.
 @end deftypefn
 
 @defmac MATH_LIBRARY
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 8680967..707d610 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -7903,7 +7903,7 @@  from shared libraries (DLLs).
 You need not define this macro if it would always evaluate to zero.
 @end defmac
 
-@hook TARGET_MD_ASM_CLOBBERS
+@hook TARGET_MD_ASM_ADJUST
 
 @defmac MATH_LIBRARY
 Define this macro as a C string constant for the linker argument to link
diff --git a/gcc/gimple.c b/gcc/gimple.c
index a5c1192..8b3b322 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -23,10 +23,10 @@  along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
-#include "target.h"
 #include "hash-set.h"
 #include "machmode.h"
 #include "vec.h"
+#include "target.h"
 #include "double-int.h"
 #include "input.h"
 #include "alias.h"
diff --git a/gcc/hooks.c b/gcc/hooks.c
index 824aeb0..92a2141 100644
--- a/gcc/hooks.c
+++ b/gcc/hooks.c
@@ -378,14 +378,6 @@  hook_uint_mode_0 (machine_mode m ATTRIBUTE_UNUSED)
   return 0;
 }
 
-/* Generic hook that takes three trees and returns the last one as is.  */
-tree
-hook_tree_tree_tree_tree_3rd_identity (tree a ATTRIBUTE_UNUSED,
-				       tree b ATTRIBUTE_UNUSED, tree c)
-{
-  return c;
-}
-
 /* Generic hook that takes no arguments and returns a NULL const string.  */
 const char *
 hook_constcharptr_void_null (void)
diff --git a/gcc/hooks.h b/gcc/hooks.h
index 8c929e8..02c24ce 100644
--- a/gcc/hooks.h
+++ b/gcc/hooks.h
@@ -93,7 +93,6 @@  extern tree hook_tree_const_tree_null (const_tree);
 
 extern tree hook_tree_tree_tree_null (tree, tree);
 extern tree hook_tree_tree_tree_tree_null (tree, tree, tree);
-extern tree hook_tree_tree_tree_tree_3rd_identity (tree, tree, tree);
 extern tree hook_tree_tree_int_treep_bool_null (tree, int, tree *, bool);
 
 extern unsigned hook_uint_void_0 (void);
diff --git a/gcc/incpath.c b/gcc/incpath.c
index 6c54ca6..c4e0574 100644
--- a/gcc/incpath.c
+++ b/gcc/incpath.c
@@ -21,6 +21,7 @@ 
 #include "system.h"
 #include "coretypes.h"
 #include "machmode.h"
+#include "vec.h"
 #include "target.h"
 #include "tm.h"
 #include "cpplib.h"
diff --git a/gcc/mode-switching.c b/gcc/mode-switching.c
index c0f865c..9af7911 100644
--- a/gcc/mode-switching.c
+++ b/gcc/mode-switching.c
@@ -21,6 +21,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "vec.h"
 #include "target.h"
 #include "rtl.h"
 #include "regs.h"
@@ -29,7 +30,6 @@  along with GCC; see the file COPYING3.  If not see
 #include "insn-config.h"
 #include "recog.h"
 #include "predict.h"
-#include "vec.h"
 #include "hashtab.h"
 #include "hash-set.h"
 #include "machmode.h"
diff --git a/gcc/system.h b/gcc/system.h
index 94d8138..448a7fe 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -964,6 +964,7 @@  extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
 	TARGET_HANDLE_PRAGMA_EXTERN_PREFIX \
 	TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN \
 	TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD \
+	TARGET_MD_ASM_CLOBBERS
 
 /* Arrays that were deleted in favor of a functional interface.  */
  #pragma GCC poison built_in_decls implicit_built_in_decls
diff --git a/gcc/target.def b/gcc/target.def
index 329ea04..eba9921 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -3742,20 +3742,22 @@  machines.  One reason you may need to define this target hook is if\n\
  rtx, (void),
  default_builtin_setjmp_frame_value)
 
-/* This target hook should add STRING_CST trees for any hard regs
-   the port wishes to automatically clobber for an asm.  */
-DEFHOOK
-(md_asm_clobbers,
- "This target hook should add to @var{clobbers} @code{STRING_CST} trees for\n\
-any hard regs the port wishes to automatically clobber for an asm.\n\
-It should return the result of the last @code{tree_cons} used to add a\n\
-clobber.  The @var{outputs}, @var{inputs} and @var{clobber} lists are the\n\
-corresponding parameters to the asm and may be inspected to avoid\n\
-clobbering a register that is an input or output of the asm.  You can use\n\
-@code{tree_overlaps_hard_reg_set}, declared in @file{tree.h}, to test\n\
-for overlap with regards to asm-declared registers.",
- tree, (tree outputs, tree inputs, tree clobbers),
- hook_tree_tree_tree_tree_3rd_identity)
+/* This target hook should manipulate the outputs, inputs, constraints,
+   and clobbers the port wishes for pre-processing the asm.  */
+DEFHOOK
+(md_asm_adjust,
+ "This target hook may add @dfn{clobbers} to @var{clobbers} and\n\
+@var{clobbered_regs} for any hard regs the port wishes to automatically\n\
+clobber for an asm.  The @var{outputs} and @var{inputs} may be inspected\n\
+to avoid clobbering a register that is already used by the asm.\n\
+\n\
+It may modify the @var{outputs}, @var{inputs}, and @var{constraints}\n\
+as necessary for other pre-processing.  In this case the return value is\n\
+a sequence of insns to emit after the asm.",
+ rtx_insn *,
+ (vec<rtx>& outputs, vec<rtx>& inputs, vec<const char *>& constraints,
+  vec<rtx>& clobbers, HARD_REG_SET& clobbered_regs),
+ NULL)
 
 /* This target hook allows the backend to specify a calling convention
    in the debug information.  This function actually returns an