aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog30
-rw-r--r--gcc/config/h8300/h8300-protos.h3
-rw-r--r--gcc/config/h8300/h8300.c253
-rw-r--r--gcc/config/h8300/h8300.md135
4 files changed, 262 insertions, 159 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 70c1556..f5ee6e6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,33 @@
+2003-01-04 Kazu Hirata <kazu@cs.umass.edu>
+
+ * config/h8300/h8300-protos.h: Add prototypes for
+ the new functions defined below.
+ * config/h8300/h8300.c (TARGET_ASM_FUNCTION_PROLOGUE): Do not
+ define.
+ (dosize): Emit RTL instead of assembly code.
+ (push): Likewise.
+ (pop): Likewise.
+ (h8300_output_function_prologue): Remove.
+ (h8300_expand_prologue): New.
+ (h8300_expand_epilogue): New.
+ (h8300_output_function_epilogue): Do only the reset of
+ pragma_saveall.
+ * config/h8300/h8300.md (push_h8300): New.
+ (push_h8300hs): Likewise.
+ (pop_h8300): Likewise.
+ (pop_h8300hs): Likewise.
+ (*stm_h8300s_2): Change the name to stm_h8300s_2.
+ (*stm_h8300s_3): Change the name to stm_h8300s_3.
+ (*stm_h8300s_4): Change the name to stm_h8300s_4.
+ (*ldm_h8300s_2): New.
+ (*ldm_h8300s_3): Likewise.
+ (*ldm_h8300s_4): Likewise.
+ (return): Likewise.
+ (*return_1): Likewise.
+ (prologue): Likewise.
+ (epilogue): Likewise.
+ (monitor_prologue): Likewise.
+
2003-01-03 Dale Johannesen <dalej@apple.com>
* config/darwin.h: (EXTRA_SECTIONS): Add machopic_symbol_stub1,
diff --git a/gcc/config/h8300/h8300-protos.h b/gcc/config/h8300/h8300-protos.h
index 1ea1ab2..f5a59d4 100644
--- a/gcc/config/h8300/h8300-protos.h
+++ b/gcc/config/h8300/h8300-protos.h
@@ -90,6 +90,9 @@ extern int h8300_tiny_data_p PARAMS ((tree));
#endif /* TREE_CODE */
extern void h8300_init_once PARAMS ((void));
+extern int h8300_can_use_return_insn_p PARAMS ((void));
+extern void h8300_expand_prologue PARAMS ((void));
+extern void h8300_expand_epilogue PARAMS ((void));
extern int h8300_current_function_interrupt_function_p PARAMS ((void));
extern void asm_file_start PARAMS ((FILE *));
extern void asm_file_end PARAMS ((FILE *));
diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c
index 5e4063c..f8ec682 100644
--- a/gcc/config/h8300/h8300.c
+++ b/gcc/config/h8300/h8300.c
@@ -50,18 +50,17 @@ static const char *byte_reg PARAMS ((rtx, int));
static int h8300_interrupt_function_p PARAMS ((tree));
static int h8300_monitor_function_p PARAMS ((tree));
static int h8300_os_task_function_p PARAMS ((tree));
-static void dosize PARAMS ((FILE *, int, unsigned int));
+static void dosize PARAMS ((int, unsigned int));
static int round_frame_size PARAMS ((int));
static unsigned int compute_saved_regs PARAMS ((void));
-static void push PARAMS ((FILE *, int));
-static void pop PARAMS ((FILE *, int));
+static void push PARAMS ((int));
+static void pop PARAMS ((int));
static const char *cond_string PARAMS ((enum rtx_code));
static unsigned int h8300_asm_insn_count PARAMS ((const char *));
const struct attribute_spec h8300_attribute_table[];
static tree h8300_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *));
static tree h8300_handle_eightbit_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
static tree h8300_handle_tiny_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
-static void h8300_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
static void h8300_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
static void h8300_insert_attributes PARAMS ((tree, tree *));
#ifndef OBJECT_FORMAT_ELF
@@ -104,8 +103,6 @@ const char *h8_push_op, *h8_pop_op, *h8_mov_op;
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE h8300_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE h8300_output_function_epilogue
#undef TARGET_ENCODE_SECTION_INFO
@@ -386,50 +383,32 @@ byte_reg (x, b)
SIZE to adjust the stack pointer. */
static void
-dosize (file, sign, size)
- FILE *file;
+dosize (sign, size)
int sign;
unsigned int size;
{
- /* On the H8/300H and H8S, for sizes <= 8 bytes, it is as good or
- better to use adds/subs insns rather than add.l/sub.l with an
- immediate value.
-
- Also, on the H8/300, if we don't have a temporary to hold the
- size of the frame in the prologue, we simply emit a sequence of
- subs since this shouldn't happen often. */
- if ((TARGET_H8300 && size <= 4)
- || ((TARGET_H8300H || TARGET_H8300S) && size <= 8)
- || (TARGET_H8300 && h8300_current_function_interrupt_function_p ())
- || (TARGET_H8300 && current_function_needs_context
- && sign < 0))
+ /* H8/300 cannot add/subtract a large constant with a single
+ instruction. If a temporary register is available, load the
+ constant to it and then do the addition. */
+ if (TARGET_H8300
+ && size > 4
+ && !h8300_current_function_interrupt_function_p ()
+ && !(current_function_needs_context && sign < 0))
{
- const char *op = (sign > 0) ? "add" : "sub";
- unsigned int amount;
-
- /* Try different amounts in descending order. */
- for (amount = (TARGET_H8300H || TARGET_H8300S) ? 4 : 2;
- amount > 0;
- amount /= 2)
- {
- char insn[100];
-
- sprintf (insn, "\t%ss\t#%d,%s\n", op, amount,
- TARGET_H8300 ? "r7" : "er7");
- for (; size >= amount; size -= amount)
- fputs (insn, file);
- }
+ rtx new_sp;
+ rtx r3 = gen_rtx_REG (Pmode, 3);
+ emit_insn (gen_rtx_SET (Pmode, r3, GEN_INT (sign * size)));
+ new_sp = gen_rtx_PLUS (Pmode, stack_pointer_rtx, r3);
+ emit_insn (gen_rtx_SET (Pmode, stack_pointer_rtx, new_sp));
}
else
{
- if (TARGET_H8300)
- {
- fprintf (file, "\tmov.w\t#%d,r3\n\tadd.w\tr3,r7\n", sign * size);
- }
- else
- {
- fprintf (file, "\tadd.l\t#%d,er7\n", sign * size);
- }
+ /* The stack adjustment made here is further optimized by the
+ splitter. In case of H8/300, the splitter always splits the
+ addition emitted here to make the adjustment
+ interrupt-safe. */
+ rtx new_sp = plus_constant (stack_pointer_rtx, sign * size);
+ emit_insn (gen_rtx_SET (Pmode, stack_pointer_rtx, new_sp));
}
}
@@ -466,30 +445,38 @@ compute_saved_regs ()
return saved_regs;
}
-/* Output assembly language code to push register RN. */
+/* Emit an insn to push register RN. */
static void
-push (file, rn)
- FILE *file;
+push (rn)
int rn;
{
+ rtx reg = gen_rtx_REG (word_mode, rn);
+ rtx x;
+
if (TARGET_H8300)
- fprintf (file, "\t%s\t%s,@-r7\n", h8_mov_op, h8_reg_names[rn]);
+ x = gen_push_h8300 (reg);
else
- fprintf (file, "\t%s\t%s,@-er7\n", h8_mov_op, h8_reg_names[rn]);
+ x = gen_push_h8300hs (reg);
+ x = emit_insn (x);
+ REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
}
-/* Output assembly language code to pop register RN. */
+/* Emit an insn to pop register RN. */
static void
-pop (file, rn)
- FILE *file;
+pop (rn)
int rn;
{
+ rtx reg = gen_rtx_REG (word_mode, rn);
+ rtx x;
+
if (TARGET_H8300)
- fprintf (file, "\t%s\t@r7+,%s\n", h8_mov_op, h8_reg_names[rn]);
+ x = gen_pop_h8300 (reg);
else
- fprintf (file, "\t%s\t@er7+,%s\n", h8_mov_op, h8_reg_names[rn]);
+ x = gen_pop_h8300hs (reg);
+ x = emit_insn (x);
+ REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
}
/* This is what the stack looks like after the prolog of
@@ -510,14 +497,12 @@ pop (file, rn)
<saved registers> <- sp
*/
-/* Output assembly language code for the function prologue. */
+/* Generate RTL code for the function prologue. */
-static void
-h8300_output_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size;
+void
+h8300_expand_prologue ()
{
- int fsize = round_frame_size (size);
+ int fsize = round_frame_size (get_frame_size ());
int regno;
int saved_regs;
int n_regs;
@@ -525,58 +510,23 @@ h8300_output_function_prologue (file, size)
/* If the current function has the OS_Task attribute set, then
we have a naked prologue. */
if (h8300_os_task_function_p (current_function_decl))
- {
- fprintf (file, ";OS_Task prologue\n");
- return;
- }
+ return;
if (h8300_monitor_function_p (current_function_decl))
- {
- /* My understanding of monitor functions is they act just
- like interrupt functions, except the prologue must
- mask interrupts. */
- fprintf (file, ";monitor prologue\n");
- if (TARGET_H8300)
- {
- fprintf (file, "\tsubs\t#2,sp\n");
- push (file, 0);
- fprintf (file, "\tstc\tccr,r0l\n");
- fprintf (file, "\tmov.b\tr0l,@(2,sp)\n");
- pop (file, 0);
- fprintf (file, "\torc\t#128,ccr\n");
- }
- else if (TARGET_H8300H)
- {
- push (file, 0);
- fprintf (file, "\tstc\tccr,r0l\n");
- fprintf (file, "\tmov.b\tr0l,@(4,sp)\n");
- pop (file, 0);
- fprintf (file, "\torc\t#128,ccr\n");
- }
- else if (TARGET_H8300S)
- {
- fprintf (file, "\tstc\texr,@-sp\n");
- push (file, 0);
- fprintf (file, "\tstc\tccr,r0l\n");
- fprintf (file, "\tmov.b\tr0l,@(6,sp)\n");
- pop (file, 0);
- fprintf (file, "\torc\t#128,ccr\n");
- }
- else
- abort ();
- }
+ /* My understanding of monitor functions is they act just like
+ interrupt functions, except the prologue must mask
+ interrupts. */
+ emit_insn (gen_monitor_prologue ());
if (frame_pointer_needed)
{
/* Push fp. */
- push (file, FRAME_POINTER_REGNUM);
- fprintf (file, "\t%s\t%s,%s\n", h8_mov_op,
- h8_reg_names[STACK_POINTER_REGNUM],
- h8_reg_names[FRAME_POINTER_REGNUM]);
+ push (FRAME_POINTER_REGNUM);
+ emit_insn (gen_rtx_SET (Pmode, frame_pointer_rtx, stack_pointer_rtx));
}
/* Leave room for locals. */
- dosize (file, -1, fsize);
+ dosize (-1, fsize);
/* Push the rest of the registers in ascending order. */
saved_regs = compute_saved_regs ();
@@ -604,22 +554,22 @@ h8300_output_function_prologue (file, size)
switch (n_regs)
{
case 1:
- push (file, regno);
+ push (regno);
break;
case 2:
- fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
- h8_reg_names[regno],
- h8_reg_names[regno + 1]);
+ emit_insn (gen_stm_h8300s_2 (gen_rtx_REG (SImode, regno),
+ gen_rtx_REG (SImode, regno + 1)));
break;
case 3:
- fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
- h8_reg_names[regno],
- h8_reg_names[regno + 2]);
+ emit_insn (gen_stm_h8300s_3 (gen_rtx_REG (SImode, regno),
+ gen_rtx_REG (SImode, regno + 1),
+ gen_rtx_REG (SImode, regno + 2)));
break;
case 4:
- fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
- h8_reg_names[regno],
- h8_reg_names[regno + 3]);
+ emit_insn (gen_stm_h8300s_4 (gen_rtx_REG (SImode, regno),
+ gen_rtx_REG (SImode, regno + 1),
+ gen_rtx_REG (SImode, regno + 2),
+ gen_rtx_REG (SImode, regno + 3)));
break;
default:
abort ();
@@ -628,38 +578,29 @@ h8300_output_function_prologue (file, size)
}
}
-/* Output assembly language code for the function epilogue. */
+int
+h8300_can_use_return_insn_p ()
+{
+ return (reload_completed
+ && !frame_pointer_needed
+ && get_frame_size () == 0
+ && compute_saved_regs () == 0);
+}
-static void
-h8300_output_function_epilogue (file, size)
- FILE *file;
- HOST_WIDE_INT size;
+/* Generate RTL code for the function epilogue. */
+
+void
+h8300_expand_epilogue ()
{
- int fsize = round_frame_size (size);
+ int fsize = round_frame_size (get_frame_size ());
int regno;
- rtx insn = get_last_insn ();
int saved_regs;
int n_regs;
if (h8300_os_task_function_p (current_function_decl))
- {
- /* OS_Task epilogues are nearly naked -- they just have an
- rts instruction. */
- fprintf (file, ";OS_task epilogue\n");
- fprintf (file, "\trts\n");
- goto out;
- }
-
- /* Monitor epilogues are the same as interrupt function epilogues.
- Just make a note that we're in a monitor epilogue. */
- if (h8300_monitor_function_p (current_function_decl))
- fprintf (file, ";monitor epilogue\n");
-
- /* If the last insn was a BARRIER, we don't have to write any code. */
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- if (insn && GET_CODE (insn) == BARRIER)
- goto out;
+ /* OS_Task epilogues are nearly naked -- they just have an
+ rts instruction. */
+ return;
/* Pop the saved registers in descending order. */
saved_regs = compute_saved_regs ();
@@ -687,22 +628,22 @@ h8300_output_function_epilogue (file, size)
switch (n_regs)
{
case 1:
- pop (file, regno);
+ pop (regno);
break;
case 2:
- fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
- h8_reg_names[regno - 1],
- h8_reg_names[regno]);
+ emit_insn (gen_ldm_h8300s_2 (gen_rtx_REG (SImode, regno - 1),
+ gen_rtx_REG (SImode, regno)));
break;
case 3:
- fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
- h8_reg_names[regno - 2],
- h8_reg_names[regno]);
+ emit_insn (gen_ldm_h8300s_3 (gen_rtx_REG (SImode, regno - 2),
+ gen_rtx_REG (SImode, regno - 1),
+ gen_rtx_REG (SImode, regno)));
break;
case 4:
- fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
- h8_reg_names[regno - 3],
- h8_reg_names[regno]);
+ emit_insn (gen_ldm_h8300s_4 (gen_rtx_REG (SImode, regno - 3),
+ gen_rtx_REG (SImode, regno - 2),
+ gen_rtx_REG (SImode, regno - 1),
+ gen_rtx_REG (SImode, regno)));
break;
default:
abort ();
@@ -711,21 +652,23 @@ h8300_output_function_epilogue (file, size)
}
/* Deallocate locals. */
- dosize (file, 1, fsize);
+ dosize (1, fsize);
/* Pop frame pointer if we had one. */
if (frame_pointer_needed)
- pop (file, FRAME_POINTER_REGNUM);
+ pop (FRAME_POINTER_REGNUM);
+}
- if (h8300_current_function_interrupt_function_p ())
- fprintf (file, "\trte\n");
- else
- fprintf (file, "\trts\n");
+/* Output assembly language code for the function epilogue. */
- out:
+static void
+h8300_output_function_epilogue (file, size)
+ FILE *file ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+{
pragma_saveall = 0;
}
-
+
/* Return nonzero if the current function is an interrupt
function. */
diff --git a/gcc/config/h8300/h8300.md b/gcc/config/h8300/h8300.md
index 6d0be34..6a9245e 100644
--- a/gcc/config/h8300/h8300.md
+++ b/gcc/config/h8300/h8300.md
@@ -51,7 +51,8 @@
;; ----------------------------------------------------------------------
(define_constants
- [(UNSPEC_INCDEC 0)])
+ [(UNSPEC_INCDEC 0)
+ (UNSPEC_MONITOR 1)])
(define_constants
[(SC_REG 3)
@@ -1783,7 +1784,32 @@
;; PROLOGUE/EPILOGUE-RELATED INSTRUCTIONS
;; ----------------------------------------------------------------------
-(define_insn "*stm_h8300s_2"
+(define_expand "push_h8300"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REG)))
+ (match_operand:HI 0 "register_operand" "=r"))]
+
+ "TARGET_H8300"
+ "")
+
+(define_expand "push_h8300hs"
+ [(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
+ (match_operand:SI 0 "register_operand" "=r"))]
+ "TARGET_H8300H && TARGET_H8300S"
+ "")
+
+(define_expand "pop_h8300"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (mem:HI (post_inc:HI (reg:HI SP_REG))))]
+ "TARGET_H8300"
+ "")
+
+(define_expand "pop_h8300hs"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (mem:SI (post_inc:SI (reg:SI SP_REG))))]
+ "TARGET_H8300H && TARGET_H8300S"
+ "")
+
+(define_insn "stm_h8300s_2"
[(parallel
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -8)))
@@ -1799,7 +1825,7 @@
[(set_attr "cc" "none")
(set_attr "length" "4")])
-(define_insn "*stm_h8300s_3"
+(define_insn "stm_h8300s_3"
[(parallel
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -12)))
@@ -1820,7 +1846,7 @@
[(set_attr "cc" "none")
(set_attr "length" "4")])
-(define_insn "*stm_h8300s_4"
+(define_insn "stm_h8300s_4"
[(parallel
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -16)))
@@ -1840,6 +1866,107 @@
"stm.l\\t%S0-%S3,@-er7"
[(set_attr "cc" "none")
(set_attr "length" "4")])
+
+(define_insn "ldm_h8300s_2"
+ [(parallel
+ [(set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG) (const_int 8)))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 4)))
+ (match_operand:SI 0 "register_operand" ""))
+ (set (mem:SI (reg:SI SP_REG))
+ (match_operand:SI 1 "register_operand" ""))])]
+ "TARGET_H8300S
+ && ((REGNO (operands[0]) == 0 && REGNO (operands[1]) == 1)
+ || (REGNO (operands[0]) == 2 && REGNO (operands[1]) == 3)
+ || (REGNO (operands[0]) == 4 && REGNO (operands[1]) == 5))"
+ "ldm.l\\t@er7+,%S0-%S1"
+ [(set_attr "cc" "none")
+ (set_attr "length" "4")])
+
+(define_insn "ldm_h8300s_3"
+ [(parallel
+ [(set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG) (const_int 12)))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 8)))
+ (match_operand:SI 0 "register_operand" ""))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 4)))
+ (match_operand:SI 1 "register_operand" ""))
+ (set (mem:SI (reg:SI SP_REG))
+ (match_operand:SI 2 "register_operand" ""))])]
+ "TARGET_H8300S
+ && ((REGNO (operands[0]) == 0
+ && REGNO (operands[1]) == 1
+ && REGNO (operands[2]) == 2)
+ || (REGNO (operands[0]) == 4
+ && REGNO (operands[1]) == 5
+ && REGNO (operands[2]) == 6))"
+ "ldm.l\\t@er7+,%S0-%S2"
+ [(set_attr "cc" "none")
+ (set_attr "length" "4")])
+
+(define_insn "ldm_h8300s_4"
+ [(parallel
+ [(set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG) (const_int 16)))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 12)))
+ (match_operand:SI 0 "register_operand" ""))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 8)))
+ (match_operand:SI 1 "register_operand" ""))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int 4)))
+ (match_operand:SI 2 "register_operand" ""))
+ (set (mem:SI (reg:SI SP_REG))
+ (match_operand:SI 3 "register_operand" ""))])]
+ "TARGET_H8300S
+ && REGNO (operands[0]) == 0
+ && REGNO (operands[1]) == 1
+ && REGNO (operands[2]) == 2
+ && REGNO (operands[3]) == 3"
+ "ldm.l\\t@er7+,%S0-%S3"
+ [(set_attr "cc" "none")
+ (set_attr "length" "4")])
+
+(define_expand "return"
+ [(return)]
+ "h8300_can_use_return_insn_p ()"
+ "")
+
+(define_insn "*return_1"
+ [(return)]
+ "reload_completed"
+ "*
+{
+ if (h8300_current_function_interrupt_function_p ())
+ return \"rte\";
+ else
+ return \"rts\";
+}"
+ [(set_attr "cc" "none")
+ (set_attr "length" "2")])
+
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+ "h8300_expand_prologue (); DONE;")
+
+(define_expand "epilogue"
+ [(return)]
+ ""
+ "h8300_expand_epilogue ();")
+
+(define_insn "monitor_prologue"
+ [(unspec_volatile [(const_int 0)] UNSPEC_MONITOR)]
+ ""
+ "*
+{
+ if (TARGET_H8300)
+ return \"subs\\t#2,r7\;mov.w\\tr0,@-r7\;stc\\tccr,r0l\;mov.b\tr0l,@(2,r7)\;mov.w\\t@r7+,r0\;orc\t#128,ccr\";
+ else if (TARGET_H8300H)
+ return \"mov.l\\ter0,@-er7\;stc\\tccr,r0l\;mov.b\\tr0l,@(4,er7)\;mov.l\\t@er7+,er0\;orc\\t#128,ccr\";
+ else if (TARGET_H8300S)
+ return \"stc\texr,@-er7\;mov.l\\ter0,@-er7\;stc\tccr,r0l\;mov.b\tr0l,@(6,er7)\;mov.l\\t@er7+,er0\;orc\t#128,ccr\";
+}"
+ [(set_attr "length" "20")
+ (set_attr "cc" "clobber")])
;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS