@@ -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
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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.
@@ -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"
@@ -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
@@ -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
@@ -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"
@@ -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)
@@ -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);
@@ -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"
@@ -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"
@@ -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
@@ -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