+/* Return nonzero if FUNC is an interrupt function as specified by the
+ "interrupt_handler" attribute. */
+static bool
+m68k_interrupt_function_p(tree func)
+{
+ tree a;
+
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return false;
+
+ a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
+ return (a != NULL_TREE);
+}
+
+/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
+ struct attribute_spec.handler. */
+static tree
+m68k_handle_fndecl_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning ("`%s' attribute only applies to functions",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+static void
+m68k_compute_frame_layout (void)
+{
+ int regno, saved;
+ unsigned int mask, rmask;
+ bool interrupt_handler = m68k_interrupt_function_p (current_function_decl);
+
+ /* Only compute the frame once per function.
+ Don't cache information until reload has been completed. */
+ if (current_frame.funcdef_no == current_function_funcdef_no
+ && reload_completed)
+ return;
+
+ current_frame.size = (get_frame_size () + 3) & -4;
+
+ mask = rmask = saved = 0;
+ for (regno = 0; regno < 16; regno++)
+ if (m68k_save_reg (regno, interrupt_handler))
+ {
+ mask |= 1 << regno;
+ rmask |= 1 << (15 - regno);
+ saved++;
+ }
+ current_frame.offset = saved * 4;
+ current_frame.reg_no = saved;
+ current_frame.reg_mask = mask;
+ current_frame.reg_rev_mask = rmask;
+
+ current_frame.foffset = 0;
+ mask = rmask = saved = 0;
+ if (TARGET_68881 /* || TARGET_CFV4E */)
+ {
+ for (regno = 16; regno < 24; regno++)
+ if (m68k_save_reg (regno, interrupt_handler))
+ {
+ mask |= 1 << (regno - 16);
+ rmask |= 1 << (23 - regno);
+ saved++;
+ }
+ current_frame.foffset = saved * 12 /* (TARGET_CFV4E ? 8 : 12) */;
+ current_frame.offset += current_frame.foffset;
+ }
+ current_frame.fpu_no = saved;
+ current_frame.fpu_mask = mask;
+ current_frame.fpu_rev_mask = rmask;
+
+ /* Remember what function this frame refers to. */
+ current_frame.funcdef_no = current_function_funcdef_no;
+}
+
+HOST_WIDE_INT
+m68k_initial_elimination_offset (int from, int to)
+{
+ /* FIXME: The correct offset to compute here would appear to be
+ (frame_pointer_needed ? -UNITS_PER_WORD * 2 : -UNITS_PER_WORD);
+ but for some obscure reason, this must be 0 to get correct code. */
+ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ return 0;
+
+ m68k_compute_frame_layout ();
+
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return current_frame.offset + current_frame.size + (frame_pointer_needed ? -UNITS_PER_WORD * 2 : -UNITS_PER_WORD);
+ else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return current_frame.offset + current_frame.size;
+
+ abort();
+}
+
+/* Refer to the array `regs_ever_live' to determine which registers
+ to save; `regs_ever_live[I]' is nonzero if register number I
+ is ever used in the function. This function is responsible for
+ knowing which registers should not be saved even if used.
+ Return true if we need to save REGNO. */
+
+static bool
+m68k_save_reg (unsigned int regno, bool interrupt_handler)