/* Subroutines for insn-output.c for Motorola 68000 family.
- Copyright (C) 1987-2017 Free Software Foundation, Inc.
+ Copyright (C) 1987-2021 Free Software Foundation, Inc.
This file is part of GCC.
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#define IN_TARGET_CODE 1
+
#include "config.h"
+#define INCLUDE_STRING
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "cfghooks.h"
#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
#include "rtl.h"
#include "df.h"
#include "alias.h"
#include "optabs.h"
#include "builtins.h"
#include "rtl-iter.h"
+#include "toplev.h"
/* This file should be included last. */
#include "target-def.h"
#endif
static void m68k_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
static void m68k_trampoline_init (rtx, tree, rtx);
-static int m68k_return_pops_args (tree, tree, int);
+static poly_int64 m68k_return_pops_args (tree, tree, poly_int64);
static rtx m68k_delegitimize_address (rtx);
-static void m68k_function_arg_advance (cumulative_args_t, machine_mode,
- const_tree, bool);
-static rtx m68k_function_arg (cumulative_args_t, machine_mode,
- const_tree, bool);
+static void m68k_function_arg_advance (cumulative_args_t,
+ const function_arg_info &);
+static rtx m68k_function_arg (cumulative_args_t, const function_arg_info &);
static bool m68k_cannot_force_const_mem (machine_mode mode, rtx x);
static bool m68k_output_addr_const_extra (FILE *, rtx);
static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
static enum flt_eval_method
m68k_excess_precision (enum excess_precision_type);
+static unsigned int m68k_hard_regno_nregs (unsigned int, machine_mode);
+static bool m68k_hard_regno_mode_ok (unsigned int, machine_mode);
+static bool m68k_modes_tieable_p (machine_mode, machine_mode);
+static machine_mode m68k_promote_function_mode (const_tree, machine_mode,
+ int *, const_tree, int);
+static void m68k_asm_final_postscan_insn (FILE *, rtx_insn *insn, rtx [], int);
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 128
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS m68k_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK m68k_hard_regno_mode_ok
+
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P m68k_modes_tieable_p
+
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE m68k_promote_function_mode
+
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
+#undef TARGET_ASM_FINAL_POSTSCAN_INSN
+#define TARGET_ASM_FINAL_POSTSCAN_INSN m68k_asm_final_postscan_insn
+
static const struct attribute_spec m68k_attribute_table[] =
{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
- affects_type_identity } */
- { "interrupt", 0, 0, true, false, false, m68k_handle_fndecl_attribute,
- false },
- { "interrupt_handler", 0, 0, true, false, false,
- m68k_handle_fndecl_attribute, false },
- { "interrupt_thread", 0, 0, true, false, false,
- m68k_handle_fndecl_attribute, false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+ { "interrupt", 0, 0, true, false, false, false,
+ m68k_handle_fndecl_attribute, NULL },
+ { "interrupt_handler", 0, 0, true, false, false, false,
+ m68k_handle_fndecl_attribute, NULL },
+ { "interrupt_thread", 0, 0, true, false, false, false,
+ m68k_handle_fndecl_attribute, NULL },
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
};
struct gcc_target targetm = TARGET_INITIALIZER;
if (m68k_arch_entry
&& (m68k_arch_entry->microarch != m68k_cpu_entry->microarch
|| (m68k_arch_entry->flags & ~m68k_cpu_entry->flags) != 0))
- warning (0, "-mcpu=%s conflicts with -march=%s",
+ warning (0, "%<-mcpu=%s%> conflicts with %<-march=%s%>",
m68k_cpu_entry->name, m68k_arch_entry->name);
entry = m68k_cpu_entry;
* both specified together. Doing so simply doesn't make sense.
*/
if (TARGET_SEP_DATA && TARGET_ID_SHARED_LIBRARY)
- error ("cannot specify both -msep-data and -mid-shared-library");
+ error ("cannot specify both %<-msep-data%> and %<-mid-shared-library%>");
/* If we're generating code for a separate A5 relative data segment,
* we've got to enable -fPIC as well. This might be relaxable to
/* -mpcrel -fPIC uses 32-bit pc-relative displacements. Raise an
error if the target does not support them. */
if (TARGET_PCREL && !TARGET_68020 && flag_pic == 2)
- error ("-mpcrel -fPIC is not currently supported on selected cpu");
+ error ("%<-mpcrel%> %<-fPIC%> is not currently supported on selected cpu");
/* ??? A historic way of turning on pic, or is this intended to
be an embedded thing that doesn't have the same name binding
}
#ifndef ASM_OUTPUT_ALIGN_WITH_NOP
- if (align_labels > 2)
+ parse_alignment_opts ();
+ int label_alignment = align_labels.levels[0].get_value ();
+ if (label_alignment > 2)
{
- warning (0, "-falign-labels=%d is not supported", align_labels);
- align_labels = 0;
+ warning (0, "%<-falign-labels=%d%> is not supported", label_alignment);
+ str_align_labels = "1";
}
- if (align_loops > 2)
+
+ int loop_alignment = align_loops.levels[0].get_value ();
+ if (loop_alignment > 2)
{
- warning (0, "-falign-loops=%d is not supported", align_loops);
- align_loops = 0;
+ warning (0, "%<-falign-loops=%d%> is not supported", loop_alignment);
+ str_align_loops = "1";
}
#endif
if ((opt_fstack_limit_symbol_arg != NULL || opt_fstack_limit_register_no >= 0)
&& !TARGET_68020)
{
- warning (0, "-fstack-limit- options are not supported on this cpu");
+ warning (0, "%<-fstack-limit-%> options are not supported on this cpu");
opt_fstack_limit_symbol_arg = NULL;
opt_fstack_limit_register_no = -1;
}
if (df_regs_ever_live_p (regno))
return true;
- if (!crtl->is_leaf && call_used_regs[regno])
+ if (!crtl->is_leaf && call_used_or_fixed_reg_p (regno))
return true;
}
return false;
/* Otherwise save everything that isn't call-clobbered. */
- return !call_used_regs[regno];
+ return !call_used_or_fixed_reg_p (regno);
}
/* Emit RTL for a MOVEM or FMOVEM instruction. BASE + OFFSET represents
emit_jump_insn (ret_rtx);
}
\f
-/* Return true if X is a valid comparison operator for the dbcc
- instruction.
-
- Note it rejects floating point comparison operators.
- (In the future we could use Fdbcc).
-
- It also rejects some comparisons when CC_NO_OVERFLOW is set. */
-
-int
-valid_dbcc_comparison_p_2 (rtx x, machine_mode mode ATTRIBUTE_UNUSED)
-{
- switch (GET_CODE (x))
- {
- case EQ: case NE: case GTU: case LTU:
- case GEU: case LEU:
- return 1;
-
- /* Reject some when CC_NO_OVERFLOW is set. This may be over
- conservative */
- case GT: case LT: case GE: case LE:
- return ! (cc_prev_status.flags & CC_NO_OVERFLOW);
- default:
- return 0;
- }
-}
-
-/* Return nonzero if flags are currently in the 68881 flag register. */
-int
-flags_in_68881 (void)
-{
- /* We could add support for these in the future */
- return cc_status.flags & CC_IN_68881;
-}
-
/* Return true if PARALLEL contains register REGNO. */
static bool
m68k_reg_present_p (const_rtx parallel, unsigned int regno)
/* On the m68k all args are always pushed. */
static rtx
-m68k_function_arg (cumulative_args_t cum ATTRIBUTE_UNUSED,
- machine_mode mode ATTRIBUTE_UNUSED,
- const_tree type ATTRIBUTE_UNUSED,
- bool named ATTRIBUTE_UNUSED)
+m68k_function_arg (cumulative_args_t, const function_arg_info &)
{
return NULL_RTX;
}
static void
-m68k_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+m68k_function_arg_advance (cumulative_args_t cum_v,
+ const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
- *cum += (mode != BLKmode
- ? (GET_MODE_SIZE (mode) + 3) & ~3
- : (int_size_in_bytes (type) + 3) & ~3);
+ *cum += (arg.promoted_size_in_bytes () + 3) & ~3;
}
/* Convert X to a legitimate function call memory reference and return the
return x;
}
+\f
+/* For eliding comparisons, we remember how the flags were set.
+ FLAGS_COMPARE_OP0 and FLAGS_COMPARE_OP1 are remembered for a direct
+ comparison, they take priority. FLAGS_OPERAND1 and FLAGS_OPERAND2
+ are used in more cases, they are a fallback for comparisons against
+ zero after a move or arithmetic insn.
+ FLAGS_VALID is set to FLAGS_VALID_NO if we should not use any of
+ these values. */
+
+static rtx flags_compare_op0, flags_compare_op1;
+static rtx flags_operand1, flags_operand2;
+static attr_flags_valid flags_valid = FLAGS_VALID_NO;
+
+/* Return a code other than UNKNOWN if we can elide a CODE comparison of
+ OP0 with OP1. */
+
+rtx_code
+m68k_find_flags_value (rtx op0, rtx op1, rtx_code code)
+{
+ if (flags_compare_op0 != NULL_RTX)
+ {
+ if (rtx_equal_p (op0, flags_compare_op0)
+ && rtx_equal_p (op1, flags_compare_op1))
+ return code;
+ if (rtx_equal_p (op0, flags_compare_op1)
+ && rtx_equal_p (op1, flags_compare_op0))
+ return swap_condition (code);
+ return UNKNOWN;
+ }
+
+ machine_mode mode = GET_MODE (op0);
+ if (op1 != CONST0_RTX (mode))
+ return UNKNOWN;
+ /* Comparisons against 0 with these two should have been optimized out. */
+ gcc_assert (code != LTU && code != GEU);
+ if (flags_valid == FLAGS_VALID_NOOV && (code == GT || code == LE))
+ return UNKNOWN;
+ if (rtx_equal_p (flags_operand1, op0) || rtx_equal_p (flags_operand2, op0))
+ return (FLOAT_MODE_P (mode) ? code
+ : code == GE ? PLUS : code == LT ? MINUS : code);
+ /* See if we are testing whether the high part of a DImode value is
+ positive or negative and we have the full value as a remembered
+ operand. */
+ if (code != GE && code != LT)
+ return UNKNOWN;
+ if (mode == SImode
+ && flags_operand1 != NULL_RTX && GET_MODE (flags_operand1) == DImode
+ && REG_P (flags_operand1) && REG_P (op0)
+ && hard_regno_nregs (REGNO (flags_operand1), DImode) == 2
+ && REGNO (flags_operand1) == REGNO (op0))
+ return code == GE ? PLUS : MINUS;
+ if (mode == SImode
+ && flags_operand2 != NULL_RTX && GET_MODE (flags_operand2) == DImode
+ && REG_P (flags_operand2) && REG_P (op0)
+ && hard_regno_nregs (REGNO (flags_operand2), DImode) == 2
+ && REGNO (flags_operand2) == REGNO (op0))
+ return code == GE ? PLUS : MINUS;
+ return UNKNOWN;
+}
+
+/* Called through CC_STATUS_INIT, which is invoked by final whenever a
+ label is encountered. */
+
+void
+m68k_init_cc ()
+{
+ flags_compare_op0 = flags_compare_op1 = NULL_RTX;
+ flags_operand1 = flags_operand2 = NULL_RTX;
+ flags_valid = FLAGS_VALID_NO;
+}
+
+/* Update flags for a move operation with OPERANDS. Called for move
+ operations where attr_flags_valid returns "set". */
+
+static void
+handle_flags_for_move (rtx *operands)
+{
+ flags_compare_op0 = flags_compare_op1 = NULL_RTX;
+ if (!ADDRESS_REG_P (operands[0]))
+ {
+ flags_valid = FLAGS_VALID_MOVE;
+ flags_operand1 = side_effects_p (operands[0]) ? NULL_RTX : operands[0];
+ if (side_effects_p (operands[1])
+ /* ??? For mem->mem moves, this can discard the source as a
+ valid compare operand. If you assume aligned moves, this
+ is unnecessary, but in theory, we could have an unaligned
+ move overwriting parts of its source. */
+ || modified_in_p (operands[1], current_output_insn))
+ flags_operand2 = NULL_RTX;
+ else
+ flags_operand2 = operands[1];
+ return;
+ }
+ if (flags_operand1 != NULL_RTX
+ && modified_in_p (flags_operand1, current_output_insn))
+ flags_operand1 = NULL_RTX;
+ if (flags_operand2 != NULL_RTX
+ && modified_in_p (flags_operand2, current_output_insn))
+ flags_operand2 = NULL_RTX;
+}
+
+/* Process INSN to remember flag operands if possible. */
+
+static void
+m68k_asm_final_postscan_insn (FILE *, rtx_insn *insn, rtx [], int)
+{
+ enum attr_flags_valid v = get_attr_flags_valid (insn);
+ if (v == FLAGS_VALID_SET)
+ return;
+ /* Comparisons use FLAGS_VALID_SET, so we can be sure we need to clear these
+ now. */
+ flags_compare_op0 = flags_compare_op1 = NULL_RTX;
+
+ if (v == FLAGS_VALID_NO)
+ {
+ flags_operand1 = flags_operand2 = NULL_RTX;
+ return;
+ }
+ else if (v == FLAGS_VALID_UNCHANGED)
+ {
+ if (flags_operand1 != NULL_RTX && modified_in_p (flags_operand1, insn))
+ flags_operand1 = NULL_RTX;
+ if (flags_operand2 != NULL_RTX && modified_in_p (flags_operand2, insn))
+ flags_operand2 = NULL_RTX;
+ return;
+ }
+
+ flags_valid = v;
+ rtx set = single_set (insn);
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+ if (side_effects_p (dest))
+ dest = NULL_RTX;
+
+ switch (v)
+ {
+ case FLAGS_VALID_YES:
+ case FLAGS_VALID_NOOV:
+ flags_operand1 = dest;
+ flags_operand2 = NULL_RTX;
+ break;
+ case FLAGS_VALID_MOVE:
+ /* fmoves to memory or data registers do not set the condition
+ codes. Normal moves _do_ set the condition codes, but not in
+ a way that is appropriate for comparison with 0, because -0.0
+ would be treated as a negative nonzero number. Note that it
+ isn't appropriate to conditionalize this restriction on
+ HONOR_SIGNED_ZEROS because that macro merely indicates whether
+ we care about the difference between -0.0 and +0.0. */
+ if (dest != NULL_RTX
+ && !FP_REG_P (dest)
+ && (FP_REG_P (src)
+ || GET_CODE (src) == FIX
+ || FLOAT_MODE_P (GET_MODE (dest))))
+ flags_operand1 = flags_operand2 = NULL_RTX;
+ else
+ {
+ flags_operand1 = dest;
+ if (GET_MODE (src) != VOIDmode && !side_effects_p (src)
+ && !modified_in_p (src, insn))
+ flags_operand2 = src;
+ else
+ flags_operand2 = NULL_RTX;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return;
+}
-
/* Output a dbCC; jCC sequence. Note we do not handle the
- floating point version of this sequence (Fdbcc). We also
- do not handle alternative conditions when CC_NO_OVERFLOW is
- set. It is assumed that valid_dbcc_comparison_p and flags_in_68881 will
- kick those out before we get here. */
+ floating point version of this sequence (Fdbcc).
+ OPERANDS are as in the two peepholes. CODE is the code
+ returned by m68k_output_branch_<mode>. */
void
-output_dbcc_and_branch (rtx *operands)
+output_dbcc_and_branch (rtx *operands, rtx_code code)
{
- switch (GET_CODE (operands[3]))
+ switch (code)
{
case EQ:
output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands);
output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands);
break;
+ case PLUS:
+ output_asm_insn ("dbpl %0,%l1\n\tjle %l2", operands);
+ break;
+
+ case MINUS:
+ output_asm_insn ("dbmi %0,%l1\n\tjle %l2", operands);
+ break;
+
default:
gcc_unreachable ();
}
to compensate for the fact that dbcc decrements in HImode. */
switch (GET_MODE (operands[0]))
{
- case SImode:
+ case E_SImode:
output_asm_insn ("clr%.w %0\n\tsubq%.l #1,%0\n\tjpl %l1", operands);
break;
- case HImode:
+ case E_HImode:
break;
default:
return "";
}
-const char *
-output_btst (rtx *operands, rtx countop, rtx dataop, rtx_insn *insn, int signpos)
+rtx_code
+m68k_output_btst (rtx countop, rtx dataop, rtx_code code, int signpos)
{
- operands[0] = countop;
- operands[1] = dataop;
+ rtx ops[2];
+ ops[0] = countop;
+ ops[1] = dataop;
if (GET_CODE (countop) == CONST_INT)
{
- register int count = INTVAL (countop);
+ int count = INTVAL (countop);
/* If COUNT is bigger than size of storage unit in use,
advance to the containing unit of same size. */
if (count > signpos)
{
int offset = (count & ~signpos) / 8;
count = count & signpos;
- operands[1] = dataop = adjust_address (dataop, QImode, offset);
+ ops[1] = dataop = adjust_address (dataop, QImode, offset);
+ }
+
+ if (code == EQ || code == NE)
+ {
+ if (count == 31)
+ {
+ output_asm_insn ("tst%.l %1", ops);
+ return code == EQ ? PLUS : MINUS;
+ }
+ if (count == 15)
+ {
+ output_asm_insn ("tst%.w %1", ops);
+ return code == EQ ? PLUS : MINUS;
+ }
+ if (count == 7)
+ {
+ output_asm_insn ("tst%.b %1", ops);
+ return code == EQ ? PLUS : MINUS;
+ }
}
- if (count == signpos)
- cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N;
- else
- cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N;
-
- /* These three statements used to use next_insns_test_no...
- but it appears that this should do the same job. */
- if (count == 31
- && next_insn_tests_no_inequality (insn))
- return "tst%.l %1";
- if (count == 15
- && next_insn_tests_no_inequality (insn))
- return "tst%.w %1";
- if (count == 7
- && next_insn_tests_no_inequality (insn))
- return "tst%.b %1";
/* Try to use `movew to ccr' followed by the appropriate branch insn.
On some m68k variants unfortunately that's slower than btst.
On 68000 and higher, that should also work for all HImode operands. */
if (TUNE_CPU32 || TARGET_COLDFIRE || optimize_size)
{
- if (count == 3 && DATA_REG_P (operands[1])
- && next_insn_tests_no_inequality (insn))
+ if (count == 3 && DATA_REG_P (ops[1]) && (code == EQ || code == NE))
{
- cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N | CC_NO_OVERFLOW;
- return "move%.w %1,%%ccr";
+ output_asm_insn ("move%.w %1,%%ccr", ops);
+ return code == EQ ? PLUS : MINUS;
}
- if (count == 2 && DATA_REG_P (operands[1])
- && next_insn_tests_no_inequality (insn))
+ if (count == 2 && DATA_REG_P (ops[1]) && (code == EQ || code == NE))
{
- cc_status.flags = CC_NOT_NEGATIVE | CC_INVERTED | CC_NO_OVERFLOW;
- return "move%.w %1,%%ccr";
+ output_asm_insn ("move%.w %1,%%ccr", ops);
+ return code == EQ ? NE : EQ;
}
/* count == 1 followed by bvc/bvs and
count == 0 followed by bcc/bcs are also possible, but need
cc_status.flags = CC_NOT_NEGATIVE;
}
- return "btst %0,%1";
+ output_asm_insn ("btst %0,%1", ops);
+ return code;
+}
+
+/* Output a bftst instruction for a zero_extract with ZXOP0, ZXOP1 and ZXOP2
+ operands. CODE is the code of the comparison, and we return the code to
+ be actually used in the jump. */
+
+rtx_code
+m68k_output_bftst (rtx zxop0, rtx zxop1, rtx zxop2, rtx_code code)
+{
+ if (zxop1 == const1_rtx && GET_CODE (zxop2) == CONST_INT)
+ {
+ int width = GET_CODE (zxop0) == REG ? 31 : 7;
+ /* Pass 1000 as SIGNPOS argument so that btst will
+ not think we are testing the sign bit for an `and'
+ and assume that nonzero implies a negative result. */
+ return m68k_output_btst (GEN_INT (width - INTVAL (zxop2)), zxop0, code, 1000);
+ }
+ rtx ops[3] = { zxop0, zxop1, zxop2 };
+ output_asm_insn ("bftst %0{%b2:%b1}", ops);
+ return code;
}
\f
/* Return true if X is a legitimate base register. STRICT_P says
return m68k_unwrap_symbol_1 (orig, unwrap_reloc32_p, NULL);
}
-/* Prescan insn before outputing assembler for it. */
+/* Adjust decorated address operand before outputing assembler for it. */
-void
-m68k_final_prescan_insn (rtx_insn *insn ATTRIBUTE_UNUSED,
- rtx *operands, int n_operands)
+static void
+m68k_adjust_decorated_operand (rtx op)
{
- int i;
-
/* Combine and, possibly, other optimizations may do good job
converting
(const (unspec [(symbol)]))
to patch up anything outside of the operand. */
subrtx_var_iterator::array_type array;
- for (i = 0; i < n_operands; ++i)
+ FOR_EACH_SUBRTX_VAR (iter, array, op, ALL)
{
- rtx op;
-
- op = operands[i];
-
- FOR_EACH_SUBRTX_VAR (iter, array, op, ALL)
+ rtx x = *iter;
+ if (m68k_unwrap_symbol (x, true) != x)
{
- rtx x = *iter;
- if (m68k_unwrap_symbol (x, true) != x)
- {
- rtx plus;
+ rtx plus;
- gcc_assert (GET_CODE (x) == CONST);
- plus = XEXP (x, 0);
+ gcc_assert (GET_CODE (x) == CONST);
+ plus = XEXP (x, 0);
- if (GET_CODE (plus) == PLUS || GET_CODE (plus) == MINUS)
- {
- rtx unspec;
- rtx addend;
+ if (GET_CODE (plus) == PLUS || GET_CODE (plus) == MINUS)
+ {
+ rtx unspec;
+ rtx addend;
- unspec = XEXP (plus, 0);
- gcc_assert (GET_CODE (unspec) == UNSPEC);
- addend = XEXP (plus, 1);
- gcc_assert (CONST_INT_P (addend));
+ unspec = XEXP (plus, 0);
+ gcc_assert (GET_CODE (unspec) == UNSPEC);
+ addend = XEXP (plus, 1);
+ gcc_assert (CONST_INT_P (addend));
- /* We now have all the pieces, rearrange them. */
+ /* We now have all the pieces, rearrange them. */
- /* Move symbol to plus. */
- XEXP (plus, 0) = XVECEXP (unspec, 0, 0);
+ /* Move symbol to plus. */
+ XEXP (plus, 0) = XVECEXP (unspec, 0, 0);
- /* Move plus inside unspec. */
- XVECEXP (unspec, 0, 0) = plus;
+ /* Move plus inside unspec. */
+ XVECEXP (unspec, 0, 0) = plus;
- /* Move unspec to top level of const. */
- XEXP (x, 0) = unspec;
- }
- iter.skip_subrtxes ();
+ /* Move unspec to top level of const. */
+ XEXP (x, 0) = unspec;
}
+ iter.skip_subrtxes ();
}
}
}
consider (plus (%a5) (const (unspec))) to be a good enough
operand for push, so it forces it into a register. The bad
thing about this is that combiner, due to copy propagation and other
- optimizations, sometimes can not later fix this. As a consequence,
+ optimizations, sometimes cannot later fix this. As a consequence,
additional register may be allocated resulting in a spill.
For reference, see args processing loops in
calls.c:emit_library_call_value_1.
m68k_libcall_value_in_a0_p = true;
a0 = emit_library_call_value (m68k_get_tls_get_addr (), NULL_RTX, LCT_PURE,
- Pmode, 1, x, Pmode);
+ Pmode, x, Pmode);
m68k_libcall_value_in_a0_p = false;
insns = get_insns ();
/* Emit the call sequence. */
m68k_libcall_value_in_a0_p = true;
a0 = emit_library_call_value (m68k_get_m68k_read_tp (), NULL_RTX, LCT_PURE,
- Pmode, 0);
+ Pmode);
m68k_libcall_value_in_a0_p = false;
insns = get_insns ();
end_sequence ();
case CONST_DOUBLE:
/* Make 0.0 cheaper than other floating constants to
encourage creating tstsf and tstdf insns. */
- if (outer_code == COMPARE
+ if ((GET_RTX_CLASS (outer_code) == RTX_COMPARE
+ || GET_RTX_CLASS (outer_code) == RTX_COMM_COMPARE)
&& (x == CONST0_RTX (SFmode) || x == CONST0_RTX (DFmode)))
*total = 4;
else
return true;
case ZERO_EXTRACT:
- if (outer_code == COMPARE)
+ if (GET_RTX_CLASS (outer_code) == RTX_COMPARE
+ || GET_RTX_CLASS (outer_code) == RTX_COMM_COMPARE)
*total = 0;
return false;
const char *
output_move_simode (rtx *operands)
{
+ handle_flags_for_move (operands);
+
if (GET_CODE (operands[1]) == CONST_INT)
return output_move_simode_const (operands);
else if ((GET_CODE (operands[1]) == SYMBOL_REF
const char *
output_move_himode (rtx *operands)
{
- if (GET_CODE (operands[1]) == CONST_INT)
+ if (GET_CODE (operands[1]) == CONST_INT)
{
if (operands[1] == const0_rtx
&& (DATA_REG_P (operands[0])
return "move%.w %1,%0";
}
else if (CONSTANT_P (operands[1]))
- return "move%.l %1,%0";
+ gcc_unreachable ();
return "move%.w %1,%0";
}
const char *
output_move_qimode (rtx *operands)
{
+ handle_flags_for_move (operands);
+
/* 68k family always modifies the stack pointer by at least 2, even for
byte pushes. The 5200 (ColdFire) does not do this. */
-
+
/* This case is generated by pushqi1 pattern now. */
gcc_assert (!(GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
if (operands[1] == const0_rtx && ADDRESS_REG_P (operands[0]))
return "sub%.l %0,%0";
if (GET_CODE (operands[1]) != CONST_INT && CONSTANT_P (operands[1]))
- return "move%.l %1,%0";
+ gcc_unreachable ();
/* 68k family (including the 5200 ColdFire) does not support byte moves to
from address registers. */
if (ADDRESS_REG_P (operands[0]) || ADDRESS_REG_P (operands[1]))
- return "move%.w %1,%0";
+ {
+ if (ADDRESS_REG_P (operands[1]))
+ CC_STATUS_INIT;
+ return "move%.w %1,%0";
+ }
return "move%.b %1,%0";
}
/* Normal case: do the two words, low-numbered first. */
- m68k_final_prescan_insn (NULL, operands, 2);
handle_movsi (operands);
/* Do the middle one of the three words for long double */
if (addreg1)
handle_reg_adjust (addreg1, 4);
- m68k_final_prescan_insn (NULL, middlehalf, 2);
handle_movsi (middlehalf);
}
handle_reg_adjust (addreg1, 4);
/* Do that word. */
- m68k_final_prescan_insn (NULL, latehalf, 2);
handle_movsi (latehalf);
/* Undo the adds we just did. */
{
const char *s;
- gcc_assert (GET_MODE (reg) == SImode
- && -12 <= n && n != 0 && n <= 12);
+ gcc_assert (GET_MODE (reg) == SImode && n >= -12 && n != 0 && n <= 12);
switch (n)
{
{
rtx reg2;
- gcc_assert (GET_MODE (reg1) == SImode
- && -12 <= n && n != 0 && n <= 12);
+ gcc_assert (GET_MODE (reg1) == SImode && n >= -12 && n != 0 && n <= 12);
reg1 = copy_rtx (reg1);
reg2 = copy_rtx (reg1);
int
emit_move_sequence (rtx *operands, machine_mode mode, rtx scratch_reg)
{
- register rtx operand0 = operands[0];
- register rtx operand1 = operands[1];
- register rtx tem;
+ rtx operand0 = operands[0];
+ rtx operand1 = operands[1];
+ rtx tem;
if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == REG
}
return "add%.l %2,%0";
}
-\f
-/* Store in cc_status the expressions that the condition codes will
- describe after execution of an instruction whose pattern is EXP.
- Do not alter them if the instruction would not alter the cc's. */
-
-/* On the 68000, all the insns to store in an address register fail to
- set the cc's. However, in some cases these instructions can make it
- possibly invalid to use the saved cc's. In those cases we clear out
- some or all of the saved cc's so they won't be used. */
-void
-notice_update_cc (rtx exp, rtx insn)
+/* Emit a comparison between OP0 and OP1. Return true iff the comparison
+ was reversed. SC1 is an SImode scratch reg, and SC2 a DImode scratch reg,
+ as needed. CODE is the code of the comparison, we return it unchanged or
+ swapped, as necessary. */
+rtx_code
+m68k_output_compare_di (rtx op0, rtx op1, rtx sc1, rtx sc2, rtx_insn *insn,
+ rtx_code code)
{
- if (GET_CODE (exp) == SET)
+ rtx ops[4];
+ ops[0] = op0;
+ ops[1] = op1;
+ ops[2] = sc1;
+ ops[3] = sc2;
+ if (op1 == const0_rtx)
{
- if (GET_CODE (SET_SRC (exp)) == CALL)
- CC_STATUS_INIT;
- else if (ADDRESS_REG_P (SET_DEST (exp)))
+ if (!REG_P (op0) || ADDRESS_REG_P (op0))
{
- if (cc_status.value1 && modified_in_p (cc_status.value1, insn))
- cc_status.value1 = 0;
- if (cc_status.value2 && modified_in_p (cc_status.value2, insn))
- cc_status.value2 = 0;
+ rtx xoperands[2];
+
+ xoperands[0] = sc2;
+ xoperands[1] = op0;
+ output_move_double (xoperands);
+ output_asm_insn ("neg%.l %R0\n\tnegx%.l %0", xoperands);
+ return swap_condition (code);
}
- /* fmoves to memory or data registers do not set the condition
- codes. Normal moves _do_ set the condition codes, but not in
- a way that is appropriate for comparison with 0, because -0.0
- would be treated as a negative nonzero number. Note that it
- isn't appropriate to conditionalize this restriction on
- HONOR_SIGNED_ZEROS because that macro merely indicates whether
- we care about the difference between -0.0 and +0.0. */
- else if (!FP_REG_P (SET_DEST (exp))
- && SET_DEST (exp) != cc0_rtx
- && (FP_REG_P (SET_SRC (exp))
- || GET_CODE (SET_SRC (exp)) == FIX
- || FLOAT_MODE_P (GET_MODE (SET_DEST (exp)))))
- CC_STATUS_INIT;
- /* A pair of move insns doesn't produce a useful overall cc. */
- else if (!FP_REG_P (SET_DEST (exp))
- && !FP_REG_P (SET_SRC (exp))
- && GET_MODE_SIZE (GET_MODE (SET_SRC (exp))) > 4
- && (GET_CODE (SET_SRC (exp)) == REG
- || GET_CODE (SET_SRC (exp)) == MEM
- || GET_CODE (SET_SRC (exp)) == CONST_DOUBLE))
- CC_STATUS_INIT;
- else if (SET_DEST (exp) != pc_rtx)
+ if (find_reg_note (insn, REG_DEAD, op0))
{
- cc_status.flags = 0;
- cc_status.value1 = SET_DEST (exp);
- cc_status.value2 = SET_SRC (exp);
+ output_asm_insn ("neg%.l %R0\n\tnegx%.l %0", ops);
+ return swap_condition (code);
+ }
+ else
+ {
+ /* 'sub' clears %1, and also clears the X cc bit.
+ 'tst' sets the Z cc bit according to the low part of the DImode
+ operand.
+ 'subx %1' (i.e. subx #0) acts as a (non-existent) tstx on the high
+ part. */
+ output_asm_insn ("sub%.l %2,%2\n\ttst%.l %R0\n\tsubx%.l %2,%0", ops);
+ return code;
}
}
- else if (GET_CODE (exp) == PARALLEL
- && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
+
+ if (rtx_equal_p (sc2, op0))
{
- rtx dest = SET_DEST (XVECEXP (exp, 0, 0));
- rtx src = SET_SRC (XVECEXP (exp, 0, 0));
+ output_asm_insn ("sub%.l %R1,%R3\n\tsubx%.l %1,%3", ops);
+ return code;
+ }
+ else
+ {
+ output_asm_insn ("sub%.l %R0,%R3\n\tsubx%.l %0,%3", ops);
+ return swap_condition (code);
+ }
+}
- if (ADDRESS_REG_P (dest))
- CC_STATUS_INIT;
- else if (dest != pc_rtx)
+static void
+remember_compare_flags (rtx op0, rtx op1)
+{
+ if (side_effects_p (op0) || side_effects_p (op1))
+ CC_STATUS_INIT;
+ else
+ {
+ flags_compare_op0 = op0;
+ flags_compare_op1 = op1;
+ flags_operand1 = flags_operand2 = NULL_RTX;
+ flags_valid = FLAGS_VALID_SET;
+ }
+}
+
+/* Emit a comparison between OP0 and OP1. CODE is the code of the
+ comparison. It is returned, potentially modified if necessary. */
+rtx_code
+m68k_output_compare_si (rtx op0, rtx op1, rtx_code code)
+{
+ rtx_code tmp = m68k_find_flags_value (op0, op1, code);
+ if (tmp != UNKNOWN)
+ return tmp;
+
+ remember_compare_flags (op0, op1);
+
+ rtx ops[2];
+ ops[0] = op0;
+ ops[1] = op1;
+ if (op1 == const0_rtx && (TARGET_68020 || TARGET_COLDFIRE || !ADDRESS_REG_P (op0)))
+ output_asm_insn ("tst%.l %0", ops);
+ else if (GET_CODE (op0) == MEM && GET_CODE (op1) == MEM)
+ output_asm_insn ("cmpm%.l %1,%0", ops);
+ else if (REG_P (op1)
+ || (!REG_P (op0) && GET_CODE (op0) != MEM))
+ {
+ output_asm_insn ("cmp%.l %d0,%d1", ops);
+ std::swap (flags_compare_op0, flags_compare_op1);
+ return swap_condition (code);
+ }
+ else if (!TARGET_COLDFIRE
+ && ADDRESS_REG_P (op0)
+ && GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) < 0x8000
+ && INTVAL (op1) >= -0x8000)
+ output_asm_insn ("cmp%.w %1,%0", ops);
+ else
+ output_asm_insn ("cmp%.l %d1,%d0", ops);
+ return code;
+}
+
+/* Emit a comparison between OP0 and OP1. CODE is the code of the
+ comparison. It is returned, potentially modified if necessary. */
+rtx_code
+m68k_output_compare_hi (rtx op0, rtx op1, rtx_code code)
+{
+ rtx_code tmp = m68k_find_flags_value (op0, op1, code);
+ if (tmp != UNKNOWN)
+ return tmp;
+
+ remember_compare_flags (op0, op1);
+
+ rtx ops[2];
+ ops[0] = op0;
+ ops[1] = op1;
+ if (op1 == const0_rtx)
+ output_asm_insn ("tst%.w %d0", ops);
+ else if (GET_CODE (op0) == MEM && GET_CODE (op1) == MEM)
+ output_asm_insn ("cmpm%.w %1,%0", ops);
+ else if ((REG_P (op1) && !ADDRESS_REG_P (op1))
+ || (!REG_P (op0) && GET_CODE (op0) != MEM))
+ {
+ output_asm_insn ("cmp%.w %d0,%d1", ops);
+ std::swap (flags_compare_op0, flags_compare_op1);
+ return swap_condition (code);
+ }
+ else
+ output_asm_insn ("cmp%.w %d1,%d0", ops);
+ return code;
+}
+
+/* Emit a comparison between OP0 and OP1. CODE is the code of the
+ comparison. It is returned, potentially modified if necessary. */
+rtx_code
+m68k_output_compare_qi (rtx op0, rtx op1, rtx_code code)
+{
+ rtx_code tmp = m68k_find_flags_value (op0, op1, code);
+ if (tmp != UNKNOWN)
+ return tmp;
+
+ remember_compare_flags (op0, op1);
+
+ rtx ops[2];
+ ops[0] = op0;
+ ops[1] = op1;
+ if (op1 == const0_rtx)
+ output_asm_insn ("tst%.b %d0", ops);
+ else if (GET_CODE (op0) == MEM && GET_CODE (op1) == MEM)
+ output_asm_insn ("cmpm%.b %1,%0", ops);
+ else if (REG_P (op1) || (!REG_P (op0) && GET_CODE (op0) != MEM))
+ {
+ output_asm_insn ("cmp%.b %d0,%d1", ops);
+ std::swap (flags_compare_op0, flags_compare_op1);
+ return swap_condition (code);
+ }
+ else
+ output_asm_insn ("cmp%.b %d1,%d0", ops);
+ return code;
+}
+
+/* Emit a comparison between OP0 and OP1. CODE is the code of the
+ comparison. It is returned, potentially modified if necessary. */
+rtx_code
+m68k_output_compare_fp (rtx op0, rtx op1, rtx_code code)
+{
+ rtx_code tmp = m68k_find_flags_value (op0, op1, code);
+ if (tmp != UNKNOWN)
+ return tmp;
+
+ rtx ops[2];
+ ops[0] = op0;
+ ops[1] = op1;
+
+ remember_compare_flags (op0, op1);
+
+ machine_mode mode = GET_MODE (op0);
+ std::string prec = mode == SFmode ? "s" : mode == DFmode ? "d" : "x";
+
+ if (op1 == CONST0_RTX (GET_MODE (op0)))
+ {
+ if (FP_REG_P (op0))
{
- cc_status.flags = 0;
- cc_status.value1 = dest;
- cc_status.value2 = src;
+ if (TARGET_COLDFIRE_FPU)
+ output_asm_insn ("ftst%.d %0", ops);
+ else
+ output_asm_insn ("ftst%.x %0", ops);
}
+ else
+ output_asm_insn (("ftst%." + prec + " %0").c_str (), ops);
+ return code;
}
- else
- CC_STATUS_INIT;
- if (cc_status.value2 != 0
- && ADDRESS_REG_P (cc_status.value2)
- && GET_MODE (cc_status.value2) == QImode)
- CC_STATUS_INIT;
- if (cc_status.value2 != 0)
- switch (GET_CODE (cc_status.value2))
- {
- case ASHIFT: case ASHIFTRT: case LSHIFTRT:
- case ROTATE: case ROTATERT:
- /* These instructions always clear the overflow bit, and set
- the carry to the bit shifted out. */
- cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
- break;
- case PLUS: case MINUS: case MULT:
- case DIV: case UDIV: case MOD: case UMOD: case NEG:
- if (GET_MODE (cc_status.value2) != VOIDmode)
- cc_status.flags |= CC_NO_OVERFLOW;
- break;
- case ZERO_EXTEND:
- /* (SET r1 (ZERO_EXTEND r2)) on this machine
- ends with a move insn moving r2 in r2's mode.
- Thus, the cc's are set for r2.
- This can set N bit spuriously. */
- cc_status.flags |= CC_NOT_NEGATIVE;
+ switch (which_alternative)
+ {
+ case 0:
+ if (TARGET_COLDFIRE_FPU)
+ output_asm_insn ("fcmp%.d %1,%0", ops);
+ else
+ output_asm_insn ("fcmp%.x %1,%0", ops);
+ break;
+ case 1:
+ output_asm_insn (("fcmp%." + prec + " %f1,%0").c_str (), ops);
+ break;
+ case 2:
+ output_asm_insn (("fcmp%." + prec + " %0,%f1").c_str (), ops);
+ std::swap (flags_compare_op0, flags_compare_op1);
+ return swap_condition (code);
+ case 3:
+ /* This is the ftst case, handled earlier. */
+ gcc_unreachable ();
+ }
+ return code;
+}
- default:
- break;
- }
- if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
- && cc_status.value2
- && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
- cc_status.value2 = 0;
- /* Check for PRE_DEC in dest modifying a register used in src. */
- if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM
- && GET_CODE (XEXP (cc_status.value1, 0)) == PRE_DEC
- && cc_status.value2
- && reg_overlap_mentioned_p (XEXP (XEXP (cc_status.value1, 0), 0),
- cc_status.value2))
- cc_status.value2 = 0;
- if (((cc_status.value1 && FP_REG_P (cc_status.value1))
- || (cc_status.value2 && FP_REG_P (cc_status.value2))))
- cc_status.flags = CC_IN_68881;
- if (cc_status.value2 && GET_CODE (cc_status.value2) == COMPARE
- && GET_MODE_CLASS (GET_MODE (XEXP (cc_status.value2, 0))) == MODE_FLOAT)
- {
- cc_status.flags = CC_IN_68881;
- if (!FP_REG_P (XEXP (cc_status.value2, 0))
- && FP_REG_P (XEXP (cc_status.value2, 1)))
- cc_status.flags |= CC_REVERSED;
+/* Return an output template for a branch with CODE. */
+const char *
+m68k_output_branch_integer (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "jeq %l3";
+ case NE:
+ return "jne %l3";
+ case GT:
+ return "jgt %l3";
+ case GTU:
+ return "jhi %l3";
+ case LT:
+ return "jlt %l3";
+ case LTU:
+ return "jcs %l3";
+ case GE:
+ return "jge %l3";
+ case GEU:
+ return "jcc %l3";
+ case LE:
+ return "jle %l3";
+ case LEU:
+ return "jls %l3";
+ case PLUS:
+ return "jpl %l3";
+ case MINUS:
+ return "jmi %l3";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return an output template for a reversed branch with CODE. */
+const char *
+m68k_output_branch_integer_rev (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "jne %l3";
+ case NE:
+ return "jeq %l3";
+ case GT:
+ return "jle %l3";
+ case GTU:
+ return "jls %l3";
+ case LT:
+ return "jge %l3";
+ case LTU:
+ return "jcc %l3";
+ case GE:
+ return "jlt %l3";
+ case GEU:
+ return "jcs %l3";
+ case LE:
+ return "jgt %l3";
+ case LEU:
+ return "jhi %l3";
+ case PLUS:
+ return "jmi %l3";
+ case MINUS:
+ return "jpl %l3";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return an output template for a scc instruction with CODE. */
+const char *
+m68k_output_scc (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "seq %0";
+ case NE:
+ return "sne %0";
+ case GT:
+ return "sgt %0";
+ case GTU:
+ return "shi %0";
+ case LT:
+ return "slt %0";
+ case LTU:
+ return "scs %0";
+ case GE:
+ return "sge %0";
+ case GEU:
+ return "scc %0";
+ case LE:
+ return "sle %0";
+ case LEU:
+ return "sls %0";
+ case PLUS:
+ return "spl %0";
+ case MINUS:
+ return "smi %0";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return an output template for a floating point branch
+ instruction with CODE. */
+const char *
+m68k_output_branch_float (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "fjeq %l3";
+ case NE:
+ return "fjne %l3";
+ case GT:
+ return "fjgt %l3";
+ case LT:
+ return "fjlt %l3";
+ case GE:
+ return "fjge %l3";
+ case LE:
+ return "fjle %l3";
+ case ORDERED:
+ return "fjor %l3";
+ case UNORDERED:
+ return "fjun %l3";
+ case UNEQ:
+ return "fjueq %l3";
+ case UNGE:
+ return "fjuge %l3";
+ case UNGT:
+ return "fjugt %l3";
+ case UNLE:
+ return "fjule %l3";
+ case UNLT:
+ return "fjult %l3";
+ case LTGT:
+ return "fjogl %l3";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return an output template for a reversed floating point branch
+ instruction with CODE. */
+const char *
+m68k_output_branch_float_rev (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "fjne %l3";
+ case NE:
+ return "fjeq %l3";
+ case GT:
+ return "fjngt %l3";
+ case LT:
+ return "fjnlt %l3";
+ case GE:
+ return "fjnge %l3";
+ case LE:
+ return "fjnle %l3";
+ case ORDERED:
+ return "fjun %l3";
+ case UNORDERED:
+ return "fjor %l3";
+ case UNEQ:
+ return "fjogl %l3";
+ case UNGE:
+ return "fjolt %l3";
+ case UNGT:
+ return "fjole %l3";
+ case UNLE:
+ return "fjogt %l3";
+ case UNLT:
+ return "fjoge %l3";
+ case LTGT:
+ return "fjueq %l3";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return an output template for a floating point scc
+ instruction with CODE. */
+const char *
+m68k_output_scc_float (rtx_code code)
+{
+ switch (code)
+ {
+ case EQ:
+ return "fseq %0";
+ case NE:
+ return "fsne %0";
+ case GT:
+ return "fsgt %0";
+ case GTU:
+ return "fshi %0";
+ case LT:
+ return "fslt %0";
+ case GE:
+ return "fsge %0";
+ case LE:
+ return "fsle %0";
+ case ORDERED:
+ return "fsor %0";
+ case UNORDERED:
+ return "fsun %0";
+ case UNEQ:
+ return "fsueq %0";
+ case UNGE:
+ return "fsuge %0";
+ case UNGT:
+ return "fsugt %0";
+ case UNLE:
+ return "fsule %0";
+ case UNLT:
+ return "fsult %0";
+ case LTGT:
+ return "fsogl %0";
+ default:
+ gcc_unreachable ();
}
}
\f
void
print_operand (FILE *file, rtx op, int letter)
{
+ if (op != NULL_RTX)
+ m68k_adjust_decorated_operand (op);
+
if (letter == '.')
{
if (MOTOROLA)
{
struct m68k_address address;
+ m68k_adjust_decorated_operand (addr);
+
if (!m68k_decompose_address (QImode, addr, true, &address))
gcc_unreachable ();
output_andsi3 (rtx *operands)
{
int logval;
+ CC_STATUS_INIT;
if (GET_CODE (operands[2]) == CONST_INT
&& (INTVAL (operands[2]) | 0xffff) == -1
&& (DATA_REG_P (operands[0])
if (GET_CODE (operands[0]) != REG)
operands[0] = adjust_address (operands[0], HImode, 2);
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
- /* Do not delete a following tstl %0 insn; that would be incorrect. */
- CC_STATUS_INIT;
if (operands[2] == const0_rtx)
return "clr%.w %0";
return "and%.w %2,%0";
operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
operands[1] = GEN_INT (logval % 8);
}
- /* This does not set condition codes in a standard way. */
- CC_STATUS_INIT;
return "bclr %1,%0";
}
+ /* Only a standard logical operation on the whole word sets the
+ condition codes in a way we can use. */
+ if (!side_effects_p (operands[0]))
+ flags_operand1 = operands[0];
+ flags_valid = FLAGS_VALID_YES;
return "and%.l %2,%0";
}
const char *
output_iorsi3 (rtx *operands)
{
- register int logval;
+ int logval;
+ CC_STATUS_INIT;
if (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) >> 16 == 0
&& (DATA_REG_P (operands[0])
{
if (GET_CODE (operands[0]) != REG)
operands[0] = adjust_address (operands[0], HImode, 2);
- /* Do not delete a following tstl %0 insn; that would be incorrect. */
- CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xffff)
return "mov%.w %2,%0";
return "or%.w %2,%0";
operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
operands[1] = GEN_INT (logval % 8);
}
- CC_STATUS_INIT;
return "bset %1,%0";
}
+ /* Only a standard logical operation on the whole word sets the
+ condition codes in a way we can use. */
+ if (!side_effects_p (operands[0]))
+ flags_operand1 = operands[0];
+ flags_valid = FLAGS_VALID_YES;
return "or%.l %2,%0";
}
const char *
output_xorsi3 (rtx *operands)
{
- register int logval;
+ int logval;
+ CC_STATUS_INIT;
if (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) >> 16 == 0
&& (offsettable_memref_p (operands[0]) || DATA_REG_P (operands[0]))
{
if (! DATA_REG_P (operands[0]))
operands[0] = adjust_address (operands[0], HImode, 2);
- /* Do not delete a following tstl %0 insn; that would be incorrect. */
- CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xffff)
return "not%.w %0";
return "eor%.w %2,%0";
operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
operands[1] = GEN_INT (logval % 8);
}
- CC_STATUS_INIT;
return "bchg %1,%0";
}
+ /* Only a standard logical operation on the whole word sets the
+ condition codes in a way we can use. */
+ if (!side_effects_p (operands[0]))
+ flags_operand1 = operands[0];
+ flags_valid = FLAGS_VALID_YES;
return "eor%.l %2,%0";
}
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk));
rtx this_slot, offset, addr, mem, tmp;
rtx_insn *insn;
/* Run just enough of rest_of_compilation. */
insn = get_insns ();
split_all_insns_noflow ();
+ assemble_start_function (thunk, fnname);
final_start_function (insn, file, 1);
final (insn, file, 1);
final_end_function ();
+ assemble_end_function (thunk, fnname);
/* Clean up the vars set above. */
reload_completed = 0;
return 1;
}
-/* Value is true if hard register REGNO can hold a value of machine-mode
- MODE. On the 68000, we let the cpu registers can hold any mode, but
- restrict the 68881 registers to floating-point modes. */
+/* Implement TARGET_HARD_REGNO_NREGS.
-bool
-m68k_regno_mode_ok (int regno, machine_mode mode)
+ On the m68k, ordinary registers hold 32 bits worth;
+ for the 68881 registers, a single register is always enough for
+ anything that can be stored in them at all. */
+
+static unsigned int
+m68k_hard_regno_nregs (unsigned int regno, machine_mode mode)
+{
+ if (regno >= 16)
+ return GET_MODE_NUNITS (mode);
+ return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK. On the 68000, we let the cpu
+ registers can hold any mode, but restrict the 68881 registers to
+ floating-point modes. */
+
+static bool
+m68k_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
if (DATA_REGNO_P (regno))
{
return false;
}
+/* Implement TARGET_MODES_TIEABLE_P. */
+
+static bool
+m68k_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+ return (!TARGET_HARD_FLOAT
+ || ((GET_MODE_CLASS (mode1) == MODE_FLOAT
+ || GET_MODE_CLASS (mode1) == MODE_COMPLEX_FLOAT)
+ == (GET_MODE_CLASS (mode2) == MODE_FLOAT
+ || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT)));
+}
+
/* Implement SECONDARY_RELOAD_CLASS. */
enum reg_class
m68k_libcall_value (machine_mode mode)
{
switch (mode) {
- case SFmode:
- case DFmode:
- case XFmode:
+ case E_SFmode:
+ case E_DFmode:
+ case E_XFmode:
if (TARGET_68881)
return gen_rtx_REG (mode, FP0_REG);
break;
mode = TYPE_MODE (valtype);
switch (mode) {
- case SFmode:
- case DFmode:
- case XFmode:
+ case E_SFmode:
+ case E_DFmode:
+ case E_XFmode:
if (TARGET_68881)
return gen_rtx_REG (mode, FP0_REG);
break;
{
switch (GET_MODE (op))
{
- case SFmode:
+ case E_SFmode:
return OP_TYPE_IMM_W;
- case VOIDmode:
- case DFmode:
+ case E_VOIDmode:
+ case E_DFmode:
return OP_TYPE_IMM_L;
default:
{
switch (GET_MODE (op))
{
- case QImode:
+ case E_QImode:
return OP_TYPE_IMM_Q;
- case HImode:
+ case E_HImode:
return OP_TYPE_IMM_W;
- case SImode:
+ case E_SImode:
return OP_TYPE_IMM_L;
default:
standard Unix calling sequences. If the option is not selected,
the caller must always pop the args. */
-static int
-m68k_return_pops_args (tree fundecl, tree funtype, int size)
+static poly_int64
+m68k_return_pops_args (tree fundecl, tree funtype, poly_int64 size)
{
return ((TARGET_RTD
&& (!fundecl
|| TREE_CODE (fundecl) != IDENTIFIER_NODE)
&& (!stdarg_p (funtype)))
- ? size : 0);
+ ? (HOST_WIDE_INT) size : 0);
}
/* Make sure everything's fine if we *don't* have a given processor.
HARD_REG_SET x;
if (!TARGET_HARD_FLOAT)
{
- COPY_HARD_REG_SET (x, reg_class_contents[(int)FP_REGS]);
+ x = reg_class_contents[FP_REGS];
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (x, i))
fixed_regs[i] = call_used_regs[i] = 1;
return FLT_EVAL_METHOD_UNPREDICTABLE;
}
+/* Implement PUSH_ROUNDING. On the 680x0, sp@- in a byte insn really pushes
+ a word. On the ColdFire, sp@- in a byte insn pushes just a byte. */
+
+poly_int64
+m68k_push_rounding (poly_int64 bytes)
+{
+ if (TARGET_COLDFIRE)
+ return bytes;
+ return (bytes + 1) & ~1;
+}
+
+/* Implement TARGET_PROMOTE_FUNCTION_MODE. */
+
+static machine_mode
+m68k_promote_function_mode (const_tree type, machine_mode mode,
+ int *punsignedp ATTRIBUTE_UNUSED,
+ const_tree fntype ATTRIBUTE_UNUSED,
+ int for_return)
+{
+ /* Promote libcall arguments narrower than int to match the normal C
+ ABI (for which promotions are handled via
+ TARGET_PROMOTE_PROTOTYPES). */
+ if (type == NULL_TREE && !for_return && (mode == QImode || mode == HImode))
+ return SImode;
+ return mode;
+}
+
#include "gt-m68k.h"