diff options
Diffstat (limited to 'gcc/config')
58 files changed, 5747 insertions, 3383 deletions
diff --git a/gcc/config/alpha/alpha-protos.h b/gcc/config/alpha/alpha-protos.h index 31551689a27..42b34d3a39c 100644 --- a/gcc/config/alpha/alpha-protos.h +++ b/gcc/config/alpha/alpha-protos.h @@ -88,15 +88,14 @@ extern bool alpha_emit_setcc (rtx[], enum machine_mode); extern int alpha_split_conditional_move (enum rtx_code, rtx, rtx, rtx, rtx); extern void alpha_emit_xfloating_arith (enum rtx_code, rtx[]); extern void alpha_emit_xfloating_cvt (enum rtx_code, rtx[]); -extern void alpha_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx); -extern void alpha_split_compare_and_swap (rtx, rtx, rtx, rtx, rtx); -extern void alpha_expand_compare_and_swap_12 (rtx, rtx, rtx, rtx); -extern void alpha_split_compare_and_swap_12 (enum machine_mode, rtx, rtx, - rtx, rtx, rtx, rtx, rtx); -extern void alpha_split_lock_test_and_set (rtx, rtx, rtx, rtx); -extern void alpha_expand_lock_test_and_set_12 (rtx, rtx, rtx); -extern void alpha_split_lock_test_and_set_12 (enum machine_mode, rtx, rtx, - rtx, rtx, rtx); +extern void alpha_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx, + enum memmodel); +extern void alpha_split_compare_and_swap (rtx op[]); +extern void alpha_expand_compare_and_swap_12 (rtx op[]); +extern void alpha_split_compare_and_swap_12 (rtx op[]); +extern void alpha_split_atomic_exchange (rtx op[]); +extern void alpha_expand_atomic_exchange_12 (rtx op[]); +extern void alpha_split_atomic_exchange_12 (rtx op[]); #endif extern rtx alpha_use_linkage (rtx, bool, bool); diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index 9a43f80243f..e970227dc4c 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -4196,6 +4196,47 @@ emit_store_conditional (enum machine_mode mode, rtx res, rtx mem, rtx val) emit_insn (fn (res, mem, val)); } +/* Subroutines of the atomic operation splitters. Emit barriers + as needed for the memory MODEL. */ + +static void +alpha_pre_atomic_barrier (enum memmodel model) +{ + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_ACQUIRE: + break; + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_memory_barrier ()); + break; + default: + gcc_unreachable (); + } +} + +static void +alpha_post_atomic_barrier (enum memmodel model) +{ + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_RELEASE: + break; + case MEMMODEL_ACQUIRE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_memory_barrier ()); + break; + default: + gcc_unreachable (); + } +} + /* A subroutine of the atomic operation splitters. Emit an insxl instruction in MODE. */ @@ -4236,13 +4277,13 @@ emit_insxl (enum machine_mode mode, rtx op1, rtx op2) a scratch register. */ void -alpha_split_atomic_op (enum rtx_code code, rtx mem, rtx val, - rtx before, rtx after, rtx scratch) +alpha_split_atomic_op (enum rtx_code code, rtx mem, rtx val, rtx before, + rtx after, rtx scratch, enum memmodel model) { enum machine_mode mode = GET_MODE (mem); rtx label, x, cond = gen_rtx_REG (DImode, REGNO (scratch)); - emit_insn (gen_memory_barrier ()); + alpha_pre_atomic_barrier (model); label = gen_label_rtx (); emit_label (label); @@ -4270,29 +4311,48 @@ alpha_split_atomic_op (enum rtx_code code, rtx mem, rtx val, x = gen_rtx_EQ (DImode, cond, const0_rtx); emit_unlikely_jump (x, label); - emit_insn (gen_memory_barrier ()); + alpha_post_atomic_barrier (model); } /* Expand a compare and swap operation. */ void -alpha_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval, - rtx scratch) +alpha_split_compare_and_swap (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); - rtx label1, label2, x, cond = gen_lowpart (DImode, scratch); + rtx cond, retval, mem, oldval, newval; + bool is_weak; + enum memmodel mod_s, mod_f; + enum machine_mode mode; + rtx label1, label2, x; + + cond = operands[0]; + retval = operands[1]; + mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + is_weak = (operands[5] != const0_rtx); + mod_s = (enum memmodel) INTVAL (operands[6]); + mod_f = (enum memmodel) INTVAL (operands[7]); + mode = GET_MODE (mem); - emit_insn (gen_memory_barrier ()); + alpha_pre_atomic_barrier (mod_s); - label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); + label1 = NULL_RTX; + if (!is_weak) + { + label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); + emit_label (XEXP (label1, 0)); + } label2 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); - emit_label (XEXP (label1, 0)); emit_load_locked (mode, retval, mem); x = gen_lowpart (DImode, retval); if (oldval == const0_rtx) - x = gen_rtx_NE (DImode, x, const0_rtx); + { + emit_move_insn (cond, const0_rtx); + x = gen_rtx_NE (DImode, x, const0_rtx); + } else { x = gen_rtx_EQ (DImode, x, oldval); @@ -4301,54 +4361,101 @@ alpha_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval, } emit_unlikely_jump (x, label2); - emit_move_insn (scratch, newval); - emit_store_conditional (mode, cond, mem, scratch); + emit_move_insn (cond, newval); + emit_store_conditional (mode, cond, mem, gen_lowpart (mode, cond)); - x = gen_rtx_EQ (DImode, cond, const0_rtx); - emit_unlikely_jump (x, label1); + if (!is_weak) + { + x = gen_rtx_EQ (DImode, cond, const0_rtx); + emit_unlikely_jump (x, label1); + } + + if (mod_f != MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); - emit_insn (gen_memory_barrier ()); - emit_label (XEXP (label2, 0)); + alpha_post_atomic_barrier (mod_s); + + if (mod_f == MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); } void -alpha_expand_compare_and_swap_12 (rtx dst, rtx mem, rtx oldval, rtx newval) +alpha_expand_compare_and_swap_12 (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); + rtx cond, dst, mem, oldval, newval, is_weak, mod_s, mod_f; + enum machine_mode mode; rtx addr, align, wdst; - rtx (*fn5) (rtx, rtx, rtx, rtx, rtx); + rtx (*gen) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx); + + cond = operands[0]; + dst = operands[1]; + mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + is_weak = operands[5]; + mod_s = operands[6]; + mod_f = operands[7]; + mode = GET_MODE (mem); + + /* We forced the address into a register via mem_noofs_operand. */ + addr = XEXP (mem, 0); + gcc_assert (register_operand (addr, DImode)); - addr = force_reg (DImode, XEXP (mem, 0)); align = expand_simple_binop (Pmode, AND, addr, GEN_INT (-8), NULL_RTX, 1, OPTAB_DIRECT); oldval = convert_modes (DImode, mode, oldval, 1); - newval = emit_insxl (mode, newval, addr); + + if (newval != const0_rtx) + newval = emit_insxl (mode, newval, addr); wdst = gen_reg_rtx (DImode); if (mode == QImode) - fn5 = gen_sync_compare_and_swapqi_1; + gen = gen_atomic_compare_and_swapqi_1; else - fn5 = gen_sync_compare_and_swaphi_1; - emit_insn (fn5 (wdst, addr, oldval, newval, align)); + gen = gen_atomic_compare_and_swaphi_1; + emit_insn (gen (cond, wdst, mem, oldval, newval, align, + is_weak, mod_s, mod_f)); emit_move_insn (dst, gen_lowpart (mode, wdst)); } void -alpha_split_compare_and_swap_12 (enum machine_mode mode, rtx dest, rtx addr, - rtx oldval, rtx newval, rtx align, - rtx scratch, rtx cond) +alpha_split_compare_and_swap_12 (rtx operands[]) { - rtx label1, label2, mem, width, mask, x; + rtx cond, dest, orig_mem, oldval, newval, align, scratch; + enum machine_mode mode; + bool is_weak; + enum memmodel mod_s, mod_f; + rtx label1, label2, mem, addr, width, mask, x; + + cond = operands[0]; + dest = operands[1]; + orig_mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + align = operands[5]; + is_weak = (operands[6] != const0_rtx); + mod_s = (enum memmodel) INTVAL (operands[7]); + mod_f = (enum memmodel) INTVAL (operands[8]); + scratch = operands[9]; + mode = GET_MODE (orig_mem); + addr = XEXP (orig_mem, 0); mem = gen_rtx_MEM (DImode, align); - MEM_VOLATILE_P (mem) = 1; + MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem); + if (MEM_ALIAS_SET (orig_mem) == ALIAS_SET_MEMORY_BARRIER) + set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); + + alpha_pre_atomic_barrier (mod_s); - emit_insn (gen_memory_barrier ()); - label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); + label1 = NULL_RTX; + if (!is_weak) + { + label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); + emit_label (XEXP (label1, 0)); + } label2 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); - emit_label (XEXP (label1, 0)); emit_load_locked (DImode, scratch, mem); @@ -4357,7 +4464,10 @@ alpha_split_compare_and_swap_12 (enum machine_mode mode, rtx dest, rtx addr, emit_insn (gen_extxl (dest, scratch, width, addr)); if (oldval == const0_rtx) - x = gen_rtx_NE (DImode, dest, const0_rtx); + { + emit_move_insn (cond, const0_rtx); + x = gen_rtx_NE (DImode, dest, const0_rtx); + } else { x = gen_rtx_EQ (DImode, dest, oldval); @@ -4366,25 +4476,47 @@ alpha_split_compare_and_swap_12 (enum machine_mode mode, rtx dest, rtx addr, } emit_unlikely_jump (x, label2); - emit_insn (gen_mskxl (scratch, scratch, mask, addr)); - emit_insn (gen_iordi3 (scratch, scratch, newval)); + emit_insn (gen_mskxl (cond, scratch, mask, addr)); - emit_store_conditional (DImode, scratch, mem, scratch); + if (newval != const0_rtx) + emit_insn (gen_iordi3 (cond, cond, newval)); - x = gen_rtx_EQ (DImode, scratch, const0_rtx); - emit_unlikely_jump (x, label1); + emit_store_conditional (DImode, cond, mem, cond); - emit_insn (gen_memory_barrier ()); - emit_label (XEXP (label2, 0)); + if (!is_weak) + { + x = gen_rtx_EQ (DImode, cond, const0_rtx); + emit_unlikely_jump (x, label1); + } + + if (mod_f != MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); + + alpha_post_atomic_barrier (mod_s); + + if (mod_f == MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); } /* Expand an atomic exchange operation. */ void -alpha_split_lock_test_and_set (rtx retval, rtx mem, rtx val, rtx scratch) +alpha_split_atomic_exchange (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); - rtx label, x, cond = gen_lowpart (DImode, scratch); + rtx retval, mem, val, scratch; + enum memmodel model; + enum machine_mode mode; + rtx label, x, cond; + + retval = operands[0]; + mem = operands[1]; + val = operands[2]; + model = (enum memmodel) INTVAL (operands[3]); + scratch = operands[4]; + mode = GET_MODE (mem); + cond = gen_lowpart (DImode, scratch); + + alpha_pre_atomic_barrier (model); label = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); emit_label (XEXP (label, 0)); @@ -4396,44 +4528,67 @@ alpha_split_lock_test_and_set (rtx retval, rtx mem, rtx val, rtx scratch) x = gen_rtx_EQ (DImode, cond, const0_rtx); emit_unlikely_jump (x, label); - emit_insn (gen_memory_barrier ()); + alpha_post_atomic_barrier (model); } void -alpha_expand_lock_test_and_set_12 (rtx dst, rtx mem, rtx val) +alpha_expand_atomic_exchange_12 (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); + rtx dst, mem, val, model; + enum machine_mode mode; rtx addr, align, wdst; - rtx (*fn4) (rtx, rtx, rtx, rtx); + rtx (*gen) (rtx, rtx, rtx, rtx, rtx); + + dst = operands[0]; + mem = operands[1]; + val = operands[2]; + model = operands[3]; + mode = GET_MODE (mem); - /* Force the address into a register. */ - addr = force_reg (DImode, XEXP (mem, 0)); + /* We forced the address into a register via mem_noofs_operand. */ + addr = XEXP (mem, 0); + gcc_assert (register_operand (addr, DImode)); - /* Align it to a multiple of 8. */ align = expand_simple_binop (Pmode, AND, addr, GEN_INT (-8), NULL_RTX, 1, OPTAB_DIRECT); /* Insert val into the correct byte location within the word. */ - val = emit_insxl (mode, val, addr); + if (val != const0_rtx) + val = emit_insxl (mode, val, addr); wdst = gen_reg_rtx (DImode); if (mode == QImode) - fn4 = gen_sync_lock_test_and_setqi_1; + gen = gen_atomic_exchangeqi_1; else - fn4 = gen_sync_lock_test_and_sethi_1; - emit_insn (fn4 (wdst, addr, val, align)); + gen = gen_atomic_exchangehi_1; + emit_insn (gen (wdst, mem, val, align, model)); emit_move_insn (dst, gen_lowpart (mode, wdst)); } void -alpha_split_lock_test_and_set_12 (enum machine_mode mode, rtx dest, rtx addr, - rtx val, rtx align, rtx scratch) +alpha_split_atomic_exchange_12 (rtx operands[]) { + rtx dest, orig_mem, addr, val, align, scratch; rtx label, mem, width, mask, x; + enum machine_mode mode; + enum memmodel model; + + dest = operands[0]; + orig_mem = operands[1]; + val = operands[2]; + align = operands[3]; + model = (enum memmodel) INTVAL (operands[4]); + scratch = operands[5]; + mode = GET_MODE (orig_mem); + addr = XEXP (orig_mem, 0); mem = gen_rtx_MEM (DImode, align); - MEM_VOLATILE_P (mem) = 1; + MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem); + if (MEM_ALIAS_SET (orig_mem) == ALIAS_SET_MEMORY_BARRIER) + set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); + + alpha_pre_atomic_barrier (model); label = gen_rtx_LABEL_REF (DImode, gen_label_rtx ()); emit_label (XEXP (label, 0)); @@ -4444,14 +4599,15 @@ alpha_split_lock_test_and_set_12 (enum machine_mode mode, rtx dest, rtx addr, mask = GEN_INT (mode == QImode ? 0xff : 0xffff); emit_insn (gen_extxl (dest, scratch, width, addr)); emit_insn (gen_mskxl (scratch, scratch, mask, addr)); - emit_insn (gen_iordi3 (scratch, scratch, val)); + if (val != const0_rtx) + emit_insn (gen_iordi3 (scratch, scratch, val)); emit_store_conditional (DImode, scratch, mem, scratch); x = gen_rtx_EQ (DImode, scratch, const0_rtx); emit_unlikely_jump (x, label); - emit_insn (gen_memory_barrier ()); + alpha_post_atomic_barrier (model); } /* Adjust the cost of a scheduling dependency. Return the new cost of diff --git a/gcc/config/alpha/alpha.md b/gcc/config/alpha/alpha.md index 64229245065..e715cc86422 100644 --- a/gcc/config/alpha/alpha.md +++ b/gcc/config/alpha/alpha.md @@ -81,6 +81,7 @@ UNSPECV_SETJMPR_ER ; builtin_setjmp_receiver fragment UNSPECV_LL ; load-locked UNSPECV_SC ; store-conditional + UNSPECV_CMPXCHG ]) ;; On non-BWX targets, CQImode must be handled the similarly to HImode diff --git a/gcc/config/alpha/constraints.md b/gcc/config/alpha/constraints.md index 35514502c20..62c7f9ae0c5 100644 --- a/gcc/config/alpha/constraints.md +++ b/gcc/config/alpha/constraints.md @@ -19,7 +19,7 @@ ;;; Unused letters: ;;; ABCDEF V YZ -;;; de ghijklmnopq stu wxyz +;;; de ghijkl pq tu wxyz ;; Integer register constraints. @@ -38,6 +38,10 @@ (define_register_constraint "v" "R0_REG" "General register 0, function value return address") +(define_memory_constraint "w" + "A memory whose address is only a register" + (match_operand 0 "mem_noofs_operand")) + ;; Integer constant constraints. (define_constraint "I" "An unsigned 8 bit constant" diff --git a/gcc/config/alpha/predicates.md b/gcc/config/alpha/predicates.md index c1e3115aead..598742f81e7 100644 --- a/gcc/config/alpha/predicates.md +++ b/gcc/config/alpha/predicates.md @@ -623,3 +623,8 @@ (ior (match_operand 0 "register_operand") (and (match_test "TARGET_BWX") (match_operand 0 "memory_operand")))) + +;; Accept a memory whose address is only a register. +(define_predicate "mem_noofs_operand" + (and (match_code "mem") + (match_code "reg" "0"))) diff --git a/gcc/config/alpha/sync.md b/gcc/config/alpha/sync.md index bb7210239fd..90f6c5cd8d4 100644 --- a/gcc/config/alpha/sync.md +++ b/gcc/config/alpha/sync.md @@ -1,5 +1,5 @@ ;; GCC machine description for Alpha synchronization instructions. -;; Copyright (C) 2005, 2007, 2008, 2009 Free Software Foundation, Inc. +;; Copyright (C) 2005, 2007, 2008, 2009, 2011 Free Software Foundation, Inc. ;; ;; This file is part of GCC. ;; @@ -62,95 +62,160 @@ [(set_attr "type" "st_c")]) ;; The Alpha Architecture Handbook says that it is UNPREDICTABLE whether -;; the lock is cleared by a TAKEN branch. This means that we can not -;; expand a ll/sc sequence until after the final basic-block reordering pass. +;; the lock is cleared by a normal load or store. This means we cannot +;; expand a ll/sc sequence before reload, lest a register spill is +;; inserted inside the sequence. It is also UNPREDICTABLE whether the +;; lock is cleared by a TAKEN branch. This means that we can not expand +;; a ll/sc sequence containing a branch (i.e. compare-and-swap) until after +;; the final basic-block reordering pass. -(define_insn_and_split "sync_<fetchop_name><mode>" - [(set (match_operand:I48MODE 0 "memory_operand" "+m") - (unspec:I48MODE - [(FETCHOP:I48MODE (match_dup 0) - (match_operand:I48MODE 1 "<fetchop_pred>" "<fetchop_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:I48MODE 2 "=&r"))] +(define_expand "atomic_compare_and_swap<mode>" + [(parallel + [(set (match_operand:DI 0 "register_operand" "") ;; bool out + (unspec_volatile:DI [(const_int 0)] UNSPECV_CMPXCHG)) + (set (match_operand:I48MODE 1 "register_operand" "") ;; val out + (unspec_volatile:I48MODE [(const_int 0)] UNSPECV_CMPXCHG)) + (set (match_operand:I48MODE 2 "memory_operand" "") ;; memory + (unspec_volatile:I48MODE + [(match_dup 2) + (match_operand:I48MODE 3 "reg_or_8bit_operand" "") ;; expected + (match_operand:I48MODE 4 "add_operand" "") ;; desired + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; succ model + (match_operand:SI 7 "const_int_operand" "")] ;; fail model + UNSPECV_CMPXCHG))])] + "" +{ + if (<MODE>mode == SImode) + { + operands[3] = convert_modes (DImode, SImode, operands[3], 0); + operands[4] = convert_modes (DImode, SImode, operands[4], 0); + } +}) + +(define_insn_and_split "*atomic_compare_and_swap<mode>" + [(set (match_operand:DI 0 "register_operand" "=&r") ;; bool out + (unspec_volatile:DI [(const_int 0)] UNSPECV_CMPXCHG)) + (set (match_operand:I48MODE 1 "register_operand" "=&r") ;; val out + (unspec_volatile:I48MODE [(const_int 0)] UNSPECV_CMPXCHG)) + (set (match_operand:I48MODE 2 "memory_operand" "+m") ;; memory + (unspec_volatile:I48MODE + [(match_dup 2) + (match_operand:DI 3 "reg_or_8bit_operand" "rI") ;; expected + (match_operand:DI 4 "add_operand" "rKL") ;; desired + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; succ model + (match_operand:SI 7 "const_int_operand" "")] ;; fail model + UNSPECV_CMPXCHG))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (<CODE>, operands[0], operands[1], - NULL, NULL, operands[2]); + alpha_split_compare_and_swap (operands); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_nand<mode>" - [(set (match_operand:I48MODE 0 "memory_operand" "+m") - (unspec:I48MODE - [(not:I48MODE - (and:I48MODE (match_dup 0) - (match_operand:I48MODE 1 "register_operand" "r")))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:I48MODE 2 "=&r"))] +(define_expand "atomic_compare_and_swap<mode>" + [(match_operand:DI 0 "register_operand" "") ;; bool out + (match_operand:I12MODE 1 "register_operand" "") ;; val out + (match_operand:I12MODE 2 "mem_noofs_operand" "") ;; memory + (match_operand:I12MODE 3 "register_operand" "") ;; expected + (match_operand:I12MODE 4 "add_operand" "") ;; desired + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; succ model + (match_operand:SI 7 "const_int_operand" "")] ;; fail model + "" +{ + alpha_expand_compare_and_swap_12 (operands); + DONE; +}) + +(define_insn_and_split "atomic_compare_and_swap<mode>_1" + [(set (match_operand:DI 0 "register_operand" "=&r") ;; bool out + (unspec_volatile:DI [(const_int 0)] UNSPECV_CMPXCHG)) + (set (match_operand:DI 1 "register_operand" "=&r") ;; val out + (zero_extend:DI + (unspec_volatile:I12MODE [(const_int 0)] UNSPECV_CMPXCHG))) + (set (match_operand:I12MODE 2 "mem_noofs_operand" "+w") ;; memory + (unspec_volatile:I12MODE + [(match_dup 2) + (match_operand:DI 3 "reg_or_8bit_operand" "rI") ;; expected + (match_operand:DI 4 "reg_or_0_operand" "rJ") ;; desired + (match_operand:DI 5 "register_operand" "r") ;; align + (match_operand:SI 6 "const_int_operand" "") ;; is_weak + (match_operand:SI 7 "const_int_operand" "") ;; succ model + (match_operand:SI 8 "const_int_operand" "")] ;; fail model + UNSPECV_CMPXCHG)) + (clobber (match_scratch:DI 9 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (NOT, operands[0], operands[1], - NULL, NULL, operands[2]); + alpha_split_compare_and_swap_12 (operands); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_old_<fetchop_name><mode>" - [(set (match_operand:I48MODE 0 "register_operand" "=&r") - (match_operand:I48MODE 1 "memory_operand" "+m")) +(define_insn_and_split "atomic_exchange<mode>" + [(set (match_operand:I48MODE 0 "register_operand" "=&r") ;; output + (match_operand:I48MODE 1 "memory_operand" "+m")) ;; memory (set (match_dup 1) (unspec:I48MODE - [(FETCHOP:I48MODE (match_dup 1) - (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:I48MODE 3 "=&r"))] + [(match_operand:I48MODE 2 "add_operand" "rKL") ;; input + (match_operand:SI 3 "const_int_operand" "")] ;; model + UNSPEC_XCHG)) + (clobber (match_scratch:I48MODE 4 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (<CODE>, operands[1], operands[2], - operands[0], NULL, operands[3]); + alpha_split_atomic_exchange (operands); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_old_nand<mode>" - [(set (match_operand:I48MODE 0 "register_operand" "=&r") - (match_operand:I48MODE 1 "memory_operand" "+m")) +(define_expand "atomic_exchange<mode>" + [(match_operand:I12MODE 0 "register_operand" "") ;; output + (match_operand:I12MODE 1 "mem_noofs_operand" "") ;; memory + (match_operand:I12MODE 2 "reg_or_0_operand" "") ;; input + (match_operand:SI 3 "const_int_operand" "")] ;; model + "" +{ + alpha_expand_atomic_exchange_12 (operands); + DONE; +}) + +(define_insn_and_split "atomic_exchange<mode>_1" + [(set (match_operand:DI 0 "register_operand" "=&r") ;; output + (zero_extend:DI + (match_operand:I12MODE 1 "mem_noofs_operand" "+w"))) ;; memory (set (match_dup 1) - (unspec:I48MODE - [(not:I48MODE - (and:I48MODE (match_dup 1) - (match_operand:I48MODE 2 "register_operand" "r")))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:I48MODE 3 "=&r"))] + (unspec:I12MODE + [(match_operand:DI 2 "reg_or_8bit_operand" "rI") ;; input + (match_operand:DI 3 "register_operand" "r") ;; align + (match_operand:SI 4 "const_int_operand" "")] ;; model + UNSPEC_XCHG)) + (clobber (match_scratch:DI 5 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (NOT, operands[1], operands[2], - operands[0], NULL, operands[3]); + alpha_split_atomic_exchange_12 (operands); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_new_<fetchop_name><mode>" - [(set (match_operand:I48MODE 0 "register_operand" "=&r") - (FETCHOP:I48MODE - (match_operand:I48MODE 1 "memory_operand" "+m") - (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>"))) - (set (match_dup 1) +(define_insn_and_split "atomic_<fetchop_name><mode>" + [(set (match_operand:I48MODE 0 "memory_operand" "+m") (unspec:I48MODE - [(FETCHOP:I48MODE (match_dup 1) (match_dup 2))] + [(FETCHOP:I48MODE (match_dup 0) + (match_operand:I48MODE 1 "<fetchop_pred>" "<fetchop_constr>")) + (match_operand:SI 2 "const_int_operand" "")] UNSPEC_ATOMIC)) (clobber (match_scratch:I48MODE 3 "=&r"))] "" @@ -158,20 +223,20 @@ "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (<CODE>, operands[1], operands[2], - NULL, operands[0], operands[3]); + alpha_split_atomic_op (<CODE>, operands[0], operands[1], + NULL, NULL, operands[3], + (enum memmodel) INTVAL (operands[2])); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_new_nand<mode>" - [(set (match_operand:I48MODE 0 "register_operand" "=&r") - (not:I48MODE - (and:I48MODE (match_operand:I48MODE 1 "memory_operand" "+m") - (match_operand:I48MODE 2 "register_operand" "r")))) - (set (match_dup 1) +(define_insn_and_split "atomic_nand<mode>" + [(set (match_operand:I48MODE 0 "memory_operand" "+m") (unspec:I48MODE - [(not:I48MODE (and:I48MODE (match_dup 1) (match_dup 2)))] + [(not:I48MODE + (and:I48MODE (match_dup 0) + (match_operand:I48MODE 1 "register_operand" "r"))) + (match_operand:SI 2 "const_int_operand" "")] UNSPEC_ATOMIC)) (clobber (match_scratch:I48MODE 3 "=&r"))] "" @@ -179,130 +244,100 @@ "epilogue_completed" [(const_int 0)] { - alpha_split_atomic_op (NOT, operands[1], operands[2], - NULL, operands[0], operands[3]); + alpha_split_atomic_op (NOT, operands[0], operands[1], + NULL, NULL, operands[3], + (enum memmodel) INTVAL (operands[2])); DONE; } [(set_attr "type" "multi")]) -(define_expand "sync_compare_and_swap<mode>" - [(match_operand:I12MODE 0 "register_operand" "") - (match_operand:I12MODE 1 "memory_operand" "") - (match_operand:I12MODE 2 "register_operand" "") - (match_operand:I12MODE 3 "add_operand" "")] - "" -{ - alpha_expand_compare_and_swap_12 (operands[0], operands[1], - operands[2], operands[3]); - DONE; -}) - -(define_insn_and_split "sync_compare_and_swap<mode>_1" - [(set (match_operand:DI 0 "register_operand" "=&r,&r") - (zero_extend:DI - (mem:I12MODE (match_operand:DI 1 "register_operand" "r,r")))) - (set (mem:I12MODE (match_dup 1)) - (unspec:I12MODE - [(match_operand:DI 2 "reg_or_8bit_operand" "J,rI") - (match_operand:DI 3 "register_operand" "r,r") - (match_operand:DI 4 "register_operand" "r,r")] - UNSPEC_CMPXCHG)) - (clobber (match_scratch:DI 5 "=&r,&r")) - (clobber (match_scratch:DI 6 "=X,&r"))] +(define_insn_and_split "atomic_fetch_<fetchop_name><mode>" + [(set (match_operand:I48MODE 0 "register_operand" "=&r") + (match_operand:I48MODE 1 "memory_operand" "+m")) + (set (match_dup 1) + (unspec:I48MODE + [(FETCHOP:I48MODE (match_dup 1) + (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>")) + (match_operand:SI 3 "const_int_operand" "")] + UNSPEC_ATOMIC)) + (clobber (match_scratch:I48MODE 4 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_compare_and_swap_12 (<MODE>mode, operands[0], operands[1], - operands[2], operands[3], operands[4], - operands[5], operands[6]); + alpha_split_atomic_op (<CODE>, operands[1], operands[2], + operands[0], NULL, operands[4], + (enum memmodel) INTVAL (operands[3])); DONE; } [(set_attr "type" "multi")]) -(define_expand "sync_compare_and_swap<mode>" - [(parallel - [(set (match_operand:I48MODE 0 "register_operand" "") - (match_operand:I48MODE 1 "memory_operand" "")) - (set (match_dup 1) - (unspec:I48MODE - [(match_operand:I48MODE 2 "reg_or_8bit_operand" "") - (match_operand:I48MODE 3 "add_operand" "rKL")] - UNSPEC_CMPXCHG)) - (clobber (match_scratch:I48MODE 4 "=&r"))])] - "" -{ - if (<MODE>mode == SImode) - operands[2] = convert_modes (DImode, SImode, operands[2], 0); -}) - -(define_insn_and_split "*sync_compare_and_swap<mode>" +(define_insn_and_split "atomic_fetch_nand<mode>" [(set (match_operand:I48MODE 0 "register_operand" "=&r") (match_operand:I48MODE 1 "memory_operand" "+m")) (set (match_dup 1) (unspec:I48MODE - [(match_operand:DI 2 "reg_or_8bit_operand" "rI") - (match_operand:I48MODE 3 "add_operand" "rKL")] - UNSPEC_CMPXCHG)) + [(not:I48MODE + (and:I48MODE (match_dup 1) + (match_operand:I48MODE 2 "register_operand" "r"))) + (match_operand:SI 3 "const_int_operand" "")] + UNSPEC_ATOMIC)) (clobber (match_scratch:I48MODE 4 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_compare_and_swap (operands[0], operands[1], operands[2], - operands[3], operands[4]); + alpha_split_atomic_op (NOT, operands[1], operands[2], + operands[0], NULL, operands[4], + (enum memmodel) INTVAL (operands[3])); DONE; } [(set_attr "type" "multi")]) -(define_expand "sync_lock_test_and_set<mode>" - [(match_operand:I12MODE 0 "register_operand" "") - (match_operand:I12MODE 1 "memory_operand" "") - (match_operand:I12MODE 2 "register_operand" "")] - "" -{ - alpha_expand_lock_test_and_set_12 (operands[0], operands[1], operands[2]); - DONE; -}) - -(define_insn_and_split "sync_lock_test_and_set<mode>_1" - [(set (match_operand:DI 0 "register_operand" "=&r") - (zero_extend:DI - (mem:I12MODE (match_operand:DI 1 "register_operand" "r")))) - (set (mem:I12MODE (match_dup 1)) - (unspec:I12MODE - [(match_operand:DI 2 "reg_or_8bit_operand" "rI") - (match_operand:DI 3 "register_operand" "r")] - UNSPEC_XCHG)) - (clobber (match_scratch:DI 4 "=&r"))] +(define_insn_and_split "atomic_<fetchop_name>_fetch<mode>" + [(set (match_operand:I48MODE 0 "register_operand" "=&r") + (FETCHOP:I48MODE + (match_operand:I48MODE 1 "memory_operand" "+m") + (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>"))) + (set (match_dup 1) + (unspec:I48MODE + [(FETCHOP:I48MODE (match_dup 1) (match_dup 2)) + (match_operand:SI 3 "const_int_operand" "")] + UNSPEC_ATOMIC)) + (clobber (match_scratch:I48MODE 4 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_lock_test_and_set_12 (<MODE>mode, operands[0], operands[1], - operands[2], operands[3], operands[4]); + alpha_split_atomic_op (<CODE>, operands[1], operands[2], + NULL, operands[0], operands[4], + (enum memmodel) INTVAL (operands[3])); DONE; } [(set_attr "type" "multi")]) -(define_insn_and_split "sync_lock_test_and_set<mode>" +(define_insn_and_split "atomic_nand_fetch<mode>" [(set (match_operand:I48MODE 0 "register_operand" "=&r") - (match_operand:I48MODE 1 "memory_operand" "+m")) + (not:I48MODE + (and:I48MODE (match_operand:I48MODE 1 "memory_operand" "+m") + (match_operand:I48MODE 2 "register_operand" "r")))) (set (match_dup 1) (unspec:I48MODE - [(match_operand:I48MODE 2 "add_operand" "rKL")] - UNSPEC_XCHG)) - (clobber (match_scratch:I48MODE 3 "=&r"))] + [(not:I48MODE (and:I48MODE (match_dup 1) (match_dup 2))) + (match_operand:SI 3 "const_int_operand" "")] + UNSPEC_ATOMIC)) + (clobber (match_scratch:I48MODE 4 "=&r"))] "" "#" "epilogue_completed" [(const_int 0)] { - alpha_split_lock_test_and_set (operands[0], operands[1], - operands[2], operands[3]); + alpha_split_atomic_op (NOT, operands[1], operands[2], + NULL, operands[0], operands[4], + (enum memmodel) INTVAL (operands[3])); DONE; } [(set_attr "type" "multi")]) diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 6ef6f62d28d..ee26c51b0a0 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -1096,6 +1096,10 @@ arm_set_fixed_conv_libfunc (convert_optab optable, enum machine_mode to, static void arm_init_libfuncs (void) { + /* For Linux, we have access to kernel support for atomic operations. */ + if (arm_abi == ARM_ABI_AAPCS_LINUX) + init_sync_libfuncs (2 * UNITS_PER_WORD); + /* There are no special library functions unless we are using the ARM BPABI. */ if (!TARGET_BPABI) @@ -10247,6 +10251,9 @@ store_multiple_sequence (rtx *operands, int nops, int nops_total, rtx base_reg_rtx = NULL; int i, stm_case; + /* Write back of base register is currently only supported for Thumb 1. */ + int base_writeback = TARGET_THUMB1; + /* Can only handle up to MAX_LDM_STM_OPS insns at present, though could be easily extended if required. */ gcc_assert (nops >= 2 && nops <= MAX_LDM_STM_OPS); @@ -10304,7 +10311,9 @@ store_multiple_sequence (rtx *operands, int nops, int nops_total, /* If it isn't an integer register, then we can't do this. */ if (unsorted_regs[i] < 0 || (TARGET_THUMB1 && unsorted_regs[i] > LAST_LO_REGNUM) - || (TARGET_THUMB2 && unsorted_regs[i] == base_reg) + /* The effects are unpredictable if the base register is + both updated and stored. */ + || (base_writeback && unsorted_regs[i] == base_reg) || (TARGET_THUMB2 && unsorted_regs[i] == SP_REGNUM) || unsorted_regs[i] > 14) return 0; @@ -20723,39 +20732,34 @@ neon_emit_pair_result_insn (enum machine_mode mode, emit_move_insn (mem, tmp2); } -/* Set up operands for a register copy from src to dest, taking care not to - clobber registers in the process. - FIXME: This has rather high polynomial complexity (O(n^3)?) but shouldn't - be called with a large N, so that should be OK. */ +/* Set up OPERANDS for a register copy from SRC to DEST, taking care + not to early-clobber SRC registers in the process. + We assume that the operands described by SRC and DEST represent a + decomposed copy of OPERANDS[1] into OPERANDS[0]. COUNT is the + number of components into which the copy has been decomposed. */ void neon_disambiguate_copy (rtx *operands, rtx *dest, rtx *src, unsigned int count) { - unsigned int copied = 0, opctr = 0; - unsigned int done = (1 << count) - 1; - unsigned int i, j; + unsigned int i; - while (copied != done) + if (!reg_overlap_mentioned_p (operands[0], operands[1]) + || REGNO (operands[0]) < REGNO (operands[1])) { for (i = 0; i < count; i++) - { - int good = 1; - - for (j = 0; good && j < count; j++) - if (i != j && (copied & (1 << j)) == 0 - && reg_overlap_mentioned_p (src[j], dest[i])) - good = 0; - - if (good) - { - operands[opctr++] = dest[i]; - operands[opctr++] = src[i]; - copied |= 1 << i; - } - } + { + operands[2 * i] = dest[i]; + operands[2 * i + 1] = src[i]; + } + } + else + { + for (i = 0; i < count; i++) + { + operands[2 * i] = dest[count - i - 1]; + operands[2 * i + 1] = src[count - i - 1]; + } } - - gcc_assert (opctr == count * 2); } /* Expand an expression EXP that calls a built-in function, diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index 55de2d7fe4b..fd03b361b5e 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ +/* Not included in avr.c since this requires C front end. */ #include "config.h" #include "system.h" @@ -27,8 +28,23 @@ #include "cpplib.h" #include "tree.h" #include "c-family/c-common.h" +#include "langhooks.h" + + +/* Implement `REGISTER_TARGET_PRAGMAS'. */ + +void +avr_register_target_pragmas (void) +{ + c_register_addr_space ("__pgm", ADDR_SPACE_PGM); + c_register_addr_space ("__pgm1", ADDR_SPACE_PGM1); + c_register_addr_space ("__pgm2", ADDR_SPACE_PGM2); + c_register_addr_space ("__pgm3", ADDR_SPACE_PGM3); + c_register_addr_space ("__pgm4", ADDR_SPACE_PGM4); + c_register_addr_space ("__pgm5", ADDR_SPACE_PGM5); + c_register_addr_space ("__pgmx", ADDR_SPACE_PGMX); +} -/* Not included in avr.c since this requires C front end. */ /* Worker function for TARGET_CPU_CPP_BUILTINS. */ @@ -90,6 +106,26 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__"); } + cpp_define_formatted (pfile, "__AVR_SFR_OFFSET__=0x%x", + avr_current_arch->sfr_offset); + + /* Define builtin macros so that the user can easily query if or if not + non-generic address spaces (and which) are supported. + This is only supported for C. For C++, a language extension is needed + (as mentioned in ISO/IEC DTR 18037; Annex F.2) which is not + implemented in GCC up to now. */ + + if (!strcmp (lang_hooks.name, "GNU C")) + { + cpp_define (pfile, "__PGM=__pgm"); + cpp_define (pfile, "__PGM1=__pgm1"); + cpp_define (pfile, "__PGM2=__pgm2"); + cpp_define (pfile, "__PGM3=__pgm3"); + cpp_define (pfile, "__PGM4=__pgm4"); + cpp_define (pfile, "__PGM5=__pgm5"); + cpp_define (pfile, "__PGMX=__pgmx"); + } + /* Define builtin macros so that the user can easily query if or if not a specific builtin is available. */ @@ -100,6 +136,8 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) cpp_define (pfile, "__BUILTIN_AVR_WDR"); cpp_define (pfile, "__BUILTIN_AVR_SLEEP"); cpp_define (pfile, "__BUILTIN_AVR_SWAP"); + cpp_define (pfile, "__BUILTIN_AVR_MAP8"); + cpp_define (pfile, "__BUILTIN_AVR_MAP16"); cpp_define (pfile, "__BUILTIN_AVR_DELAY_CYCLES"); cpp_define (pfile, "__BUILTIN_AVR_FMUL"); diff --git a/gcc/config/avr/avr-devices.c b/gcc/config/avr/avr-devices.c index d0dda8536af..8d6e947f129 100644 --- a/gcc/config/avr/avr-devices.c +++ b/gcc/config/avr/avr-devices.c @@ -23,20 +23,30 @@ #include "coretypes.h" #include "tm.h" -/* List of all known AVR MCU architectures. */ +/* List of all known AVR MCU architectures. + Order as of enum avr_arch from avr.h. */ -const struct base_arch_s avr_arch_types[] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, NULL, "avr2" }, /* unknown device specified */ - { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=1", "avr1" }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=2", "avr2" }, - { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=25", "avr25" }, - { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=3", "avr3" }, - { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=31", "avr31" }, - { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=35", "avr35" }, - { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=4", "avr4" }, - { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=5", "avr5" }, - { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, "__AVR_ARCH__=51", "avr51" }, - { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, "__AVR_ARCH__=6", "avr6" } +const struct base_arch_s +avr_arch_types[] = +{ + /* unknown device specified */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, 1, NULL, "avr2" }, + /* + A M J LM E E E d S S O # F + S U M PO L L I a t F ff 6 l + M L P MV P P J - - t a R s 4 a + XW M M M a r e s + X P t t k h */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=1", "avr1" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=2", "avr2" }, + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=25", "avr25" }, + { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=3", "avr3" }, + { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 32, 2, "__AVR_ARCH__=31", "avr31" }, + { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=35", "avr35" }, + { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=4", "avr4" }, + { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, 1, "__AVR_ARCH__=5", "avr5" }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, 2, "__AVR_ARCH__=51", "avr51" }, + { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 32, 4, "__AVR_ARCH__=6", "avr6" } }; const struct mcu_type_s avr_mcu_types[] = { diff --git a/gcc/config/avr/avr-log.c b/gcc/config/avr/avr-log.c index cdeb6690fde..3c4bccfa282 100644 --- a/gcc/config/avr/avr-log.c +++ b/gcc/config/avr/avr-log.c @@ -49,6 +49,8 @@ C: enum rtx_code m: enum machine_mode R: enum reg_class + D: double_int (signed decimal) + X: double_int (unsigned hex) L: insn list H: location_t @@ -59,6 +61,8 @@ F: caller (via __FUNCTION__) P: Pass name and number ?: Print caller, current function and pass info + !: Ditto, but only print if in a pass with static pass number, + else return. == same as printf == @@ -80,9 +84,9 @@ static void avr_log_vadump (FILE*, const char*, va_list); /* As we have no variadic macros, avr_edump maps to a call to avr_log_set_caller_e which saves __FUNCTION__ to avr_log_caller and - returns a function pointer to avr_log_fdump_e. avr_fdump_e + returns a function pointer to avr_log_fdump_e. avr_log_fdump_e gets the printf-like arguments and calls avr_log_vadump, the - worker function. avr_fdump works the same way. */ + worker function. avr_fdump works the same way. */ /* Provide avr_log_fdump_e/f so that avr_log_set_caller_e/_f can return their address. */ @@ -133,6 +137,49 @@ avr_log_set_caller_f (const char *caller) return avr_log_fdump_f; } + +/* Copy-paste from double-int.c:double_int_split_digit (it's static there). + Splits last digit of *CST (taken as unsigned) in BASE and returns it. */ + +static unsigned +avr_double_int_pop_digit (double_int *cst, unsigned base) +{ + unsigned HOST_WIDE_INT resl, reml; + HOST_WIDE_INT resh, remh; + + div_and_round_double (FLOOR_DIV_EXPR, true, cst->low, cst->high, base, 0, + &resl, &resh, &reml, &remh); + cst->high = resh; + cst->low = resl; + + return reml; +} + + +/* Dump VAL as hex value to FILE. */ + +static void +avr_dump_double_int_hex (FILE *file, double_int val) +{ + unsigned digit[4]; + + digit[0] = avr_double_int_pop_digit (&val, 1 << 16); + digit[1] = avr_double_int_pop_digit (&val, 1 << 16); + digit[2] = avr_double_int_pop_digit (&val, 1 << 16); + digit[3] = avr_double_int_pop_digit (&val, 1 << 16); + + fprintf (file, "0x"); + + if (digit[3] | digit[2]) + fprintf (file, "%04x%04x", digit[3], digit[2]); + + if (digit[3] | digit[2] | digit[1] | digit[0]) + fprintf (file, "%04x%04x", digit[1], digit[0]); + else + fprintf (file, "0"); +} + + /* Worker function implementing the %-codes and forwarding to respective print/dump function. */ @@ -187,6 +234,14 @@ avr_log_vadump (FILE *file, const char *fmt, va_list ap) fprintf (file, "%d", va_arg (ap, int)); break; + case 'D': + dump_double_int (file, va_arg (ap, double_int), false); + break; + + case 'X': + avr_dump_double_int_hex (file, va_arg (ap, double_int)); + break; + case 'x': fprintf (file, "%x", va_arg (ap, int)); break; @@ -249,7 +304,7 @@ avr_log_vadump (FILE *file, const char *fmt, va_list ap) location_t loc = va_arg (ap, location_t); if (BUILTINS_LOCATION == loc) - fprintf (file, "<BUILTIN-LOCATION"); + fprintf (file, "<BUILTIN-LOCATION>"); else if (UNKNOWN_LOCATION == loc) fprintf (file, "<UNKNOWN-LOCATION>"); else @@ -304,27 +359,43 @@ avr_log_vadump (FILE *file, const char *fmt, va_list ap) void avr_log_set_avr_log (void) { - if (avr_log_details) + bool all = TARGET_ALL_DEBUG != 0; + + if (all || avr_log_details) { /* Adding , at beginning and end of string makes searching easier. */ char *str = (char*) alloca (3 + strlen (avr_log_details)); + bool info; str[0] = ','; strcat (stpcpy (str+1, avr_log_details), ","); - -#define SET_DUMP_DETAIL(S) \ - avr_log.S = (TARGET_ALL_DEBUG \ - || NULL != strstr (str, "," #S ",") \ - || NULL != strstr (str, ",all,")) - SET_DUMP_DETAIL (rtx_costs); + all |= NULL != strstr (str, ",all,"); + info = NULL != strstr (str, ",?,"); + + if (info) + fprintf (stderr, "\n-mlog="); + +#define SET_DUMP_DETAIL(S) \ + do { \ + avr_log.S = (all || NULL != strstr (str, "," #S ",")); \ + if (info) \ + fprintf (stderr, #S ","); \ + } while (0) + + SET_DUMP_DETAIL (address_cost); + SET_DUMP_DETAIL (builtin); + SET_DUMP_DETAIL (constraints); SET_DUMP_DETAIL (legitimate_address_p); SET_DUMP_DETAIL (legitimize_address); SET_DUMP_DETAIL (legitimize_reload_address); - SET_DUMP_DETAIL (constraints); - SET_DUMP_DETAIL (address_cost); + SET_DUMP_DETAIL (progmem); + SET_DUMP_DETAIL (rtx_costs); #undef SET_DUMP_DETAIL + + if (info) + fprintf (stderr, "?\n\n"); } } diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index d3518335a00..bafd794a302 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -32,7 +32,9 @@ extern int avr_initial_elimination_offset (int from, int to); extern int avr_simple_epilogue (void); extern int avr_hard_regno_rename_ok (unsigned int, unsigned int); extern rtx avr_return_addr_rtx (int count, rtx tem); +extern void avr_register_target_pragmas (void); extern bool avr_accumulate_outgoing_args (void); +extern void avr_init_expanders (void); #ifdef TREE_CODE extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool); @@ -47,15 +49,8 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, #endif /* TREE_CODE */ #ifdef RTX_CODE -extern void asm_output_external_libcall (FILE *file, rtx symref); extern const char *output_movqi (rtx insn, rtx operands[], int *l); extern const char *output_movhi (rtx insn, rtx operands[], int *l); -extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l); -extern const char *out_movqi_mr_r (rtx insn, rtx op[], int *l); -extern const char *out_movhi_r_mr (rtx insn, rtx op[], int *l); -extern const char *out_movhi_mr_r (rtx insn, rtx op[], int *l); -extern const char *out_movsi_r_mr (rtx insn, rtx op[], int *l); -extern const char *out_movsi_mr_r (rtx insn, rtx op[], int *l); extern const char *output_movsisf (rtx insn, rtx operands[], int *l); extern const char *avr_out_tstsi (rtx, rtx*, int*); extern const char *avr_out_tsthi (rtx, rtx*, int*); @@ -84,6 +79,7 @@ extern bool avr_rotate_bytes (rtx operands[]); extern void expand_prologue (void); extern void expand_epilogue (bool); +extern bool avr_emit_movmemhi (rtx*); extern int avr_epilogue_uses (int regno); extern int avr_starting_frame_offset (void); @@ -94,6 +90,9 @@ extern const char* avr_out_bitop (rtx, rtx*, int*); extern const char* avr_out_plus (rtx*, int*, int*); extern const char* avr_out_plus_noclobber (rtx*, int*, int*); extern const char* avr_out_addto_sp (rtx*, int*); +extern const char* avr_out_xload (rtx, rtx*, int*); +extern const char* avr_out_movmem (rtx, rtx*, int*); +extern const char* avr_out_map_bits (rtx, rtx*, int*); extern bool avr_popcount_each_byte (rtx, int, int); extern int extra_constraint_Q (rtx x); @@ -121,6 +120,10 @@ extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE); extern rtx avr_incoming_return_addr_rtx (void); extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int)); +extern bool avr_mem_pgm_p (rtx); +extern bool avr_mem_pgmx_p (rtx); +extern bool avr_load_libgcc_p (rtx); +extern bool avr_xload_libgcc_p (enum machine_mode); #endif /* RTX_CODE */ #ifdef REAL_VALUE_TYPE @@ -139,12 +142,14 @@ extern void avr_log_set_avr_log (void); typedef struct { - unsigned rtx_costs :1; + unsigned address_cost :1; + unsigned builtin :1; + unsigned constraints :1; unsigned legitimate_address_p :1; unsigned legitimize_address :1; unsigned legitimize_reload_address :1; - unsigned constraints :1; - unsigned address_cost :1; + unsigned progmem :1; + unsigned rtx_costs :1; } avr_log_t; extern avr_log_t avr_log; diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 65d43b8ca07..bb4a08dad71 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -35,6 +35,7 @@ #include "tree.h" #include "output.h" #include "expr.h" +#include "c-family/c-common.h" #include "diagnostic-core.h" #include "obstack.h" #include "function.h" @@ -54,11 +55,21 @@ /* Return true if STR starts with PREFIX and false, otherwise. */ #define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX))) -#define AVR_SECTION_PROGMEM (SECTION_MACH_DEP << 0) +/* The 4 bits starting at SECTION_MACH_DEP are reverved to store + 1 + flash segment where progmem data is to be located. + For example, data with __pgm2 is stored as (1+2) * SECTION_MACH_DEP. */ +#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP) /* Prototypes for local helper functions. */ +static const char* out_movqi_r_mr (rtx, rtx[], int*); +static const char* out_movhi_r_mr (rtx, rtx[], int*); +static const char* out_movsi_r_mr (rtx, rtx[], int*); +static const char* out_movqi_mr_r (rtx, rtx[], int*); +static const char* out_movhi_mr_r (rtx, rtx[], int*); +static const char* out_movsi_mr_r (rtx, rtx[], int*); + static int avr_naked_function_p (tree); static int interrupt_function_p (tree); static int signal_function_p (tree); @@ -84,12 +95,28 @@ static bool avr_rtx_costs (rtx, int, int, int, int *, bool); /* Allocate registers from r25 to r8 for parameters for function calls. */ #define FIRST_CUM_REG 26 +/* Implicit target register of LPM instruction (R0) */ +static GTY(()) rtx lpm_reg_rtx; + +/* (Implicit) address register of LPM instruction (R31:R30 = Z) */ +static GTY(()) rtx lpm_addr_reg_rtx; + /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */ static GTY(()) rtx tmp_reg_rtx; /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */ static GTY(()) rtx zero_reg_rtx; +/* RAMPZ special function register */ +static GTY(()) rtx rampz_rtx; + +/* RTX containing the strings "" and "e", respectively */ +static GTY(()) rtx xstring_empty; +static GTY(()) rtx xstring_e; + +/* RTXs for all general purpose registers as QImode */ +static GTY(()) rtx all_regs_rtx[32]; + /* AVR register names {"r0", "r1", ..., "r31"} */ static const char *const avr_regnames[] = REGISTER_NAMES; @@ -106,7 +133,17 @@ const struct mcu_type_s *avr_current_device; static GTY(()) section *progmem_swtable_section; /* Unnamed section associated to __attribute__((progmem)) aka. PROGMEM. */ -static GTY(()) section *progmem_section; +static GTY(()) section *progmem_section[6]; + +static const char * const progmem_section_prefix[6] = + { + ".progmem.data", + ".progmem1.data", + ".progmem2.data", + ".progmem3.data", + ".progmem4.data", + ".progmem5.data" + }; /* To track if code will use .bss and/or .data. */ bool avr_need_clear_bss_p = false; @@ -172,9 +209,6 @@ bool avr_need_copy_data_p = false; #undef TARGET_FUNCTION_ARG_ADVANCE #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance -#undef TARGET_LEGITIMIZE_ADDRESS -#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address - #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY avr_return_in_memory @@ -189,9 +223,6 @@ bool avr_need_copy_data_p = false; #undef TARGET_CASE_VALUES_THRESHOLD #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold -#undef TARGET_LEGITIMATE_ADDRESS_P -#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p - #undef TARGET_FRAME_POINTER_REQUIRED #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p #undef TARGET_CAN_ELIMINATE @@ -221,6 +252,24 @@ bool avr_need_copy_data_p = false; #undef TARGET_SCALAR_MODE_SUPPORTED_P #define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p +#undef TARGET_ADDR_SPACE_SUBSET_P +#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p + +#undef TARGET_ADDR_SPACE_CONVERT +#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert + +#undef TARGET_ADDR_SPACE_ADDRESS_MODE +#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode + +#undef TARGET_ADDR_SPACE_POINTER_MODE +#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode + +#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P +#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p + +#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS +#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address + /* Custom function to replace string prefix. @@ -323,9 +372,6 @@ avr_option_override (void) avr_current_arch = &avr_arch_types[avr_current_device->arch]; avr_extra_arch_macro = avr_current_device->macro; - tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); - zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO); - init_machine_status = avr_init_machine_status; avr_log_set_avr_log(); @@ -339,6 +385,38 @@ avr_init_machine_status (void) return ggc_alloc_cleared_machine_function (); } + +/* Implement `INIT_EXPANDERS'. */ +/* The function works like a singleton. */ + +void +avr_init_expanders (void) +{ + int regno; + + static bool done = false; + + if (done) + return; + else + done = true; + + for (regno = 0; regno < 32; regno ++) + all_regs_rtx[regno] = gen_rtx_REG (QImode, regno); + + lpm_reg_rtx = all_regs_rtx[LPM_REGNO]; + tmp_reg_rtx = all_regs_rtx[TMP_REGNO]; + zero_reg_rtx = all_regs_rtx[ZERO_REGNO]; + + lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z); + + rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)); + + xstring_empty = gen_rtx_CONST_STRING (VOIDmode, ""); + xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e"); +} + + /* Return register class for register R. */ enum reg_class @@ -384,6 +462,81 @@ avr_scalar_mode_supported_p (enum machine_mode mode) } +/* Return the segment number of pgm address space AS, i.e. + the 64k block it lives in. + Return -1 if unknown, i.e. 24-bit AS in flash. + Return -2 for anything else. */ + +static int +avr_pgm_segment (addr_space_t as) +{ + switch (as) + { + default: return -2; + + case ADDR_SPACE_PGMX: return -1; + case ADDR_SPACE_PGM: return 0; + case ADDR_SPACE_PGM1: return 1; + case ADDR_SPACE_PGM2: return 2; + case ADDR_SPACE_PGM3: return 3; + case ADDR_SPACE_PGM4: return 4; + case ADDR_SPACE_PGM5: return 5; + } +} + + +/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */ + +static bool +avr_decl_pgm_p (tree decl) +{ + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } + + return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + +/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash + address space and FALSE, otherwise. */ + +static bool +avr_decl_pgmx_p (tree decl) +{ + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } + + return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + +/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */ + +bool +avr_mem_pgm_p (rtx x) +{ + return (MEM_P (x) + && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x))); +} + + +/* Return TRUE if X is a MEM rtx located in the 24-bit Flash + address space and FALSE, otherwise. */ + +bool +avr_mem_pgmx_p (rtx x) +{ + return (MEM_P (x) + && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x)); +} + + /* A helper for the subsequent function attribute used to dig for attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ @@ -989,8 +1142,7 @@ expand_prologue (void) && TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { - emit_move_insn (tmp_reg_rtx, - gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR))); + emit_move_insn (tmp_reg_rtx, rampz_rtx); emit_push_byte (TMP_REGNO, false); } @@ -1228,8 +1380,7 @@ expand_epilogue (bool sibcall_p) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { emit_pop_byte (TMP_REGNO); - emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), - tmp_reg_rtx); + emit_move_insn (rampz_rtx, tmp_reg_rtx); } /* Restore SREG using tmp reg as scratch. */ @@ -1379,6 +1530,9 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) return ok; } + +/* Former implementation of TARGET_LEGITIMIZE_ADDRESS, + now only a helper for avr_addr_space_legitimize_address. */ /* Attempts to replace X with a valid memory address for an operand of mode MODE */ @@ -1641,8 +1795,9 @@ print_operand_address (FILE *file, rtx addr) } -/* Output X as assembler operand to file FILE. */ - +/* Output X as assembler operand to file FILE. + For a description of supported %-codes, see top of avr.md. */ + void print_operand (FILE *file, rtx x, int code) { @@ -1661,6 +1816,31 @@ print_operand (FILE *file, rtx x, int code) if (AVR_HAVE_EIJMP_EICALL) fputc ('e', file); } + else if (code == 't' + || code == 'T') + { + static int t_regno = -1; + static int t_nbits = -1; + + if (REG_P (x) && t_regno < 0 && code == 'T') + { + t_regno = REGNO (x); + t_nbits = GET_MODE_BITSIZE (GET_MODE (x)); + } + else if (CONST_INT_P (x) && t_regno >= 0 + && IN_RANGE (INTVAL (x), 0, t_nbits - 1)) + { + int bpos = INTVAL (x); + + fprintf (file, "%s", reg_names[t_regno + bpos / 8]); + if (code == 'T') + fprintf (file, ",%d", bpos % 8); + + t_regno = -1; + } + else + fatal_insn ("operands to %T/%t must be reg + const_int:", x); + } else if (REG_P (x)) { if (x == zero_reg_rtx) @@ -1668,16 +1848,39 @@ print_operand (FILE *file, rtx x, int code) else fprintf (file, reg_names[true_regnum (x) + abcd]); } - else if (GET_CODE (x) == CONST_INT) - fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) + abcd); - else if (GET_CODE (x) == MEM) + else if (CONST_INT_P (x)) + { + HOST_WIDE_INT ival = INTVAL (x); + + if ('i' != code) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival + abcd); + else if (low_io_address_operand (x, VOIDmode) + || high_io_address_operand (x, VOIDmode)) + { + switch (ival) + { + case RAMPZ_ADDR: fprintf (file, "__RAMPZ__"); break; + case SREG_ADDR: fprintf (file, "__SREG__"); break; + case SP_ADDR: fprintf (file, "__SP_L__"); break; + case SP_ADDR+1: fprintf (file, "__SP_H__"); break; + + default: + fprintf (file, HOST_WIDE_INT_PRINT_HEX, + ival - avr_current_arch->sfr_offset); + break; + } + } + else + fatal_insn ("bad address, not an I/O address:", x); + } + else if (MEM_P (x)) { rtx addr = XEXP (x, 0); if (code == 'm') { if (!CONSTANT_P (addr)) - fatal_insn ("bad address, not a constant):", addr); + fatal_insn ("bad address, not a constant:", addr); /* Assembler template with m-code is data - not progmem section */ if (text_segment_operand (addr, VOIDmode)) if (warning (0, "accessing data memory with" @@ -1688,6 +1891,10 @@ print_operand (FILE *file, rtx x, int code) } output_addr_const (file, addr); } + else if (code == 'i') + { + print_operand (file, addr, 'i'); + } else if (code == 'o') { if (GET_CODE (addr) != PLUS) @@ -1717,6 +1924,10 @@ print_operand (FILE *file, rtx x, int code) else print_operand_address (file, addr); } + else if (code == 'i') + { + fatal_insn ("bad address, not an I/O address:", x); + } else if (code == 'x') { /* Constant progmem address - like used in jmp or call */ @@ -1740,6 +1951,8 @@ print_operand (FILE *file, rtx x, int code) REAL_VALUE_TO_TARGET_SINGLE (rv, val); fprintf (file, "0x%lx", val); } + else if (GET_CODE (x) == CONST_STRING) + fputs (XSTR (x, 0), file); else if (code == 'j') fputs (cond_string (GET_CODE (x)), file); else if (code == 'k') @@ -2177,6 +2390,436 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee) /*********************************************************************** Functions for outputting various mov's for a various modes ************************************************************************/ + +/* Return true if a value of mode MODE is read from flash by + __load_* function from libgcc. */ + +bool +avr_load_libgcc_p (rtx op) +{ + enum machine_mode mode = GET_MODE (op); + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 2 + && !AVR_HAVE_LPMX + && avr_mem_pgm_p (op)); +} + +/* Return true if a value of mode MODE is read by __xload_* function. */ + +bool +avr_xload_libgcc_p (enum machine_mode mode) +{ + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 1 + && avr_current_arch->n_segments > 1 + && !AVR_HAVE_ELPMX); +} + + +/* Find an unused d-register to be used as scratch in INSN. + EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE + is a register, skip all possible return values that overlap EXCLUDE. + The policy for the returned register is similar to that of + `reg_unused_after', i.e. the returned register may overlap the SET_DEST + of INSN. + + Return a QImode d-register or NULL_RTX if nothing found. */ + +static rtx +avr_find_unused_d_reg (rtx insn, rtx exclude) +{ + int regno; + bool isr_p = (interrupt_function_p (current_function_decl) + || signal_function_p (current_function_decl)); + + for (regno = 16; regno < 32; regno++) + { + rtx reg = all_regs_rtx[regno]; + + if ((exclude + && reg_overlap_mentioned_p (exclude, reg)) + || fixed_regs[regno]) + { + continue; + } + + /* Try non-live register */ + + if (!df_regs_ever_live_p (regno) + && (TREE_THIS_VOLATILE (current_function_decl) + || cfun->machine->is_OS_task + || cfun->machine->is_OS_main + || (!isr_p && call_used_regs[regno]))) + { + return reg; + } + + /* Any live register can be used if it is unused after. + Prologue/epilogue will care for it as needed. */ + + if (df_regs_ever_live_p (regno) + && reg_unused_after (insn, reg)) + { + return reg; + } + } + + return NULL_RTX; +} + + +/* Helper function for the next function in the case where only restricted + version of LPM instruction is available. */ + +static const char* +avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) +{ + rtx dest = xop[0]; + rtx addr = xop[1]; + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + int regno_dest; + + regno_dest = REGNO (dest); + + /* The implicit target register of LPM. */ + xop[3] = lpm_reg_rtx; + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + avr_asm_len ("%4lpm", xop, plen, 1); + + if (regno_dest != LPM_REGNO) + avr_asm_len ("mov %0,%3", xop, plen, 1); + + return ""; + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm" CR_TAB + "push %3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "pop %A0", xop, plen, 6); + + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3", xop, plen, 5); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + + break; /* 2 */ + } + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + if (regno_dest == LPM_REGNO) + avr_asm_len ("%4lpm" CR_TAB + "adiw %2,1", xop, plen, 2); + else + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 2) + avr_asm_len ("%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 3) + avr_asm_len ("%4lpm" CR_TAB + "mov %C0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 4) + avr_asm_len ("%4lpm" CR_TAB + "mov %D0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + return ""; +} + + +/* If PLEN == NULL: Ouput instructions to load a value from a memory location + OP[1] in AS1 to register OP[0]. + If PLEN != 0 set *PLEN to the length in words of the instruction sequence. + Return "". */ + +static const char* +avr_out_lpm (rtx insn, rtx *op, int *plen) +{ + rtx xop[6]; + rtx dest = op[0]; + rtx src = SET_SRC (single_set (insn)); + rtx addr; + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + int regno_dest; + int segment; + + if (plen) + *plen = 0; + + if (MEM_P (dest)) + { + warning (0, "writing to address space %qs not supported", + c_addr_space_name (MEM_ADDR_SPACE (dest))); + + return ""; + } + + addr = XEXP (src, 0); + + segment = avr_pgm_segment (MEM_ADDR_SPACE (src)); + + gcc_assert (REG_P (dest) + && ((segment >= 0 + && (REG_P (addr) || POST_INC == GET_CODE (addr))) + || (GET_CODE (addr) == LO_SUM && segment == -1))); + + if (segment == -1) + { + /* We are called from avr_out_xload because someone wrote + __pgmx on a device with just one flash segment. */ + + addr = XEXP (addr, 1); + } + + xop[0] = dest; + xop[1] = addr; + xop[2] = lpm_addr_reg_rtx; + xop[4] = xstring_empty; + xop[5] = tmp_reg_rtx; + + regno_dest = REGNO (dest); + + /* Cut down segment number to a number the device actually + supports. We do this late to preserve the address space's + name for diagnostics. */ + + segment %= avr_current_arch->n_segments; + + /* Set RAMPZ as needed. */ + + if (segment) + { + xop[4] = GEN_INT (segment); + + if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx), + xop[3]) + { + avr_asm_len ("ldi %3,%4" CR_TAB + "out __RAMPZ__,%3", xop, plen, 2); + } + else if (segment == 1) + { + avr_asm_len ("clr %5" CR_TAB + "inc %5" CR_TAB + "out __RAMPZ__,%5", xop, plen, 3); + } + else + { + avr_asm_len ("mov %5,%2" CR_TAB + "ldi %2,%4" CR_TAB + "out __RAMPZ__,%2" CR_TAB + "mov %2,%5", xop, plen, 4); + } + + xop[4] = xstring_e; + } + + if ((segment == 0 && !AVR_HAVE_LPMX) + || (segment != 0 && !AVR_HAVE_ELPMX)) + { + return avr_out_lpm_no_lpmx (insn, xop, plen); + } + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1); + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %B0,%a2" CR_TAB + "mov %A0,%5", xop, plen, 3); + else + { + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2", xop, plen, 2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + } + + break; /* 2 */ + + case 3: + + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+" CR_TAB + "%4lpm %C0,%a2", xop, plen, 3); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,2", xop, plen, 1); + + break; /* 3 */ + + case 4: + + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+", xop, plen, 2); + + if (REGNO (dest) == REG_Z - 2) + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %C0,%a2" CR_TAB + "mov %D0,%5", xop, plen, 3); + else + { + avr_asm_len ("%4lpm %C0,%a2+" CR_TAB + "%4lpm %D0,%a2", xop, plen, 2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,3", xop, plen, 1); + } + + break; /* 4 */ + } /* n_bytes */ + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + avr_asm_len ("%4lpm %A0,%a2+", xop, plen, 1); + if (n_bytes >= 2) avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1); + if (n_bytes >= 3) avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1); + if (n_bytes >= 4) avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + return ""; +} + + +/* Worker function for xload_<mode> and xload_8 insns. */ + +const char* +avr_out_xload (rtx insn, rtx *op, int *plen) +{ + rtx xop[5]; + rtx reg = op[0]; + int n_bytes = GET_MODE_SIZE (GET_MODE (reg)); + unsigned int regno = REGNO (reg); + + if (avr_current_arch->n_segments == 1) + return avr_out_lpm (insn, op, plen); + + xop[0] = reg; + xop[1] = op[1]; + xop[2] = lpm_addr_reg_rtx; + xop[3] = lpm_reg_rtx; + xop[4] = tmp_reg_rtx; + + avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1); + + if (1 == n_bytes) + { + if (AVR_HAVE_ELPMX) + return avr_asm_len ("elpm %0,%a2", xop, plen, 1); + else + return avr_asm_len ("elpm" CR_TAB + "mov %0,%3", xop, plen, 2); + } + + gcc_assert (AVR_HAVE_ELPMX); + + if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx)) + { + /* Insn clobbers the Z-register so we can use post-increment. */ + + avr_asm_len ("elpm %A0,%a2+", xop, plen, 1); + if (n_bytes >= 2) avr_asm_len ("elpm %B0,%a2+", xop, plen, 1); + if (n_bytes >= 3) avr_asm_len ("elpm %C0,%a2+", xop, plen, 1); + if (n_bytes >= 4) avr_asm_len ("elpm %D0,%a2+", xop, plen, 1); + + return ""; + } + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 2: + gcc_assert (regno == REGNO (lpm_addr_reg_rtx)); + + return avr_asm_len ("elpm %4,%a2+" CR_TAB + "elpm %B0,%a2" CR_TAB + "mov %A0,%4", xop, plen, 3); + + case 3: + case 4: + gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx)); + + avr_asm_len ("elpm %A0,%a2+" CR_TAB + "elpm %B0,%a2+", xop, plen, 2); + + if (n_bytes == 3) + return avr_asm_len ("elpm %C0,%a2", xop, plen, 1); + else + return avr_asm_len ("elpm %4,%a2+" CR_TAB + "elpm %D0,%a2" CR_TAB + "mov %C0,%4", xop, plen, 3); + } + + return ""; +} + + const char * output_movqi (rtx insn, rtx operands[], int *l) { @@ -2185,6 +2828,12 @@ output_movqi (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -2211,17 +2860,12 @@ output_movqi (rtx insn, rtx operands[], int *l) } else if (GET_CODE (dest) == MEM) { - const char *templ; - - if (src == const0_rtx) - operands[1] = zero_reg_rtx; - - templ = out_movqi_mr_r (insn, operands, real_l); + rtx xop[2]; - if (!real_l) - output_asm_insn (templ, operands); + xop[0] = dest; + xop[1] = src == const0_rtx ? zero_reg_rtx : src; - operands[1] = src; + return out_movqi_mr_r (insn, xop, real_l); } return ""; } @@ -2235,6 +2879,12 @@ output_movhi (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -2286,93 +2936,79 @@ output_movhi (rtx insn, rtx operands[], int *l) } else if (GET_CODE (dest) == MEM) { - const char *templ; - - if (src == const0_rtx) - operands[1] = zero_reg_rtx; - - templ = out_movhi_mr_r (insn, operands, real_l); + rtx xop[2]; - if (!real_l) - output_asm_insn (templ, operands); + xop[0] = dest; + xop[1] = src == const0_rtx ? zero_reg_rtx : src; - operands[1] = src; - return ""; + return out_movhi_mr_r (insn, xop, real_l); } fatal_insn ("invalid insn:", insn); return ""; } -const char * -out_movqi_r_mr (rtx insn, rtx op[], int *l) +static const char* +out_movqi_r_mr (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; rtx x = XEXP (src, 0); - int dummy; - - if (!l) - l = &dummy; if (CONSTANT_ADDRESS_P (x)) { - if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) - { - *l = 1; - return AS2 (in,%0,__SREG__); - } - if (optimize > 0 && io_address_operand (x, QImode)) - { - *l = 1; - return AS2 (in,%0,%m1-0x20); - } - *l = 2; - return AS2 (lds,%0,%m1); + return optimize > 0 && io_address_operand (x, QImode) + ? avr_asm_len ("in %0,%i1", op, plen, -1) + : avr_asm_len ("lds %0,%m1", op, plen, -2); } - /* memory access by reg+disp */ else if (GET_CODE (x) == PLUS - && REG_P (XEXP (x,0)) - && GET_CODE (XEXP (x,1)) == CONST_INT) + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) { - if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63) - { - int disp = INTVAL (XEXP (x,1)); - if (REGNO (XEXP (x,0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); + /* memory access by reg+disp */ - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) - return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB - AS2 (ldd,%0,Y+63) CR_TAB - AS2 (sbiw,r28,%o1-63)); + int disp = INTVAL (XEXP (x, 1)); + + if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); - return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB - AS2 (sbci,r29,hi8(-%o1)) CR_TAB - AS2 (ld,%0,Y) CR_TAB - AS2 (subi,r28,lo8(%o1)) CR_TAB - AS2 (sbci,r29,hi8(%o1))); - } - else if (REGNO (XEXP (x,0)) == REG_X) - { - /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude - it but I have this situation with extremal optimizing options. */ - if (reg_overlap_mentioned_p (dest, XEXP (x,0)) - || reg_unused_after (insn, XEXP (x,0))) - return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,%0,X)); - - return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,%0,X) CR_TAB - AS2 (sbiw,r26,%o1)); - } - *l = 1; - return AS2 (ldd,%0,%1); + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + return avr_asm_len ("adiw r28,%o1-63" CR_TAB + "ldd %0,Y+63" CR_TAB + "sbiw r28,%o1-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %0,Y" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -5); + } + else if (REGNO (XEXP (x, 0)) == REG_X) + { + /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude + it but I have this situation with extremal optimizing options. */ + + avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %0,X", op, plen, -2); + + if (!reg_overlap_mentioned_p (dest, XEXP (x,0)) + && !reg_unused_after (insn, XEXP (x,0))) + { + avr_asm_len ("sbiw r26,%o1", op, plen, 1); + } + + return ""; + } + + return avr_asm_len ("ldd %0,%1", op, plen, -1); } - *l = 1; - return AS2 (ld,%0,%1); + + return avr_asm_len ("ld %0,%1", op, plen, -1); } -const char * -out_movhi_r_mr (rtx insn, rtx op[], int *l) +static const char* +out_movhi_r_mr (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; @@ -2382,39 +3018,25 @@ out_movhi_r_mr (rtx insn, rtx op[], int *l) /* "volatile" forces reading low byte first, even if less efficient, for correct operation with 16-bit I/O registers. */ int mem_volatile_p = MEM_VOLATILE_P (src); - int tmp; - - if (!l) - l = &tmp; if (reg_base > 0) { if (reg_dest == reg_base) /* R = (R) */ - { - *l = 3; - return (AS2 (ld,__tmp_reg__,%1+) CR_TAB - AS2 (ld,%B0,%1) CR_TAB - AS2 (mov,%A0,__tmp_reg__)); - } - else if (reg_base == REG_X) /* (R26) */ - { - if (reg_unused_after (insn, base)) - { - *l = 2; - return (AS2 (ld,%A0,X+) CR_TAB - AS2 (ld,%B0,X)); - } - *l = 3; - return (AS2 (ld,%A0,X+) CR_TAB - AS2 (ld,%B0,X) CR_TAB - AS2 (sbiw,r26,1)); - } - else /* (R) */ - { - *l = 2; - return (AS2 (ld,%A0,%1) CR_TAB - AS2 (ldd,%B0,%1+1)); - } + return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB + "ld %B0,%1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -3); + + if (reg_base != REG_X) + return avr_asm_len ("ld %A0,%1" CR_TAB + "ldd %B0,%1+1", op, plen, -2); + + avr_asm_len ("ld %A0,X+" CR_TAB + "ld %B0,X", op, plen, -2); + + if (!reg_unused_after (insn, base)) + avr_asm_len ("sbiw r26,1", op, plen, 1); + + return ""; } else if (GET_CODE (base) == PLUS) /* (R + i) */ { @@ -2422,109 +3044,90 @@ out_movhi_r_mr (rtx insn, rtx op[], int *l) int reg_base = true_regnum (XEXP (base, 0)); if (disp > MAX_LD_OFFSET (GET_MODE (src))) - { - if (REGNO (XEXP (base, 0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); - - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) - return *l = 4, (AS2 (adiw,r28,%o1-62) CR_TAB - AS2 (ldd,%A0,Y+62) CR_TAB - AS2 (ldd,%B0,Y+63) CR_TAB - AS2 (sbiw,r28,%o1-62)); + { + if (REGNO (XEXP (base, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + return disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)) + ? avr_asm_len ("adiw r28,%o1-62" CR_TAB + "ldd %A0,Y+62" CR_TAB + "ldd %B0,Y+63" CR_TAB + "sbiw r28,%o1-62", op, plen, -4) + + : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %A0,Y" CR_TAB + "ldd %B0,Y+1" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -6); + } + + /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude + it but I have this situation with extremal + optimization options. */ - return *l = 6, (AS2 (subi,r28,lo8(-%o1)) CR_TAB - AS2 (sbci,r29,hi8(-%o1)) CR_TAB - AS2 (ld,%A0,Y) CR_TAB - AS2 (ldd,%B0,Y+1) CR_TAB - AS2 (subi,r28,lo8(%o1)) CR_TAB - AS2 (sbci,r29,hi8(%o1))); - } if (reg_base == REG_X) - { - /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude - it but I have this situation with extremal - optimization options. */ - - *l = 4; - if (reg_base == reg_dest) - return (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,__tmp_reg__,X+) CR_TAB - AS2 (ld,%B0,X) CR_TAB - AS2 (mov,%A0,__tmp_reg__)); + return reg_base == reg_dest + ? avr_asm_len ("adiw r26,%o1" CR_TAB + "ld __tmp_reg__,X+" CR_TAB + "ld %B0,X" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -4) - return (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,%A0,X+) CR_TAB - AS2 (ld,%B0,X) CR_TAB - AS2 (sbiw,r26,%o1+1)); - } + : avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X" CR_TAB + "sbiw r26,%o1+1", op, plen, -4); - if (reg_base == reg_dest) - { - *l = 3; - return (AS2 (ldd,__tmp_reg__,%A1) CR_TAB - AS2 (ldd,%B0,%B1) CR_TAB - AS2 (mov,%A0,__tmp_reg__)); - } - - *l = 2; - return (AS2 (ldd,%A0,%A1) CR_TAB - AS2 (ldd,%B0,%B1)); + return reg_base == reg_dest + ? avr_asm_len ("ldd __tmp_reg__,%A1" CR_TAB + "ldd %B0,%B1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -3) + + : avr_asm_len ("ldd %A0,%A1" CR_TAB + "ldd %B0,%B1", op, plen, -2); } else if (GET_CODE (base) == PRE_DEC) /* (--R) */ { if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) - fatal_insn ("incorrect insn:", insn); + fatal_insn ("incorrect insn:", insn); - if (mem_volatile_p) - { - if (REGNO (XEXP (base, 0)) == REG_X) - { - *l = 4; - return (AS2 (sbiw,r26,2) CR_TAB - AS2 (ld,%A0,X+) CR_TAB - AS2 (ld,%B0,X) CR_TAB - AS2 (sbiw,r26,1)); - } - else - { - *l = 3; - return (AS2 (sbiw,%r1,2) CR_TAB - AS2 (ld,%A0,%p1) CR_TAB - AS2 (ldd,%B0,%p1+1)); - } - } - - *l = 2; - return (AS2 (ld,%B0,%1) CR_TAB - AS2 (ld,%A0,%1)); + if (!mem_volatile_p) + return avr_asm_len ("ld %B0,%1" CR_TAB + "ld %A0,%1", op, plen, -2); + + return REGNO (XEXP (base, 0)) == REG_X + ? avr_asm_len ("sbiw r26,2" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X" CR_TAB + "sbiw r26,1", op, plen, -4) + + : avr_asm_len ("sbiw %r1,2" CR_TAB + "ld %A0,%p1" CR_TAB + "ldd %B0,%p1+1", op, plen, -3); } else if (GET_CODE (base) == POST_INC) /* (R++) */ { if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) - fatal_insn ("incorrect insn:", insn); + fatal_insn ("incorrect insn:", insn); - *l = 2; - return (AS2 (ld,%A0,%1) CR_TAB - AS2 (ld,%B0,%1)); + return avr_asm_len ("ld %A0,%1" CR_TAB + "ld %B0,%1", op, plen, -2); } else if (CONSTANT_ADDRESS_P (base)) { - if (optimize > 0 && io_address_operand (base, HImode)) - { - *l = 2; - return (AS2 (in,%A0,%m1-0x20) CR_TAB - AS2 (in,%B0,%m1+1-0x20)); - } - *l = 4; - return (AS2 (lds,%A0,%m1) CR_TAB - AS2 (lds,%B0,%m1+1)); + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("in %A0,%i1" CR_TAB + "in %B0,%i1+1", op, plen, -2) + + : avr_asm_len ("lds %A0,%m1" CR_TAB + "lds %B0,%m1+1", op, plen, -4); } fatal_insn ("unknown move insn:",insn); return ""; } -const char * +static const char* out_movsi_r_mr (rtx insn, rtx op[], int *l) { rtx dest = op[0]; @@ -2685,7 +3288,7 @@ out_movsi_r_mr (rtx insn, rtx op[], int *l) return ""; } -const char * +static const char* out_movsi_mr_r (rtx insn, rtx op[], int *l) { rtx dest = op[0]; @@ -2848,6 +3451,12 @@ output_movsisf (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -2884,34 +3493,10 @@ output_movsisf (rtx insn, rtx operands[], int *l) AS2 (mov,%D0,%D1)); } } - else if (CONST_INT_P (src) - || CONST_DOUBLE_P (src)) - { - return output_reload_insisf (operands, NULL_RTX, real_l); - } else if (CONSTANT_P (src)) { - if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */ - { - *l = 4; - return (AS2 (ldi,%A0,lo8(%1)) CR_TAB - AS2 (ldi,%B0,hi8(%1)) CR_TAB - AS2 (ldi,%C0,hlo8(%1)) CR_TAB - AS2 (ldi,%D0,hhi8(%1))); - } - /* Last resort, better than loading from memory. */ - *l = 10; - return (AS2 (mov,__tmp_reg__,r31) CR_TAB - AS2 (ldi,r31,lo8(%1)) CR_TAB - AS2 (mov,%A0,r31) CR_TAB - AS2 (ldi,r31,hi8(%1)) CR_TAB - AS2 (mov,%B0,r31) CR_TAB - AS2 (ldi,r31,hlo8(%1)) CR_TAB - AS2 (mov,%C0,r31) CR_TAB - AS2 (ldi,r31,hhi8(%1)) CR_TAB - AS2 (mov,%D0,r31) CR_TAB - AS2 (mov,r31,__tmp_reg__)); - } + return output_reload_insisf (operands, NULL_RTX, real_l); + } else if (GET_CODE (src) == MEM) return out_movsi_r_mr (insn, operands, real_l); /* mov r,m */ } @@ -3167,6 +3752,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) rtx dest = op[0]; rtx src = op[1]; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, op, plen); + } + if (register_operand (dest, VOIDmode)) { if (register_operand (src, VOIDmode)) /* mov r,r */ @@ -3192,41 +3783,21 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) return avr_asm_len ("mov %C0,%C1", op, plen, 1); } } - else if (CONST_INT_P (src)) - { - return avr_out_reload_inpsi (op, NULL_RTX, plen); - } else if (CONSTANT_P (src)) { - if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */ - { - return avr_asm_len ("ldi %A0,lo8(%1)" CR_TAB - "ldi %B0,hi8(%1)" CR_TAB - "ldi %C0,hh8(%1)", op, plen, -3); - } - - /* Last resort, better than loading from memory. */ - return avr_asm_len ("mov __tmp_reg__,r31" CR_TAB - "ldi r31,lo8(%1)" CR_TAB - "mov %A0,r31" CR_TAB - "ldi r31,hi8(%1)" CR_TAB - "mov %B0,r31" CR_TAB - "ldi r31,hh8(%1)" CR_TAB - "mov %C0,r31" CR_TAB - "mov r31,__tmp_reg__", op, plen, -8); + return avr_out_reload_inpsi (op, NULL_RTX, plen); } else if (MEM_P (src)) return avr_out_load_psi (insn, op, plen); /* mov r,m */ } else if (MEM_P (dest)) { - if (src == CONST0_RTX (GET_MODE (dest))) - op[1] = zero_reg_rtx; - - avr_out_store_psi (insn, op, plen); + rtx xop[2]; + + xop[0] = dest; + xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src; - op[1] = src; - return ""; + return avr_out_store_psi (insn, xop, plen); } fatal_insn ("invalid insn:", insn); @@ -3234,88 +3805,71 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) } -const char * -out_movqi_mr_r (rtx insn, rtx op[], int *l) +static const char* +out_movqi_mr_r (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; rtx x = XEXP (dest, 0); - int dummy; - - if (!l) - l = &dummy; if (CONSTANT_ADDRESS_P (x)) { - if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) - { - *l = 1; - return AS2 (out,__SREG__,%1); - } - if (optimize > 0 && io_address_operand (x, QImode)) - { - *l = 1; - return AS2 (out,%m0-0x20,%1); - } - *l = 2; - return AS2 (sts,%m0,%1); + return optimize > 0 && io_address_operand (x, QImode) + ? avr_asm_len ("out %i0,%1", op, plen, -1) + : avr_asm_len ("sts %m0,%1", op, plen, -2); } - /* memory access by reg+disp */ - else if (GET_CODE (x) == PLUS - && REG_P (XEXP (x,0)) - && GET_CODE (XEXP (x,1)) == CONST_INT) + else if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) { - if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63) - { - int disp = INTVAL (XEXP (x,1)); - if (REGNO (XEXP (x,0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); + /* memory access by reg+disp */ - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) - return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB - AS2 (std,Y+63,%1) CR_TAB - AS2 (sbiw,r28,%o0-63)); + int disp = INTVAL (XEXP (x, 1)); - return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB - AS2 (sbci,r29,hi8(-%o0)) CR_TAB - AS2 (st,Y,%1) CR_TAB - AS2 (subi,r28,lo8(%o0)) CR_TAB - AS2 (sbci,r29,hi8(%o0))); - } + if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) + return avr_asm_len ("adiw r28,%o0-63" CR_TAB + "std Y+63,%1" CR_TAB + "sbiw r28,%o0-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -5); + } else if (REGNO (XEXP (x,0)) == REG_X) - { - if (reg_overlap_mentioned_p (src, XEXP (x, 0))) - { - if (reg_unused_after (insn, XEXP (x,0))) - return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB - AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,__tmp_reg__)); - - return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB - AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,__tmp_reg__) CR_TAB - AS2 (sbiw,r26,%o0)); - } - else - { - if (reg_unused_after (insn, XEXP (x,0))) - return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,%1)); + { + if (reg_overlap_mentioned_p (src, XEXP (x, 0))) + { + avr_asm_len ("mov __tmp_reg__,%1" CR_TAB + "adiw r26,%o0" CR_TAB + "st X,__tmp_reg__", op, plen, -3); + } + else + { + avr_asm_len ("adiw r26,%o0" CR_TAB + "st X,%1", op, plen, -2); + } + + if (!reg_unused_after (insn, XEXP (x,0))) + avr_asm_len ("sbiw r26,%o0", op, plen, 1); - return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,%1) CR_TAB - AS2 (sbiw,r26,%o0)); - } - } - *l = 1; - return AS2 (std,%0,%1); + return ""; + } + + return avr_asm_len ("std %0,%1", op, plen, 1); } - *l = 1; - return AS2 (st,%0,%1); + + return avr_asm_len ("st %0,%1", op, plen, 1); } -const char * -out_movhi_mr_r (rtx insn, rtx op[], int *l) +static const char* +out_movhi_mr_r (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; @@ -3325,127 +3879,103 @@ out_movhi_mr_r (rtx insn, rtx op[], int *l) /* "volatile" forces writing high byte first, even if less efficient, for correct operation with 16-bit I/O registers. */ int mem_volatile_p = MEM_VOLATILE_P (dest); - int tmp; - if (!l) - l = &tmp; if (CONSTANT_ADDRESS_P (base)) - { - if (optimize > 0 && io_address_operand (base, HImode)) - { - *l = 2; - return (AS2 (out,%m0+1-0x20,%B1) CR_TAB - AS2 (out,%m0-0x20,%A1)); - } - return *l = 4, (AS2 (sts,%m0+1,%B1) CR_TAB - AS2 (sts,%m0,%A1)); - } + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("out %i0+1,%B1" CR_TAB + "out %i0,%A1", op, plen, -2) + + : avr_asm_len ("sts %m0+1,%B1" CR_TAB + "sts %m0,%A1", op, plen, -4); + if (reg_base > 0) { - if (reg_base == REG_X) - { - if (reg_src == REG_X) - { - /* "st X+,r26" and "st -X,r26" are undefined. */ - if (!mem_volatile_p && reg_unused_after (insn, src)) - return *l=4, (AS2 (mov,__tmp_reg__,r27) CR_TAB - AS2 (st,X,r26) CR_TAB - AS2 (adiw,r26,1) CR_TAB - AS2 (st,X,__tmp_reg__)); - else - return *l=5, (AS2 (mov,__tmp_reg__,r27) CR_TAB - AS2 (adiw,r26,1) CR_TAB - AS2 (st,X,__tmp_reg__) CR_TAB - AS2 (sbiw,r26,1) CR_TAB - AS2 (st,X,r26)); - } - else - { - if (!mem_volatile_p && reg_unused_after (insn, base)) - return *l=2, (AS2 (st,X+,%A1) CR_TAB - AS2 (st,X,%B1)); - else - return *l=3, (AS2 (adiw,r26,1) CR_TAB - AS2 (st,X,%B1) CR_TAB - AS2 (st,-X,%A1)); - } - } - else - return *l=2, (AS2 (std,%0+1,%B1) CR_TAB - AS2 (st,%0,%A1)); + if (reg_base != REG_X) + return avr_asm_len ("std %0+1,%B1" CR_TAB + "st %0,%A1", op, plen, -2); + + if (reg_src == REG_X) + /* "st X+,r26" and "st -X,r26" are undefined. */ + return !mem_volatile_p && reg_unused_after (insn, src) + ? avr_asm_len ("mov __tmp_reg__,r27" CR_TAB + "st X,r26" CR_TAB + "adiw r26,1" CR_TAB + "st X,__tmp_reg__", op, plen, -4) + + : avr_asm_len ("mov __tmp_reg__,r27" CR_TAB + "adiw r26,1" CR_TAB + "st X,__tmp_reg__" CR_TAB + "sbiw r26,1" CR_TAB + "st X,r26", op, plen, -5); + + return !mem_volatile_p && reg_unused_after (insn, base) + ? avr_asm_len ("st X+,%A1" CR_TAB + "st X,%B1", op, plen, -2) + : avr_asm_len ("adiw r26,1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1", op, plen, -3); } else if (GET_CODE (base) == PLUS) { int disp = INTVAL (XEXP (base, 1)); reg_base = REGNO (XEXP (base, 0)); if (disp > MAX_LD_OFFSET (GET_MODE (dest))) - { - if (reg_base != REG_Y) - fatal_insn ("incorrect insn:",insn); - - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) - return *l = 4, (AS2 (adiw,r28,%o0-62) CR_TAB - AS2 (std,Y+63,%B1) CR_TAB - AS2 (std,Y+62,%A1) CR_TAB - AS2 (sbiw,r28,%o0-62)); - - return *l = 6, (AS2 (subi,r28,lo8(-%o0)) CR_TAB - AS2 (sbci,r29,hi8(-%o0)) CR_TAB - AS2 (std,Y+1,%B1) CR_TAB - AS2 (st,Y,%A1) CR_TAB - AS2 (subi,r28,lo8(%o0)) CR_TAB - AS2 (sbci,r29,hi8(%o0))); - } - if (reg_base == REG_X) - { - /* (X + d) = R */ - if (reg_src == REG_X) - { - *l = 7; - return (AS2 (mov,__tmp_reg__,r26) CR_TAB - AS2 (mov,__zero_reg__,r27) CR_TAB - AS2 (adiw,r26,%o0+1) CR_TAB - AS2 (st,X,__zero_reg__) CR_TAB - AS2 (st,-X,__tmp_reg__) CR_TAB - AS1 (clr,__zero_reg__) CR_TAB - AS2 (sbiw,r26,%o0)); - } - *l = 4; - return (AS2 (adiw,r26,%o0+1) CR_TAB - AS2 (st,X,%B1) CR_TAB - AS2 (st,-X,%A1) CR_TAB - AS2 (sbiw,r26,%o0)); - } - return *l=2, (AS2 (std,%B0,%B1) CR_TAB - AS2 (std,%A0,%A1)); + { + if (reg_base != REG_Y) + fatal_insn ("incorrect insn:",insn); + + return disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)) + ? avr_asm_len ("adiw r28,%o0-62" CR_TAB + "std Y+63,%B1" CR_TAB + "std Y+62,%A1" CR_TAB + "sbiw r28,%o0-62", op, plen, -4) + + : avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "std Y+1,%B1" CR_TAB + "st Y,%A1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -6); + } + + if (reg_base != REG_X) + return avr_asm_len ("std %B0,%B1" CR_TAB + "std %A0,%A1", op, plen, -2); + /* (X + d) = R */ + return reg_src == REG_X + ? avr_asm_len ("mov __tmp_reg__,r26" CR_TAB + "mov __zero_reg__,r27" CR_TAB + "adiw r26,%o0+1" CR_TAB + "st X,__zero_reg__" CR_TAB + "st -X,__tmp_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,%o0", op, plen, -7) + + : avr_asm_len ("adiw r26,%o0+1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1" CR_TAB + "sbiw r26,%o0", op, plen, -4); } else if (GET_CODE (base) == PRE_DEC) /* (--R) */ - return *l=2, (AS2 (st,%0,%B1) CR_TAB - AS2 (st,%0,%A1)); + { + return avr_asm_len ("st %0,%B1" CR_TAB + "st %0,%A1", op, plen, -2); + } else if (GET_CODE (base) == POST_INC) /* (R++) */ { - if (mem_volatile_p) - { - if (REGNO (XEXP (base, 0)) == REG_X) - { - *l = 4; - return (AS2 (adiw,r26,1) CR_TAB - AS2 (st,X,%B1) CR_TAB - AS2 (st,-X,%A1) CR_TAB - AS2 (adiw,r26,2)); - } - else - { - *l = 3; - return (AS2 (std,%p0+1,%B1) CR_TAB - AS2 (st,%p0,%A1) CR_TAB - AS2 (adiw,%r0,2)); - } - } + if (!mem_volatile_p) + return avr_asm_len ("st %0,%A1" CR_TAB + "st %0,%B1", op, plen, -2); + + return REGNO (XEXP (base, 0)) == REG_X + ? avr_asm_len ("adiw r26,1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1" CR_TAB + "adiw r26,2", op, plen, -4) - *l = 2; - return (AS2 (st,%0,%A1) CR_TAB - AS2 (st,%0,%B1)); + : avr_asm_len ("std %p0+1,%B1" CR_TAB + "st %p0,%A1" CR_TAB + "adiw %r0,2", op, plen, -3); } fatal_insn ("unknown move insn:",insn); return ""; @@ -3740,150 +4270,126 @@ avr_out_tstsi (rtx insn, rtx *op, int *plen) } -/* Generate asm equivalent for various shifts. - Shift count is a CONST_INT, MEM or REG. - This only handles cases that are not already - carefully hand-optimized in ?sh??i3_out. */ +/* Generate asm equivalent for various shifts. This only handles cases + that are not already carefully hand-optimized in ?sh??i3_out. + + OPERANDS[0] resp. %0 in TEMPL is the operand to be shifted. + OPERANDS[2] is the shift count as CONST_INT, MEM or REG. + OPERANDS[3] is a QImode scratch register from LD regs if + available and SCRATCH, otherwise (no scratch available) + + TEMPL is an assembler template that shifts by one position. + T_LEN is the length of this template. */ void out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], - int *len, int t_len) + int *plen, int t_len) { - rtx op[10]; - char str[500]; - int second_label = 1; - int saved_in_tmp = 0; - int use_zero_reg = 0; + bool second_label = true; + bool saved_in_tmp = false; + bool use_zero_reg = false; + rtx op[5]; op[0] = operands[0]; op[1] = operands[1]; op[2] = operands[2]; op[3] = operands[3]; - str[0] = 0; - if (len) - *len = 1; + if (plen) + *plen = 0; - if (GET_CODE (operands[2]) == CONST_INT) + if (CONST_INT_P (operands[2])) { - int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); + bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL + && REG_P (operands[3])); int count = INTVAL (operands[2]); int max_len = 10; /* If larger than this, always use a loop. */ if (count <= 0) - { - if (len) - *len = 0; - return; - } + return; if (count < 8 && !scratch) - use_zero_reg = 1; + use_zero_reg = true; if (optimize_size) - max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5)); + max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5)); if (t_len * count <= max_len) - { - /* Output shifts inline with no loop - faster. */ - if (len) - *len = t_len * count; - else - { - while (count-- > 0) - output_asm_insn (templ, op); - } + { + /* Output shifts inline with no loop - faster. */ + + while (count-- > 0) + avr_asm_len (templ, op, plen, t_len); - return; - } + return; + } if (scratch) - { - if (!len) - strcat (str, AS2 (ldi,%3,%2)); - } + { + avr_asm_len ("ldi %3,%2", op, plen, 1); + } else if (use_zero_reg) - { - /* Hack to save one word: use __zero_reg__ as loop counter. - Set one bit, then shift in a loop until it is 0 again. */ + { + /* Hack to save one word: use __zero_reg__ as loop counter. + Set one bit, then shift in a loop until it is 0 again. */ - op[3] = zero_reg_rtx; - if (len) - *len = 2; - else - strcat (str, ("set" CR_TAB - AS2 (bld,%3,%2-1))); - } + op[3] = zero_reg_rtx; + + avr_asm_len ("set" CR_TAB + "bld %3,%2-1", op, plen, 2); + } else - { - /* No scratch register available, use one from LD_REGS (saved in - __tmp_reg__) that doesn't overlap with registers to shift. */ + { + /* No scratch register available, use one from LD_REGS (saved in + __tmp_reg__) that doesn't overlap with registers to shift. */ - op[3] = gen_rtx_REG (QImode, - ((true_regnum (operands[0]) - 1) & 15) + 16); - op[4] = tmp_reg_rtx; - saved_in_tmp = 1; + op[3] = all_regs_rtx[((REGNO (op[0]) - 1) & 15) + 16]; + op[4] = tmp_reg_rtx; + saved_in_tmp = true; - if (len) - *len = 3; /* Includes "mov %3,%4" after the loop. */ - else - strcat (str, (AS2 (mov,%4,%3) CR_TAB - AS2 (ldi,%3,%2))); - } + avr_asm_len ("mov %4,%3" CR_TAB + "ldi %3,%2", op, plen, 2); + } - second_label = 0; + second_label = false; } - else if (GET_CODE (operands[2]) == MEM) + else if (MEM_P (op[2])) { - rtx op_mov[10]; + rtx op_mov[2]; - op[3] = op_mov[0] = tmp_reg_rtx; + op_mov[0] = op[3] = tmp_reg_rtx; op_mov[1] = op[2]; - if (len) - out_movqi_r_mr (insn, op_mov, len); - else - output_asm_insn (out_movqi_r_mr (insn, op_mov, NULL), op_mov); + out_movqi_r_mr (insn, op_mov, plen); } - else if (register_operand (operands[2], QImode)) + else if (register_operand (op[2], QImode)) { - if (reg_unused_after (insn, operands[2]) - && !reg_overlap_mentioned_p (operands[0], operands[2])) + op[3] = op[2]; + + if (!reg_unused_after (insn, op[2]) + || reg_overlap_mentioned_p (op[0], op[2])) { - op[3] = op[2]; + op[3] = tmp_reg_rtx; + avr_asm_len ("mov %3,%2", op, plen, 1); } - else - { - op[3] = tmp_reg_rtx; - if (!len) - strcat (str, (AS2 (mov,%3,%2) CR_TAB)); - } } else fatal_insn ("bad shift insn:", insn); if (second_label) - { - if (len) - ++*len; - else - strcat (str, AS1 (rjmp,2f)); - } + avr_asm_len ("rjmp 2f", op, plen, 1); - if (len) - *len += t_len + 2; /* template + dec + brXX */ - else - { - strcat (str, "\n1:\t"); - strcat (str, templ); - strcat (str, second_label ? "\n2:\t" : "\n\t"); - strcat (str, use_zero_reg ? AS1 (lsr,%3) : AS1 (dec,%3)); - strcat (str, CR_TAB); - strcat (str, second_label ? AS1 (brpl,1b) : AS1 (brne,1b)); - if (saved_in_tmp) - strcat (str, (CR_TAB AS2 (mov,%3,%4))); - output_asm_insn (str, op); - } + avr_asm_len ("1:", op, plen, 0); + avr_asm_len (templ, op, plen, t_len); + + if (second_label) + avr_asm_len ("2:", op, plen, 0); + + avr_asm_len (use_zero_reg ? "lsr %3" : "dec %3", op, plen, 1); + avr_asm_len (second_label ? "brpl 1b" : "brne 1b", op, plen, 1); + + if (saved_in_tmp) + avr_asm_len ("mov %3,%4", op, plen, 1); } @@ -5348,7 +5854,7 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc) bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8); op[0] = reg8; - op[1] = GEN_INT (val8); + op[1] = gen_int_mode (val8, QImode); /* To get usable cc0 no low-bytes must have been skipped. */ @@ -5897,7 +6403,9 @@ adjust_insn_length (rtx insn, int len) case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break; case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break; case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break; - + case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break; + case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break; + case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break; case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break; case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break; @@ -5921,6 +6429,8 @@ adjust_insn_length (rtx insn, int len) case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break; + case ADJUST_LEN_MAP_BITS: avr_out_map_bits (insn, op, &len); break; + default: gcc_unreachable(); } @@ -6042,6 +6552,49 @@ _reg_unused_after (rtx insn, rtx reg) return 1; } + +/* Return RTX that represents the lower 16 bits of a constant address. + Unfortunately, simplify_gen_subreg does not handle this case. */ + +static rtx +avr_const_address_lo16 (rtx x) +{ + rtx lo16; + + switch (GET_CODE (x)) + { + default: + break; + + case CONST: + if (PLUS == GET_CODE (XEXP (x, 0)) + && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0)) + && CONST_INT_P (XEXP (XEXP (x, 0), 1))) + { + HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1)); + const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0); + + lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); + lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset)); + + return lo16; + } + + break; + + case SYMBOL_REF: + { + const char *name = XSTR (x, 0); + + return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); + } + } + + avr_edump ("\n%?: %r\n", x); + gcc_unreachable(); +} + + /* Target hook for assembling integer objects. The AVR version needs special handling for references to certain labels. */ @@ -6054,11 +6607,30 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p) fputs ("\t.word\tgs(", asm_out_file); output_addr_const (asm_out_file, x); fputs (")\n", asm_out_file); + + return true; + } + else if (GET_MODE (x) == PSImode) + { + default_assemble_integer (avr_const_address_lo16 (x), + GET_MODE_SIZE (HImode), aligned_p); + + fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(", + asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\"\n", asm_out_file); + + fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\n", asm_out_file); + return true; } + return default_assemble_integer (x, size, aligned_p); } + /* Worker function for ASM_DECLARE_FUNCTION_NAME. */ void @@ -6208,8 +6780,16 @@ avr_attribute_table[] = { NULL, 0, 0, false, false, false, NULL, false } }; -/* Look for attribute `progmem' in DECL - if found return 1, otherwise 0. */ + +/* Look if DECL shall be placed in program memory space by + means of attribute `progmem' or some address-space qualifier. + Return non-zero if DECL is data that must end up in Flash and + zero if the data lives in RAM (.bss, .data, .rodata, ...). + + Return 2 if DECL is located in 24-bit flash address-space + Return 1 if DECL is located in 16-bit flash address-space + Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES + Return 0 otherwise */ int avr_progmem_p (tree decl, tree attributes) @@ -6219,11 +6799,18 @@ avr_progmem_p (tree decl, tree attributes) if (TREE_CODE (decl) != VAR_DECL) return 0; + if (avr_decl_pgmx_p (decl)) + return 2; + + if (avr_decl_pgm_p (decl)) + return 1; + if (NULL_TREE != lookup_attribute ("progmem", attributes)) - return 1; + return -1; - a=decl; + a = decl; + do a = TREE_TYPE(a); while (TREE_CODE (a) == ARRAY_TYPE); @@ -6232,16 +6819,123 @@ avr_progmem_p (tree decl, tree attributes) return 0; if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a))) - return 1; + return -1; return 0; } + +/* Scan type TYP for pointer references to address space ASn. + Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting + the AS are also declared to be CONST. + Otherwise, return the respective addres space, i.e. a value != 0. */ + +static addr_space_t +avr_nonconst_pointer_addrspace (tree typ) +{ + while (ARRAY_TYPE == TREE_CODE (typ)) + typ = TREE_TYPE (typ); + + if (POINTER_TYPE_P (typ)) + { + tree target = TREE_TYPE (typ); + + /* Pointer to function: Test the function's return type. */ + + if (FUNCTION_TYPE == TREE_CODE (target)) + return avr_nonconst_pointer_addrspace (TREE_TYPE (target)); + + /* "Ordinary" pointers... */ + + while (TREE_CODE (target) == ARRAY_TYPE) + target = TREE_TYPE (target); + + if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target)) + && !TYPE_READONLY (target)) + { + /* Pointers to non-generic address space must be const. */ + + return TYPE_ADDR_SPACE (target); + } + + /* Scan pointer's target type. */ + + return avr_nonconst_pointer_addrspace (target); + } + + return ADDR_SPACE_GENERIC; +} + + +/* Sanity check NODE so that all pointers targeting address space AS1 + go along with CONST qualifier. Writing to this address space should + be detected and complained about as early as possible. */ + +static bool +avr_pgm_check_var_decl (tree node) +{ + const char *reason = NULL; + + addr_space_t as = ADDR_SPACE_GENERIC; + + gcc_assert (as == 0); + + if (avr_log.progmem) + avr_edump ("%?: %t\n", node); + + switch (TREE_CODE (node)) + { + default: + break; + + case VAR_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "variable"; + break; + + case PARM_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "function parameter"; + break; + + case FIELD_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "structure field"; + break; + + case FUNCTION_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))), + as) + reason = "return type of function"; + break; + + case POINTER_TYPE: + if (as = avr_nonconst_pointer_addrspace (node), as) + reason = "pointer"; + break; + } + + if (reason) + { + if (TYPE_P (node)) + error ("pointer targeting address space %qs must be const in %qT", + c_addr_space_name (as), node); + else + error ("pointer targeting address space %qs must be const in %s %q+D", + c_addr_space_name (as), reason, node); + } + + return reason == NULL; +} + + /* Add the section attribute if the variable is in progmem. */ static void avr_insert_attributes (tree node, tree *attributes) { + avr_pgm_check_var_decl (node); + if (TREE_CODE (node) == VAR_DECL && (TREE_STATIC (node) || DECL_EXTERNAL (node)) && avr_progmem_p (node, *attributes)) @@ -6258,11 +6952,20 @@ avr_insert_attributes (tree node, tree *attributes) if (error_mark_node == node0) return; - if (!TYPE_READONLY (node0)) + if (!TYPE_READONLY (node0) + && !TREE_READONLY (node)) { + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node)); + const char *reason = "__attribute__((progmem))"; + + if (!ADDR_SPACE_GENERIC_P (as)) + reason = c_addr_space_name (as); + + if (avr_log.progmem) + avr_edump ("\n%?: %t\n%t\n", node, node0); + error ("variable %q+D must be const in order to be put into" - " read-only section by means of %<__attribute__((progmem))%>", - node); + " read-only section by means of %qs", node, reason); } } } @@ -6314,11 +7017,23 @@ avr_output_bss_section_asm_op (const void *data) } +/* Unnamed section callback for progmem*.data sections. */ + +static void +avr_output_progmem_section_asm_op (const void *data) +{ + fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n", + (const char*) data); +} + + /* Implement `TARGET_ASM_INIT_SECTIONS'. */ static void avr_asm_init_sections (void) { + unsigned int n; + /* Set up a section for jump tables. Alignment is handled by ASM_OUTPUT_BEFORE_CASE_LABEL. */ @@ -6337,9 +7052,12 @@ avr_asm_init_sections (void) ",\"ax\",@progbits"); } - progmem_section - = get_unnamed_section (0, output_section_asm_op, - "\t.section\t.progmem.data,\"a\",@progbits"); + for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++) + { + progmem_section[n] + = get_unnamed_section (0, avr_output_progmem_section_asm_op, + progmem_section_prefix[n]); + } /* Override section callbacks to keep track of `avr_need_clear_bss_p' resp. `avr_need_copy_data_p'. */ @@ -6418,8 +7136,9 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl) { if (flags & AVR_SECTION_PROGMEM) { + int segment = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP - 1; const char *old_prefix = ".rodata"; - const char *new_prefix = ".progmem.data"; + const char *new_prefix = progmem_section_prefix[segment]; const char *sname = new_prefix; if (STR_PREFIX_P (name, old_prefix)) @@ -6446,6 +7165,7 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl) static unsigned int avr_section_type_flags (tree decl, const char *name, int reloc) { + int prog; unsigned int flags = default_section_type_flags (decl, name, reloc); if (STR_PREFIX_P (name, ".noinit")) @@ -6459,10 +7179,16 @@ avr_section_type_flags (tree decl, const char *name, int reloc) } if (decl && DECL_P (decl) - && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog)) { + int segment = 0; + + if (prog == 1) + segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl))); + flags &= ~SECTION_WRITE; - flags |= AVR_SECTION_PROGMEM; + flags &= ~SECTION_BSS; + flags |= (1 + segment % avr_current_arch->n_segments) * SECTION_MACH_DEP; } return flags; @@ -6498,16 +7224,25 @@ avr_encode_section_info (tree decl, rtx rtl, static section * avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) { + int prog; + section * sect = default_elf_select_section (decl, reloc, align); if (decl && DECL_P (decl) - && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog)) { + int segment = 0; + + if (prog == 1) + segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl))); + + segment %= avr_current_arch->n_segments; + if (sect->common.flags & SECTION_NAMED) { const char * name = sect->named.name; const char * old_prefix = ".rodata"; - const char * new_prefix = ".progmem.data"; + const char * new_prefix = progmem_section_prefix[segment]; if (STR_PREFIX_P (name, old_prefix)) { @@ -6518,31 +7253,38 @@ avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) } } - return progmem_section; + return progmem_section[segment]; } return sect; } /* Implement `TARGET_ASM_FILE_START'. */ -/* Outputs some appropriate text to go at the start of an assembler - file. */ +/* Outputs some text at the start of each assembler file. */ static void avr_file_start (void) { + int sfr_offset = avr_current_arch->sfr_offset; + if (avr_current_arch->asm_only) error ("MCU %qs supported for assembler only", avr_current_device->name); default_file_start (); -/* fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/ - fputs ("__SREG__ = 0x3f\n" - "__SP_H__ = 0x3e\n" - "__SP_L__ = 0x3d\n", asm_out_file); - - fputs ("__tmp_reg__ = 0\n" - "__zero_reg__ = 1\n", asm_out_file); + fprintf (asm_out_file, + "__SREG__ = 0x%02x\n" + "__SP_H__ = 0x%02x\n" + "__SP_L__ = 0x%02x\n" + "__RAMPZ__ = 0x%02x\n" + "__tmp_reg__ = %d\n" + "__zero_reg__ = %d\n", + -sfr_offset + SREG_ADDR, + -sfr_offset + SP_ADDR + 1, + -sfr_offset + SP_ADDR, + -sfr_offset + RAMPZ_ADDR, + TMP_REGNO, + ZERO_REGNO); } @@ -8049,10 +8791,14 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode) reg_class_t avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED, - addr_space_t as ATTRIBUTE_UNUSED, - RTX_CODE outer_code, + addr_space_t as, RTX_CODE outer_code, RTX_CODE index_code ATTRIBUTE_UNUSED) { + if (!ADDR_SPACE_GENERIC_P (as)) + { + return POINTER_Z_REGS; + } + if (!avr_strict_X) return reload_completed ? BASE_POINTER_REGS : POINTER_REGS; @@ -8071,6 +8817,27 @@ avr_regno_mode_code_ok_for_base_p (int regno, { bool ok = false; + if (!ADDR_SPACE_GENERIC_P (as)) + { + if (regno < FIRST_PSEUDO_REGISTER + && regno == REG_Z) + { + return true; + } + + if (reg_renumber) + { + regno = reg_renumber[regno]; + + if (regno == REG_Z) + { + return true; + } + } + + return false; + } + if (regno < FIRST_PSEUDO_REGISTER && (regno == REG_X || regno == REG_Y @@ -8110,7 +8877,13 @@ avr_regno_mode_code_ok_for_base_p (int regno, LEN != NULL: set *LEN to the length of the instruction sequence (in words) printed with LEN = NULL. If CLEAR_P is true, OP[0] had been cleard to Zero already. - If CLEAR_P is false, nothing is known about OP[0]. */ + If CLEAR_P is false, nothing is known about OP[0]. + + The effect on cc0 is as follows: + + Load 0 to any register : NONE + Load ld register with any value : NONE + Anything else: : CLOBBER */ static void output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) @@ -8122,9 +8895,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) int clobber_val = 1234; bool cooked_clobber_p = false; bool set_p = false; - unsigned int n; enum machine_mode mode = GET_MODE (dest); - int n_bytes = GET_MODE_SIZE (mode); + int n, n_bytes = GET_MODE_SIZE (mode); gcc_assert (REG_P (dest) && CONSTANT_P (src)); @@ -8138,7 +8910,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) if (REGNO (dest) < 16 && REGNO (dest) + GET_MODE_SIZE (mode) > 16) { - clobber_reg = gen_rtx_REG (QImode, REGNO (dest) + n_bytes - 1); + clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1]; } /* We might need a clobber reg but don't have one. Look at the value to @@ -8155,7 +8927,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) That's cheaper than loading from constant pool. */ cooked_clobber_p = true; - clobber_reg = gen_rtx_REG (QImode, REG_Z + 1); + clobber_reg = all_regs_rtx[REG_Z + 1]; avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1); } @@ -8165,7 +8937,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) { int ldreg_p; bool done_byte = false; - unsigned int j; + int j; rtx xop[3]; /* Crop the n-th destination byte. */ @@ -8188,8 +8960,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) xop[1] = src; xop[2] = clobber_reg; - if (n >= 2) - avr_asm_len ("clr %0", xop, len, 1); + if (n >= 2 + (avr_current_arch->n_segments > 1)) + avr_asm_len ("mov %0,__zero_reg__", xop, len, 1); else avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2); continue; @@ -8221,14 +8993,13 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) } } - /* Use CLR to zero a value so that cc0 is set as expected - for zero. */ + /* Don't use CLR so that cc0 is set as expected. */ if (ival[n] == 0) { if (!clear_p) - avr_asm_len ("clr %0", &xdest[n], len, 1); - + avr_asm_len (ldreg_p ? "ldi %0,0" : "mov %0,__zero_reg__", + &xdest[n], len, 1); continue; } @@ -8364,7 +9135,9 @@ const char * output_reload_insisf (rtx *op, rtx clobber_reg, int *len) { if (AVR_HAVE_MOVW - && !test_hard_reg_class (LD_REGS, op[0])) + && !test_hard_reg_class (LD_REGS, op[0]) + && (CONST_INT_P (op[1]) + || CONST_DOUBLE_P (op[1]))) { int len_clr, len_noclr; @@ -8498,8 +9271,8 @@ const char * avr_out_sbxx_branch (rtx insn, rtx operands[]) { enum rtx_code comp = GET_CODE (operands[0]); - int long_jump = (get_attr_length (insn) >= 4); - int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); + bool long_jump = get_attr_length (insn) >= 4; + bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); if (comp == GE) comp = EQ; @@ -8509,49 +9282,61 @@ avr_out_sbxx_branch (rtx insn, rtx operands[]) if (reverse) comp = reverse_condition (comp); - if (GET_CODE (operands[1]) == CONST_INT) + switch (GET_CODE (operands[1])) { - if (INTVAL (operands[1]) < 0x40) - { - if (comp == EQ) - output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands); - else - output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands); - } + default: + gcc_unreachable(); + + case CONST_INT: + + if (low_io_address_operand (operands[1], QImode)) + { + if (comp == EQ) + output_asm_insn ("sbis %i1,%2", operands); + else + output_asm_insn ("sbic %i1,%2", operands); + } else - { - output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands); - if (comp == EQ) - output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands); - else - output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands); - } - } - else /* GET_CODE (operands[1]) == REG */ - { + { + output_asm_insn ("in __tmp_reg__,%i1", operands); + if (comp == EQ) + output_asm_insn ("sbrs __tmp_reg__,%2", operands); + else + output_asm_insn ("sbrc __tmp_reg__,%2", operands); + } + + break; /* CONST_INT */ + + case REG: + if (GET_MODE (operands[1]) == QImode) - { - if (comp == EQ) - output_asm_insn (AS2 (sbrs,%1,%2), operands); - else - output_asm_insn (AS2 (sbrc,%1,%2), operands); - } - else /* HImode or SImode */ - { - static char buf[] = "sbrc %A1,0"; - int bit_nr = INTVAL (operands[2]); - buf[3] = (comp == EQ) ? 's' : 'c'; - buf[6] = 'A' + (bit_nr >> 3); - buf[9] = '0' + (bit_nr & 7); - output_asm_insn (buf, operands); - } - } + { + if (comp == EQ) + output_asm_insn ("sbrs %1,%2", operands); + else + output_asm_insn ("sbrc %1,%2", operands); + } + else /* HImode, PSImode or SImode */ + { + static char buf[] = "sbrc %A1,0"; + unsigned int bit_nr = UINTVAL (operands[2]); + + buf[3] = (comp == EQ) ? 's' : 'c'; + buf[6] = 'A' + (bit_nr / 8); + buf[9] = '0' + (bit_nr % 8); + output_asm_insn (buf, operands); + } + + break; /* REG */ + } /* switch */ if (long_jump) - return (AS1 (rjmp,.+4) CR_TAB - AS1 (jmp,%x3)); + return ("rjmp .+4" CR_TAB + "jmp %x3"); + if (!reverse) - return AS1 (rjmp,%x3); + return "rjmp %x3"; + return ""; } @@ -8595,6 +9380,451 @@ avr_case_values_threshold (void) return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17; } + +/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */ + +static enum machine_mode +avr_addr_space_address_mode (addr_space_t as) +{ + return as == ADDR_SPACE_PGMX ? PSImode : HImode; +} + + +/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */ + +static enum machine_mode +avr_addr_space_pointer_mode (addr_space_t as) +{ + return as == ADDR_SPACE_PGMX ? PSImode : HImode; +} + + +/* Helper for following function. */ + +static bool +avr_reg_ok_for_pgm_addr (rtx reg, bool strict) +{ + gcc_assert (REG_P (reg)); + + if (strict) + { + return REGNO (reg) == REG_Z; + } + + /* Avoid combine to propagate hard regs. */ + + if (can_create_pseudo_p() + && REGNO (reg) < REG_Z) + { + return false; + } + + return true; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */ + +static bool +avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, + bool strict, addr_space_t as) +{ + bool ok = false; + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + return avr_legitimate_address_p (mode, x, strict); + + case ADDR_SPACE_PGM: + case ADDR_SPACE_PGM1: + case ADDR_SPACE_PGM2: + case ADDR_SPACE_PGM3: + case ADDR_SPACE_PGM4: + case ADDR_SPACE_PGM5: + + switch (GET_CODE (x)) + { + case REG: + ok = avr_reg_ok_for_pgm_addr (x, strict); + break; + + case POST_INC: + ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict); + break; + + default: + break; + } + + break; /* PGM */ + + case ADDR_SPACE_PGMX: + if (REG_P (x)) + ok = (!strict + && can_create_pseudo_p()); + + if (LO_SUM == GET_CODE (x)) + { + rtx hi = XEXP (x, 0); + rtx lo = XEXP (x, 1); + + ok = (REG_P (hi) + && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER) + && REG_P (lo) + && REGNO (lo) == REG_Z); + } + + break; /* PGMX */ + } + + if (avr_log.legitimate_address_p) + { + avr_edump ("\n%?: ret=%b, mode=%m strict=%d " + "reload_completed=%d reload_in_progress=%d %s:", + ok, mode, strict, reload_completed, reload_in_progress, + reg_renumber ? "(reg_renumber)" : ""); + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode)) + && reg_renumber) + { + avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)), + true_regnum (XEXP (x, 0))); + } + + avr_edump ("\n%r\n", x); + } + + return ok; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'. */ + +static rtx +avr_addr_space_legitimize_address (rtx x, rtx old_x, + enum machine_mode mode, addr_space_t as) +{ + if (ADDR_SPACE_GENERIC_P (as)) + return avr_legitimize_address (x, old_x, mode); + + if (avr_log.legitimize_address) + { + avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x); + } + + return old_x; +} + + +/* Implement `TARGET_ADDR_SPACE_CONVERT'. */ + +static rtx +avr_addr_space_convert (rtx src, tree type_from, tree type_to) +{ + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from)); + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to)); + + if (avr_log.progmem) + avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n", + src, type_from, type_to); + + if (as_from != ADDR_SPACE_PGMX + && as_to == ADDR_SPACE_PGMX) + { + rtx new_src; + int n_segments = avr_current_arch->n_segments; + RTX_CODE code = GET_CODE (src); + + if (CONST == code + && PLUS == GET_CODE (XEXP (src, 0)) + && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0)) + && CONST_INT_P (XEXP (XEXP (src, 0), 1))) + { + HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1)); + const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0); + + new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); + new_src = gen_rtx_CONST (PSImode, + plus_constant (new_src, offset)); + return new_src; + } + + if (SYMBOL_REF == code) + { + const char *name = XSTR (src, 0); + + return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); + } + + src = force_reg (Pmode, src); + + if (ADDR_SPACE_GENERIC_P (as_from) + || as_from == ADDR_SPACE_PGM + || n_segments == 1) + { + return gen_rtx_ZERO_EXTEND (PSImode, src); + } + else + { + int segment = avr_pgm_segment (as_from) % n_segments; + + new_src = gen_reg_rtx (PSImode); + emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src)); + + return new_src; + } + } + + return src; +} + + +/* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */ + +static bool +avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset) +{ + if (subset == ADDR_SPACE_PGMX + && superset != ADDR_SPACE_PGMX) + { + return false; + } + + return true; +} + + +/* Worker function for movmemhi insn. + XOP[0] Destination as MEM:BLK + XOP[1] Source " " + XOP[2] # Bytes to copy + + Return TRUE if the expansion is accomplished. + Return FALSE if the operand compination is not supported. */ + +bool +avr_emit_movmemhi (rtx *xop) +{ + HOST_WIDE_INT count; + enum machine_mode loop_mode; + addr_space_t as = MEM_ADDR_SPACE (xop[1]); + rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x; + rtx a_hi8 = NULL_RTX; + + if (avr_mem_pgm_p (xop[0])) + return false; + + if (!CONST_INT_P (xop[2])) + return false; + + count = INTVAL (xop[2]); + if (count <= 0) + return false; + + a_src = XEXP (xop[1], 0); + a_dest = XEXP (xop[0], 0); + + /* See if constant fits in 8 bits. */ + + loop_mode = (count <= 0x100) ? QImode : HImode; + + if (PSImode == GET_MODE (a_src)) + { + addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0); + a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2); + } + else + { + int seg = avr_pgm_segment (as); + + addr1 = a_src; + + if (seg > 0 + && seg % avr_current_arch->n_segments > 0) + { + a_hi8 = GEN_INT (seg % avr_current_arch->n_segments); + } + } + + if (a_hi8 + && avr_current_arch->n_segments > 1) + { + emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8)); + } + else if (!ADDR_SPACE_GENERIC_P (as)) + { + as = ADDR_SPACE_PGM; + } + + xas = GEN_INT (as); + + /* Create loop counter register */ + + loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode)); + + /* Copy pointers into new pseudos - they will be changed */ + + addr0 = copy_to_mode_reg (HImode, a_dest); + addr1 = copy_to_mode_reg (HImode, addr1); + + /* FIXME: Register allocator might come up with spill fails if it is left + on its own. Thus, we allocate the pointer registers by hand. */ + + emit_move_insn (lpm_addr_reg_rtx, addr1); + addr1 = lpm_addr_reg_rtx; + + reg_x = gen_rtx_REG (HImode, REG_X); + emit_move_insn (reg_x, addr0); + addr0 = reg_x; + + /* FIXME: Register allocator does a bad job and might spill address + register(s) inside the loop leading to additional move instruction + to/from stack which could clobber tmp_reg. Thus, do *not* emit + load and store as seperate insns. Instead, we perform the copy + by means of one monolithic insn. */ + + if (ADDR_SPACE_GENERIC_P (as)) + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; + + insn = fun (addr0, addr1, xas, loop_reg, + addr0, addr1, tmp_reg_rtx, loop_reg); + } + else if (as == ADDR_SPACE_PGM) + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; + + insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, + AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg); + } + else + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm; + + insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, + AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg, + a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR)); + } + + set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as); + emit_insn (insn); + + return true; +} + + +/* Print assembler for movmem_qi, movmem_hi insns... + $0, $4 : & dest + $1, $5 : & src + $2 : Address Space + $3, $7 : Loop register + $6 : Scratch register + + ...and movmem_qi_elpm, movmem_hi_elpm insns. + + $8, $9 : hh8 (& src) + $10 : RAMPZ_ADDR +*/ + +const char* +avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen) +{ + addr_space_t as = (addr_space_t) INTVAL (xop[2]); + enum machine_mode loop_mode = GET_MODE (xop[3]); + + bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]); + + gcc_assert (REG_X == REGNO (xop[0]) + && REG_Z == REGNO (xop[1])); + + if (plen) + *plen = 0; + + /* Loop label */ + + avr_asm_len ("0:", xop, plen, 0); + + /* Load with post-increment */ + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + + avr_asm_len ("ld %6,%a1+", xop, plen, 1); + break; + + case ADDR_SPACE_PGM: + + if (AVR_HAVE_LPMX) + avr_asm_len ("lpm %6,%a1+", xop, plen, 1); + else + avr_asm_len ("lpm" CR_TAB + "adiw %1,1", xop, plen, 2); + break; + + case ADDR_SPACE_PGM1: + case ADDR_SPACE_PGM2: + case ADDR_SPACE_PGM3: + case ADDR_SPACE_PGM4: + case ADDR_SPACE_PGM5: + case ADDR_SPACE_PGMX: + + if (AVR_HAVE_ELPMX) + avr_asm_len ("elpm %6,%a1+", xop, plen, 1); + else + avr_asm_len ("elpm" CR_TAB + "adiw %1,1", xop, plen, 2); + + if (as == ADDR_SPACE_PGMX + && !AVR_HAVE_ELPMX) + { + avr_asm_len ("adc %8,__zero_reg__" CR_TAB + "out __RAMPZ__,%8", xop, plen, 2); + } + + break; + } + + /* Store with post-increment */ + + avr_asm_len ("st %a0+,%6", xop, plen, 1); + + /* Decrement loop-counter and set Z-flag */ + + if (QImode == loop_mode) + { + avr_asm_len ("dec %3", xop, plen, 1); + } + else if (sbiw_p) + { + avr_asm_len ("sbiw %3,1", xop, plen, 1); + } + else + { + avr_asm_len ("subi %A3,1" CR_TAB + "sbci %B3,0", xop, plen, 2); + } + + /* Loop until zero */ + + return avr_asm_len ("brne 0b", xop, plen, 1); +} + + + /* Helper for __builtin_avr_delay_cycles */ static void @@ -8655,6 +9885,345 @@ avr_expand_delay_cycles (rtx operands0) } } + +/* Return VAL * BASE + DIGIT. BASE = 0 is shortcut for BASE = 2^{32} */ + +static double_int +avr_double_int_push_digit (double_int val, int base, + unsigned HOST_WIDE_INT digit) +{ + val = 0 == base + ? double_int_lshift (val, 32, 64, false) + : double_int_mul (val, uhwi_to_double_int (base)); + + return double_int_add (val, uhwi_to_double_int (digit)); +} + + +/* Compute the image of x under f, i.e. perform x --> f(x) */ + +static int +avr_map (double_int f, int x) +{ + return 0xf & double_int_to_uhwi (double_int_rshift (f, 4*x, 64, false)); +} + + +/* Return the map R that reverses the bits of byte B. + + R(0) = (0 7) o (1 6) o (2 5) o (3 4) + R(1) = (8 15) o (9 14) o (10 13) o (11 12) + + Notice that R o R = id. */ + +static double_int +avr_revert_map (int b) +{ + int i; + double_int r = double_int_zero; + + for (i = 16-1; i >= 0; i--) + r = avr_double_int_push_digit (r, 16, i >> 3 == b ? i ^ 7 : i); + + return r; +} + + +/* Return the map R that swaps bit-chunks of size SIZE in byte B. + + R(1,0) = (0 1) o (2 3) o (4 5) o (6 7) + R(1,1) = (8 9) o (10 11) o (12 13) o (14 15) + + R(4,0) = (0 4) o (1 5) o (2 6) o (3 7) + R(4,1) = (8 12) o (9 13) o (10 14) o (11 15) + + Notice that R o R = id. */ + +static double_int +avr_swap_map (int size, int b) +{ + int i; + double_int r = double_int_zero; + + for (i = 16-1; i >= 0; i--) + r = avr_double_int_push_digit (r, 16, i ^ (i >> 3 == b ? size : 0)); + + return r; +} + + +/* Return Identity. */ + +static double_int +avr_id_map (void) +{ + int i; + double_int r = double_int_zero; + + for (i = 16-1; i >= 0; i--) + r = avr_double_int_push_digit (r, 16, i); + + return r; +} + + +enum + { + SIG_ID = 0, + /* for QI and HI */ + SIG_ROL = 0xf, + SIG_REVERT_0 = 1 << 4, + SIG_SWAP1_0 = 1 << 5, + /* HI only */ + SIG_REVERT_1 = 1 << 6, + SIG_SWAP1_1 = 1 << 7, + SIG_SWAP4_0 = 1 << 8, + SIG_SWAP4_1 = 1 << 9 + }; + + +/* Return basic map with signature SIG. */ + +static double_int +avr_sig_map (int n ATTRIBUTE_UNUSED, int sig) +{ + if (sig == SIG_ID) return avr_id_map (); + else if (sig == SIG_REVERT_0) return avr_revert_map (0); + else if (sig == SIG_REVERT_1) return avr_revert_map (1); + else if (sig == SIG_SWAP1_0) return avr_swap_map (1, 0); + else if (sig == SIG_SWAP1_1) return avr_swap_map (1, 1); + else if (sig == SIG_SWAP4_0) return avr_swap_map (4, 0); + else if (sig == SIG_SWAP4_1) return avr_swap_map (4, 1); + else + gcc_unreachable(); +} + + +/* Return the Hamming distance between the B-th byte of A and C. */ + +static bool +avr_map_hamming_byte (int n, int b, double_int a, double_int c, bool strict) +{ + int i, hamming = 0; + + for (i = 8*b; i < n && i < 8*b + 8; i++) + { + int ai = avr_map (a, i); + int ci = avr_map (c, i); + + hamming += ai != ci && (strict || (ai < n && ci < n)); + } + + return hamming; +} + + +/* Return the non-strict Hamming distance between A and B. */ + +#define avr_map_hamming_nonstrict(N,A,B) \ + (+ avr_map_hamming_byte (N, 0, A, B, false) \ + + avr_map_hamming_byte (N, 1, A, B, false)) + + +/* Return TRUE iff A and B represent the same mapping. */ + +#define avr_map_equal_p(N,A,B) (0 == avr_map_hamming_nonstrict (N, A, B)) + + +/* Return TRUE iff A is a map of signature S. Notice that there is no + 1:1 correspondance between maps and signatures and thus this is + only supported for basic signatures recognized by avr_sig_map(). */ + +#define avr_map_sig_p(N,A,S) avr_map_equal_p (N, A, avr_sig_map (N, S)) + + +/* Swap odd/even bits of ld-reg %0: %0 = bit-swap (%0) */ + +static const char* +avr_out_swap_bits (rtx *xop, int *plen) +{ + xop[1] = tmp_reg_rtx; + + return avr_asm_len ("mov %1,%0" CR_TAB + "andi %0,0xaa" CR_TAB + "eor %1,%0" CR_TAB + "lsr %0" CR_TAB + "lsl %1" CR_TAB + "or %0,%1", xop, plen, 6); +} + +/* Revert bit order: %0 = Revert (%1) with %0 != %1 and clobber %1 */ + +static const char* +avr_out_revert_bits (rtx *xop, int *plen) +{ + return avr_asm_len ("inc __zero_reg__" "\n" + "0:\tror %1" CR_TAB + "rol %0" CR_TAB + "lsl __zero_reg__" CR_TAB + "brne 0b", xop, plen, 5); +} + + +/* If OUT_P = true: Output BST/BLD instruction according to MAP. + If OUT_P = false: Just dry-run and fix XOP[1] to resolve + early-clobber conflicts if XOP[0] = XOP[1]. */ + +static void +avr_move_bits (rtx *xop, double_int map, int n_bits, bool out_p, int *plen) +{ + int bit_dest, b, clobber = 0; + + /* T-flag contains this bit of the source, i.e. of XOP[1] */ + int t_bit_src = -1; + + if (!optimize && !out_p) + { + avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); + xop[1] = tmp_reg_rtx; + return; + } + + /* We order the operations according to the requested source bit b. */ + + for (b = 0; b < n_bits; b++) + for (bit_dest = 0; bit_dest < n_bits; bit_dest++) + { + int bit_src = avr_map (map, bit_dest); + + if (b != bit_src + /* Same position: No need to copy as the caller did MOV. */ + || bit_dest == bit_src + /* Accessing bits 8..f for 8-bit version is void. */ + || bit_src >= n_bits) + continue; + + if (t_bit_src != bit_src) + { + /* Source bit is not yet in T: Store it to T. */ + + t_bit_src = bit_src; + + if (out_p) + { + xop[2] = GEN_INT (bit_src); + avr_asm_len ("bst %T1%T2", xop, plen, 1); + } + else if (clobber & (1 << bit_src)) + { + /* Bit to be read was written already: Backup input + to resolve early-clobber conflict. */ + + avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); + xop[1] = tmp_reg_rtx; + return; + } + } + + /* Load destination bit with T. */ + + if (out_p) + { + xop[2] = GEN_INT (bit_dest); + avr_asm_len ("bld %T0%T2", xop, plen, 1); + } + + clobber |= 1 << bit_dest; + } +} + + +/* Print assembler code for `map_bitsqi' and `map_bitshi'. */ + +const char* +avr_out_map_bits (rtx insn, rtx *operands, int *plen) +{ + bool copy_0, copy_1; + int n_bits = GET_MODE_BITSIZE (GET_MODE (operands[0])); + double_int map = rtx_to_double_int (operands[1]); + rtx xop[3]; + + xop[0] = operands[0]; + xop[1] = operands[2]; + + if (plen) + *plen = 0; + else if (flag_print_asm_name) + avr_fdump (asm_out_file, ASM_COMMENT_START "%X\n", map); + + switch (n_bits) + { + default: + gcc_unreachable(); + + case 8: + if (avr_map_sig_p (n_bits, map, SIG_SWAP1_0)) + { + return avr_out_swap_bits (xop, plen); + } + else if (avr_map_sig_p (n_bits, map, SIG_REVERT_0)) + { + if (REGNO (xop[0]) == REGNO (xop[1]) + || !reg_unused_after (insn, xop[1])) + { + avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); + xop[1] = tmp_reg_rtx; + } + + return avr_out_revert_bits (xop, plen); + } + + break; /* 8 */ + + case 16: + + break; /* 16 */ + } + + /* Copy whole byte is cheaper than moving bits that stay at the same + position. Some bits in a byte stay at the same position iff the + strict Hamming distance to Identity is not 8. */ + + copy_0 = 8 != avr_map_hamming_byte (n_bits, 0, map, avr_id_map(), true); + copy_1 = 8 != avr_map_hamming_byte (n_bits, 1, map, avr_id_map(), true); + + /* Perform the move(s) just worked out. */ + + if (n_bits == 8) + { + if (REGNO (xop[0]) == REGNO (xop[1])) + { + /* Fix early-clobber clashes. + Notice XOP[0] hat no eary-clobber in its constraint. */ + + avr_move_bits (xop, map, n_bits, false, plen); + } + else if (copy_0) + { + avr_asm_len ("mov %0,%1", xop, plen, 1); + } + } + else if (AVR_HAVE_MOVW && copy_0 && copy_1) + { + avr_asm_len ("movw %A0,%A1", xop, plen, 1); + } + else + { + if (copy_0) + avr_asm_len ("mov %A0,%A1", xop, plen, 1); + + if (copy_1) + avr_asm_len ("mov %B0,%B1", xop, plen, 1); + } + + /* Move individual bits. */ + + avr_move_bits (xop, map, n_bits, true, plen); + + return ""; +} + + /* IDs for all the AVR builtins. */ enum avr_builtin_id @@ -8665,6 +10234,8 @@ enum avr_builtin_id AVR_BUILTIN_WDR, AVR_BUILTIN_SLEEP, AVR_BUILTIN_SWAP, + AVR_BUILTIN_MAP8, + AVR_BUILTIN_MAP16, AVR_BUILTIN_FMUL, AVR_BUILTIN_FMULS, AVR_BUILTIN_FMULSU, @@ -8721,6 +10292,18 @@ avr_init_builtins (void) long_unsigned_type_node, NULL_TREE); + tree uchar_ftype_ulong_uchar + = build_function_type_list (unsigned_char_type_node, + long_unsigned_type_node, + unsigned_char_type_node, + NULL_TREE); + + tree uint_ftype_ullong_uint + = build_function_type_list (unsigned_type_node, + long_long_unsigned_type_node, + unsigned_type_node, + NULL_TREE); + DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP); DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI); DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI); @@ -8737,6 +10320,11 @@ avr_init_builtins (void) DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, AVR_BUILTIN_FMULSU); + DEF_BUILTIN ("__builtin_avr_map8", uchar_ftype_ulong_uchar, + AVR_BUILTIN_MAP8); + DEF_BUILTIN ("__builtin_avr_map16", uint_ftype_ullong_uint, + AVR_BUILTIN_MAP16); + avr_init_builtin_int24 (); } @@ -8760,7 +10348,9 @@ bdesc_2arg[] = { { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL }, { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS }, - { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU } + { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }, + { CODE_FOR_map_bitsqi, "__builtin_avr_map8", AVR_BUILTIN_MAP8 }, + { CODE_FOR_map_bitshi, "__builtin_avr_map16", AVR_BUILTIN_MAP16 } }; /* Subroutine of avr_expand_builtin to take care of unop insns. */ @@ -8876,6 +10466,7 @@ avr_expand_builtin (tree exp, rtx target, size_t i; const struct avr_builtin_description *d; tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + const char* bname = IDENTIFIER_POINTER (DECL_NAME (fndecl)); unsigned int id = DECL_FUNCTION_CODE (fndecl); tree arg0; rtx op0; @@ -8908,12 +10499,37 @@ avr_expand_builtin (tree exp, rtx target, op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); if (! CONST_INT_P (op0)) - error ("__builtin_avr_delay_cycles expects a" - " compile time integer constant."); + error ("%s expects a compile time integer constant", bname); avr_expand_delay_cycles (op0); return 0; } + + case AVR_BUILTIN_MAP8: + { + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); + + if (!CONST_INT_P (op0)) + { + error ("%s expects a compile time long integer constant" + " as first argument", bname); + return target; + } + } + + case AVR_BUILTIN_MAP16: + { + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); + + if (!const_double_operand (op0, VOIDmode)) + { + error ("%s expects a compile time long long integer constant" + " as first argument", bname); + return target; + } + } } for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index 7a3bc738e92..96133b8da28 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -23,7 +23,8 @@ along with GCC; see the file COPYING3. If not see /* Names to predefine in the preprocessor for this target machine. */ -struct base_arch_s { +struct base_arch_s +{ /* Assembler only. */ int asm_only; @@ -54,6 +55,13 @@ struct base_arch_s { /* Default start of data section address for architecture. */ int default_data_section_start; + /* Offset between SFR address and RAM address: + SFR-address = RAM-address - sfr_offset */ + int sfr_offset; + + /* Number of 64k segments in the flash. */ + int n_segments; + const char *const macro; /* Architecture name. */ @@ -131,6 +139,8 @@ extern const struct base_arch_s avr_arch_types[]; #define AVR_HAVE_MUL (avr_current_arch->have_mul) #define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx) #define AVR_HAVE_LPMX (avr_current_arch->have_movw_lpmx) +#define AVR_HAVE_ELPM (avr_current_arch->have_elpm) +#define AVR_HAVE_ELPMX (avr_current_arch->have_elpmx) #define AVR_HAVE_RAMPZ (avr_current_arch->have_elpm) #define AVR_HAVE_EIJMP_EICALL (avr_current_arch->have_eijmp_eicall) #define AVR_HAVE_8BIT_SP (avr_current_device->short_sp || TARGET_TINY_STACK) @@ -391,6 +401,20 @@ typedef struct avr_args { #define NO_FUNCTION_CSE + +#define ADDR_SPACE_PGM 1 +#define ADDR_SPACE_PGM1 2 +#define ADDR_SPACE_PGM2 3 +#define ADDR_SPACE_PGM3 4 +#define ADDR_SPACE_PGM4 5 +#define ADDR_SPACE_PGM5 6 +#define ADDR_SPACE_PGMX 7 + +#define REGISTER_TARGET_PRAGMAS() \ + do { \ + avr_register_target_pragmas(); \ + } while (0) + #define TEXT_SECTION_ASM_OP "\t.text" #define DATA_SECTION_ASM_OP "\t.data" @@ -637,3 +661,5 @@ struct GTY(()) machine_function #define PUSH_ROUNDING(X) (X) #define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args() + +#define INIT_EXPANDERS avr_init_expanders() diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 59330104fa0..bddfe933ee6 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -28,9 +28,21 @@ ;; j Branch condition. ;; k Reverse branch condition. ;;..m..Constant Direct Data memory address. +;; i Print the SFR address quivalent of a CONST_INT or a CONST_INT +;; RAM address. The resulting addres is suitable to be used in IN/OUT. ;; o Displacement for (mem (plus (reg) (const_int))) operands. ;; p POST_INC or PRE_DEC address as a pointer (X, Y, Z) ;; r POST_INC or PRE_DEC address as a register (r26, r28, r30) +;; T/T Print operand suitable for BLD/BST instruction, i.e. register and +;; bit number. This gets 2 operands: The first %T gets a REG_P and +;; just cashes the operand for the next %T. The second %T gets +;; a CONST_INT that represents a bit position. +;; Example: With %0 = (reg:HI 18) and %1 = (const_int 13) +;; "%T0%T1" it will print "r19,5". +;; Notice that you must not write a comma between %T0 and %T1. +;; T/t Similar to above, but don't print the comma and the bit number. +;; Example: With %0 = (reg:HI 18) and %1 = (const_int 13) +;; "%T0%t1" it will print "r19". ;;..x..Constant Direct Program memory address. ;; ~ Output 'r' if not AVR_HAVE_JMP_CALL. ;; ! Output 'e' if AVR_HAVE_EIJMP_EICALL. @@ -42,21 +54,27 @@ (REG_Z 30) (REG_W 24) (REG_SP 32) + (LPM_REGNO 0) ; implicit target register of LPM (TMP_REGNO 0) ; temporary register r0 (ZERO_REGNO 1) ; zero register r1 - - (SREG_ADDR 0x5F) - (RAMPZ_ADDR 0x5B) + + ;; RAM addresses of some SFRs common to all Devices. + + (SREG_ADDR 0x5F) ; Status Register + (SP_ADDR 0x5D) ; Stack Pointer + (RAMPZ_ADDR 0x5B) ; Address' high part when loading via ELPM ]) (define_c_enum "unspec" [UNSPEC_STRLEN + UNSPEC_MOVMEM UNSPEC_INDEX_JMP UNSPEC_FMUL UNSPEC_FMULS UNSPEC_FMULSU UNSPEC_COPYSIGN UNSPEC_IDENTITY + UNSPEC_MAP_BITS ]) (define_c_enum "unspecv" @@ -127,24 +145,25 @@ "out_bitop, out_plus, out_plus_noclobber, addto_sp, tsthi, tstpsi, tstsi, compare, call, mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, + xload, movmem, ashlqi, ashrqi, lshrqi, ashlhi, ashrhi, lshrhi, ashlsi, ashrsi, lshrsi, ashlpsi, ashrpsi, lshrpsi, + map_bits, no" (const_string "no")) ;; Flavours of instruction set architecture (ISA), used in enabled attribute -;; mov: ISA has no MOVW -;; movw: ISA has MOVW -;; rjmp: ISA has no CALL/JMP -;; jmp: ISA has CALL/JMP -;; ijmp: ISA has no EICALL/EIJMP -;; eijmp: ISA has EICALL/EIJMP +;; mov : ISA has no MOVW movw : ISA has MOVW +;; rjmp : ISA has no CALL/JMP jmp : ISA has CALL/JMP +;; ijmp : ISA has no EICALL/EIJMP eijmp : ISA has EICALL/EIJMP +;; lpm : ISA has no LPMX lpmx : ISA has LPMX +;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX (define_attr "isa" - "mov,movw, rjmp,jmp, ijmp,eijmp, + "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, standard" (const_string "standard")) @@ -175,6 +194,22 @@ (and (eq_attr "isa" "eijmp") (match_test "AVR_HAVE_EIJMP_EICALL")) (const_int 1) + + (and (eq_attr "isa" "lpm") + (match_test "!AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "lpmx") + (match_test "AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpm") + (match_test "AVR_HAVE_ELPM && !AVR_HAVE_ELPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpmx") + (match_test "AVR_HAVE_ELPMX")) + (const_int 1) ] (const_int 0))) @@ -242,12 +277,10 @@ ;; even though its function is identical to that in builtins.c (define_expand "nonlocal_goto" - [ - (use (match_operand 0 "general_operand")) - (use (match_operand 1 "general_operand")) - (use (match_operand 2 "general_operand")) - (use (match_operand 3 "general_operand")) - ] + [(use (match_operand 0 "general_operand")) + (use (match_operand 1 "general_operand")) + (use (match_operand 2 "general_operand")) + (use (match_operand 3 "general_operand"))] "" { rtx r_label = copy_to_reg (operands[1]); @@ -315,6 +348,168 @@ "") ;;======================================================================== +;; Move stuff around + +(define_expand "load<mode>_libgcc" + [(set (match_dup 3) + (match_dup 2)) + (set (reg:MOVMODE 22) + (match_operand:MOVMODE 1 "memory_operand" "")) + (set (match_operand:MOVMODE 0 "register_operand" "") + (reg:MOVMODE 22))] + "avr_load_libgcc_p (operands[1])" + { + operands[3] = gen_rtx_REG (HImode, REG_Z); + operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX); + operands[1] = replace_equiv_address (operands[1], operands[3]); + set_mem_addr_space (operands[1], ADDR_SPACE_PGM); + }) + +(define_insn "load_<mode>_libgcc" + [(set (reg:MOVMODE 22) + (match_operand:MOVMODE 0 "memory_operand" "m,m"))] + "avr_load_libgcc_p (operands[0]) + && REG_P (XEXP (operands[0], 0)) + && REG_Z == REGNO (XEXP (operands[0], 0))" + { + operands[0] = GEN_INT (GET_MODE_SIZE (<MODE>mode)); + return "%~call __load_%0"; + } + [(set_attr "length" "1,2") + (set_attr "isa" "rjmp,jmp") + (set_attr "cc" "clobber")]) + + +(define_insn_and_split "xload8_A" + [(set (match_operand:QI 0 "register_operand" "=r") + (match_operand:QI 1 "memory_operand" "m")) + (clobber (reg:HI REG_Z))] + "can_create_pseudo_p() + && avr_mem_pgmx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + rtx insn, addr = XEXP (operands[1], 0); + rtx hi8 = gen_reg_rtx (QImode); + rtx reg_z = gen_rtx_REG (HImode, REG_Z); + + emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0)); + emit_move_insn (hi8, simplify_gen_subreg (QImode, addr, PSImode, 2)); + + insn = emit_insn (gen_xload_8 (operands[0], hi8)); + set_mem_addr_space (SET_SRC (single_set (insn)), + MEM_ADDR_SPACE (operands[1])); + DONE; + }) + +(define_insn_and_split "xload<mode>_A" + [(set (match_operand:MOVMODE 0 "register_operand" "=r") + (match_operand:MOVMODE 1 "memory_operand" "m")) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "QImode != <MODE>mode + && can_create_pseudo_p() + && avr_mem_pgmx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + rtx addr = XEXP (operands[1], 0); + rtx reg_z = gen_rtx_REG (HImode, REG_Z); + rtx addr_hi8 = simplify_gen_subreg (QImode, addr, PSImode, 2); + addr_space_t as = MEM_ADDR_SPACE (operands[1]); + rtx hi8, insn; + + emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0)); + + if (avr_xload_libgcc_p (<MODE>mode)) + { + emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8); + insn = emit_insn (gen_xload_<mode>_libgcc ()); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22)); + } + else if (avr_current_arch->n_segments == 1 + && GET_MODE_SIZE (<MODE>mode) > 2 + && !AVR_HAVE_LPMX) + { + rtx src = gen_rtx_MEM (<MODE>mode, reg_z); + + as = ADDR_SPACE_PGM; + insn = emit_insn (gen_load_<mode>_libgcc (src)); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22)); + } + else + { + hi8 = gen_reg_rtx (QImode); + emit_move_insn (hi8, addr_hi8); + insn = emit_insn (gen_xload_<mode> (operands[0], hi8)); + } + + set_mem_addr_space (SET_SRC (single_set (insn)), as); + + DONE; + }) + +;; Move value from address space pgmx to a register +;; These insns must be prior to respective generic move insn. + +(define_insn "xload_8" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (lo_sum:PSI (match_operand:QI 1 "register_operand" "r") + (reg:HI REG_Z))))] + "" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "adjust_len" "xload") + (set_attr "cc" "clobber")]) + +;; "xload_hi_libgcc" +;; "xload_psi_libgcc" +;; "xload_si_libgcc" +;; "xload_sf_libgcc" +(define_insn "xload_<mode>_libgcc" + [(set (reg:MOVMODE 22) + (mem:MOVMODE (lo_sum:PSI (reg:QI 21) + (reg:HI REG_Z)))) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "<MODE>mode != QImode + && avr_xload_libgcc_p (<MODE>mode)" + { + rtx x_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode)); + + /* Devices with ELPM* also have CALL. */ + + output_asm_insn ("call __xload_%0", &x_bytes); + return ""; + } + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + +;; "xload_hi" +;; "xload_psi" +;; "xload_si" +;; "xload_sf" +(define_insn "xload_<mode>" + [(set (match_operand:MOVMODE 0 "register_operand" "=r") + (mem:MOVMODE (lo_sum:PSI (match_operand:QI 1 "register_operand" "r") + (reg:HI REG_Z)))) + (clobber (scratch:HI)) + (clobber (reg:HI REG_Z))] + "<MODE>mode != QImode + && !avr_xload_libgcc_p (<MODE>mode)" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "adjust_len" "xload") + (set_attr "cc" "clobber")]) + + +;; General move expanders ;; "movqi" ;; "movhi" @@ -327,12 +522,40 @@ (match_operand:MOVMODE 1 "general_operand" ""))] "" { - /* One of the ops has to be in a register. */ - if (!register_operand (operands[0], <MODE>mode) - && !(register_operand (operands[1], <MODE>mode) - || CONST0_RTX (<MODE>mode) == operands[1])) + rtx dest = operands[0]; + rtx src = operands[1]; + + if (avr_mem_pgm_p (dest)) + DONE; + + /* One of the operands has to be in a register. */ + if (!register_operand (dest, <MODE>mode) + && !(register_operand (src, <MODE>mode) + || src == CONST0_RTX (<MODE>mode))) + { + operands[1] = src = copy_to_mode_reg (<MODE>mode, src); + } + + if (avr_mem_pgmx_p (src)) + { + rtx addr = XEXP (src, 0); + + if (!REG_P (addr)) + src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr)); + + if (QImode == <MODE>mode) + emit_insn (gen_xload8_A (dest, src)); + else + emit_insn (gen_xload<mode>_A (dest, src)); + + DONE; + } + + if (avr_load_libgcc_p (src)) { - operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); + /* For the small devices, do loads per libgcc call. */ + emit_insn (gen_load<mode>_libgcc (dest, src)); + DONE; } }) @@ -346,11 +569,14 @@ ;; so this may still be a win for registers live across function calls. (define_insn "movqi_insn" - [(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r") - (match_operand:QI 1 "general_operand" "rL,i,rL,Qm,r,q,i"))] - "(register_operand (operands[0],QImode) - || register_operand (operands[1], QImode) || const0_rtx == operands[1])" - "* return output_movqi (insn, operands, NULL);" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r ,d,Qm,r ,q,r,*r") + (match_operand:QI 1 "nox_general_operand" "rL,i,rL,Qm,r,q,i"))] + "register_operand (operands[0], QImode) + || register_operand (operands[1], QImode) + || const0_rtx == operands[1]" + { + return output_movqi (insn, operands, NULL); + } [(set_attr "length" "1,1,5,5,1,1,4") (set_attr "adjust_len" "mov8") (set_attr "cc" "none,none,clobber,clobber,none,none,clobber")]) @@ -425,14 +651,17 @@ (set_attr "cc" "none")]) (define_insn "*movhi" - [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m,d,*r,q,r") - (match_operand:HI 1 "general_operand" "r,L,m,rL,i,i,r,q"))] - "(register_operand (operands[0],HImode) - || register_operand (operands[1],HImode) || const0_rtx == operands[1])" - "* return output_movhi (insn, operands, NULL);" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m ,d,*r,q,r") + (match_operand:HI 1 "nox_general_operand" "r,L,m,rL,i,i ,r,q"))] + "register_operand (operands[0], HImode) + || register_operand (operands[1], HImode) + || const0_rtx == operands[1]" + { + return output_movhi (insn, operands, NULL); + } [(set_attr "length" "2,2,6,7,2,6,5,2") (set_attr "adjust_len" "mov16") - (set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")]) + (set_attr "cc" "none,none,clobber,clobber,none,clobber,none,none")]) (define_peephole2 ; movw [(set (match_operand:QI 0 "even_register_operand" "") @@ -462,6 +691,40 @@ operands[5] = gen_rtx_REG (HImode, REGNO (operands[3])); }) +;; For LPM loads from AS1 we split +;; R = *Z +;; to +;; R = *Z++ +;; Z = Z - sizeof (R) +;; +;; so that the second instruction can be optimized out. + +(define_split ; "split-lpmx" + [(set (match_operand:HISI 0 "register_operand" "") + (match_operand:HISI 1 "memory_operand" ""))] + "reload_completed + && AVR_HAVE_LPMX" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 3) + (plus:HI (match_dup 3) + (match_dup 4)))] + { + rtx addr = XEXP (operands[1], 0); + + if (!avr_mem_pgm_p (operands[1]) + || !REG_P (addr) + || reg_overlap_mentioned_p (addr, operands[0])) + { + FAIL; + } + + operands[2] = replace_equiv_address (operands[1], + gen_rtx_POST_INC (Pmode, addr)); + operands[3] = addr; + operands[4] = gen_int_mode (-GET_MODE_SIZE (<MODE>mode), HImode); + }) + ;;========================================================================== ;; xpointer move (24 bit) @@ -492,7 +755,7 @@ (define_insn "*movpsi" [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") - (match_operand:PSI 1 "general_operand" "r,L,Qm,rL,i ,i"))] + (match_operand:PSI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))] "register_operand (operands[0], PSImode) || register_operand (operands[1], PSImode) || const0_rtx == operands[1]" @@ -501,7 +764,7 @@ } [(set_attr "length" "3,3,8,9,4,10") (set_attr "adjust_len" "mov24") - (set_attr "cc" "none,set_zn,clobber,clobber,clobber,clobber")]) + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) ;;========================================================================== ;; move double word (32 bit) @@ -532,23 +795,24 @@ (define_insn "*movsi" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") - (match_operand:SI 1 "general_operand" "r,L,Qm,rL,i,i"))] - "(register_operand (operands[0],SImode) - || register_operand (operands[1],SImode) || const0_rtx == operands[1])" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:SI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))] + "register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || const0_rtx == operands[1]" { return output_movsisf (insn, operands, NULL); } [(set_attr "length" "4,4,8,9,4,10") (set_attr "adjust_len" "mov32") - (set_attr "cc" "none,set_zn,clobber,clobber,clobber,clobber")]) + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) ;; fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ;; move floating point numbers (32 bit) (define_insn "*movsf" - [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") - (match_operand:SF 1 "general_operand" "r,G,Qm,rG,F,F"))] + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))] "register_operand (operands[0], SFmode) || register_operand (operands[1], SFmode) || operands[1] == CONST0_RTX (SFmode)" @@ -557,7 +821,7 @@ } [(set_attr "length" "4,4,8,9,4,10") (set_attr "adjust_len" "mov32") - (set_attr "cc" "none,set_zn,clobber,clobber,clobber,clobber")]) + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) (define_peephole2 ; *reload_insf [(match_scratch:QI 2 "d") @@ -585,166 +849,164 @@ ;;========================================================================= ;; move string (like memcpy) -;; implement as RTL loop (define_expand "movmemhi" [(parallel [(set (match_operand:BLK 0 "memory_operand" "") - (match_operand:BLK 1 "memory_operand" "")) - (use (match_operand:HI 2 "const_int_operand" "")) - (use (match_operand:HI 3 "const_int_operand" ""))])] + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand:HI 2 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" ""))])] "" - "{ - int prob; - HOST_WIDE_INT count; - enum machine_mode mode; - rtx label = gen_label_rtx (); - rtx loop_reg; - rtx jump; - - /* Copy pointers into new psuedos - they will be changed. */ - rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); - rtx addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); - - /* Create rtx for tmp register - we use this as scratch. */ - rtx tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); - - if (GET_CODE (operands[2]) != CONST_INT) - FAIL; - - count = INTVAL (operands[2]); - if (count <= 0) + { + if (avr_emit_movmemhi (operands)) + DONE; + FAIL; + }) - /* Work out branch probability for latter use. */ - prob = REG_BR_PROB_BASE - REG_BR_PROB_BASE / count; - - /* See if constant fit 8 bits. */ - mode = (count < 0x100) ? QImode : HImode; - /* Create loop counter register. */ - loop_reg = copy_to_mode_reg (mode, gen_int_mode (count, mode)); - - /* Now create RTL code for move loop. */ - /* Label at top of loop. */ - emit_label (label); - - /* Move one byte into scratch and inc pointer. */ - emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, addr1)); - emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx)); - - /* Move to mem and inc pointer. */ - emit_move_insn (gen_rtx_MEM (QImode, addr0), tmp_reg_rtx); - emit_move_insn (addr0, gen_rtx_PLUS (Pmode, addr0, const1_rtx)); +(define_mode_attr MOVMEM_r_d [(QI "r") + (HI "d")]) + +;; $0, $4 : & dest +;; $1, $5 : & src +;; $2 : Address Space +;; $3, $7 : Loop register +;; $6 : Scratch register + +;; "movmem_qi" +;; "movmem_hi" +(define_insn "movmem_<mode>" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "x")) + (mem:BLK (match_operand:HI 1 "register_operand" "z"))) + (unspec [(match_operand:QI 2 "const_int_operand" "LP")] + UNSPEC_MOVMEM) + (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>")) + (clobber (match_operand:HI 4 "register_operand" "=0")) + (clobber (match_operand:HI 5 "register_operand" "=1")) + (clobber (match_operand:QI 6 "register_operand" "=&r")) + (clobber (match_operand:QIHI 7 "register_operand" "=3"))] + "" + { + return avr_out_movmem (insn, operands, NULL); + } + [(set_attr "adjust_len" "movmem") + (set_attr "cc" "clobber")]) - /* Decrement count. */ - emit_move_insn (loop_reg, gen_rtx_PLUS (mode, loop_reg, constm1_rtx)); +;; Ditto and +;; $8, $9 : hh8 (& src) +;; $10 : RAMPZ_ADDR + +;; "movmem_qi_elpm" +;; "movmem_hi_elpm" +(define_insn "movmem_<mode>_elpm" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "x")) + (mem:BLK (lo_sum:PSI (match_operand:QI 8 "register_operand" "r") + (match_operand:HI 1 "register_operand" "z")))) + (unspec [(match_operand:QI 2 "const_int_operand" "n")] + UNSPEC_MOVMEM) + (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>")) + (clobber (match_operand:HI 4 "register_operand" "=0")) + (clobber (match_operand:HI 5 "register_operand" "=1")) + (clobber (match_operand:QI 6 "register_operand" "=&r")) + (clobber (match_operand:QIHI 7 "register_operand" "=3")) + (clobber (match_operand:QI 9 "register_operand" "=8")) + (clobber (mem:QI (match_operand:QI 10 "io_address_operand" "n")))] + "" + { + return avr_out_movmem (insn, operands, NULL); + } + [(set_attr "adjust_len" "movmem") + (set_attr "cc" "clobber")]) - /* Compare with zero and jump if not equal. */ - emit_cmp_and_jump_insns (loop_reg, const0_rtx, NE, NULL_RTX, mode, 1, - label); - /* Set jump probability based on loop count. */ - jump = get_last_insn (); - add_reg_note (jump, REG_BR_PROB, GEN_INT (prob)); - DONE; -}") -;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 +;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 ;; memset (%0, %2, %1) (define_expand "setmemhi" [(parallel [(set (match_operand:BLK 0 "memory_operand" "") - (match_operand 2 "const_int_operand" "")) - (use (match_operand:HI 1 "const_int_operand" "")) - (use (match_operand:HI 3 "const_int_operand" "n")) - (clobber (match_scratch:HI 4 "")) - (clobber (match_dup 5))])] + (match_operand 2 "const_int_operand" "")) + (use (match_operand:HI 1 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" "")) + (clobber (match_scratch:HI 4 "")) + (clobber (match_dup 5))])] "" - "{ - rtx addr0; - enum machine_mode mode; + { + rtx addr0; + enum machine_mode mode; - /* If value to set is not zero, use the library routine. */ - if (operands[2] != const0_rtx) - FAIL; + /* If value to set is not zero, use the library routine. */ + if (operands[2] != const0_rtx) + FAIL; - if (!CONST_INT_P (operands[1])) - FAIL; + if (!CONST_INT_P (operands[1])) + FAIL; + + mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode; + operands[5] = gen_rtx_SCRATCH (mode); + operands[1] = copy_to_mode_reg (mode, + gen_int_mode (INTVAL (operands[1]), mode)); + addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); + operands[0] = gen_rtx_MEM (BLKmode, addr0); + }) - mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode; - operands[5] = gen_rtx_SCRATCH (mode); - operands[1] = copy_to_mode_reg (mode, - gen_int_mode (INTVAL (operands[1]), mode)); - addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); - operands[0] = gen_rtx_MEM (BLKmode, addr0); -}") (define_insn "*clrmemqi" [(set (mem:BLK (match_operand:HI 0 "register_operand" "e")) - (const_int 0)) + (const_int 0)) (use (match_operand:QI 1 "register_operand" "r")) (use (match_operand:QI 2 "const_int_operand" "n")) (clobber (match_scratch:HI 3 "=0")) (clobber (match_scratch:QI 4 "=&1"))] "" - "st %a0+,__zero_reg__ - dec %1 - brne .-6" + "0:\;st %a0+,__zero_reg__\;dec %1\;brne 0b" [(set_attr "length" "3") (set_attr "cc" "clobber")]) + (define_insn "*clrmemhi" [(set (mem:BLK (match_operand:HI 0 "register_operand" "e,e")) - (const_int 0)) + (const_int 0)) (use (match_operand:HI 1 "register_operand" "!w,d")) (use (match_operand:HI 2 "const_int_operand" "n,n")) (clobber (match_scratch:HI 3 "=0,0")) (clobber (match_scratch:HI 4 "=&1,&1"))] "" - "*{ - if (which_alternative==0) - return (AS2 (st,%a0+,__zero_reg__) CR_TAB - AS2 (sbiw,%A1,1) CR_TAB - AS1 (brne,.-6)); - else - return (AS2 (st,%a0+,__zero_reg__) CR_TAB - AS2 (subi,%A1,1) CR_TAB - AS2 (sbci,%B1,0) CR_TAB - AS1 (brne,.-8)); -}" + "@ + 0:\;st %a0+,__zero_reg__\;sbiw %A1,1\;brne 0b + 0:\;st %a0+,__zero_reg__\;subi %A1,1\;sbci %B1,0\;brne 0b" [(set_attr "length" "3,4") (set_attr "cc" "clobber,clobber")]) (define_expand "strlenhi" - [(set (match_dup 4) - (unspec:HI [(match_operand:BLK 1 "memory_operand" "") - (match_operand:QI 2 "const_int_operand" "") - (match_operand:HI 3 "immediate_operand" "")] - UNSPEC_STRLEN)) - (set (match_dup 4) (plus:HI (match_dup 4) - (const_int -1))) - (set (match_operand:HI 0 "register_operand" "") - (minus:HI (match_dup 4) - (match_dup 5)))] - "" - "{ - rtx addr; - if (operands[2] != const0_rtx) - FAIL; - addr = copy_to_mode_reg (Pmode, XEXP (operands[1],0)); - operands[1] = gen_rtx_MEM (BLKmode, addr); - operands[5] = addr; - operands[4] = gen_reg_rtx (HImode); -}") + [(set (match_dup 4) + (unspec:HI [(match_operand:BLK 1 "memory_operand" "") + (match_operand:QI 2 "const_int_operand" "") + (match_operand:HI 3 "immediate_operand" "")] + UNSPEC_STRLEN)) + (set (match_dup 4) + (plus:HI (match_dup 4) + (const_int -1))) + (set (match_operand:HI 0 "register_operand" "") + (minus:HI (match_dup 4) + (match_dup 5)))] + "" + { + rtx addr; + if (operands[2] != const0_rtx) + FAIL; + addr = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); + operands[1] = gen_rtx_MEM (BLKmode, addr); + operands[5] = addr; + operands[4] = gen_reg_rtx (HImode); + }) (define_insn "*strlenhi" - [(set (match_operand:HI 0 "register_operand" "=e") - (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "%0")) - (const_int 0) - (match_operand:HI 2 "immediate_operand" "i")] - UNSPEC_STRLEN))] - "" - "ld __tmp_reg__,%a0+ - tst __tmp_reg__ - brne .-6" + [(set (match_operand:HI 0 "register_operand" "=e") + (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "0")) + (const_int 0) + (match_operand:HI 2 "immediate_operand" "i")] + UNSPEC_STRLEN))] + "" + "0:\;ld __tmp_reg__,%a0+\;tst __tmp_reg__\;brne 0b" [(set_attr "length" "3") (set_attr "cc" "clobber")]) @@ -2770,7 +3032,7 @@ (const_int 15)))] "" "bst %A0,0\;ror %B0\;ror %A0\;bld %B0,7" - [(set_attr "length" "3") + [(set_attr "length" "4") (set_attr "cc" "clobber")]) (define_insn "*rotlpsi2.1" @@ -2868,10 +3130,8 @@ (define_expand "ashlqi3" [(set (match_operand:QI 0 "register_operand" "") - (ashift:QI (match_operand:QI 1 "register_operand" "") - (match_operand:QI 2 "general_operand" "")))] - "" - "") + (ashift:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) (define_split ; ashlqi3_const4 [(set (match_operand:QI 0 "d_register_operand" "") @@ -2903,21 +3163,25 @@ "") (define_insn "*ashlqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") - (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") + (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] "" - "* return ashlqi3_out (insn, operands, NULL);" + { + return ashlqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,4,6,9") (set_attr "adjust_len" "ashlqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (define_insn "ashlhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashlhi3_out (insn, operands, NULL);" + { + return ashlhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,2,4,10,10") (set_attr "adjust_len" "ashlhi") (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) @@ -3005,11 +3269,13 @@ (define_insn "ashlsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashlsi3_out (insn, operands, NULL);" + { + return ashlsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,4,8,10,12") (set_attr "adjust_len" "ashlsi") (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) @@ -3063,11 +3329,13 @@ (define_insn "*ashlhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return ashlhi3_out (insn, operands, NULL);" + { + return ashlhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,2,4,10") (set_attr "adjust_len" "ashlhi") (set_attr "cc" "none,set_n,clobber,set_n,clobber")]) @@ -3084,11 +3352,13 @@ (define_insn "*ashlsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return ashlsi3_out (insn, operands, NULL);" + { + return ashlsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "ashlsi") (set_attr "cc" "none,set_n,clobber,clobber")]) @@ -3109,21 +3379,25 @@ ;; arithmetic shift right (define_insn "ashrqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r") - (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r") + (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] "" - "* return ashrqi3_out (insn, operands, NULL);" + { + return ashrqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,5,4,9") (set_attr "adjust_len" "ashrqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")]) (define_insn "ashrhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashrhi3_out (insn, operands, NULL);" + { + return ashrhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,4,4,10,10") (set_attr "adjust_len" "ashrhi") (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) @@ -3141,11 +3415,13 @@ (set_attr "cc" "clobber")]) (define_insn "ashrsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashrsi3_out (insn, operands, NULL);" + { + return ashrsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,6,8,10,12") (set_attr "adjust_len" "ashrsi") (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) @@ -3164,11 +3440,13 @@ (define_insn "*ashrhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return ashrhi3_out (insn, operands, NULL);" + { + return ashrhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,4,4,10") (set_attr "adjust_len" "ashrhi") (set_attr "cc" "none,clobber,set_n,clobber,clobber")]) @@ -3185,11 +3463,13 @@ (define_insn "*ashrsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return ashrsi3_out (insn, operands, NULL);" + { + return ashrsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "ashrsi") (set_attr "cc" "none,clobber,set_n,clobber")]) @@ -3198,11 +3478,9 @@ ;; logical shift right (define_expand "lshrqi3" - [(set (match_operand:QI 0 "register_operand" "") - (lshiftrt:QI (match_operand:QI 1 "register_operand" "") - (match_operand:QI 2 "general_operand" "")))] - "" - "") + [(set (match_operand:QI 0 "register_operand" "") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) (define_split ; lshrqi3_const4 [(set (match_operand:QI 0 "d_register_operand" "") @@ -3234,21 +3512,25 @@ "") (define_insn "*lshrqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") - (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] "" - "* return lshrqi3_out (insn, operands, NULL);" + { + return lshrqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,4,6,9") (set_attr "adjust_len" "lshrqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (define_insn "lshrhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return lshrhi3_out (insn, operands, NULL);" + { + return lshrhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,2,4,10,10") (set_attr "adjust_len" "lshrhi") (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) @@ -3266,11 +3548,13 @@ (set_attr "cc" "clobber")]) (define_insn "lshrsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return lshrsi3_out (insn, operands, NULL);" + { + return lshrsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,4,8,10,12") (set_attr "adjust_len" "lshrsi") (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) @@ -3324,11 +3608,13 @@ (define_insn "*lshrhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return lshrhi3_out (insn, operands, NULL);" + { + return lshrhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,2,4,10") (set_attr "adjust_len" "lshrhi") (set_attr "cc" "none,clobber,clobber,clobber,clobber")]) @@ -3345,11 +3631,13 @@ (define_insn "*lshrsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return lshrsi3_out (insn, operands, NULL);" + { + return lshrsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "lshrsi") (set_attr "cc" "none,clobber,clobber,clobber")]) @@ -3615,6 +3903,27 @@ operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); }) +(define_insn_and_split "n_extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r,r,d,r") + (lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n") + (match_operand:HI 2 "register_operand" "r,r,r,r"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "" + "#" + "reload_completed" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 3) (match_dup 6)) + ; no-op move in the case where no scratch is needed + (set (match_dup 5) (match_dup 3))] + { + operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0); + operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + operands[6] = operands[1]; + + if (GET_CODE (operands[3]) == SCRATCH) + operands[3] = operands[5]; + }) + (define_insn_and_split "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))] @@ -4363,25 +4672,25 @@ (define_insn "*cbi" [(set (mem:QI (match_operand 0 "low_io_address_operand" "n")) - (and:QI (mem:QI (match_dup 0)) - (match_operand:QI 1 "single_zero_operand" "n")))] - "(optimize > 0)" -{ - operands[2] = GEN_INT (exact_log2 (~INTVAL (operands[1]) & 0xff)); - return AS2 (cbi,%m0-0x20,%2); -} + (and:QI (mem:QI (match_dup 0)) + (match_operand:QI 1 "single_zero_operand" "n")))] + "optimize > 0" + { + operands[2] = GEN_INT (exact_log2 (~INTVAL (operands[1]) & 0xff)); + return "cbi %i0,%2"; + } [(set_attr "length" "1") (set_attr "cc" "none")]) (define_insn "*sbi" [(set (mem:QI (match_operand 0 "low_io_address_operand" "n")) - (ior:QI (mem:QI (match_dup 0)) - (match_operand:QI 1 "single_one_operand" "n")))] - "(optimize > 0)" -{ - operands[2] = GEN_INT (exact_log2 (INTVAL (operands[1]) & 0xff)); - return AS2 (sbi,%m0-0x20,%2); -} + (ior:QI (mem:QI (match_dup 0)) + (match_operand:QI 1 "single_one_operand" "n")))] + "optimize > 0" + { + operands[2] = GEN_INT (exact_log2 (INTVAL (operands[1]) & 0xff)); + return "sbi %i0,%2"; + } [(set_attr "length" "1") (set_attr "cc" "none")]) @@ -4796,6 +5105,30 @@ [(set_attr "length" "9") (set_attr "cc" "clobber")]) +(define_insn "map_bitsqi" + [(set (match_operand:QI 0 "register_operand" "=d") + (unspec:QI [(match_operand:SI 1 "const_int_operand" "n") + (match_operand:QI 2 "register_operand" "r")] + UNSPEC_MAP_BITS))] + "" + { + return avr_out_map_bits (insn, operands, NULL); + } + [(set_attr "adjust_len" "map_bits") + (set_attr "cc" "clobber")]) + +(define_insn "map_bitshi" + [(set (match_operand:HI 0 "register_operand" "=&r") + (unspec:HI [(match_operand:DI 1 "const_double_operand" "n") + (match_operand:HI 2 "register_operand" "r")] + UNSPEC_MAP_BITS))] + "" + { + return avr_out_map_bits (insn, operands, NULL); + } + [(set_attr "adjust_len" "map_bits") + (set_attr "cc" "clobber")]) + ;; Parity @@ -5340,9 +5673,9 @@ (match_operand:QI 2 "nonmemory_operand" "L,P,r"))] "" "@ - cbi %m0-0x20,%1 - sbi %m0-0x20,%1 - sbrc %2,0\;sbi %m0-0x20,%1\;sbrs %2,0\;cbi %m0-0x20,%1" + cbi %i0,%1 + sbi %i0,%1 + sbrc %2,0\;sbi %i0,%1\;sbrs %2,0\;cbi %i0,%1" [(set_attr "length" "1,1,4") (set_attr "cc" "none")]) @@ -5352,7 +5685,7 @@ (match_operand:QI 1 "const_0_to_7_operand" "n")) (not:QI (match_operand:QI 2 "register_operand" "r")))] "" - "sbrs %2,0\;sbi %m0-0x20,%1\;sbrc %2,0\;cbi %m0-0x20,%1" + "sbrs %2,0\;sbi %i0,%1\;sbrc %2,0\;cbi %i0,%1" [(set_attr "length" "4") (set_attr "cc" "none")]) diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md index 55a25b8b05a..fff34b58464 100644 --- a/gcc/config/avr/predicates.md +++ b/gcc/config/avr/predicates.md @@ -57,6 +57,18 @@ (and (match_code "const_int") (match_test "IN_RANGE((INTVAL (op)), 0x20, (0x60 - GET_MODE_SIZE(mode)))"))) +;; Return 1 if OP is a general operand not in program memory +(define_predicate "nop_general_operand" + (and (match_operand 0 "general_operand") + (match_test "!avr_mem_pgm_p (op)"))) + +;; Return 1 if OP is an "ordinary" general operand, i.e. a general +;; operand whose load is not handled by a libgcc call or ELPM. +(define_predicate "nox_general_operand" + (and (match_operand 0 "general_operand") + (not (match_test "avr_load_libgcc_p (op)")) + (not (match_test "avr_mem_pgmx_p (op)")))) + ;; Return 1 if OP is the zero constant for MODE. (define_predicate "const0_operand" (and (match_code "const_int,const_double") diff --git a/gcc/config/cris/cris-protos.h b/gcc/config/cris/cris-protos.h index 291981d5707..545f821ac0f 100644 --- a/gcc/config/cris/cris-protos.h +++ b/gcc/config/cris/cris-protos.h @@ -65,5 +65,3 @@ extern int cris_fatal (char *); extern int cris_initial_elimination_offset (int, int); extern void cris_init_expanders (void); - -extern bool cris_function_value_regno_p (const unsigned int); diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c index 06568023051..de9e26984ea 100644 --- a/gcc/config/cris/cris.c +++ b/gcc/config/cris/cris.c @@ -153,6 +153,7 @@ static void cris_trampoline_init (rtx, tree, rtx); static rtx cris_function_value(const_tree, const_tree, bool); static rtx cris_libcall_value (enum machine_mode, const_rtx); +static bool cris_function_value_regno_p (const unsigned int); /* This is the parsed result of the "-max-stack-stackframe=" option. If it (still) is zero, then there was no such option given. */ @@ -252,6 +253,8 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION; #define TARGET_FUNCTION_VALUE cris_function_value #undef TARGET_LIBCALL_VALUE #define TARGET_LIBCALL_VALUE cris_libcall_value +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P cris_function_value_regno_p struct gcc_target targetm = TARGET_INITIALIZER; @@ -3931,7 +3934,7 @@ cris_libcall_value (enum machine_mode mode, /* Let's assume all functions return in r[CRIS_FIRST_ARG_REG] for the time being. */ -bool +static bool cris_function_value_regno_p (const unsigned int regno) { return (regno == CRIS_FIRST_ARG_REG); diff --git a/gcc/config/cris/cris.h b/gcc/config/cris/cris.h index 5a6ebda2e48..ff0be0041f0 100644 --- a/gcc/config/cris/cris.h +++ b/gcc/config/cris/cris.h @@ -712,12 +712,6 @@ struct cum_args {int regs;}; && (REGNO) < CRIS_FIRST_ARG_REG + (CRIS_MAX_ARGS_IN_REGS)) -/* Node: Scalar Return */ - -#define FUNCTION_VALUE_REGNO_P(N) cris_function_value_regno_p (N) - - - /* Node: Aggregate Return */ #define CRIS_STRUCT_VALUE_REGNUM ((CRIS_FIRST_ARG_REG) - 1) diff --git a/gcc/config/darwin-protos.h b/gcc/config/darwin-protos.h index 21092af66e2..e0220864fac 100644 --- a/gcc/config/darwin-protos.h +++ b/gcc/config/darwin-protos.h @@ -58,6 +58,7 @@ extern int machopic_reloc_rw_mask (void); extern section *machopic_select_section (tree, int, unsigned HOST_WIDE_INT); extern section *darwin_function_section (tree, enum node_frequency, bool, bool); +extern section *darwin_tm_clone_table_section (void); extern void darwin_function_switched_text_sections (FILE *, tree, bool); extern void darwin_unique_section (tree decl, int reloc); diff --git a/gcc/config/darwin.c b/gcc/config/darwin.c index 7c33a533fff..b1038f40660 100644 --- a/gcc/config/darwin.c +++ b/gcc/config/darwin.c @@ -1265,6 +1265,14 @@ darwin_mergeable_constant_section (tree exp, return readonly_data_section; } +section * +darwin_tm_clone_table_section (void) +{ + return get_named_section (NULL, + "__DATA,__tm_clone_table,regular,no_dead_strip", + 3); +} + int machopic_reloc_rw_mask (void) { diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h index 0c8f2769abe..29a24ddc6d6 100644 --- a/gcc/config/darwin.h +++ b/gcc/config/darwin.h @@ -359,8 +359,8 @@ extern GTY(()) int darwin_ms_struct; #undef STARTFILE_SPEC #define STARTFILE_SPEC \ - "%{Zdynamiclib: %(darwin_dylib1) } \ - %{!Zdynamiclib:%{Zbundle:%{!static:-lbundle1.o}} \ + "%{Zdynamiclib: %(darwin_dylib1) %{fgnu-tm: -lcrttms.o}} \ + %{!Zdynamiclib:%{Zbundle:%{!static:-lbundle1.o %{fgnu-tm: -lcrttms.o}}} \ %{!Zbundle:%{pg:%{static:-lgcrt0.o} \ %{!static:%{object:-lgcrt0.o} \ %{!object:%{preload:-lgcrt0.o} \ @@ -372,10 +372,8 @@ extern GTY(()) int darwin_ms_struct; %(darwin_crt2)}}}}}} \ %{shared-libgcc:%:version-compare(< 10.5 mmacosx-version-min= crt3.o%s)}" -/* The native Darwin linker doesn't necessarily place files in the order - that they're specified on the link line. Thus, it is pointless - to put anything in ENDFILE_SPEC. */ -/* #define ENDFILE_SPEC "" */ +/* We want a destructor last in the list. */ +#define ENDFILE_SPEC "%{fgnu-tm: -lcrttme.o}" #define DARWIN_EXTRA_SPECS \ { "darwin_crt1", DARWIN_CRT1_SPEC }, \ @@ -388,13 +386,19 @@ extern GTY(()) int darwin_ms_struct; #define DARWIN_CRT1_SPEC \ "%:version-compare(!> 10.5 mmacosx-version-min= -lcrt1.o) \ - %:version-compare(>= 10.5 mmacosx-version-min= -lcrt1.10.5.o)" + %:version-compare(>= 10.5 mmacosx-version-min= -lcrt1.10.5.o) \ + %{fgnu-tm: -lcrttms.o}" /* Default Darwin ASM_SPEC, very simple. */ #define ASM_SPEC "-arch %(darwin_arch) \ %{Zforce_cpusubtype_ALL:-force_cpusubtype_ALL} \ %{static}" +/* Default ASM_DEBUG_SPEC. Darwin's as cannot currently produce dwarf + debugging data. */ + +#define ASM_DEBUG_SPEC "%{g*:%{!g0:%{!gdwarf*:--gstabs}}}" + /* We still allow output of STABS. */ #define DBX_DEBUGGING_INFO 1 @@ -692,6 +696,10 @@ extern GTY(()) section * darwin_sections[NUM_DARWIN_SECTIONS]; #define TARGET_ASM_UNIQUE_SECTION darwin_unique_section #undef TARGET_ASM_FUNCTION_RODATA_SECTION #define TARGET_ASM_FUNCTION_RODATA_SECTION default_no_function_rodata_section + +#undef TARGET_ASM_TM_CLONE_TABLE_SECTION +#define TARGET_ASM_TM_CLONE_TABLE_SECTION darwin_tm_clone_table_section + #undef TARGET_ASM_RELOC_RW_MASK #define TARGET_ASM_RELOC_RW_MASK machopic_reloc_rw_mask diff --git a/gcc/config/darwin9.h b/gcc/config/darwin9.h index 483a7e5269c..92429cae57c 100644 --- a/gcc/config/darwin9.h +++ b/gcc/config/darwin9.h @@ -41,6 +41,12 @@ along with GCC; see the file COPYING3. If not see %{mdynamic-no-pic: %n'-mdynamic-no-pic' overrides '-pie', '-fpie' or '-fPIE'; \ :-pie}}" +/* Only ask as for debug data if the debug style is stabs (since as doesn't + yet generate dwarf.) */ + +#undef ASM_DEBUG_SPEC +#define ASM_DEBUG_SPEC "%{g*:%{!g0:%{gstabs:--gstabs}}}" + #undef ASM_OUTPUT_ALIGNED_COMMON #define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \ do { \ diff --git a/gcc/config/epiphany/epiphany.md b/gcc/config/epiphany/epiphany.md index c8354e8eddb..7f8f2a95231 100644 --- a/gcc/config/epiphany/epiphany.md +++ b/gcc/config/epiphany/epiphany.md @@ -1711,7 +1711,7 @@ cmp_in_mode = GET_MODE (cmp_op0); if (cmp_in_mode == VOIDmode) - cmp_in_mode = GET_MODE (cmp_op0); + cmp_in_mode = GET_MODE (cmp_op1); if (cmp_in_mode == VOIDmode) cmp_in_mode = SImode; /* If the operands are a better match when reversed, swap them now. diff --git a/gcc/config/host-linux.c b/gcc/config/host-linux.c index ec6105577a6..94b7a0b89a7 100644 --- a/gcc/config/host-linux.c +++ b/gcc/config/host-linux.c @@ -86,6 +86,10 @@ # define TRY_EMPTY_VM_SPACE 0x40000000 #elif defined(__ARM_EABI__) # define TRY_EMPTY_VM_SPACE 0x60000000 +#elif defined(__mips__) && defined(__LP64__) +# define TRY_EMPTY_VM_SPACE 0x8000000000 +#elif defined(__mips__) +# define TRY_EMPTY_VM_SPACE 0x60000000 #else # define TRY_EMPTY_VM_SPACE 0 #endif diff --git a/gcc/config/i386/darwin.h b/gcc/config/i386/darwin.h index 705fe98c613..83da2932512 100644 --- a/gcc/config/i386/darwin.h +++ b/gcc/config/i386/darwin.h @@ -132,7 +132,8 @@ extern int darwin_emit_branch_islands; "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ %{mpc32:crtprec32.o%s} \ %{mpc64:crtprec64.o%s} \ - %{mpc80:crtprec80.o%s}" + %{mpc80:crtprec80.o%s} \ + %{fgnu-tm: -lcrttme.o}" #undef SUBTARGET_EXTRA_SPECS #define SUBTARGET_EXTRA_SPECS \ diff --git a/gcc/config/i386/emmintrin.h b/gcc/config/i386/emmintrin.h index fe4cd6abaea..07ac9f3d8ae 100644 --- a/gcc/config/i386/emmintrin.h +++ b/gcc/config/i386/emmintrin.h @@ -1418,6 +1418,14 @@ _mm_stream_si32 (int *__A, int __B) __builtin_ia32_movnti (__A, __B); } +#ifdef __x86_64__ +extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_stream_si64 (long long int *__A, long long int __B) +{ + __builtin_ia32_movnti64 (__A, __B); +} +#endif + extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_stream_si128 (__m128i *__A, __m128i __B) { diff --git a/gcc/config/i386/i386-builtin-types.def b/gcc/config/i386/i386-builtin-types.def index 5dcb68c2d43..d00b05341b9 100644 --- a/gcc/config/i386/i386-builtin-types.def +++ b/gcc/config/i386/i386-builtin-types.def @@ -111,6 +111,7 @@ DEF_POINTER_TYPE (PDOUBLE, DOUBLE) DEF_POINTER_TYPE (PFLOAT, FLOAT) DEF_POINTER_TYPE (PUSHORT, USHORT) DEF_POINTER_TYPE (PINT, INT) +DEF_POINTER_TYPE (PLONGLONG, LONGLONG) DEF_POINTER_TYPE (PULONGLONG, ULONGLONG) DEF_POINTER_TYPE (PUNSIGNED, UNSIGNED) @@ -357,6 +358,7 @@ DEF_FUNCTION_TYPE (VOID, PDOUBLE, V4DF) DEF_FUNCTION_TYPE (VOID, PFLOAT, V4SF) DEF_FUNCTION_TYPE (VOID, PFLOAT, V8SF) DEF_FUNCTION_TYPE (VOID, PINT, INT) +DEF_FUNCTION_TYPE (VOID, PLONGLONG, LONGLONG) DEF_FUNCTION_TYPE (VOID, PULONGLONG, ULONGLONG) DEF_FUNCTION_TYPE (VOID, PV2SI, V2SI) DEF_FUNCTION_TYPE (VOID, PV2DI, V2DI) @@ -465,6 +467,11 @@ DEF_FUNCTION_TYPE_ALIAS (V4DF_FTYPE_V4DF, ROUND) DEF_FUNCTION_TYPE_ALIAS (V4SF_FTYPE_V4SF, ROUND) DEF_FUNCTION_TYPE_ALIAS (V8SF_FTYPE_V8SF, ROUND) +DEF_FUNCTION_TYPE_ALIAS (V4SI_FTYPE_V2DF_V2DF, ROUND) +DEF_FUNCTION_TYPE_ALIAS (V8SI_FTYPE_V4DF_V4DF, ROUND) +DEF_FUNCTION_TYPE_ALIAS (V4SI_FTYPE_V4SF, ROUND) +DEF_FUNCTION_TYPE_ALIAS (V8SI_FTYPE_V8SF, ROUND) + DEF_FUNCTION_TYPE_ALIAS (INT_FTYPE_V2DF_V2DF, PTEST) DEF_FUNCTION_TYPE_ALIAS (INT_FTYPE_V2DI_V2DI, PTEST) DEF_FUNCTION_TYPE_ALIAS (INT_FTYPE_V4DF_V4DF, PTEST) diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h index 07f58b99e35..3cc2253c3c2 100644 --- a/gcc/config/i386/i386-opts.h +++ b/gcc/config/i386/i386-opts.h @@ -37,8 +37,7 @@ enum stringop_alg rep_prefix_8_byte, loop_1_byte, loop, - unrolled_loop, - sse_loop + unrolled_loop }; /* Available call abi. */ diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index a8e8a6a40e1..1b871be8480 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -561,14 +561,10 @@ struct processor_costs ix86_size_cost = {/* costs for tuning for size */ COSTS_N_BYTES (2), /* cost of FABS instruction. */ COSTS_N_BYTES (2), /* cost of FCHS instruction. */ COSTS_N_BYTES (2), /* cost of FSQRT instruction. */ - {{{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, + {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, {rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}}, - {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, - {rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}}}, - {{{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, + {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, {rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}}, - {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, - {rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -636,14 +632,10 @@ struct processor_costs i386_cost = { /* 386 specific costs */ COSTS_N_INSNS (22), /* cost of FABS instruction. */ COSTS_N_INSNS (24), /* cost of FCHS instruction. */ COSTS_N_INSNS (122), /* cost of FSQRT instruction. */ - {{{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, + {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, - DUMMY_STRINGOP_ALGS}}, - {{{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, + {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_1_byte, {{-1, rep_prefix_1_byte}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -712,14 +704,10 @@ struct processor_costs i486_cost = { /* 486 specific costs */ COSTS_N_INSNS (3), /* cost of FABS instruction. */ COSTS_N_INSNS (3), /* cost of FCHS instruction. */ COSTS_N_INSNS (83), /* cost of FSQRT instruction. */ - {{{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, + {{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, - DUMMY_STRINGOP_ALGS}}, - {{{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, + {{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_4_byte, {{-1, rep_prefix_4_byte}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -786,14 +774,10 @@ struct processor_costs pentium_cost = { COSTS_N_INSNS (1), /* cost of FABS instruction. */ COSTS_N_INSNS (1), /* cost of FCHS instruction. */ COSTS_N_INSNS (70), /* cost of FSQRT instruction. */ - {{{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, - {{{libcall, {{-1, rep_prefix_4_byte}}}, + {{libcall, {{-1, rep_prefix_4_byte}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{-1, rep_prefix_4_byte}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -865,18 +849,12 @@ struct processor_costs pentiumpro_cost = { noticeable win, for bigger blocks either rep movsl or rep movsb is way to go. Rep movsb has apparently more expensive startup time in CPU, but after 4K the difference is down in the noise. */ - {{{rep_prefix_4_byte, {{128, loop}, {1024, unrolled_loop}, + {{rep_prefix_4_byte, {{128, loop}, {1024, unrolled_loop}, {8192, rep_prefix_4_byte}, {-1, rep_prefix_1_byte}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_4_byte, {{128, loop}, {1024, unrolled_loop}, - {8192, rep_prefix_4_byte}, {-1, rep_prefix_1_byte}}}, - DUMMY_STRINGOP_ALGS}}, - {{{rep_prefix_4_byte, {{1024, unrolled_loop}, - {8192, rep_prefix_4_byte}, {-1, libcall}}}, + {{rep_prefix_4_byte, {{1024, unrolled_loop}, + {8192, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{rep_prefix_4_byte, {{1024, unrolled_loop}, - {8192, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -944,14 +922,10 @@ struct processor_costs geode_cost = { COSTS_N_INSNS (1), /* cost of FABS instruction. */ COSTS_N_INSNS (1), /* cost of FCHS instruction. */ COSTS_N_INSNS (54), /* cost of FSQRT instruction. */ - {{{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, - {{{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1021,14 +995,10 @@ struct processor_costs k6_cost = { COSTS_N_INSNS (2), /* cost of FABS instruction. */ COSTS_N_INSNS (2), /* cost of FCHS instruction. */ COSTS_N_INSNS (56), /* cost of FSQRT instruction. */ - {{{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, - {{{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{256, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1098,14 +1068,10 @@ struct processor_costs athlon_cost = { /* For some reason, Athlon deals better with REP prefix (relative to loops) compared to K8. Alignment becomes important after 8 bytes for memcpy and 128 bytes for memset. */ - {{{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, - {{{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{2048, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1180,16 +1146,11 @@ struct processor_costs k8_cost = { /* K8 has optimized REP instruction for medium sized blocks, but for very small blocks it is better to use loop. For large blocks, libcall can do nontemporary accesses and beat inline considerably. */ - {{{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, + {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, - {{{libcall, {{8, loop}, {24, unrolled_loop}, + {{libcall, {{8, loop}, {24, unrolled_loop}, {2048, rep_prefix_4_byte}, {-1, libcall}}}, {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{8, loop}, {24, unrolled_loop}, - {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, 4, /* scalar_stmt_cost. */ 2, /* scalar load_cost. */ 2, /* scalar_store_cost. */ @@ -1272,16 +1233,11 @@ struct processor_costs amdfam10_cost = { /* AMDFAM10 has optimized REP instruction for medium sized blocks, but for very small blocks it is better to use loop. For large blocks, libcall can do nontemporary accesses and beat inline considerably. */ - {{{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {512, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {512, rep_prefix_8_byte}, {-1, libcall}}}}}, - {{{libcall, {{8, loop}, {24, unrolled_loop}, + {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, + {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, + {{libcall, {{8, loop}, {24, unrolled_loop}, {2048, rep_prefix_4_byte}, {-1, libcall}}}, {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{8, loop}, {24, unrolled_loop}, - {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, 4, /* scalar_stmt_cost. */ 2, /* scalar load_cost. */ 2, /* scalar_store_cost. */ @@ -1364,16 +1320,11 @@ struct processor_costs bdver1_cost = { /* BDVER1 has optimized REP instruction for medium sized blocks, but for very small blocks it is better to use loop. For large blocks, libcall can do nontemporary accesses and beat inline considerably. */ - {{{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, + {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, - {{{libcall, {{8, loop}, {24, unrolled_loop}, + {{libcall, {{8, loop}, {24, unrolled_loop}, {2048, rep_prefix_4_byte}, {-1, libcall}}}, {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{8, loop}, {24, unrolled_loop}, - {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, 6, /* scalar_stmt_cost. */ 4, /* scalar load_cost. */ 4, /* scalar_store_cost. */ @@ -1456,16 +1407,11 @@ struct processor_costs bdver2_cost = { /* BDVER2 has optimized REP instruction for medium sized blocks, but for very small blocks it is better to use loop. For large blocks, libcall can do nontemporary accesses and beat inline considerably. */ - {{{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, - {{{libcall, {{8, loop}, {24, unrolled_loop}, - {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, + {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, {{libcall, {{8, loop}, {24, unrolled_loop}, {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, + {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, 6, /* scalar_stmt_cost. */ 4, /* scalar load_cost. */ 4, /* scalar_store_cost. */ @@ -1543,16 +1489,11 @@ struct processor_costs btver1_cost = { /* BTVER1 has optimized REP instruction for medium sized blocks, but for very small blocks it is better to use loop. For large blocks, libcall can do nontemporary accesses and beat inline considerably. */ - {{{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, + {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{6, loop}, {14, unrolled_loop}, {-1, rep_prefix_4_byte}}}, - {libcall, {{16, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, - {{{libcall, {{8, loop}, {24, unrolled_loop}, + {{libcall, {{8, loop}, {24, unrolled_loop}, {2048, rep_prefix_4_byte}, {-1, libcall}}}, {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{8, loop}, {24, unrolled_loop}, - {2048, rep_prefix_4_byte}, {-1, libcall}}}, - {libcall, {{48, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, 4, /* scalar_stmt_cost. */ 2, /* scalar load_cost. */ 2, /* scalar_store_cost. */ @@ -1619,18 +1560,11 @@ struct processor_costs pentium4_cost = { COSTS_N_INSNS (2), /* cost of FABS instruction. */ COSTS_N_INSNS (2), /* cost of FCHS instruction. */ COSTS_N_INSNS (43), /* cost of FSQRT instruction. */ - - {{{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, + {{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, - DUMMY_STRINGOP_ALGS}}, - - {{{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, + {{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, - {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1697,22 +1631,13 @@ struct processor_costs nocona_cost = { COSTS_N_INSNS (3), /* cost of FABS instruction. */ COSTS_N_INSNS (3), /* cost of FCHS instruction. */ COSTS_N_INSNS (44), /* cost of FSQRT instruction. */ - - {{{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, + {{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, {libcall, {{32, loop}, {20000, rep_prefix_8_byte}, {100000, unrolled_loop}, {-1, libcall}}}}, - {{libcall, {{12, loop_1_byte}, {-1, rep_prefix_4_byte}}}, - {libcall, {{32, loop}, {20000, rep_prefix_8_byte}, - {100000, unrolled_loop}, {-1, libcall}}}}}, - - {{{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, + {{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, {-1, libcall}}}, {libcall, {{24, loop}, {64, unrolled_loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{6, loop_1_byte}, {48, loop}, {20480, rep_prefix_4_byte}, - {-1, libcall}}}, - {libcall, {{24, loop}, {64, unrolled_loop}, - {8192, rep_prefix_8_byte}, {-1, libcall}}}}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1779,108 +1704,13 @@ struct processor_costs atom_cost = { COSTS_N_INSNS (8), /* cost of FABS instruction. */ COSTS_N_INSNS (8), /* cost of FCHS instruction. */ COSTS_N_INSNS (40), /* cost of FSQRT instruction. */ - - /* stringop_algs for memcpy. - SSE loops works best on Atom, but fall back into non-SSE unrolled loop variant - if that fails. */ - {{{libcall, {{4096, sse_loop}, {4096, unrolled_loop}, {-1, libcall}}}, /* Known alignment. */ - {libcall, {{4096, sse_loop}, {4096, unrolled_loop}, {-1, libcall}}}}, - {{libcall, {{-1, libcall}}}, /* Unknown alignment. */ - {libcall, {{2048, sse_loop}, {2048, unrolled_loop}, - {-1, libcall}}}}}, - - /* stringop_algs for memset. */ - {{{libcall, {{4096, sse_loop}, {4096, unrolled_loop}, {-1, libcall}}}, /* Known alignment. */ - {libcall, {{4096, sse_loop}, {4096, unrolled_loop}, {-1, libcall}}}}, - {{libcall, {{1024, sse_loop}, {1024, unrolled_loop}, /* Unknown alignment. */ - {-1, libcall}}}, - {libcall, {{2048, sse_loop}, {2048, unrolled_loop}, - {-1, libcall}}}}}, - 1, /* scalar_stmt_cost. */ - 1, /* scalar load_cost. */ - 1, /* scalar_store_cost. */ - 1, /* vec_stmt_cost. */ - 1, /* vec_to_scalar_cost. */ - 1, /* scalar_to_vec_cost. */ - 1, /* vec_align_load_cost. */ - 2, /* vec_unalign_load_cost. */ - 1, /* vec_store_cost. */ - 3, /* cond_taken_branch_cost. */ - 1, /* cond_not_taken_branch_cost. */ -}; - -/* Core should produce code tuned for core variants. */ -static const -struct processor_costs core_cost = { - COSTS_N_INSNS (1), /* cost of an add instruction */ - /* On all chips taken into consideration lea is 2 cycles and more. With - this cost however our current implementation of synth_mult results in - use of unnecessary temporary registers causing regression on several - SPECfp benchmarks. */ - COSTS_N_INSNS (1) + 1, /* cost of a lea instruction */ - COSTS_N_INSNS (1), /* variable shift costs */ - COSTS_N_INSNS (1), /* constant shift costs */ - {COSTS_N_INSNS (3), /* cost of starting multiply for QI */ - COSTS_N_INSNS (4), /* HI */ - COSTS_N_INSNS (3), /* SI */ - COSTS_N_INSNS (4), /* DI */ - COSTS_N_INSNS (2)}, /* other */ - 0, /* cost of multiply per each bit set */ - {COSTS_N_INSNS (18), /* cost of a divide/mod for QI */ - COSTS_N_INSNS (26), /* HI */ - COSTS_N_INSNS (42), /* SI */ - COSTS_N_INSNS (74), /* DI */ - COSTS_N_INSNS (74)}, /* other */ - COSTS_N_INSNS (1), /* cost of movsx */ - COSTS_N_INSNS (1), /* cost of movzx */ - 8, /* "large" insn */ - 17, /* MOVE_RATIO */ - 4, /* cost for loading QImode using movzbl */ - {4, 4, 4}, /* cost of loading integer registers - in QImode, HImode and SImode. - Relative to reg-reg move (2). */ - {4, 4, 4}, /* cost of storing integer registers */ - 4, /* cost of reg,reg fld/fst */ - {12, 12, 12}, /* cost of loading fp registers - in SFmode, DFmode and XFmode */ - {6, 6, 8}, /* cost of storing fp registers - in SFmode, DFmode and XFmode */ - 2, /* cost of moving MMX register */ - {8, 8}, /* cost of loading MMX registers - in SImode and DImode */ - {8, 8}, /* cost of storing MMX registers - in SImode and DImode */ - 2, /* cost of moving SSE register */ - {8, 8, 8}, /* cost of loading SSE registers - in SImode, DImode and TImode */ - {8, 8, 8}, /* cost of storing SSE registers - in SImode, DImode and TImode */ - 5, /* MMX or SSE register to integer */ - 32, /* size of l1 cache. */ - 512, /* size of l2 cache. */ - 64, /* size of prefetch block */ - 6, /* number of parallel prefetches */ - /* Benchmarks shows large regressions on K8 sixtrack benchmark when this - value is increased to perhaps more appropriate value of 5. */ - 3, /* Branch cost */ - COSTS_N_INSNS (8), /* cost of FADD and FSUB insns. */ - COSTS_N_INSNS (8), /* cost of FMUL instruction. */ - COSTS_N_INSNS (20), /* cost of FDIV instruction. */ - COSTS_N_INSNS (8), /* cost of FABS instruction. */ - COSTS_N_INSNS (8), /* cost of FCHS instruction. */ - COSTS_N_INSNS (40), /* cost of FSQRT instruction. */ - - /* stringop_algs for memcpy. */ - {{{libcall, {{16, loop}, {24, unrolled_loop}, {1024, rep_prefix_4_byte}, {-1, libcall}}}, /* Known alignment. */ - {libcall, {{16, loop}, {24, unrolled_loop}, {1024, rep_prefix_8_byte}, {-1, libcall}}}}, - {{libcall, {{16, loop}, {24, unrolled_loop}, {1024, rep_prefix_4_byte}, {-1, libcall}}}, /* Unknown alignment. */ - {libcall, {{16, loop}, {24, unrolled_loop}, {1024, rep_prefix_8_byte}, {-1, libcall}}}}}, - - /* stringop_algs for memset. */ - {{{libcall, {{256, rep_prefix_4_byte}}}, /* Known alignment. */ - {libcall, {{256, rep_prefix_8_byte}}}}, - {{libcall, {{256, rep_prefix_4_byte}}}, /* Unknown alignment. */ - {libcall, {{256, rep_prefix_8_byte}}}}}, + {{libcall, {{11, loop}, {-1, rep_prefix_4_byte}}}, + {libcall, {{32, loop}, {64, rep_prefix_4_byte}, + {8192, rep_prefix_8_byte}, {-1, libcall}}}}, + {{libcall, {{8, loop}, {15, unrolled_loop}, + {2048, rep_prefix_4_byte}, {-1, libcall}}}, + {libcall, {{24, loop}, {32, unrolled_loop}, + {8192, rep_prefix_8_byte}, {-1, libcall}}}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1894,7 +1724,7 @@ struct processor_costs core_cost = { 1, /* cond_not_taken_branch_cost. */ }; -/* Generic64 should produce code tuned for Nocona, Core, K8, Amdfam10 and buldozer. */ +/* Generic64 should produce code tuned for Nocona and K8. */ static const struct processor_costs generic64_cost = { COSTS_N_INSNS (1), /* cost of an add instruction */ @@ -1954,16 +1784,10 @@ struct processor_costs generic64_cost = { COSTS_N_INSNS (8), /* cost of FABS instruction. */ COSTS_N_INSNS (8), /* cost of FCHS instruction. */ COSTS_N_INSNS (40), /* cost of FSQRT instruction. */ - - {{DUMMY_STRINGOP_ALGS, - {libcall, {{16, rep_prefix_4_byte}, {128, rep_prefix_8_byte}, {4096, rep_prefix_1_byte}, {-1, libcall}}}}, - {DUMMY_STRINGOP_ALGS, - {libcall, {{128, rep_prefix_4_byte}, {4096, rep_prefix_1_byte}, {-1, libcall}}}}}, - - {{DUMMY_STRINGOP_ALGS, - {libcall, {{16, rep_prefix_4_byte}, {512, unrolled_loop}, {4096, rep_prefix_1_byte}, {-1, libcall}}}}, - {DUMMY_STRINGOP_ALGS, - {libcall, {{16, rep_prefix_4_byte}, {512, unrolled_loop}, {4096, rep_prefix_1_byte}, {-1, libcall}}}}}, + {DUMMY_STRINGOP_ALGS, + {libcall, {{32, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, + {DUMMY_STRINGOP_ALGS, + {libcall, {{32, loop}, {8192, rep_prefix_8_byte}, {-1, libcall}}}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -1977,8 +1801,8 @@ struct processor_costs generic64_cost = { 1, /* cond_not_taken_branch_cost. */ }; -/* Generic32 should produce code tuned for PPro, Pentium4, Nocona, Core - Athlon, K8, amdfam10, buldozer. */ +/* Generic32 should produce code tuned for PPro, Pentium4, Nocona, + Athlon and K8. */ static const struct processor_costs generic32_cost = { COSTS_N_INSNS (1), /* cost of an add instruction */ @@ -2032,16 +1856,10 @@ struct processor_costs generic32_cost = { COSTS_N_INSNS (8), /* cost of FABS instruction. */ COSTS_N_INSNS (8), /* cost of FCHS instruction. */ COSTS_N_INSNS (40), /* cost of FSQRT instruction. */ - /* stringop_algs for memcpy. */ - {{{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, - /* stringop_algs for memset. */ - {{{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, + {{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, DUMMY_STRINGOP_ALGS}, - {{libcall, {{32, loop}, {8192, rep_prefix_4_byte}, {-1, libcall}}}, - DUMMY_STRINGOP_ALGS}}, 1, /* scalar_stmt_cost. */ 1, /* scalar load_cost. */ 1, /* scalar_store_cost. */ @@ -2718,8 +2536,6 @@ static void ix86_set_current_function (tree); static unsigned int ix86_minimum_incoming_stack_boundary (bool); static enum calling_abi ix86_function_abi (const_tree); -static rtx promote_duplicated_reg (enum machine_mode, rtx); -static rtx promote_duplicated_reg_to_size (rtx, int, int, int); #ifndef SUBTARGET32_DEFAULT_CPU @@ -2766,13 +2582,13 @@ static const struct ptt processor_target_table[PROCESSOR_max] = {&k8_cost, 16, 7, 16, 7, 16}, {&nocona_cost, 0, 0, 0, 0, 0}, /* Core 2 32-bit. */ - {&core_cost, 16, 10, 16, 10, 16}, + {&generic32_cost, 16, 10, 16, 10, 16}, /* Core 2 64-bit. */ - {&core_cost, 16, 10, 16, 10, 16}, + {&generic64_cost, 16, 10, 16, 10, 16}, /* Core i7 32-bit. */ - {&core_cost, 16, 10, 16, 10, 16}, + {&generic32_cost, 16, 10, 16, 10, 16}, /* Core i7 64-bit. */ - {&core_cost, 16, 10, 16, 10, 16}, + {&generic64_cost, 16, 10, 16, 10, 16}, {&generic32_cost, 16, 7, 16, 7, 16}, {&generic64_cost, 16, 10, 16, 10, 16}, {&amdfam10_cost, 32, 24, 32, 7, 32}, @@ -16457,7 +16273,6 @@ distance_non_agu_define_in_bb (unsigned int regno1, unsigned int regno2, basic_block bb = start ? BLOCK_FOR_INSN (start) : NULL; rtx prev = start; rtx next = NULL; - enum attr_type insn_type; *found = false; @@ -16470,8 +16285,8 @@ distance_non_agu_define_in_bb (unsigned int regno1, unsigned int regno2, distance = increase_distance (prev, next, distance); if (insn_defines_reg (regno1, regno2, prev)) { - insn_type = get_attr_type (prev); - if (insn_type != TYPE_LEA) + if (recog_memoized (prev) < 0 + || get_attr_type (prev) != TYPE_LEA) { *found = true; return distance; @@ -19619,8 +19434,12 @@ ix86_expand_int_vcond (rtx operands[]) cop0 = operands[4]; cop1 = operands[5]; - /* XOP supports all of the comparisons on all vector int types. */ - if (!TARGET_XOP) + /* XOP supports all of the comparisons on all 128-bit vector int types. */ + if (TARGET_XOP + && (mode == V16QImode || mode == V8HImode + || mode == V4SImode || mode == V2DImode)) + ; + else { /* Canonicalize the comparison to EQ, GT, GTU. */ switch (code) @@ -21107,37 +20926,22 @@ counter_mode (rtx count_exp) return SImode; } -/* Helper function for expand_set_or_movmem_via_loop. - - When SRCPTR is non-NULL, output simple loop to move memory +/* When SRCPTR is non-NULL, output simple loop to move memory pointer to SRCPTR to DESTPTR via chunks of MODE unrolled UNROLL times, overall size is COUNT specified in bytes. When SRCPTR is NULL, output the equivalent loop to set memory by VALUE (supposed to be in MODE). The size is rounded down to whole number of chunk size moved at once. - SRCMEM and DESTMEM provide MEMrtx to feed proper aliasing info. - - If ITER isn't NULL, than it'll be used in the generated loop without - initialization (that allows to generate several consequent loops using the - same iterator). - If CHANGE_PTRS is specified, DESTPTR and SRCPTR would be increased by - iterator value at the end of the function (as if they iterate in the loop). - Otherwise, their vaules'll stay unchanged. + SRCMEM and DESTMEM provide MEMrtx to feed proper aliasing info. */ - If EXPECTED_SIZE isn't -1, than it's used to compute branch-probabilities on - the loop backedge. When expected size is unknown (it's -1), the probability - is set to 80%. - Return value is rtx of iterator, used in the loop - it could be reused in - consequent calls of this function. */ -static rtx -expand_set_or_movmem_via_loop_with_iter (rtx destmem, rtx srcmem, - rtx destptr, rtx srcptr, rtx value, - rtx count, rtx iter, - enum machine_mode mode, int unroll, - int expected_size, bool change_ptrs) +static void +expand_set_or_movmem_via_loop (rtx destmem, rtx srcmem, + rtx destptr, rtx srcptr, rtx value, + rtx count, enum machine_mode mode, int unroll, + int expected_size) { - rtx out_label, top_label, tmp; + rtx out_label, top_label, iter, tmp; enum machine_mode iter_mode = counter_mode (count); rtx piece_size = GEN_INT (GET_MODE_SIZE (mode) * unroll); rtx piece_size_mask = GEN_INT (~((GET_MODE_SIZE (mode) * unroll) - 1)); @@ -21145,12 +20949,10 @@ expand_set_or_movmem_via_loop_with_iter (rtx destmem, rtx srcmem, rtx x_addr; rtx y_addr; int i; - bool reuse_iter = (iter != NULL_RTX); top_label = gen_label_rtx (); out_label = gen_label_rtx (); - if (!reuse_iter) - iter = gen_reg_rtx (iter_mode); + iter = gen_reg_rtx (iter_mode); size = expand_simple_binop (iter_mode, AND, count, piece_size_mask, NULL, 1, OPTAB_DIRECT); @@ -21161,21 +20963,18 @@ expand_set_or_movmem_via_loop_with_iter (rtx destmem, rtx srcmem, true, out_label); predict_jump (REG_BR_PROB_BASE * 10 / 100); } - if (!reuse_iter) - emit_move_insn (iter, const0_rtx); + emit_move_insn (iter, const0_rtx); emit_label (top_label); tmp = convert_modes (Pmode, iter_mode, iter, true); x_addr = gen_rtx_PLUS (Pmode, destptr, tmp); - destmem = - adjust_automodify_address_nv (copy_rtx (destmem), mode, x_addr, 0); + destmem = change_address (destmem, mode, x_addr); if (srcmem) { y_addr = gen_rtx_PLUS (Pmode, srcptr, copy_rtx (tmp)); - srcmem = - adjust_automodify_address_nv (copy_rtx (srcmem), mode, y_addr, 0); + srcmem = change_address (srcmem, mode, y_addr); /* When unrolling for chips that reorder memory reads and writes, we can save registers by using single temporary. @@ -21247,43 +21046,19 @@ expand_set_or_movmem_via_loop_with_iter (rtx destmem, rtx srcmem, } else predict_jump (REG_BR_PROB_BASE * 80 / 100); - if (change_ptrs) + iter = ix86_zero_extend_to_Pmode (iter); + tmp = expand_simple_binop (Pmode, PLUS, destptr, iter, destptr, + true, OPTAB_LIB_WIDEN); + if (tmp != destptr) + emit_move_insn (destptr, tmp); + if (srcptr) { - iter = ix86_zero_extend_to_Pmode (iter); - tmp = expand_simple_binop (Pmode, PLUS, destptr, iter, destptr, + tmp = expand_simple_binop (Pmode, PLUS, srcptr, iter, srcptr, true, OPTAB_LIB_WIDEN); - if (tmp != destptr) - emit_move_insn (destptr, tmp); - if (srcptr) - { - tmp = expand_simple_binop (Pmode, PLUS, srcptr, iter, srcptr, - true, OPTAB_LIB_WIDEN); - if (tmp != srcptr) - emit_move_insn (srcptr, tmp); - } + if (tmp != srcptr) + emit_move_insn (srcptr, tmp); } emit_label (out_label); - return iter; -} - -/* When SRCPTR is non-NULL, output simple loop to move memory - pointer to SRCPTR to DESTPTR via chunks of MODE unrolled UNROLL times, - overall size is COUNT specified in bytes. When SRCPTR is NULL, output the - equivalent loop to set memory by VALUE (supposed to be in MODE). - - The size is rounded down to whole number of chunk size moved at once. - SRCMEM and DESTMEM provide MEMrtx to feed proper aliasing info. */ - -static void -expand_set_or_movmem_via_loop (rtx destmem, rtx srcmem, - rtx destptr, rtx srcptr, rtx value, - rtx count, enum machine_mode mode, int unroll, - int expected_size) -{ - expand_set_or_movmem_via_loop_with_iter (destmem, srcmem, - destptr, srcptr, value, - count, NULL_RTX, mode, unroll, - expected_size, true); } /* Output "rep; mov" instruction. @@ -21387,18 +21162,7 @@ emit_strmov (rtx destmem, rtx srcmem, emit_insn (gen_strmov (destptr, dest, srcptr, src)); } -/* Emit strset instuction. If RHS is constant, and vector mode will be used, - then move this constant to a vector register before emitting strset. */ -static void -emit_strset (rtx destmem, rtx value, - rtx destptr, enum machine_mode mode, int offset) -{ - rtx dest = adjust_automodify_address_nv (destmem, mode, destptr, offset); - emit_insn (gen_strset (destptr, dest, value)); -} - -/* Output code to copy (COUNT % MAX_SIZE) bytes from SRCPTR to DESTPTR. - SRCMEM and DESTMEM provide MEMrtx to feed proper aliasing info. */ +/* Output code to copy at most count & (max_size - 1) bytes from SRC to DEST. */ static void expand_movmem_epilogue (rtx destmem, rtx srcmem, rtx destptr, rtx srcptr, rtx count, int max_size) @@ -21409,55 +21173,43 @@ expand_movmem_epilogue (rtx destmem, rtx srcmem, HOST_WIDE_INT countval = INTVAL (count); int offset = 0; - int remainder_size = countval % max_size; - enum machine_mode move_mode = Pmode; - - /* Firstly, try to move data with the widest possible mode. - Remaining part we'll move using Pmode and narrower modes. */ - if (TARGET_SSE) - { - if (max_size >= GET_MODE_SIZE (V4SImode)) - move_mode = V4SImode; - else if (max_size >= GET_MODE_SIZE (DImode)) - move_mode = DImode; - } - - while (remainder_size >= GET_MODE_SIZE (move_mode)) + if ((countval & 0x10) && max_size > 16) { - emit_strmov (destmem, srcmem, destptr, srcptr, move_mode, offset); - offset += GET_MODE_SIZE (move_mode); - remainder_size -= GET_MODE_SIZE (move_mode); + if (TARGET_64BIT) + { + emit_strmov (destmem, srcmem, destptr, srcptr, DImode, offset); + emit_strmov (destmem, srcmem, destptr, srcptr, DImode, offset + 8); + } + else + gcc_unreachable (); + offset += 16; } - - /* Move the remaining part of epilogue - its size might be - a size of the widest mode. */ - move_mode = Pmode; - while (remainder_size >= GET_MODE_SIZE (move_mode)) + if ((countval & 0x08) && max_size > 8) { - emit_strmov (destmem, srcmem, destptr, srcptr, move_mode, offset); - offset += GET_MODE_SIZE (move_mode); - remainder_size -= GET_MODE_SIZE (move_mode); + if (TARGET_64BIT) + emit_strmov (destmem, srcmem, destptr, srcptr, DImode, offset); + else + { + emit_strmov (destmem, srcmem, destptr, srcptr, SImode, offset); + emit_strmov (destmem, srcmem, destptr, srcptr, SImode, offset + 4); + } + offset += 8; } - - if (remainder_size >= 4) + if ((countval & 0x04) && max_size > 4) { - emit_strmov (destmem, srcmem, destptr, srcptr, SImode, offset); + emit_strmov (destmem, srcmem, destptr, srcptr, SImode, offset); offset += 4; - remainder_size -= 4; } - if (remainder_size >= 2) + if ((countval & 0x02) && max_size > 2) { - emit_strmov (destmem, srcmem, destptr, srcptr, HImode, offset); + emit_strmov (destmem, srcmem, destptr, srcptr, HImode, offset); offset += 2; - remainder_size -= 2; } - if (remainder_size >= 1) + if ((countval & 0x01) && max_size > 1) { - emit_strmov (destmem, srcmem, destptr, srcptr, QImode, offset); + emit_strmov (destmem, srcmem, destptr, srcptr, QImode, offset); offset += 1; - remainder_size -= 1; } - gcc_assert (remainder_size == 0); return; } if (max_size > 8) @@ -21563,121 +21315,87 @@ expand_setmem_epilogue_via_loop (rtx destmem, rtx destptr, rtx value, 1, max_size / 2); } -/* Output code to set with VALUE at most (COUNT % MAX_SIZE) bytes starting from - DESTPTR. - DESTMEM provides MEMrtx to feed proper aliasing info. - PROMOTED_TO_GPR_VALUE is rtx representing a GPR containing broadcasted VALUE. - PROMOTED_TO_VECTOR_VALUE is rtx representing a vector register containing - broadcasted VALUE. - PROMOTED_TO_GPR_VALUE and PROMOTED_TO_VECTOR_VALUE could be NULL if the - promotion hasn't been generated before. */ +/* Output code to set at most count & (max_size - 1) bytes starting by DEST. */ static void -expand_setmem_epilogue (rtx destmem, rtx destptr, rtx promoted_to_vector_value, - rtx promoted_to_gpr_value, rtx value, rtx count, - int max_size) +expand_setmem_epilogue (rtx destmem, rtx destptr, rtx value, rtx count, int max_size) { + rtx dest; + if (CONST_INT_P (count)) { HOST_WIDE_INT countval = INTVAL (count); int offset = 0; - int remainder_size = countval % max_size; - enum machine_mode move_mode = Pmode; - - /* Firstly, try to move data with the widest possible mode. - Remaining part we'll move using Pmode and narrower modes. */ - - if (promoted_to_vector_value) - while (remainder_size >= 16) - { - if (GET_MODE (destmem) != move_mode) - destmem = adjust_automodify_address_nv (destmem, move_mode, - destptr, offset); - emit_strset (destmem, promoted_to_vector_value, destptr, - move_mode, offset); - - offset += 16; - remainder_size -= 16; - } - - /* Move the remaining part of epilogue - its size might be - a size of the widest mode. */ - while (remainder_size >= GET_MODE_SIZE (Pmode)) + if ((countval & 0x10) && max_size > 16) { - if (!promoted_to_gpr_value) - promoted_to_gpr_value = promote_duplicated_reg (Pmode, value); - emit_strset (destmem, promoted_to_gpr_value, destptr, Pmode, offset); - offset += GET_MODE_SIZE (Pmode); - remainder_size -= GET_MODE_SIZE (Pmode); + if (TARGET_64BIT) + { + dest = adjust_automodify_address_nv (destmem, DImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, value)); + dest = adjust_automodify_address_nv (destmem, DImode, destptr, offset + 8); + emit_insn (gen_strset (destptr, dest, value)); + } + else + gcc_unreachable (); + offset += 16; } - - if (!promoted_to_gpr_value && remainder_size > 1) - promoted_to_gpr_value = promote_duplicated_reg (remainder_size >= 4 - ? SImode : HImode, value); - if (remainder_size >= 4) + if ((countval & 0x08) && max_size > 8) { - emit_strset (destmem, gen_lowpart (SImode, promoted_to_gpr_value), destptr, - SImode, offset); + if (TARGET_64BIT) + { + dest = adjust_automodify_address_nv (destmem, DImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, value)); + } + else + { + dest = adjust_automodify_address_nv (destmem, SImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, value)); + dest = adjust_automodify_address_nv (destmem, SImode, destptr, offset + 4); + emit_insn (gen_strset (destptr, dest, value)); + } + offset += 8; + } + if ((countval & 0x04) && max_size > 4) + { + dest = adjust_automodify_address_nv (destmem, SImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, gen_lowpart (SImode, value))); offset += 4; - remainder_size -= 4; } - if (remainder_size >= 2) + if ((countval & 0x02) && max_size > 2) { - emit_strset (destmem, gen_lowpart (HImode, promoted_to_gpr_value), destptr, - HImode, offset); - offset +=2; - remainder_size -= 2; + dest = adjust_automodify_address_nv (destmem, HImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, gen_lowpart (HImode, value))); + offset += 2; } - if (remainder_size >= 1) + if ((countval & 0x01) && max_size > 1) { - emit_strset (destmem, - promoted_to_gpr_value ? gen_lowpart (QImode, promoted_to_gpr_value) : value, - destptr, - QImode, offset); + dest = adjust_automodify_address_nv (destmem, QImode, destptr, offset); + emit_insn (gen_strset (destptr, dest, gen_lowpart (QImode, value))); offset += 1; - remainder_size -= 1; } - gcc_assert (remainder_size == 0); return; } - - /* count isn't const. */ if (max_size > 32) { - expand_setmem_epilogue_via_loop (destmem, destptr, value, count, - max_size); + expand_setmem_epilogue_via_loop (destmem, destptr, value, count, max_size); return; } - - if (!promoted_to_gpr_value) - promoted_to_gpr_value = promote_duplicated_reg_to_size (value, - GET_MODE_SIZE (Pmode), - GET_MODE_SIZE (Pmode), - GET_MODE_SIZE (Pmode)); - if (max_size > 16) { rtx label = ix86_expand_aligntest (count, 16, true); - if (TARGET_SSE && promoted_to_vector_value) - { - destmem = change_address (destmem, - GET_MODE (promoted_to_vector_value), - destptr); - emit_insn (gen_strset (destptr, destmem, promoted_to_vector_value)); - } - else if (TARGET_64BIT) + if (TARGET_64BIT) { - destmem = change_address (destmem, DImode, destptr); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); + dest = change_address (destmem, DImode, destptr); + emit_insn (gen_strset (destptr, dest, value)); + emit_insn (gen_strset (destptr, dest, value)); } else { - destmem = change_address (destmem, SImode, destptr); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); + dest = change_address (destmem, SImode, destptr); + emit_insn (gen_strset (destptr, dest, value)); + emit_insn (gen_strset (destptr, dest, value)); + emit_insn (gen_strset (destptr, dest, value)); + emit_insn (gen_strset (destptr, dest, value)); } emit_label (label); LABEL_NUSES (label) = 1; @@ -21687,22 +21405,14 @@ expand_setmem_epilogue (rtx destmem, rtx destptr, rtx promoted_to_vector_value, rtx label = ix86_expand_aligntest (count, 8, true); if (TARGET_64BIT) { - destmem = change_address (destmem, DImode, destptr); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - } - /* FIXME: When this hunk it output, IRA classifies promoted_to_vector_value - as NO_REGS. */ - else if (TARGET_SSE && promoted_to_vector_value && 0) - { - destmem = change_address (destmem, V2SImode, destptr); - emit_insn (gen_strset (destptr, destmem, - gen_lowpart (V2SImode, promoted_to_vector_value))); + dest = change_address (destmem, DImode, destptr); + emit_insn (gen_strset (destptr, dest, value)); } else { - destmem = change_address (destmem, SImode, destptr); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); - emit_insn (gen_strset (destptr, destmem, promoted_to_gpr_value)); + dest = change_address (destmem, SImode, destptr); + emit_insn (gen_strset (destptr, dest, value)); + emit_insn (gen_strset (destptr, dest, value)); } emit_label (label); LABEL_NUSES (label) = 1; @@ -21710,27 +21420,24 @@ expand_setmem_epilogue (rtx destmem, rtx destptr, rtx promoted_to_vector_value, if (max_size > 4) { rtx label = ix86_expand_aligntest (count, 4, true); - destmem = change_address (destmem, SImode, destptr); - emit_insn (gen_strset (destptr, destmem, - gen_lowpart (SImode, promoted_to_gpr_value))); + dest = change_address (destmem, SImode, destptr); + emit_insn (gen_strset (destptr, dest, gen_lowpart (SImode, value))); emit_label (label); LABEL_NUSES (label) = 1; } if (max_size > 2) { rtx label = ix86_expand_aligntest (count, 2, true); - destmem = change_address (destmem, HImode, destptr); - emit_insn (gen_strset (destptr, destmem, - gen_lowpart (HImode, promoted_to_gpr_value))); + dest = change_address (destmem, HImode, destptr); + emit_insn (gen_strset (destptr, dest, gen_lowpart (HImode, value))); emit_label (label); LABEL_NUSES (label) = 1; } if (max_size > 1) { rtx label = ix86_expand_aligntest (count, 1, true); - destmem = change_address (destmem, QImode, destptr); - emit_insn (gen_strset (destptr, destmem, - gen_lowpart (QImode, promoted_to_gpr_value))); + dest = change_address (destmem, QImode, destptr); + emit_insn (gen_strset (destptr, dest, gen_lowpart (QImode, value))); emit_label (label); LABEL_NUSES (label) = 1; } @@ -21746,8 +21453,8 @@ expand_movmem_prologue (rtx destmem, rtx srcmem, if (align <= 1 && desired_alignment > 1) { rtx label = ix86_expand_aligntest (destptr, 1, false); - srcmem = adjust_automodify_address_nv (srcmem, QImode, srcptr, 0); - destmem = adjust_automodify_address_nv (destmem, QImode, destptr, 0); + srcmem = change_address (srcmem, QImode, srcptr); + destmem = change_address (destmem, QImode, destptr); emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); ix86_adjust_counter (count, 1); emit_label (label); @@ -21756,8 +21463,8 @@ expand_movmem_prologue (rtx destmem, rtx srcmem, if (align <= 2 && desired_alignment > 2) { rtx label = ix86_expand_aligntest (destptr, 2, false); - srcmem = adjust_automodify_address_nv (srcmem, HImode, srcptr, 0); - destmem = adjust_automodify_address_nv (destmem, HImode, destptr, 0); + srcmem = change_address (srcmem, HImode, srcptr); + destmem = change_address (destmem, HImode, destptr); emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); ix86_adjust_counter (count, 2); emit_label (label); @@ -21766,34 +21473,14 @@ expand_movmem_prologue (rtx destmem, rtx srcmem, if (align <= 4 && desired_alignment > 4) { rtx label = ix86_expand_aligntest (destptr, 4, false); - srcmem = adjust_automodify_address_nv (srcmem, SImode, srcptr, 0); - destmem = adjust_automodify_address_nv (destmem, SImode, destptr, 0); + srcmem = change_address (srcmem, SImode, srcptr); + destmem = change_address (destmem, SImode, destptr); emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); ix86_adjust_counter (count, 4); emit_label (label); LABEL_NUSES (label) = 1; } - if (align <= 8 && desired_alignment > 8) - { - rtx label = ix86_expand_aligntest (destptr, 8, false); - if (TARGET_64BIT || TARGET_SSE) - { - srcmem = adjust_automodify_address_nv (srcmem, DImode, srcptr, 0); - destmem = adjust_automodify_address_nv (destmem, DImode, destptr, 0); - emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); - } - else - { - srcmem = adjust_automodify_address_nv (srcmem, SImode, srcptr, 0); - destmem = adjust_automodify_address_nv (destmem, SImode, destptr, 0); - emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); - emit_insn (gen_strmov (destptr, destmem, srcptr, srcmem)); - } - ix86_adjust_counter (count, 8); - emit_label (label); - LABEL_NUSES (label) = 1; - } - gcc_assert (desired_alignment <= 16); + gcc_assert (desired_alignment <= 8); } /* Copy enough from DST to SRC to align DST known to DESIRED_ALIGN. @@ -21848,37 +21535,6 @@ expand_constant_movmem_prologue (rtx dst, rtx *srcp, rtx destreg, rtx srcreg, off = 4; emit_insn (gen_strmov (destreg, dst, srcreg, src)); } - if (align_bytes & 8) - { - if (TARGET_64BIT || TARGET_SSE) - { - dst = adjust_automodify_address_nv (dst, DImode, destreg, off); - src = adjust_automodify_address_nv (src, DImode, srcreg, off); - emit_insn (gen_strmov (destreg, dst, srcreg, src)); - } - else - { - dst = adjust_automodify_address_nv (dst, SImode, destreg, off); - src = adjust_automodify_address_nv (src, SImode, srcreg, off); - emit_insn (gen_strmov (destreg, dst, srcreg, src)); - emit_insn (gen_strmov (destreg, dst, srcreg, src)); - } - if (MEM_ALIGN (dst) < 8 * BITS_PER_UNIT) - set_mem_align (dst, 8 * BITS_PER_UNIT); - if (src_align_bytes >= 0) - { - unsigned int src_align = 0; - if ((src_align_bytes & 7) == (align_bytes & 7)) - src_align = 8; - else if ((src_align_bytes & 3) == (align_bytes & 3)) - src_align = 4; - else if ((src_align_bytes & 1) == (align_bytes & 1)) - src_align = 2; - if (MEM_ALIGN (src) < src_align * BITS_PER_UNIT) - set_mem_align (src, src_align * BITS_PER_UNIT); - } - off = 8; - } dst = adjust_automodify_address_nv (dst, BLKmode, destreg, off); src = adjust_automodify_address_nv (src, BLKmode, srcreg, off); if (MEM_ALIGN (dst) < (unsigned int) desired_align * BITS_PER_UNIT) @@ -21886,9 +21542,7 @@ expand_constant_movmem_prologue (rtx dst, rtx *srcp, rtx destreg, rtx srcreg, if (src_align_bytes >= 0) { unsigned int src_align = 0; - if ((src_align_bytes & 15) == (align_bytes & 15)) - src_align = 16; - else if ((src_align_bytes & 7) == (align_bytes & 7)) + if ((src_align_bytes & 7) == (align_bytes & 7)) src_align = 8; else if ((src_align_bytes & 3) == (align_bytes & 3)) src_align = 4; @@ -21916,7 +21570,7 @@ expand_setmem_prologue (rtx destmem, rtx destptr, rtx value, rtx count, if (align <= 1 && desired_alignment > 1) { rtx label = ix86_expand_aligntest (destptr, 1, false); - destmem = adjust_automodify_address_nv (destmem, QImode, destptr, 0); + destmem = change_address (destmem, QImode, destptr); emit_insn (gen_strset (destptr, destmem, gen_lowpart (QImode, value))); ix86_adjust_counter (count, 1); emit_label (label); @@ -21925,7 +21579,7 @@ expand_setmem_prologue (rtx destmem, rtx destptr, rtx value, rtx count, if (align <= 2 && desired_alignment > 2) { rtx label = ix86_expand_aligntest (destptr, 2, false); - destmem = adjust_automodify_address_nv (destmem, HImode, destptr, 0); + destmem = change_address (destmem, HImode, destptr); emit_insn (gen_strset (destptr, destmem, gen_lowpart (HImode, value))); ix86_adjust_counter (count, 2); emit_label (label); @@ -21934,23 +21588,13 @@ expand_setmem_prologue (rtx destmem, rtx destptr, rtx value, rtx count, if (align <= 4 && desired_alignment > 4) { rtx label = ix86_expand_aligntest (destptr, 4, false); - destmem = adjust_automodify_address_nv (destmem, SImode, destptr, 0); + destmem = change_address (destmem, SImode, destptr); emit_insn (gen_strset (destptr, destmem, gen_lowpart (SImode, value))); ix86_adjust_counter (count, 4); emit_label (label); LABEL_NUSES (label) = 1; } - if (align <= 8 && desired_alignment > 8) - { - rtx label = ix86_expand_aligntest (destptr, 8, false); - destmem = adjust_automodify_address_nv (destmem, SImode, destptr, 0); - emit_insn (gen_strset (destptr, destmem, gen_lowpart (SImode, value))); - emit_insn (gen_strset (destptr, destmem, gen_lowpart (SImode, value))); - ix86_adjust_counter (count, 8); - emit_label (label); - LABEL_NUSES (label) = 1; - } - gcc_assert (desired_alignment <= 16); + gcc_assert (desired_alignment <= 8); } /* Set enough from DST to align DST known to by aligned by ALIGN to @@ -21986,19 +21630,6 @@ expand_constant_setmem_prologue (rtx dst, rtx destreg, rtx value, emit_insn (gen_strset (destreg, dst, gen_lowpart (SImode, value))); } - if (align_bytes & 8) - { - dst = adjust_automodify_address_nv (dst, SImode, destreg, off); - emit_insn (gen_strset (destreg, dst, - gen_lowpart (SImode, value))); - off = 4; - dst = adjust_automodify_address_nv (dst, SImode, destreg, off); - emit_insn (gen_strset (destreg, dst, - gen_lowpart (SImode, value))); - if (MEM_ALIGN (dst) < 8 * BITS_PER_UNIT) - set_mem_align (dst, 8 * BITS_PER_UNIT); - off = 4; - } dst = adjust_automodify_address_nv (dst, BLKmode, destreg, off); if (MEM_ALIGN (dst) < (unsigned int) desired_align * BITS_PER_UNIT) set_mem_align (dst, desired_align * BITS_PER_UNIT); @@ -22010,7 +21641,7 @@ expand_constant_setmem_prologue (rtx dst, rtx destreg, rtx value, /* Given COUNT and EXPECTED_SIZE, decide on codegen of string operation. */ static enum stringop_alg decide_alg (HOST_WIDE_INT count, HOST_WIDE_INT expected_size, bool memset, - int *dynamic_check, bool align_unknown) + int *dynamic_check) { const struct stringop_algs * algs; bool optimize_for_speed; @@ -22019,7 +21650,7 @@ decide_alg (HOST_WIDE_INT count, HOST_WIDE_INT expected_size, bool memset, consider such algorithms if the user has appropriated those registers for their own purposes. */ bool rep_prefix_usable = !(fixed_regs[CX_REG] || fixed_regs[DI_REG] - || (memset + || (memset ? fixed_regs[AX_REG] : fixed_regs[SI_REG])); #define ALG_USABLE_P(alg) (rep_prefix_usable \ @@ -22032,7 +21663,7 @@ decide_alg (HOST_WIDE_INT count, HOST_WIDE_INT expected_size, bool memset, of time processing large blocks. */ if (optimize_function_for_size_p (cfun) || (optimize_insn_for_size_p () - && expected_size != -1 && expected_size < 256)) + && expected_size != -1 && expected_size < 256)) optimize_for_speed = false; else optimize_for_speed = true; @@ -22041,9 +21672,9 @@ decide_alg (HOST_WIDE_INT count, HOST_WIDE_INT expected_size, bool memset, *dynamic_check = -1; if (memset) - algs = &cost->memset[align_unknown][TARGET_64BIT != 0]; + algs = &cost->memset[TARGET_64BIT != 0]; else - algs = &cost->memcpy[align_unknown][TARGET_64BIT != 0]; + algs = &cost->memcpy[TARGET_64BIT != 0]; if (ix86_stringop_alg != no_stringop && ALG_USABLE_P (ix86_stringop_alg)) return ix86_stringop_alg; /* rep; movq or rep; movl is the smallest variant. */ @@ -22107,33 +21738,29 @@ decide_alg (HOST_WIDE_INT count, HOST_WIDE_INT expected_size, bool memset, enum stringop_alg alg; int i; bool any_alg_usable_p = true; - bool only_libcall_fits = true; for (i = 0; i < MAX_STRINGOP_ALGS; i++) - { - enum stringop_alg candidate = algs->size[i].alg; - any_alg_usable_p = any_alg_usable_p && ALG_USABLE_P (candidate); + { + enum stringop_alg candidate = algs->size[i].alg; + any_alg_usable_p = any_alg_usable_p && ALG_USABLE_P (candidate); - if (candidate != libcall && candidate - && ALG_USABLE_P (candidate)) - { - max = algs->size[i].max; - only_libcall_fits = false; - } - } + if (candidate != libcall && candidate + && ALG_USABLE_P (candidate)) + max = algs->size[i].max; + } /* If there aren't any usable algorithms, then recursing on - smaller sizes isn't going to find anything. Just return the - simple byte-at-a-time copy loop. */ - if (!any_alg_usable_p || only_libcall_fits) - { - /* Pick something reasonable. */ - if (TARGET_INLINE_STRINGOPS_DYNAMICALLY) - *dynamic_check = 128; - return loop_1_byte; - } + smaller sizes isn't going to find anything. Just return the + simple byte-at-a-time copy loop. */ + if (!any_alg_usable_p) + { + /* Pick something reasonable. */ + if (TARGET_INLINE_STRINGOPS_DYNAMICALLY) + *dynamic_check = 128; + return loop_1_byte; + } if (max == -1) max = 4096; - alg = decide_alg (count, max / 2, memset, dynamic_check, align_unknown); + alg = decide_alg (count, max / 2, memset, dynamic_check); gcc_assert (*dynamic_check == -1); gcc_assert (alg != libcall); if (TARGET_INLINE_STRINGOPS_DYNAMICALLY) @@ -22157,14 +21784,9 @@ decide_alignment (int align, case no_stringop: gcc_unreachable (); case loop: - desired_align = GET_MODE_SIZE (Pmode); - break; case unrolled_loop: desired_align = GET_MODE_SIZE (Pmode); break; - case sse_loop: - desired_align = 16; - break; case rep_prefix_8_byte: desired_align = 8; break; @@ -22252,11 +21874,6 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, enum stringop_alg alg; int dynamic_check; bool need_zero_guard = false; - bool align_unknown; - int unroll_factor; - enum machine_mode move_mode; - rtx loop_iter = NULL_RTX; - int dst_offset, src_offset; if (CONST_INT_P (align_exp)) align = INTVAL (align_exp); @@ -22280,17 +21897,9 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, /* Step 0: Decide on preferred algorithm, desired alignment and size of chunks to be copied by main loop. */ - dst_offset = get_mem_align_offset (dst, MOVE_MAX*BITS_PER_UNIT); - src_offset = get_mem_align_offset (src, MOVE_MAX*BITS_PER_UNIT); - align_unknown = (dst_offset < 0 - || src_offset < 0 - || src_offset != dst_offset); - alg = decide_alg (count, expected_size, false, &dynamic_check, align_unknown); + + alg = decide_alg (count, expected_size, false, &dynamic_check); desired_align = decide_alignment (align, alg, expected_size); - if (align_unknown) - desired_align = align; - unroll_factor = 1; - move_mode = Pmode; if (!TARGET_ALIGN_STRINGOPS) align = desired_align; @@ -22309,22 +21918,11 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, gcc_unreachable (); case loop: need_zero_guard = true; - move_mode = Pmode; - unroll_factor = 1; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; + size_needed = GET_MODE_SIZE (Pmode); break; case unrolled_loop: need_zero_guard = true; - move_mode = Pmode; - unroll_factor = TARGET_64BIT ? 4 : 2; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; - break; - case sse_loop: - need_zero_guard = true; - /* Use SSE instructions, if possible. */ - move_mode = align_unknown ? DImode : V4SImode; - unroll_factor = TARGET_64BIT ? 4 : 2; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; + size_needed = GET_MODE_SIZE (Pmode) * (TARGET_64BIT ? 4 : 2); break; case rep_prefix_8_byte: size_needed = 8; @@ -22385,12 +21983,6 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, } else { - /* SSE and unrolled algs re-use iteration counter in the epilogue. */ - if (alg == sse_loop || alg == unrolled_loop) - { - loop_iter = gen_reg_rtx (counter_mode (count_exp)); - emit_move_insn (loop_iter, const0_rtx); - } label = gen_label_rtx (); emit_cmp_and_jump_insns (count_exp, GEN_INT (epilogue_size_needed), @@ -22442,8 +22034,6 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, dst = change_address (dst, BLKmode, destreg); expand_movmem_prologue (dst, src, destreg, srcreg, count_exp, align, desired_align); - set_mem_align (src, desired_align*BITS_PER_UNIT); - set_mem_align (dst, desired_align*BITS_PER_UNIT); } else { @@ -22500,16 +22090,12 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, expand_set_or_movmem_via_loop (dst, src, destreg, srcreg, NULL, count_exp, Pmode, 1, expected_size); break; - case sse_loop: case unrolled_loop: - /* In some cases we want to use the same iterator in several adjacent - loops, so here we save loop iterator rtx and don't update addresses. */ - loop_iter = expand_set_or_movmem_via_loop_with_iter (dst, src, destreg, - srcreg, NULL, - count_exp, loop_iter, - move_mode, - unroll_factor, - expected_size, false); + /* Unroll only by factor of 2 in 32bit mode, since we don't have enough + registers for 4 temporaries anyway. */ + expand_set_or_movmem_via_loop (dst, src, destreg, srcreg, NULL, + count_exp, Pmode, TARGET_64BIT ? 4 : 2, + expected_size); break; case rep_prefix_8_byte: expand_movmem_via_rep_mov (dst, src, destreg, srcreg, count_exp, @@ -22560,41 +22146,9 @@ ix86_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp, LABEL_NUSES (label) = 1; } - /* We haven't updated addresses, so we'll do it now. - Also, if the epilogue seems to be big, we'll generate a loop (not - unrolled) in it. We'll do it only if alignment is unknown, because in - this case in epilogue we have to perform memmove by bytes, which is very - slow. */ - if (alg == sse_loop || alg == unrolled_loop) - { - rtx tmp; - if (align_unknown && unroll_factor > 1) - { - /* Reduce epilogue's size by creating not-unrolled loop. If we won't - do this, we can have very big epilogue - when alignment is statically - unknown we'll have the epilogue byte by byte which may be very slow. */ - loop_iter = expand_set_or_movmem_via_loop_with_iter (dst, src, destreg, - srcreg, NULL, count_exp, - loop_iter, move_mode, 1, - expected_size, false); - src = change_address (src, BLKmode, srcreg); - dst = change_address (dst, BLKmode, destreg); - epilogue_size_needed = GET_MODE_SIZE (move_mode); - } - tmp = expand_simple_binop (Pmode, PLUS, destreg, loop_iter, destreg, - true, OPTAB_LIB_WIDEN); - if (tmp != destreg) - emit_move_insn (destreg, tmp); - - tmp = expand_simple_binop (Pmode, PLUS, srcreg, loop_iter, srcreg, - true, OPTAB_LIB_WIDEN); - if (tmp != srcreg) - emit_move_insn (srcreg, tmp); - } if (count_exp != const0_rtx && epilogue_size_needed > 1) expand_movmem_epilogue (dst, src, destreg, srcreg, count_exp, epilogue_size_needed); - if (jump_around_label) emit_label (jump_around_label); return true; @@ -22612,37 +22166,7 @@ promote_duplicated_reg (enum machine_mode mode, rtx val) rtx tmp; int nops = mode == DImode ? 3 : 2; - if (VECTOR_MODE_P (mode)) - { - enum machine_mode inner = GET_MODE_INNER (mode); - rtx promoted_val, vec_reg; - if (CONST_INT_P (val)) - return ix86_build_const_vector (mode, true, val); - - promoted_val = promote_duplicated_reg (inner, val); - vec_reg = gen_reg_rtx (mode); - switch (mode) - { - case V2DImode: - emit_insn (gen_vec_dupv2di (vec_reg, promoted_val)); - break; - case V4SImode: - emit_insn (gen_vec_dupv4si (vec_reg, promoted_val)); - break; - default: - gcc_unreachable (); - break; - } - - return vec_reg; - } gcc_assert (mode == SImode || mode == DImode); - if (mode == DImode && !TARGET_64BIT) - { - rtx vec_reg = promote_duplicated_reg (V4SImode, val); - vec_reg = convert_to_mode (V2DImode, vec_reg, 1); - return vec_reg; - } if (val == const0_rtx) return copy_to_mode_reg (mode, const0_rtx); if (CONST_INT_P (val)) @@ -22708,27 +22232,11 @@ promote_duplicated_reg (enum machine_mode mode, rtx val) static rtx promote_duplicated_reg_to_size (rtx val, int size_needed, int desired_align, int align) { - rtx promoted_val = NULL_RTX; + rtx promoted_val; - if (size_needed > 8 || (desired_align > align && desired_align > 8)) - { - /* We want to promote to vector register, so we expect that at least SSE - is available. */ - gcc_assert (TARGET_SSE); - - /* In case of promotion to vector register, we expect that val is a - constant or already promoted to GPR value. */ - gcc_assert (GET_MODE (val) == Pmode || CONSTANT_P (val)); - if (TARGET_64BIT) - promoted_val = promote_duplicated_reg (V2DImode, val); - else - promoted_val = promote_duplicated_reg (V4SImode, val); - } - else if (size_needed > 4 || (desired_align > align && desired_align > 4)) - { - gcc_assert (TARGET_64BIT); - promoted_val = promote_duplicated_reg (DImode, val); - } + if (TARGET_64BIT + && (size_needed > 4 || (desired_align > align && desired_align > 4))) + promoted_val = promote_duplicated_reg (DImode, val); else if (size_needed > 2 || (desired_align > align && desired_align > 2)) promoted_val = promote_duplicated_reg (SImode, val); else if (size_needed > 1 || (desired_align > align && desired_align > 1)) @@ -22756,14 +22264,10 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, int size_needed = 0, epilogue_size_needed; int desired_align = 0, align_bytes = 0; enum stringop_alg alg; - rtx gpr_promoted_val = NULL; - rtx vec_promoted_val = NULL; + rtx promoted_val = NULL; + bool force_loopy_epilogue = false; int dynamic_check; bool need_zero_guard = false; - bool align_unknown; - unsigned int unroll_factor; - enum machine_mode move_mode; - rtx loop_iter = NULL_RTX; if (CONST_INT_P (align_exp)) align = INTVAL (align_exp); @@ -22783,11 +22287,8 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, /* Step 0: Decide on preferred algorithm, desired alignment and size of chunks to be copied by main loop. */ - align_unknown = CONST_INT_P (align_exp) && INTVAL (align_exp) > 0; - alg = decide_alg (count, expected_size, true, &dynamic_check, align_unknown); + alg = decide_alg (count, expected_size, true, &dynamic_check); desired_align = decide_alignment (align, alg, expected_size); - unroll_factor = 1; - move_mode = Pmode; if (!TARGET_ALIGN_STRINGOPS) align = desired_align; @@ -22805,28 +22306,11 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, gcc_unreachable (); case loop: need_zero_guard = true; - move_mode = Pmode; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; + size_needed = GET_MODE_SIZE (Pmode); break; case unrolled_loop: need_zero_guard = true; - move_mode = Pmode; - unroll_factor = 1; - /* Select maximal available 1,2 or 4 unroll factor. */ - while (GET_MODE_SIZE (move_mode) * unroll_factor * 2 < count - && unroll_factor < 4) - unroll_factor *= 2; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; - break; - case sse_loop: - need_zero_guard = true; - move_mode = TARGET_64BIT ? V2DImode : V4SImode; - unroll_factor = 1; - /* Select maximal available 1,2 or 4 unroll factor. */ - while (GET_MODE_SIZE (move_mode) * unroll_factor * 2 < count - && unroll_factor < 4) - unroll_factor *= 2; - size_needed = GET_MODE_SIZE (move_mode) * unroll_factor; + size_needed = GET_MODE_SIZE (Pmode) * 4; break; case rep_prefix_8_byte: size_needed = 8; @@ -22871,10 +22355,8 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, main loop and epilogue (ie one load of the big constant in the front of all code. */ if (CONST_INT_P (val_exp)) - gpr_promoted_val = promote_duplicated_reg_to_size (val_exp, - GET_MODE_SIZE (Pmode), - GET_MODE_SIZE (Pmode), - align); + promoted_val = promote_duplicated_reg_to_size (val_exp, size_needed, + desired_align, align); /* Ensure that alignment prologue won't copy past end of block. */ if (size_needed > 1 || (desired_align > 1 && desired_align > align)) { @@ -22883,6 +22365,12 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, Make sure it is power of 2. */ epilogue_size_needed = smallest_pow2_greater_than (epilogue_size_needed); + /* To improve performance of small blocks, we jump around the VAL + promoting mode. This mean that if the promoted VAL is not constant, + we might not use it in the epilogue and have to use byte + loop variant. */ + if (epilogue_size_needed > 2 && !promoted_val) + force_loopy_epilogue = true; if (count) { if (count < (unsigned HOST_WIDE_INT)epilogue_size_needed) @@ -22897,12 +22385,6 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, } else { - /* SSE and unrolled_lopo algs re-use iteration counter in the epilogue. */ - if (alg == sse_loop || alg == unrolled_loop) - { - loop_iter = gen_reg_rtx (counter_mode (count_exp)); - emit_move_insn (loop_iter, const0_rtx); - } label = gen_label_rtx (); emit_cmp_and_jump_insns (count_exp, GEN_INT (epilogue_size_needed), @@ -22928,11 +22410,9 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, /* Step 2: Alignment prologue. */ /* Do the expensive promotion once we branched off the small blocks. */ - if (!gpr_promoted_val) - gpr_promoted_val = promote_duplicated_reg_to_size (val_exp, - GET_MODE_SIZE (Pmode), - GET_MODE_SIZE (Pmode), - align); + if (!promoted_val) + promoted_val = promote_duplicated_reg_to_size (val_exp, size_needed, + desired_align, align); gcc_assert (desired_align >= 1 && align >= 1); if (desired_align > align) @@ -22944,20 +22424,17 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, the pain to maintain it for the first move, so throw away the info early. */ dst = change_address (dst, BLKmode, destreg); - expand_setmem_prologue (dst, destreg, gpr_promoted_val, count_exp, align, + expand_setmem_prologue (dst, destreg, promoted_val, count_exp, align, desired_align); - set_mem_align (dst, desired_align*BITS_PER_UNIT); } else { /* If we know how many bytes need to be stored before dst is sufficiently aligned, maintain aliasing info accurately. */ - dst = expand_constant_setmem_prologue (dst, destreg, gpr_promoted_val, + dst = expand_constant_setmem_prologue (dst, destreg, promoted_val, desired_align, align_bytes); count_exp = plus_constant (count_exp, -align_bytes); count -= align_bytes; - if (count < (unsigned HOST_WIDE_INT) size_needed) - goto epilogue; } if (need_zero_guard && (count < (unsigned HOST_WIDE_INT) size_needed @@ -22985,7 +22462,7 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, emit_label (label); LABEL_NUSES (label) = 1; label = NULL; - gpr_promoted_val = val_exp; + promoted_val = val_exp; epilogue_size_needed = 1; } else if (label == NULL_RTX) @@ -22999,40 +22476,27 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, case no_stringop: gcc_unreachable (); case loop_1_byte: - expand_set_or_movmem_via_loop (dst, NULL, destreg, NULL, val_exp, + expand_set_or_movmem_via_loop (dst, NULL, destreg, NULL, promoted_val, count_exp, QImode, 1, expected_size); break; case loop: - expand_set_or_movmem_via_loop (dst, NULL, destreg, NULL, gpr_promoted_val, + expand_set_or_movmem_via_loop (dst, NULL, destreg, NULL, promoted_val, count_exp, Pmode, 1, expected_size); break; case unrolled_loop: - loop_iter = expand_set_or_movmem_via_loop_with_iter (dst, NULL, destreg, - NULL, gpr_promoted_val, count_exp, - loop_iter, move_mode, unroll_factor, - expected_size, false); - break; - case sse_loop: - vec_promoted_val = - promote_duplicated_reg_to_size (gpr_promoted_val, - GET_MODE_SIZE (move_mode), - desired_align, align); - loop_iter = expand_set_or_movmem_via_loop_with_iter (dst, NULL, destreg, - NULL, vec_promoted_val, count_exp, - loop_iter, move_mode, unroll_factor, - expected_size, false); + expand_set_or_movmem_via_loop (dst, NULL, destreg, NULL, promoted_val, + count_exp, Pmode, 4, expected_size); break; case rep_prefix_8_byte: - gcc_assert (TARGET_64BIT); - expand_setmem_via_rep_stos (dst, destreg, gpr_promoted_val, count_exp, + expand_setmem_via_rep_stos (dst, destreg, promoted_val, count_exp, DImode, val_exp); break; case rep_prefix_4_byte: - expand_setmem_via_rep_stos (dst, destreg, gpr_promoted_val, count_exp, + expand_setmem_via_rep_stos (dst, destreg, promoted_val, count_exp, SImode, val_exp); break; case rep_prefix_1_byte: - expand_setmem_via_rep_stos (dst, destreg, gpr_promoted_val, count_exp, + expand_setmem_via_rep_stos (dst, destreg, promoted_val, count_exp, QImode, val_exp); break; } @@ -23063,43 +22527,16 @@ ix86_expand_setmem (rtx dst, rtx count_exp, rtx val_exp, rtx align_exp, } emit_label (label); LABEL_NUSES (label) = 1; - /* We can not rely on fact that promoved value is known. */ - vec_promoted_val = 0; - gpr_promoted_val = 0; } epilogue: - if (alg == unrolled_loop || alg == sse_loop) - { - rtx tmp; - if (align_unknown && unroll_factor > 1 - && epilogue_size_needed >= GET_MODE_SIZE (move_mode) - && vec_promoted_val) - { - /* Reduce epilogue's size by creating not-unrolled loop. If we won't - do this, we can have very big epilogue - when alignment is statically - unknown we'll have the epilogue byte by byte which may be very slow. */ - loop_iter = expand_set_or_movmem_via_loop_with_iter (dst, NULL, destreg, - NULL, vec_promoted_val, count_exp, - loop_iter, move_mode, 1, - expected_size, false); - dst = change_address (dst, BLKmode, destreg); - epilogue_size_needed = GET_MODE_SIZE (move_mode); - } - tmp = expand_simple_binop (Pmode, PLUS, destreg, loop_iter, destreg, - true, OPTAB_LIB_WIDEN); - if (tmp != destreg) - emit_move_insn (destreg, tmp); - } - if (count_exp == const0_rtx) - ; - else if (!gpr_promoted_val && epilogue_size_needed > 1) - expand_setmem_epilogue_via_loop (dst, destreg, val_exp, count_exp, - epilogue_size_needed); - else + if (count_exp != const0_rtx && epilogue_size_needed > 1) { - if (epilogue_size_needed > 1) - expand_setmem_epilogue (dst, destreg, vec_promoted_val, gpr_promoted_val, - val_exp, count_exp, epilogue_size_needed); + if (force_loopy_epilogue) + expand_setmem_epilogue_via_loop (dst, destreg, val_exp, count_exp, + epilogue_size_needed); + else + expand_setmem_epilogue (dst, destreg, promoted_val, count_exp, + epilogue_size_needed); } if (jump_around_label) emit_label (jump_around_label); @@ -25245,6 +24682,7 @@ enum ix86_builtins IX86_BUILTIN_CVTTPS2DQ, IX86_BUILTIN_MOVNTI, + IX86_BUILTIN_MOVNTI64, IX86_BUILTIN_MOVNTPD, IX86_BUILTIN_MOVNTDQ, @@ -25457,22 +24895,32 @@ enum ix86_builtins IX86_BUILTIN_PMULDQ128, IX86_BUILTIN_PMULLD128, - IX86_BUILTIN_ROUNDPD, - IX86_BUILTIN_ROUNDPS, IX86_BUILTIN_ROUNDSD, IX86_BUILTIN_ROUNDSS, + IX86_BUILTIN_ROUNDPD, + IX86_BUILTIN_ROUNDPS, + IX86_BUILTIN_FLOORPD, IX86_BUILTIN_CEILPD, IX86_BUILTIN_TRUNCPD, IX86_BUILTIN_RINTPD, IX86_BUILTIN_ROUNDPD_AZ, + + IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX, + IX86_BUILTIN_CEILPD_VEC_PACK_SFIX, + IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX, + IX86_BUILTIN_FLOORPS, IX86_BUILTIN_CEILPS, IX86_BUILTIN_TRUNCPS, IX86_BUILTIN_RINTPS, IX86_BUILTIN_ROUNDPS_AZ, + IX86_BUILTIN_FLOORPS_SFIX, + IX86_BUILTIN_CEILPS_SFIX, + IX86_BUILTIN_ROUNDPS_AZ_SFIX, + IX86_BUILTIN_PTESTZ, IX86_BUILTIN_PTESTC, IX86_BUILTIN_PTESTNZC, @@ -25646,12 +25094,21 @@ enum ix86_builtins IX86_BUILTIN_TRUNCPD256, IX86_BUILTIN_RINTPD256, IX86_BUILTIN_ROUNDPD_AZ256, + + IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX256, + IX86_BUILTIN_CEILPD_VEC_PACK_SFIX256, + IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX256, + IX86_BUILTIN_FLOORPS256, IX86_BUILTIN_CEILPS256, IX86_BUILTIN_TRUNCPS256, IX86_BUILTIN_RINTPS256, IX86_BUILTIN_ROUNDPS_AZ256, + IX86_BUILTIN_FLOORPS_SFIX256, + IX86_BUILTIN_CEILPS_SFIX256, + IX86_BUILTIN_ROUNDPS_AZ_SFIX256, + IX86_BUILTIN_UNPCKHPD256, IX86_BUILTIN_UNPCKLPD256, IX86_BUILTIN_UNPCKHPS256, @@ -26299,7 +25756,7 @@ static const struct builtin_description bdesc_special_args[] = /* SSE or 3DNow!A */ { OPTION_MASK_ISA_SSE | OPTION_MASK_ISA_3DNOW_A, CODE_FOR_sse_sfence, "__builtin_ia32_sfence", IX86_BUILTIN_SFENCE, UNKNOWN, (int) VOID_FTYPE_VOID }, - { OPTION_MASK_ISA_SSE | OPTION_MASK_ISA_3DNOW_A, CODE_FOR_sse_movntdi, "__builtin_ia32_movntq", IX86_BUILTIN_MOVNTQ, UNKNOWN, (int) VOID_FTYPE_PULONGLONG_ULONGLONG }, + { OPTION_MASK_ISA_SSE | OPTION_MASK_ISA_3DNOW_A, CODE_FOR_sse_movntq, "__builtin_ia32_movntq", IX86_BUILTIN_MOVNTQ, UNKNOWN, (int) VOID_FTYPE_PULONGLONG_ULONGLONG }, /* SSE2 */ { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_lfence, "__builtin_ia32_lfence", IX86_BUILTIN_LFENCE, UNKNOWN, (int) VOID_FTYPE_VOID }, @@ -26308,7 +25765,8 @@ static const struct builtin_description bdesc_special_args[] = { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movdqu, "__builtin_ia32_storedqu", IX86_BUILTIN_STOREDQU, UNKNOWN, (int) VOID_FTYPE_PCHAR_V16QI }, { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movntv2df, "__builtin_ia32_movntpd", IX86_BUILTIN_MOVNTPD, UNKNOWN, (int) VOID_FTYPE_PDOUBLE_V2DF }, { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movntv2di, "__builtin_ia32_movntdq", IX86_BUILTIN_MOVNTDQ, UNKNOWN, (int) VOID_FTYPE_PV2DI_V2DI }, - { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movntsi, "__builtin_ia32_movnti", IX86_BUILTIN_MOVNTI, UNKNOWN, (int) VOID_FTYPE_PINT_INT }, + { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movntisi, "__builtin_ia32_movnti", IX86_BUILTIN_MOVNTI, UNKNOWN, (int) VOID_FTYPE_PINT_INT }, + { OPTION_MASK_ISA_SSE2 | OPTION_MASK_ISA_64BIT, CODE_FOR_sse2_movntidi, "__builtin_ia32_movnti64", IX86_BUILTIN_MOVNTI64, UNKNOWN, (int) VOID_FTYPE_PLONGLONG_LONGLONG }, { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movupd, "__builtin_ia32_loadupd", IX86_BUILTIN_LOADUPD, UNKNOWN, (int) V2DF_FTYPE_PCDOUBLE }, { OPTION_MASK_ISA_SSE2, CODE_FOR_sse2_movdqu, "__builtin_ia32_loaddqu", IX86_BUILTIN_LOADDQU, UNKNOWN, (int) V16QI_FTYPE_PCCHAR }, @@ -26856,14 +26314,22 @@ static const struct builtin_description bdesc_args[] = { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundpd, "__builtin_ia32_truncpd", IX86_BUILTIN_TRUNCPD, (enum rtx_code) ROUND_TRUNC, (int) V2DF_FTYPE_V2DF_ROUND }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundpd, "__builtin_ia32_rintpd", IX86_BUILTIN_RINTPD, (enum rtx_code) ROUND_MXCSR, (int) V2DF_FTYPE_V2DF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundpd_vec_pack_sfix, "__builtin_ia32_floorpd_vec_pack_sfix", IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX, (enum rtx_code) ROUND_FLOOR, (int) V4SI_FTYPE_V2DF_V2DF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundpd_vec_pack_sfix, "__builtin_ia32_ceilpd_vec_pack_sfix", IX86_BUILTIN_CEILPD_VEC_PACK_SFIX, (enum rtx_code) ROUND_CEIL, (int) V4SI_FTYPE_V2DF_V2DF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_roundv2df2, "__builtin_ia32_roundpd_az", IX86_BUILTIN_ROUNDPD_AZ, UNKNOWN, (int) V2DF_FTYPE_V2DF }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_roundv2df2_vec_pack_sfix, "__builtin_ia32_roundpd_az_vec_pack_sfix", IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX, UNKNOWN, (int) V4SI_FTYPE_V2DF_V2DF }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps, "__builtin_ia32_floorps", IX86_BUILTIN_FLOORPS, (enum rtx_code) ROUND_FLOOR, (int) V4SF_FTYPE_V4SF_ROUND }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps, "__builtin_ia32_ceilps", IX86_BUILTIN_CEILPS, (enum rtx_code) ROUND_CEIL, (int) V4SF_FTYPE_V4SF_ROUND }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps, "__builtin_ia32_truncps", IX86_BUILTIN_TRUNCPS, (enum rtx_code) ROUND_TRUNC, (int) V4SF_FTYPE_V4SF_ROUND }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps, "__builtin_ia32_rintps", IX86_BUILTIN_RINTPS, (enum rtx_code) ROUND_MXCSR, (int) V4SF_FTYPE_V4SF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps_sfix, "__builtin_ia32_floorps_sfix", IX86_BUILTIN_FLOORPS_SFIX, (enum rtx_code) ROUND_FLOOR, (int) V4SI_FTYPE_V4SF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_roundps_sfix, "__builtin_ia32_ceilps_sfix", IX86_BUILTIN_CEILPS_SFIX, (enum rtx_code) ROUND_CEIL, (int) V4SI_FTYPE_V4SF_ROUND }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_roundv4sf2, "__builtin_ia32_roundps_az", IX86_BUILTIN_ROUNDPS_AZ, UNKNOWN, (int) V4SF_FTYPE_V4SF }, + { OPTION_MASK_ISA_ROUND, CODE_FOR_roundv4sf2_sfix, "__builtin_ia32_roundps_az_sfix", IX86_BUILTIN_ROUNDPS_AZ_SFIX, UNKNOWN, (int) V4SI_FTYPE_V4SF }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_ptest, "__builtin_ia32_ptestz128", IX86_BUILTIN_PTESTZ, EQ, (int) INT_FTYPE_V2DI_V2DI_PTEST }, { OPTION_MASK_ISA_ROUND, CODE_FOR_sse4_1_ptest, "__builtin_ia32_ptestc128", IX86_BUILTIN_PTESTC, LTU, (int) INT_FTYPE_V2DI_V2DI_PTEST }, @@ -26983,13 +26449,21 @@ static const struct builtin_description bdesc_args[] = { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundpd256, "__builtin_ia32_rintpd256", IX86_BUILTIN_RINTPD256, (enum rtx_code) ROUND_MXCSR, (int) V4DF_FTYPE_V4DF_ROUND }, { OPTION_MASK_ISA_AVX, CODE_FOR_roundv4df2, "__builtin_ia32_roundpd_az256", IX86_BUILTIN_ROUNDPD_AZ256, UNKNOWN, (int) V4DF_FTYPE_V4DF }, + { OPTION_MASK_ISA_AVX, CODE_FOR_roundv4df2_vec_pack_sfix, "__builtin_ia32_roundpd_az_vec_pack_sfix256", IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX256, UNKNOWN, (int) V8SI_FTYPE_V4DF_V4DF }, + + { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundpd_vec_pack_sfix256, "__builtin_ia32_floorpd_vec_pack_sfix256", IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX256, (enum rtx_code) ROUND_FLOOR, (int) V8SI_FTYPE_V4DF_V4DF_ROUND }, + { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundpd_vec_pack_sfix256, "__builtin_ia32_ceilpd_vec_pack_sfix256", IX86_BUILTIN_CEILPD_VEC_PACK_SFIX256, (enum rtx_code) ROUND_CEIL, (int) V8SI_FTYPE_V4DF_V4DF_ROUND }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps256, "__builtin_ia32_floorps256", IX86_BUILTIN_FLOORPS256, (enum rtx_code) ROUND_FLOOR, (int) V8SF_FTYPE_V8SF_ROUND }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps256, "__builtin_ia32_ceilps256", IX86_BUILTIN_CEILPS256, (enum rtx_code) ROUND_CEIL, (int) V8SF_FTYPE_V8SF_ROUND }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps256, "__builtin_ia32_truncps256", IX86_BUILTIN_TRUNCPS256, (enum rtx_code) ROUND_TRUNC, (int) V8SF_FTYPE_V8SF_ROUND }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps256, "__builtin_ia32_rintps256", IX86_BUILTIN_RINTPS256, (enum rtx_code) ROUND_MXCSR, (int) V8SF_FTYPE_V8SF_ROUND }, + { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps_sfix256, "__builtin_ia32_floorps_sfix256", IX86_BUILTIN_FLOORPS_SFIX256, (enum rtx_code) ROUND_FLOOR, (int) V8SI_FTYPE_V8SF_ROUND }, + { OPTION_MASK_ISA_AVX, CODE_FOR_avx_roundps_sfix256, "__builtin_ia32_ceilps_sfix256", IX86_BUILTIN_CEILPS_SFIX256, (enum rtx_code) ROUND_CEIL, (int) V8SI_FTYPE_V8SF_ROUND }, + { OPTION_MASK_ISA_AVX, CODE_FOR_roundv8sf2, "__builtin_ia32_roundps_az256", IX86_BUILTIN_ROUNDPS_AZ256, UNKNOWN, (int) V8SF_FTYPE_V8SF }, + { OPTION_MASK_ISA_AVX, CODE_FOR_roundv8sf2_sfix, "__builtin_ia32_roundps_az_sfix256", IX86_BUILTIN_ROUNDPS_AZ_SFIX256, UNKNOWN, (int) V8SI_FTYPE_V8SF }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_unpckhpd256, "__builtin_ia32_unpckhpd256", IX86_BUILTIN_UNPCKHPD256, UNKNOWN, (int) V4DF_FTYPE_V4DF_V4DF }, { OPTION_MASK_ISA_AVX, CODE_FOR_avx_unpcklpd256, "__builtin_ia32_unpcklpd256", IX86_BUILTIN_UNPCKLPD256, UNKNOWN, (int) V4DF_FTYPE_V4DF_V4DF }, @@ -28430,7 +27904,7 @@ ix86_expand_sse_comi (const struct builtin_description *d, tree exp, return SUBREG_REG (target); } -/* Subroutine of ix86_expand_args_builtin to take care of round insns. */ +/* Subroutines of ix86_expand_args_builtin to take care of round insns. */ static rtx ix86_expand_sse_round (const struct builtin_description *d, tree exp, @@ -28463,6 +27937,44 @@ ix86_expand_sse_round (const struct builtin_description *d, tree exp, return target; } +static rtx +ix86_expand_sse_round_vec_pack_sfix (const struct builtin_description *d, + tree exp, rtx target) +{ + rtx pat; + tree arg0 = CALL_EXPR_ARG (exp, 0); + tree arg1 = CALL_EXPR_ARG (exp, 1); + rtx op0 = expand_normal (arg0); + rtx op1 = expand_normal (arg1); + rtx op2; + enum machine_mode tmode = insn_data[d->icode].operand[0].mode; + enum machine_mode mode0 = insn_data[d->icode].operand[1].mode; + enum machine_mode mode1 = insn_data[d->icode].operand[2].mode; + + if (optimize || target == 0 + || GET_MODE (target) != tmode + || !insn_data[d->icode].operand[0].predicate (target, tmode)) + target = gen_reg_rtx (tmode); + + op0 = safe_vector_operand (op0, mode0); + op1 = safe_vector_operand (op1, mode1); + + if ((optimize && !register_operand (op0, mode0)) + || !insn_data[d->icode].operand[0].predicate (op0, mode0)) + op0 = copy_to_mode_reg (mode0, op0); + if ((optimize && !register_operand (op1, mode1)) + || !insn_data[d->icode].operand[1].predicate (op1, mode1)) + op1 = copy_to_mode_reg (mode1, op1); + + op2 = GEN_INT (d->comparison); + + pat = GEN_FCN (d->icode) (target, op0, op1, op2); + if (! pat) + return 0; + emit_insn (pat); + return target; +} + /* Subroutine of ix86_expand_builtin to take care of ptest insns. */ static rtx @@ -28736,7 +28248,12 @@ ix86_expand_args_builtin (const struct builtin_description *d, case V4DF_FTYPE_V4DF_ROUND: case V4SF_FTYPE_V4SF_ROUND: case V8SF_FTYPE_V8SF_ROUND: + case V4SI_FTYPE_V4SF_ROUND: + case V8SI_FTYPE_V8SF_ROUND: return ix86_expand_sse_round (d, exp, target); + case V4SI_FTYPE_V2DF_V2DF_ROUND: + case V8SI_FTYPE_V4DF_V4DF_ROUND: + return ix86_expand_sse_round_vec_pack_sfix (d, exp, target); case INT_FTYPE_V8SF_V8SF_PTEST: case INT_FTYPE_V4DI_V4DI_PTEST: case INT_FTYPE_V4DF_V4DF_PTEST: @@ -29053,15 +28570,22 @@ ix86_expand_args_builtin (const struct builtin_description *d, error ("the last argument must be an 1-bit immediate"); return const0_rtx; - case CODE_FOR_sse4_1_roundpd: - case CODE_FOR_sse4_1_roundps: case CODE_FOR_sse4_1_roundsd: case CODE_FOR_sse4_1_roundss: + + case CODE_FOR_sse4_1_roundpd: + case CODE_FOR_sse4_1_roundps: + case CODE_FOR_avx_roundpd256: + case CODE_FOR_avx_roundps256: + + case CODE_FOR_sse4_1_roundpd_vec_pack_sfix: + case CODE_FOR_sse4_1_roundps_sfix: + case CODE_FOR_avx_roundpd_vec_pack_sfix256: + case CODE_FOR_avx_roundps_sfix256: + case CODE_FOR_sse4_1_blendps: case CODE_FOR_avx_blendpd256: case CODE_FOR_avx_vpermilv4df: - case CODE_FOR_avx_roundpd256: - case CODE_FOR_avx_roundps256: error ("the last argument must be a 4-bit immediate"); return const0_rtx; @@ -29228,6 +28752,7 @@ ix86_expand_special_args_builtin (const struct builtin_description *d, case VOID_FTYPE_PFLOAT_V4SF: case VOID_FTYPE_PDOUBLE_V4DF: case VOID_FTYPE_PDOUBLE_V2DF: + case VOID_FTYPE_PLONGLONG_LONGLONG: case VOID_FTYPE_PULONGLONG_ULONGLONG: case VOID_FTYPE_PINT_INT: nargs = 1; @@ -29829,7 +29354,7 @@ rdrand_step: icode = CODE_FOR_avx2_gatherdiv8sf; goto gather_gen; case IX86_BUILTIN_GATHERALTSIV4DI: - icode = CODE_FOR_avx2_gathersiv4df; + icode = CODE_FOR_avx2_gathersiv4di; goto gather_gen; case IX86_BUILTIN_GATHERALTDIV8SI: icode = CODE_FOR_avx2_gatherdiv8si; @@ -30087,6 +29612,70 @@ ix86_builtin_vectorized_function (tree fndecl, tree type_out, } break; + case BUILT_IN_IFLOOR: + case BUILT_IN_LFLOOR: + case BUILT_IN_LLFLOOR: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == DFmode) + { + if (out_n == 4 && in_n == 2) + return ix86_builtins[IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX]; + else if (out_n == 8 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_FLOORPD_VEC_PACK_SFIX256]; + } + break; + + case BUILT_IN_IFLOORF: + case BUILT_IN_LFLOORF: + case BUILT_IN_LLFLOORF: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == SFmode) + { + if (out_n == 4 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_FLOORPS_SFIX]; + else if (out_n == 8 && in_n == 8) + return ix86_builtins[IX86_BUILTIN_FLOORPS_SFIX256]; + } + break; + + case BUILT_IN_ICEIL: + case BUILT_IN_LCEIL: + case BUILT_IN_LLCEIL: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == DFmode) + { + if (out_n == 4 && in_n == 2) + return ix86_builtins[IX86_BUILTIN_CEILPD_VEC_PACK_SFIX]; + else if (out_n == 8 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_CEILPD_VEC_PACK_SFIX256]; + } + break; + + case BUILT_IN_ICEILF: + case BUILT_IN_LCEILF: + case BUILT_IN_LLCEILF: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == SFmode) + { + if (out_n == 4 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_CEILPS_SFIX]; + else if (out_n == 8 && in_n == 8) + return ix86_builtins[IX86_BUILTIN_CEILPS_SFIX256]; + } + break; + case BUILT_IN_IRINT: case BUILT_IN_LRINT: case BUILT_IN_LLRINT: @@ -30111,6 +29700,38 @@ ix86_builtin_vectorized_function (tree fndecl, tree type_out, } break; + case BUILT_IN_IROUND: + case BUILT_IN_LROUND: + case BUILT_IN_LLROUND: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == DFmode) + { + if (out_n == 4 && in_n == 2) + return ix86_builtins[IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX]; + else if (out_n == 8 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_ROUNDPD_AZ_VEC_PACK_SFIX256]; + } + break; + + case BUILT_IN_IROUNDF: + case BUILT_IN_LROUNDF: + case BUILT_IN_LLROUNDF: + /* The round insn does not trap on denormals. */ + if (flag_trapping_math || !TARGET_ROUND) + break; + + if (out_mode == SImode && in_mode == SFmode) + { + if (out_n == 4 && in_n == 4) + return ix86_builtins[IX86_BUILTIN_ROUNDPS_AZ_SFIX]; + else if (out_n == 8 && in_n == 8) + return ix86_builtins[IX86_BUILTIN_ROUNDPS_AZ_SFIX256]; + } + break; + case BUILT_IN_COPYSIGN: if (out_mode == DFmode && in_mode == DFmode) { diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index b10a13cad8a..7721c465832 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -159,12 +159,8 @@ struct processor_costs { const int fchs; /* cost of FCHS instruction. */ const int fsqrt; /* cost of FSQRT instruction. */ /* Specify what algorithm - to use for stringops on unknown size. - First index is used to specify whether - alignment is known or not. - Second - to specify whether 32 or 64 bits - are used. */ - struct stringop_algs memcpy[2][2], memset[2][2]; + to use for stringops on unknown size. */ + struct stringop_algs memcpy[2], memset[2]; const int scalar_stmt_cost; /* Cost of any scalar operation, excluding load and store. */ const int scalar_load_cost; /* Cost of scalar load. */ diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index bc602532304..fa306ba79d8 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -62,8 +62,6 @@ ;; ; -- print a semicolon (after prefixes due to bug in older gas). ;; @ -- print a segment register of thread base pointer load -;; UNSPEC usage: - (define_c_enum "unspec" [ ;; Relocation specifiers UNSPEC_GOT @@ -108,6 +106,7 @@ UNSPEC_LD_MPIC ; load_macho_picbase UNSPEC_TRUNC_NOOP UNSPEC_DIV_ALREADY_SPLIT + UNSPEC_MS_TO_SYSV_CALL UNSPEC_CALL_NEEDS_VZEROUPPER UNSPEC_PAUSE @@ -115,21 +114,9 @@ UNSPEC_FIX_NOTRUNC UNSPEC_MASKMOV UNSPEC_MOVMSK - UNSPEC_MOVNT - UNSPEC_MOVU UNSPEC_RCP UNSPEC_RSQRT - UNSPEC_SFENCE - UNSPEC_PFRCP - UNSPEC_PFRCPIT1 - UNSPEC_PFRCPIT2 - UNSPEC_PFRSQRT - UNSPEC_PFRSQIT1 - UNSPEC_MFENCE - UNSPEC_LFENCE UNSPEC_PSADBW - UNSPEC_LDDQU - UNSPEC_MS_TO_SYSV_CALL ;; Generic math support UNSPEC_COPYSIGN @@ -177,105 +164,32 @@ UNSPEC_SP_TLS_SET UNSPEC_SP_TLS_TEST - ;; SSSE3 - UNSPEC_PSHUFB - UNSPEC_PSIGN - UNSPEC_PALIGNR - - ;; For SSE4A support - UNSPEC_EXTRQI - UNSPEC_EXTRQ - UNSPEC_INSERTQI - UNSPEC_INSERTQ - - ;; For SSE4.1 support - UNSPEC_BLENDV - UNSPEC_INSERTPS - UNSPEC_DP - UNSPEC_MOVNTDQA - UNSPEC_MPSADBW - UNSPEC_PHMINPOSUW - UNSPEC_PTEST + ;; For ROUND support UNSPEC_ROUND - ;; For SSE4.2 support + ;; For CRC32 support UNSPEC_CRC32 - UNSPEC_PCMPESTR - UNSPEC_PCMPISTR - - ;; For FMA4 support - UNSPEC_FMADDSUB - UNSPEC_XOP_UNSIGNED_CMP - UNSPEC_XOP_TRUEFALSE - UNSPEC_XOP_PERMUTE - UNSPEC_FRCZ - - ;; For AES support - UNSPEC_AESENC - UNSPEC_AESENCLAST - UNSPEC_AESDEC - UNSPEC_AESDECLAST - UNSPEC_AESIMC - UNSPEC_AESKEYGENASSIST - - ;; For PCLMUL support - UNSPEC_PCLMUL - - ;; For AVX support - UNSPEC_PCMP - UNSPEC_VPERMIL - UNSPEC_VPERMIL2 - UNSPEC_VPERMIL2F128 - UNSPEC_CAST - UNSPEC_VTESTP - UNSPEC_VCVTPH2PS - UNSPEC_VCVTPS2PH - - ;; For AVX2 support - UNSPEC_VPERMSI - UNSPEC_VPERMDF - UNSPEC_VPERMSF - UNSPEC_VPERMTI - UNSPEC_GATHER - UNSPEC_VSIBADDR - - ;; For BMI support - UNSPEC_BEXTR ;; For RDRAND support UNSPEC_RDRAND + ;; For BMI support + UNSPEC_BEXTR + ;; For BMI2 support UNSPEC_PDEP UNSPEC_PEXT - - ;; For __atomic support - UNSPEC_MOVA ]) (define_c_enum "unspecv" [ UNSPECV_BLOCKAGE UNSPECV_STACK_PROBE UNSPECV_PROBE_STACK_RANGE - UNSPECV_EMMS - UNSPECV_LDMXCSR - UNSPECV_STMXCSR - UNSPECV_FEMMS - UNSPECV_CLFLUSH UNSPECV_ALIGN - UNSPECV_MONITOR - UNSPECV_MWAIT - UNSPECV_CMPXCHG_1 - UNSPECV_CMPXCHG_2 - UNSPECV_CMPXCHG_3 - UNSPECV_CMPXCHG_4 - UNSPECV_XCHG - UNSPECV_LOCK UNSPECV_PROLOGUE_USE + UNSPECV_SPLIT_STACK_RETURN UNSPECV_CLD UNSPECV_NOPS - UNSPECV_VZEROALL - UNSPECV_VZEROUPPER UNSPECV_RDTSC UNSPECV_RDTSCP UNSPECV_RDPMC @@ -287,7 +201,6 @@ UNSPECV_RDGSBASE UNSPECV_WRFSBASE UNSPECV_WRGSBASE - UNSPECV_SPLIT_STACK_RETURN ]) ;; Constants to represent rounding modes in the ROUND instruction diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index e797362d2dc..6c516e7b869 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -324,9 +324,6 @@ Enum(stringop_alg) String(loop) Value(loop) EnumValue Enum(stringop_alg) String(unrolled_loop) Value(unrolled_loop) -EnumValue -Enum(stringop_alg) String(sse_loop) Value(sse_loop) - mtls-dialect= Target RejectNegative Joined Var(ix86_tls_dialect) Enum(tls_dialect) Init(TLS_DIALECT_GNU) Use given thread-local storage dialect diff --git a/gcc/config/i386/i386elf.h b/gcc/config/i386/i386elf.h index 1b942cba914..1bf3feb7479 100644 --- a/gcc/config/i386/i386elf.h +++ b/gcc/config/i386/i386elf.h @@ -20,10 +20,6 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -/* Use stabs instead of DWARF debug format. */ -#undef PREFERRED_DEBUGGING_TYPE -#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG - /* The ELF ABI for the i386 says that records and unions are returned in memory. */ @@ -50,7 +46,7 @@ along with GCC; see the file COPYING3. If not see generated assembly code more compact (and thus faster to assemble) as well as more readable. Note that if we find subparts of the character sequence which end with NUL (and which are shorter than - STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */ + ELF_STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */ #undef ASM_OUTPUT_ASCII #define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \ @@ -70,7 +66,7 @@ along with GCC; see the file COPYING3. If not see } \ for (p = _ascii_bytes; p < limit && *p != '\0'; p++) \ continue; \ - if (p < limit && (p - _ascii_bytes) <= (long) STRING_LIMIT) \ + if (p < limit && (p - _ascii_bytes) <= (long) ELF_STRING_LIMIT) \ { \ if (bytes_in_chunk > 0) \ { \ diff --git a/gcc/config/i386/mmx.md b/gcc/config/i386/mmx.md index f76834e5ab2..e859b9f1bed 100644 --- a/gcc/config/i386/mmx.md +++ b/gcc/config/i386/mmx.md @@ -30,6 +30,20 @@ ;; means that we should never use any of these patterns except at the ;; direction of the user via a builtin. +(define_c_enum "unspec" [ + UNSPEC_MOVNTQ + UNSPEC_PFRCP + UNSPEC_PFRCPIT1 + UNSPEC_PFRCPIT2 + UNSPEC_PFRSQRT + UNSPEC_PFRSQIT1 +]) + +(define_c_enum "unspecv" [ + UNSPECV_EMMS + UNSPECV_FEMMS +]) + ;; 8 byte integral modes handled by MMX (and by extension, SSE) (define_mode_iterator MMXMODEI [V8QI V4HI V2SI]) (define_mode_iterator MMXMODEI8 [V8QI V4HI V2SI V1DI]) @@ -329,10 +343,10 @@ DONE; }) -(define_insn "sse_movntdi" +(define_insn "sse_movntq" [(set (match_operand:DI 0 "memory_operand" "=m") (unspec:DI [(match_operand:DI 1 "register_operand" "y")] - UNSPEC_MOVNT))] + UNSPEC_MOVNTQ))] "TARGET_SSE || TARGET_3DNOW_A" "movntq\t{%1, %0|%0, %1}" [(set_attr "type" "mmxmov") diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index 4938abffce5..8c434b79bb8 100644 --- a/gcc/config/i386/sse.md +++ b/gcc/config/i386/sse.md @@ -18,6 +18,85 @@ ;; along with GCC; see the file COPYING3. If not see ;; <http://www.gnu.org/licenses/>. +(define_c_enum "unspec" [ + ;; SSE + UNSPEC_MOVNT + UNSPEC_MOVU + + ;; SSE3 + UNSPEC_LDDQU + + ;; SSSE3 + UNSPEC_PSHUFB + UNSPEC_PSIGN + UNSPEC_PALIGNR + + ;; For SSE4A support + UNSPEC_EXTRQI + UNSPEC_EXTRQ + UNSPEC_INSERTQI + UNSPEC_INSERTQ + + ;; For SSE4.1 support + UNSPEC_BLENDV + UNSPEC_INSERTPS + UNSPEC_DP + UNSPEC_MOVNTDQA + UNSPEC_MPSADBW + UNSPEC_PHMINPOSUW + UNSPEC_PTEST + + ;; For SSE4.2 support + UNSPEC_PCMPESTR + UNSPEC_PCMPISTR + + ;; For FMA4 support + UNSPEC_FMADDSUB + UNSPEC_XOP_UNSIGNED_CMP + UNSPEC_XOP_TRUEFALSE + UNSPEC_XOP_PERMUTE + UNSPEC_FRCZ + + ;; For AES support + UNSPEC_AESENC + UNSPEC_AESENCLAST + UNSPEC_AESDEC + UNSPEC_AESDECLAST + UNSPEC_AESIMC + UNSPEC_AESKEYGENASSIST + + ;; For PCLMUL support + UNSPEC_PCLMUL + + ;; For AVX support + UNSPEC_PCMP + UNSPEC_VPERMIL + UNSPEC_VPERMIL2 + UNSPEC_VPERMIL2F128 + UNSPEC_CAST + UNSPEC_VTESTP + UNSPEC_VCVTPH2PS + UNSPEC_VCVTPS2PH + + ;; For AVX2 support + UNSPEC_VPERMSI + UNSPEC_VPERMDF + UNSPEC_VPERMSF + UNSPEC_VPERMTI + UNSPEC_GATHER + UNSPEC_VSIBADDR +]) + +(define_c_enum "unspecv" [ + UNSPECV_LDMXCSR + UNSPECV_STMXCSR + UNSPECV_CLFLUSH + UNSPECV_MONITOR + UNSPECV_MWAIT + UNSPECV_VZEROALL + UNSPECV_VZEROUPPER +]) + ;; All vector modes including V?TImode, used in move patterns. (define_mode_iterator V16 [(V32QI "TARGET_AVX") V16QI @@ -573,15 +652,15 @@ (set_attr "prefix" "maybe_vex") (set_attr "mode" "<sseinsnmode>")]) -(define_insn "sse2_movntsi" - [(set (match_operand:SI 0 "memory_operand" "=m") - (unspec:SI [(match_operand:SI 1 "register_operand" "r")] - UNSPEC_MOVNT))] +(define_insn "sse2_movnti<mode>" + [(set (match_operand:SWI48 0 "memory_operand" "=m") + (unspec:SWI48 [(match_operand:SWI48 1 "register_operand" "r")] + UNSPEC_MOVNT))] "TARGET_SSE2" "movnti\t{%1, %0|%0, %1}" [(set_attr "type" "ssemov") (set_attr "prefix_data16" "0") - (set_attr "mode" "V2DF")]) + (set_attr "mode" "<MODE>")]) (define_insn "<sse>_movnt<mode>" [(set (match_operand:VF 0 "memory_operand" "=m") @@ -614,8 +693,9 @@ ;; Modes handled by storent patterns. (define_mode_iterator STORENT_MODE - [(SI "TARGET_SSE2") (SF "TARGET_SSE4A") (DF "TARGET_SSE4A") - (V2DI "TARGET_SSE2") + [(DI "TARGET_SSE2 && TARGET_64BIT") (SI "TARGET_SSE2") + (SF "TARGET_SSE4A") (DF "TARGET_SSE4A") + (V4DI "TARGET_AVX") (V2DI "TARGET_SSE2") (V8SF "TARGET_AVX") V4SF (V4DF "TARGET_AVX") (V2DF "TARGET_SSE2")]) @@ -3038,14 +3118,25 @@ (match_operand:V2DF 2 "nonimmediate_operand" "")] "TARGET_SSE2" { - rtx r1, r2; + rtx tmp0, tmp1; - r1 = gen_reg_rtx (V4SFmode); - r2 = gen_reg_rtx (V4SFmode); + if (TARGET_AVX && !TARGET_PREFER_AVX128) + { + tmp0 = gen_reg_rtx (V4DFmode); + tmp1 = force_reg (V2DFmode, operands[1]); - emit_insn (gen_sse2_cvtpd2ps (r1, operands[1])); - emit_insn (gen_sse2_cvtpd2ps (r2, operands[2])); - emit_insn (gen_sse_movlhps (operands[0], r1, r2)); + emit_insn (gen_avx_vec_concatv4df (tmp0, tmp1, operands[2])); + emit_insn (gen_avx_cvtpd2ps256 (operands[0], tmp0)); + } + else + { + tmp0 = gen_reg_rtx (V4SFmode); + tmp1 = gen_reg_rtx (V4SFmode); + + emit_insn (gen_sse2_cvtpd2ps (tmp0, operands[1])); + emit_insn (gen_sse2_cvtpd2ps (tmp1, operands[2])); + emit_insn (gen_sse_movlhps (operands[0], tmp0, tmp1)); + } DONE; }) @@ -3057,12 +3148,12 @@ { rtx r1, r2; - r1 = gen_reg_rtx (V8SImode); - r2 = gen_reg_rtx (V8SImode); + r1 = gen_reg_rtx (V4SImode); + r2 = gen_reg_rtx (V4SImode); - emit_insn (gen_avx_cvttpd2dq256_2 (r1, operands[1])); - emit_insn (gen_avx_cvttpd2dq256_2 (r2, operands[2])); - emit_insn (gen_avx_vperm2f128v8si3 (operands[0], r1, r2, GEN_INT (0x20))); + emit_insn (gen_fix_truncv4dfv4si2 (r1, operands[1])); + emit_insn (gen_fix_truncv4dfv4si2 (r2, operands[2])); + emit_insn (gen_avx_vec_concatv8si (operands[0], r1, r2)); DONE; }) @@ -3072,16 +3163,28 @@ (match_operand:V2DF 2 "nonimmediate_operand" "")] "TARGET_SSE2" { - rtx r1, r2; + rtx tmp0, tmp1; - r1 = gen_reg_rtx (V4SImode); - r2 = gen_reg_rtx (V4SImode); + if (TARGET_AVX && !TARGET_PREFER_AVX128) + { + tmp0 = gen_reg_rtx (V4DFmode); + tmp1 = force_reg (V2DFmode, operands[1]); - emit_insn (gen_sse2_cvttpd2dq (r1, operands[1])); - emit_insn (gen_sse2_cvttpd2dq (r2, operands[2])); - emit_insn (gen_vec_interleave_lowv2di (gen_lowpart (V2DImode, operands[0]), - gen_lowpart (V2DImode, r1), - gen_lowpart (V2DImode, r2))); + emit_insn (gen_avx_vec_concatv4df (tmp0, tmp1, operands[2])); + emit_insn (gen_fix_truncv4dfv4si2 (operands[0], tmp0)); + } + else + { + tmp0 = gen_reg_rtx (V4SImode); + tmp1 = gen_reg_rtx (V4SImode); + + emit_insn (gen_sse2_cvttpd2dq (tmp0, operands[1])); + emit_insn (gen_sse2_cvttpd2dq (tmp1, operands[2])); + emit_insn + (gen_vec_interleave_lowv2di (gen_lowpart (V2DImode, operands[0]), + gen_lowpart (V2DImode, tmp0), + gen_lowpart (V2DImode, tmp1))); + } DONE; }) @@ -3126,12 +3229,12 @@ { rtx r1, r2; - r1 = gen_reg_rtx (V8SImode); - r2 = gen_reg_rtx (V8SImode); + r1 = gen_reg_rtx (V4SImode); + r2 = gen_reg_rtx (V4SImode); - emit_insn (gen_avx_cvtpd2dq256_2 (r1, operands[1])); - emit_insn (gen_avx_cvtpd2dq256_2 (r2, operands[2])); - emit_insn (gen_avx_vperm2f128v8si3 (operands[0], r1, r2, GEN_INT (0x20))); + emit_insn (gen_avx_cvtpd2dq256 (r1, operands[1])); + emit_insn (gen_avx_cvtpd2dq256 (r2, operands[2])); + emit_insn (gen_avx_vec_concatv8si (operands[0], r1, r2)); DONE; }) @@ -3141,16 +3244,28 @@ (match_operand:V2DF 2 "nonimmediate_operand" "")] "TARGET_SSE2" { - rtx r1, r2; + rtx tmp0, tmp1; - r1 = gen_reg_rtx (V4SImode); - r2 = gen_reg_rtx (V4SImode); + if (TARGET_AVX && !TARGET_PREFER_AVX128) + { + tmp0 = gen_reg_rtx (V4DFmode); + tmp1 = force_reg (V2DFmode, operands[1]); - emit_insn (gen_sse2_cvtpd2dq (r1, operands[1])); - emit_insn (gen_sse2_cvtpd2dq (r2, operands[2])); - emit_insn (gen_vec_interleave_lowv2di (gen_lowpart (V2DImode, operands[0]), - gen_lowpart (V2DImode, r1), - gen_lowpart (V2DImode, r2))); + emit_insn (gen_avx_vec_concatv4df (tmp0, tmp1, operands[2])); + emit_insn (gen_avx_cvtpd2dq256 (operands[0], tmp0)); + } + else + { + tmp0 = gen_reg_rtx (V4SImode); + tmp1 = gen_reg_rtx (V4SImode); + + emit_insn (gen_sse2_cvtpd2dq (tmp0, operands[1])); + emit_insn (gen_sse2_cvtpd2dq (tmp1, operands[2])); + emit_insn + (gen_vec_interleave_lowv2di (gen_lowpart (V2DImode, operands[0]), + gen_lowpart (V2DImode, tmp0), + gen_lowpart (V2DImode, tmp1))); + } DONE; }) @@ -7501,16 +7616,6 @@ (set_attr "prefix" "maybe_vex,orig,vex,maybe_vex,orig,orig") (set_attr "mode" "V2SF,TI,TI,TI,V4SF,V2SF")]) -(define_expand "vec_dupv4si" - [(set (match_operand:V4SI 0 "register_operand" "") - (vec_duplicate:V4SI - (match_operand:SI 1 "nonimmediate_operand" "")))] - "TARGET_SSE" -{ - if (!TARGET_AVX) - operands[1] = force_reg (V4SImode, operands[1]); -}) - (define_insn "*vec_dupv4si" [(set (match_operand:V4SI 0 "register_operand" "=x,x,x") (vec_duplicate:V4SI @@ -7527,16 +7632,6 @@ (set_attr "prefix" "maybe_vex,vex,orig") (set_attr "mode" "TI,V4SF,V4SF")]) -(define_expand "vec_dupv2di" - [(set (match_operand:V2DI 0 "register_operand" "") - (vec_duplicate:V2DI - (match_operand:DI 1 "nonimmediate_operand" "")))] - "TARGET_SSE" -{ - if (!TARGET_AVX) - operands[1] = force_reg (V2DImode, operands[1]); -}) - (define_insn "*vec_dupv2di" [(set (match_operand:V2DI 0 "register_operand" "=x,x,x,x") (vec_duplicate:V2DI @@ -8005,25 +8100,6 @@ (set_attr "prefix" "maybe_vex") (set_attr "memory" "store")]) -(define_expand "sse_sfence" - [(set (match_dup 0) - (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] - "TARGET_SSE || TARGET_3DNOW_A" -{ - operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); - MEM_VOLATILE_P (operands[0]) = 1; -}) - -(define_insn "*sse_sfence" - [(set (match_operand:BLK 0 "" "") - (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] - "TARGET_SSE || TARGET_3DNOW_A" - "sfence" - [(set_attr "type" "sse") - (set_attr "length_address" "0") - (set_attr "atom_sse_attr" "fence") - (set_attr "memory" "unknown")]) - (define_insn "sse2_clflush" [(unspec_volatile [(match_operand 0 "address_operand" "p")] UNSPECV_CLFLUSH)] @@ -8033,43 +8109,6 @@ (set_attr "atom_sse_attr" "fence") (set_attr "memory" "unknown")]) -(define_expand "sse2_mfence" - [(set (match_dup 0) - (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] - "TARGET_SSE2" -{ - operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); - MEM_VOLATILE_P (operands[0]) = 1; -}) - -(define_insn "*sse2_mfence" - [(set (match_operand:BLK 0 "" "") - (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] - "TARGET_64BIT || TARGET_SSE2" - "mfence" - [(set_attr "type" "sse") - (set_attr "length_address" "0") - (set_attr "atom_sse_attr" "fence") - (set_attr "memory" "unknown")]) - -(define_expand "sse2_lfence" - [(set (match_dup 0) - (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] - "TARGET_SSE2" -{ - operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); - MEM_VOLATILE_P (operands[0]) = 1; -}) - -(define_insn "*sse2_lfence" - [(set (match_operand:BLK 0 "" "") - (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] - "TARGET_SSE2" - "lfence" - [(set_attr "type" "sse") - (set_attr "length_address" "0") - (set_attr "atom_sse_attr" "lfence") - (set_attr "memory" "unknown")]) (define_insn "sse3_mwait" [(unspec_volatile [(match_operand:SI 0 "register_operand" "a") @@ -9902,6 +9941,60 @@ (set_attr "prefix" "maybe_vex") (set_attr "mode" "<MODE>")]) +(define_expand "<sse4_1>_round<ssemodesuffix>_sfix<avxsizesuffix>" + [(match_operand:<sseintvecmode> 0 "register_operand" "") + (match_operand:VF1 1 "nonimmediate_operand" "") + (match_operand:SI 2 "const_0_to_15_operand" "")] + "TARGET_ROUND" +{ + rtx tmp = gen_reg_rtx (<MODE>mode); + + emit_insn + (gen_<sse4_1>_round<ssemodesuffix><avxsizesuffix> (tmp, operands[1], + operands[2])); + emit_insn + (gen_fix_trunc<mode><sseintvecmodelower>2 (operands[0], tmp)); + DONE; +}) + +(define_expand "<sse4_1>_round<ssemodesuffix>_vec_pack_sfix<avxsizesuffix>" + [(match_operand:<ssepackfltmode> 0 "register_operand" "") + (match_operand:VF2 1 "nonimmediate_operand" "") + (match_operand:VF2 2 "nonimmediate_operand" "") + (match_operand:SI 3 "const_0_to_15_operand" "")] + "TARGET_ROUND" +{ + rtx tmp0, tmp1; + + if (<MODE>mode == V2DFmode + && TARGET_AVX && !TARGET_PREFER_AVX128) + { + rtx tmp2 = gen_reg_rtx (V4DFmode); + + tmp0 = gen_reg_rtx (V4DFmode); + tmp1 = force_reg (V2DFmode, operands[1]); + + emit_insn (gen_avx_vec_concatv4df (tmp0, tmp1, operands[2])); + emit_insn (gen_avx_roundpd256 (tmp2, tmp0, operands[3])); + emit_insn (gen_fix_truncv4dfv4si2 (operands[0], tmp2)); + } + else + { + tmp0 = gen_reg_rtx (<MODE>mode); + tmp1 = gen_reg_rtx (<MODE>mode); + + emit_insn + (gen_<sse4_1>_round<ssemodesuffix><avxsizesuffix> (tmp0, operands[1], + operands[3])); + emit_insn + (gen_<sse4_1>_round<ssemodesuffix><avxsizesuffix> (tmp1, operands[2], + operands[3])); + emit_insn + (gen_vec_pack_sfix_trunc_<mode> (operands[0], tmp0, tmp1)); + } + DONE; +}) + (define_insn "sse4_1_round<ssescalarmodesuffix>" [(set (match_operand:VF_128 0 "register_operand" "=x,x") (vec_merge:VF_128 @@ -9926,7 +10019,7 @@ (define_expand "round<mode>2" [(set (match_dup 4) (plus:VF - (match_operand:VF 1 "nonimmediate_operand" "") + (match_operand:VF 1 "register_operand" "") (match_dup 3))) (set (match_operand:VF 0 "register_operand" "") (unspec:VF @@ -9957,6 +10050,54 @@ operands[5] = GEN_INT (ROUND_TRUNC); }) +(define_expand "round<mode>2_sfix" + [(match_operand:<sseintvecmode> 0 "register_operand" "") + (match_operand:VF1 1 "register_operand" "")] + "TARGET_ROUND && !flag_trapping_math" +{ + rtx tmp = gen_reg_rtx (<MODE>mode); + + emit_insn (gen_round<mode>2 (tmp, operands[1])); + + emit_insn + (gen_fix_trunc<mode><sseintvecmodelower>2 (operands[0], tmp)); + DONE; +}) + +(define_expand "round<mode>2_vec_pack_sfix" + [(match_operand:<ssepackfltmode> 0 "register_operand" "") + (match_operand:VF2 1 "register_operand" "") + (match_operand:VF2 2 "register_operand" "")] + "TARGET_ROUND && !flag_trapping_math" +{ + rtx tmp0, tmp1; + + if (<MODE>mode == V2DFmode + && TARGET_AVX && !TARGET_PREFER_AVX128) + { + rtx tmp2 = gen_reg_rtx (V4DFmode); + + tmp0 = gen_reg_rtx (V4DFmode); + tmp1 = force_reg (V2DFmode, operands[1]); + + emit_insn (gen_avx_vec_concatv4df (tmp0, tmp1, operands[2])); + emit_insn (gen_roundv4df2 (tmp2, tmp0)); + emit_insn (gen_fix_truncv4dfv4si2 (operands[0], tmp2)); + } + else + { + tmp0 = gen_reg_rtx (<MODE>mode); + tmp1 = gen_reg_rtx (<MODE>mode); + + emit_insn (gen_round<mode>2 (tmp0, operands[1])); + emit_insn (gen_round<mode>2 (tmp1, operands[2])); + + emit_insn + (gen_vec_pack_sfix_trunc_<mode> (operands[0], tmp0, tmp1)); + } + DONE; +}) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Intel SSE4.2 string/text processing instructions diff --git a/gcc/config/i386/sync.md b/gcc/config/i386/sync.md index 0ff17123f27..5799b0aca50 100644 --- a/gcc/config/i386/sync.md +++ b/gcc/config/i386/sync.md @@ -18,26 +18,79 @@ ;; along with GCC; see the file COPYING3. If not see ;; <http://www.gnu.org/licenses/>. -(define_expand "mem_thread_fence" - [(match_operand:SI 0 "const_int_operand" "")] ;; model - "" +(define_c_enum "unspec" [ + UNSPEC_LFENCE + UNSPEC_SFENCE + UNSPEC_MFENCE + UNSPEC_MOVA ; For __atomic support +]) + +(define_c_enum "unspecv" [ + UNSPECV_CMPXCHG_1 + UNSPECV_CMPXCHG_2 + UNSPECV_CMPXCHG_3 + UNSPECV_CMPXCHG_4 + UNSPECV_XCHG + UNSPECV_LOCK +]) + +(define_expand "sse2_lfence" + [(set (match_dup 0) + (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] + "TARGET_SSE2" { - /* Unless this is a SEQ_CST fence, the i386 memory model is strong - enough not to require barriers of any kind. */ - if (INTVAL (operands[0]) != MEMMODEL_SEQ_CST) - DONE; + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; +}) - if (TARGET_64BIT || TARGET_SSE2) - emit_insn (gen_sse2_mfence ()); - else - { - rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); - MEM_VOLATILE_P (mem) = 1; - emit_insn (gen_mfence_nosse (mem)); - } - DONE; +(define_insn "*sse2_lfence" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_LFENCE))] + "TARGET_SSE2" + "lfence" + [(set_attr "type" "sse") + (set_attr "length_address" "0") + (set_attr "atom_sse_attr" "lfence") + (set_attr "memory" "unknown")]) + +(define_expand "sse_sfence" + [(set (match_dup 0) + (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] + "TARGET_SSE || TARGET_3DNOW_A" +{ + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; }) +(define_insn "*sse_sfence" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_SFENCE))] + "TARGET_SSE || TARGET_3DNOW_A" + "sfence" + [(set_attr "type" "sse") + (set_attr "length_address" "0") + (set_attr "atom_sse_attr" "fence") + (set_attr "memory" "unknown")]) + +(define_expand "sse2_mfence" + [(set (match_dup 0) + (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] + "TARGET_SSE2" +{ + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; +}) + +(define_insn "mfence_sse2" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE))] + "TARGET_64BIT || TARGET_SSE2" + "mfence" + [(set_attr "type" "sse") + (set_attr "length_address" "0") + (set_attr "atom_sse_attr" "fence") + (set_attr "memory" "unknown")]) + (define_insn "mfence_nosse" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_MFENCE)) @@ -46,7 +99,31 @@ "lock{%;} or{l}\t{$0, (%%esp)|DWORD PTR [esp], 0}" [(set_attr "memory" "unknown")]) -;; ??? From volume 3 section 7.1.1 Guaranteed Atomic Operations, +(define_expand "mem_thread_fence" + [(match_operand:SI 0 "const_int_operand" "")] ;; model + "" +{ + /* Unless this is a SEQ_CST fence, the i386 memory model is strong + enough not to require barriers of any kind. */ + if (INTVAL (operands[0]) == MEMMODEL_SEQ_CST) + { + rtx (*mfence_insn)(rtx); + rtx mem; + + if (TARGET_64BIT || TARGET_SSE2) + mfence_insn = gen_mfence_sse2; + else + mfence_insn = gen_mfence_nosse; + + mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (mem) = 1; + + emit_insn (mfence_insn (mem)); + } + DONE; +}) + +;; ??? From volume 3 section 8.1.1 Guaranteed Atomic Operations, ;; Only beginning at Pentium family processors do we get any guarantee of ;; atomicity in aligned 64-bit quantities. Beginning at P6, we get a ;; guarantee for 64-bit accesses that do not cross a cacheline boundary. @@ -204,7 +281,7 @@ (unspec:DI [(match_operand:DI 1 "memory_operand" "m")] UNSPEC_MOVA)) (clobber (match_operand:DF 2 "register_operand" "=f"))] "TARGET_80387" - "fild\t%1\;fistp\t%0" + "fild%Z1\t%1\;fistp%Z0\t%0" [(set_attr "type" "multi") ;; Worst case based on full sib+offset32 addressing modes (set_attr "length" "14")]) diff --git a/gcc/config/ia64/ia64-protos.h b/gcc/config/ia64/ia64-protos.h index 893ed886103..c24f831601c 100644 --- a/gcc/config/ia64/ia64-protos.h +++ b/gcc/config/ia64/ia64-protos.h @@ -47,7 +47,8 @@ extern void ia64_expand_dot_prod_v8qi (rtx[], bool); extern void ia64_expand_call (rtx, rtx, rtx, int); extern void ia64_split_call (rtx, rtx, rtx, rtx, rtx, int, int); extern void ia64_reload_gp (void); -extern void ia64_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx); +extern void ia64_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, + enum memmodel); extern HOST_WIDE_INT ia64_initial_elimination_offset (int, int); extern void ia64_expand_prologue (void); diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c index cad6d0f156c..14993676ccc 100644 --- a/gcc/config/ia64/ia64.c +++ b/gcc/config/ia64/ia64.c @@ -2266,7 +2266,7 @@ ia64_split_call (rtx retval, rtx addr, rtx retaddr, rtx scratch_r, void ia64_expand_atomic_op (enum rtx_code code, rtx mem, rtx val, - rtx old_dst, rtx new_dst) + rtx old_dst, rtx new_dst, enum memmodel model) { enum machine_mode mode = GET_MODE (mem); rtx old_reg, new_reg, cmp_reg, ar_ccv, label; @@ -2283,12 +2283,31 @@ ia64_expand_atomic_op (enum rtx_code code, rtx mem, rtx val, if (!old_dst) old_dst = gen_reg_rtx (mode); - emit_insn (gen_memory_barrier ()); + switch (model) + { + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_memory_barrier ()); + /* FALLTHRU */ + case MEMMODEL_RELAXED: + case MEMMODEL_ACQUIRE: + case MEMMODEL_CONSUME: + if (mode == SImode) + icode = CODE_FOR_fetchadd_acq_si; + else + icode = CODE_FOR_fetchadd_acq_di; + break; + case MEMMODEL_RELEASE: + if (mode == SImode) + icode = CODE_FOR_fetchadd_rel_si; + else + icode = CODE_FOR_fetchadd_rel_di; + break; + + default: + gcc_unreachable (); + } - if (mode == SImode) - icode = CODE_FOR_fetchadd_acq_si; - else - icode = CODE_FOR_fetchadd_acq_di; emit_insn (GEN_FCN (icode) (old_dst, mem, val)); if (new_dst) @@ -2302,8 +2321,12 @@ ia64_expand_atomic_op (enum rtx_code code, rtx mem, rtx val, } /* Because of the volatile mem read, we get an ld.acq, which is the - front half of the full barrier. The end half is the cmpxchg.rel. */ - gcc_assert (MEM_VOLATILE_P (mem)); + front half of the full barrier. The end half is the cmpxchg.rel. + For relaxed and release memory models, we don't need this. But we + also don't bother trying to prevent it either. */ + gcc_assert (model == MEMMODEL_RELAXED + || model == MEMMODEL_RELEASE + || MEM_VOLATILE_P (mem)); old_reg = gen_reg_rtx (DImode); cmp_reg = gen_reg_rtx (DImode); @@ -2342,12 +2365,36 @@ ia64_expand_atomic_op (enum rtx_code code, rtx mem, rtx val, if (new_dst) emit_move_insn (new_dst, new_reg); - switch (mode) + switch (model) { - case QImode: icode = CODE_FOR_cmpxchg_rel_qi; break; - case HImode: icode = CODE_FOR_cmpxchg_rel_hi; break; - case SImode: icode = CODE_FOR_cmpxchg_rel_si; break; - case DImode: icode = CODE_FOR_cmpxchg_rel_di; break; + case MEMMODEL_RELAXED: + case MEMMODEL_ACQUIRE: + case MEMMODEL_CONSUME: + switch (mode) + { + case QImode: icode = CODE_FOR_cmpxchg_acq_qi; break; + case HImode: icode = CODE_FOR_cmpxchg_acq_hi; break; + case SImode: icode = CODE_FOR_cmpxchg_acq_si; break; + case DImode: icode = CODE_FOR_cmpxchg_acq_di; break; + default: + gcc_unreachable (); + } + break; + + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + switch (mode) + { + case QImode: icode = CODE_FOR_cmpxchg_rel_qi; break; + case HImode: icode = CODE_FOR_cmpxchg_rel_hi; break; + case SImode: icode = CODE_FOR_cmpxchg_rel_si; break; + case DImode: icode = CODE_FOR_cmpxchg_rel_di; break; + default: + gcc_unreachable (); + } + break; + default: gcc_unreachable (); } @@ -6342,6 +6389,7 @@ rtx_needs_barrier (rtx x, struct reg_flags flags, int pred) case UNSPEC_PIC_CALL: case UNSPEC_MF: case UNSPEC_FETCHADD_ACQ: + case UNSPEC_FETCHADD_REL: case UNSPEC_BSP_VALUE: case UNSPEC_FLUSHRS: case UNSPEC_BUNDLE_SELECTOR: @@ -6385,6 +6433,7 @@ rtx_needs_barrier (rtx x, struct reg_flags flags, int pred) break; case UNSPEC_CMPXCHG_ACQ: + case UNSPEC_CMPXCHG_REL: need_barrier = rtx_needs_barrier (XVECEXP (x, 0, 1), flags, pred); need_barrier |= rtx_needs_barrier (XVECEXP (x, 0, 2), flags, pred); break; diff --git a/gcc/config/ia64/ia64.md b/gcc/config/ia64/ia64.md index 46eebc25ad8..129cec80fcf 100644 --- a/gcc/config/ia64/ia64.md +++ b/gcc/config/ia64/ia64.md @@ -48,61 +48,63 @@ ;; ??? Need a better way to describe alternate fp status registers. -(define_constants +(define_c_enum "unspec" [; Relocations - (UNSPEC_LTOFF_DTPMOD 0) - (UNSPEC_LTOFF_DTPREL 1) - (UNSPEC_DTPREL 2) - (UNSPEC_LTOFF_TPREL 3) - (UNSPEC_TPREL 4) - (UNSPEC_DTPMOD 5) - - (UNSPEC_LD_BASE 9) - (UNSPEC_GR_SPILL 10) - (UNSPEC_GR_RESTORE 11) - (UNSPEC_FR_SPILL 12) - (UNSPEC_FR_RESTORE 13) - (UNSPEC_FR_RECIP_APPROX 14) - (UNSPEC_PRED_REL_MUTEX 15) - (UNSPEC_GETF_EXP 16) - (UNSPEC_PIC_CALL 17) - (UNSPEC_MF 18) - (UNSPEC_CMPXCHG_ACQ 19) - (UNSPEC_FETCHADD_ACQ 20) - (UNSPEC_BSP_VALUE 21) - (UNSPEC_FLUSHRS 22) - (UNSPEC_BUNDLE_SELECTOR 23) - (UNSPEC_ADDP4 24) - (UNSPEC_PROLOGUE_USE 25) - (UNSPEC_RET_ADDR 26) - (UNSPEC_SETF_EXP 27) - (UNSPEC_FR_SQRT_RECIP_APPROX 28) - (UNSPEC_SHRP 29) - (UNSPEC_COPYSIGN 30) - (UNSPEC_VECT_EXTR 31) - (UNSPEC_LDA 40) - (UNSPEC_LDS 41) - (UNSPEC_LDS_A 42) - (UNSPEC_LDSA 43) - (UNSPEC_LDCCLR 44) - (UNSPEC_LDCNC 45) - (UNSPEC_CHKACLR 46) - (UNSPEC_CHKANC 47) - (UNSPEC_CHKS 48) - (UNSPEC_FR_RECIP_APPROX_RES 49) - (UNSPEC_FR_SQRT_RECIP_APPROX_RES 50) + UNSPEC_LTOFF_DTPMOD + UNSPEC_LTOFF_DTPREL + UNSPEC_DTPREL + UNSPEC_LTOFF_TPREL + UNSPEC_TPREL + UNSPEC_DTPMOD + + UNSPEC_LD_BASE + UNSPEC_GR_SPILL + UNSPEC_GR_RESTORE + UNSPEC_FR_SPILL + UNSPEC_FR_RESTORE + UNSPEC_FR_RECIP_APPROX + UNSPEC_PRED_REL_MUTEX + UNSPEC_GETF_EXP + UNSPEC_PIC_CALL + UNSPEC_MF + UNSPEC_CMPXCHG_ACQ + UNSPEC_CMPXCHG_REL + UNSPEC_FETCHADD_ACQ + UNSPEC_FETCHADD_REL + UNSPEC_BSP_VALUE + UNSPEC_FLUSHRS + UNSPEC_BUNDLE_SELECTOR + UNSPEC_ADDP4 + UNSPEC_PROLOGUE_USE + UNSPEC_RET_ADDR + UNSPEC_SETF_EXP + UNSPEC_FR_SQRT_RECIP_APPROX + UNSPEC_SHRP + UNSPEC_COPYSIGN + UNSPEC_VECT_EXTR + UNSPEC_LDA + UNSPEC_LDS + UNSPEC_LDS_A + UNSPEC_LDSA + UNSPEC_LDCCLR + UNSPEC_LDCNC + UNSPEC_CHKACLR + UNSPEC_CHKANC + UNSPEC_CHKS + UNSPEC_FR_RECIP_APPROX_RES + UNSPEC_FR_SQRT_RECIP_APPROX_RES ]) -(define_constants - [(UNSPECV_ALLOC 0) - (UNSPECV_BLOCKAGE 1) - (UNSPECV_INSN_GROUP_BARRIER 2) - (UNSPECV_BREAK 3) - (UNSPECV_SET_BSP 4) - (UNSPECV_PSAC_ALL 5) ; pred.safe_across_calls - (UNSPECV_PSAC_NORMAL 6) - (UNSPECV_SETJMP_RECEIVER 7) - (UNSPECV_GOTO_RECEIVER 8) +(define_c_enum "unspecv" [ + UNSPECV_ALLOC + UNSPECV_BLOCKAGE + UNSPECV_INSN_GROUP_BARRIER + UNSPECV_BREAK + UNSPECV_SET_BSP + UNSPECV_PSAC_ALL ; pred.safe_across_calls + UNSPECV_PSAC_NORMAL + UNSPECV_SETJMP_RECEIVER + UNSPECV_GOTO_RECEIVER ]) (include "predicates.md") diff --git a/gcc/config/ia64/sync.md b/gcc/config/ia64/sync.md index 06ca8f09b40..152e4f5a8a0 100644 --- a/gcc/config/ia64/sync.md +++ b/gcc/config/ia64/sync.md @@ -18,6 +18,9 @@ ;; along with GCC; see the file COPYING3. If not see ;; <http://www.gnu.org/licenses/>. +;; Conversion to C++11 memory model based on +;; http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html + (define_mode_iterator IMODE [QI HI SI DI]) (define_mode_iterator I124MODE [QI HI SI]) (define_mode_iterator I48MODE [SI DI]) @@ -27,6 +30,15 @@ (define_code_attr fetchop_name [(plus "add") (minus "sub") (ior "ior") (xor "xor") (and "and")]) +(define_expand "mem_thread_fence" + [(match_operand:SI 0 "const_int_operand" "")] ;; model + "" +{ + if (INTVAL (operands[0]) == MEMMODEL_SEQ_CST) + emit_insn (gen_memory_barrier ()); + DONE; +}) + (define_expand "memory_barrier" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_MF))] @@ -43,107 +55,94 @@ "mf" [(set_attr "itanium_class" "syst_m")]) -(define_insn "fetchadd_acq_<mode>" - [(set (match_operand:I48MODE 0 "gr_register_operand" "=r") - (match_operand:I48MODE 1 "not_postinc_memory_operand" "+S")) - (set (match_dup 1) - (unspec:I48MODE [(match_dup 1) - (match_operand:I48MODE 2 "fetchadd_operand" "n")] - UNSPEC_FETCHADD_ACQ))] - "" - "fetchadd<modesuffix>.acq %0 = %1, %2" - [(set_attr "itanium_class" "sem")]) - -(define_expand "sync_<fetchop_name><mode>" - [(set (match_operand:IMODE 0 "memory_operand" "") - (FETCHOP:IMODE (match_dup 0) - (match_operand:IMODE 1 "general_operand" "")))] - "" -{ - ia64_expand_atomic_op (<CODE>, operands[0], operands[1], NULL, NULL); - DONE; -}) - -(define_expand "sync_nand<mode>" - [(set (match_operand:IMODE 0 "memory_operand" "") - (not:IMODE - (and:IMODE (match_dup 0) - (match_operand:IMODE 1 "general_operand" ""))))] +(define_expand "atomic_load<mode>" + [(match_operand:IMODE 0 "gr_register_operand" "") ;; output + (match_operand:IMODE 1 "memory_operand" "") ;; memory + (match_operand:SI 2 "const_int_operand" "")] ;; model "" { - ia64_expand_atomic_op (NOT, operands[0], operands[1], NULL, NULL); - DONE; -}) + enum memmodel model = (enum memmodel) INTVAL (operands[2]); -(define_expand "sync_old_<fetchop_name><mode>" - [(set (match_operand:IMODE 0 "gr_register_operand" "") - (FETCHOP:IMODE - (match_operand:IMODE 1 "memory_operand" "") - (match_operand:IMODE 2 "general_operand" "")))] - "" -{ - ia64_expand_atomic_op (<CODE>, operands[1], operands[2], operands[0], NULL); + /* Unless the memory model is relaxed, we want to emit ld.acq, which + will happen automatically for volatile memories. */ + gcc_assert (model == MEMMODEL_RELAXED || MEM_VOLATILE_P (operands[1])); + emit_move_insn (operands[0], operands[1]); DONE; }) -(define_expand "sync_old_nand<mode>" - [(set (match_operand:IMODE 0 "gr_register_operand" "") - (not:IMODE - (and:IMODE (match_operand:IMODE 1 "memory_operand" "") - (match_operand:IMODE 2 "general_operand" ""))))] +(define_expand "atomic_store<mode>" + [(match_operand:IMODE 0 "memory_operand" "") ;; memory + (match_operand:IMODE 1 "gr_reg_or_0_operand" "") ;; input + (match_operand:SI 2 "const_int_operand" "")] ;; model "" { - ia64_expand_atomic_op (NOT, operands[1], operands[2], operands[0], NULL); - DONE; -}) + enum memmodel model = (enum memmodel) INTVAL (operands[2]); -(define_expand "sync_new_<fetchop_name><mode>" - [(set (match_operand:IMODE 0 "gr_register_operand" "") - (FETCHOP:IMODE - (match_operand:IMODE 1 "memory_operand" "") - (match_operand:IMODE 2 "general_operand" "")))] - "" -{ - ia64_expand_atomic_op (<CODE>, operands[1], operands[2], NULL, operands[0]); - DONE; -}) + /* Unless the memory model is relaxed, we want to emit st.rel, which + will happen automatically for volatile memories. */ + gcc_assert (model == MEMMODEL_RELAXED || MEM_VOLATILE_P (operands[0])); + emit_move_insn (operands[0], operands[1]); -(define_expand "sync_new_nand<mode>" - [(set (match_operand:IMODE 0 "gr_register_operand" "") - (not:IMODE - (and:IMODE (match_operand:IMODE 1 "memory_operand" "") - (match_operand:IMODE 2 "general_operand" ""))))] - "" -{ - ia64_expand_atomic_op (NOT, operands[1], operands[2], NULL, operands[0]); + /* Sequentially consistent stores need a subsequent MF. See + http://www.decadent.org.uk/pipermail/cpp-threads/2008-December/001952.html + for a discussion of why a MF is needed here, but not for atomic_load. */ + if (model == MEMMODEL_SEQ_CST) + emit_insn (gen_memory_barrier ()); DONE; }) -(define_expand "sync_compare_and_swap<mode>" - [(match_operand:IMODE 0 "gr_register_operand" "") - (match_operand:IMODE 1 "memory_operand" "") - (match_operand:IMODE 2 "gr_register_operand" "") - (match_operand:IMODE 3 "gr_register_operand" "")] +(define_expand "atomic_compare_and_swap<mode>" + [(match_operand:DI 0 "gr_register_operand" "") ;; bool out + (match_operand:IMODE 1 "gr_register_operand" "") ;; val out + (match_operand:IMODE 2 "not_postinc_memory_operand" "") ;; memory + (match_operand:IMODE 3 "gr_register_operand" "") ;; expected + (match_operand:IMODE 4 "gr_reg_or_0_operand" "") ;; desired + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; succ model + (match_operand:SI 7 "const_int_operand" "")] ;; fail model "" { + enum memmodel model = (enum memmodel) INTVAL (operands[6]); rtx ccv = gen_rtx_REG (DImode, AR_CCV_REGNUM); - rtx dst; + rtx dval, eval; + + eval = gen_reg_rtx (DImode); + convert_move (eval, operands[3], 1); + emit_move_insn (ccv, eval); - convert_move (ccv, operands[2], 1); + if (<MODE>mode == DImode) + dval = operands[1]; + else + dval = gen_reg_rtx (DImode); - dst = operands[0]; - if (GET_MODE (dst) != DImode) - dst = gen_reg_rtx (DImode); + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_ACQUIRE: + case MEMMODEL_CONSUME: + emit_insn (gen_cmpxchg_acq_<mode> (dval, operands[2], ccv, operands[4])); + break; + case MEMMODEL_RELEASE: + emit_insn (gen_cmpxchg_rel_<mode> (dval, operands[2], ccv, operands[4])); + break; + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_cmpxchg_rel_<mode> (dval, operands[2], ccv, operands[4])); + emit_insn (gen_memory_barrier ()); + break; + default: + gcc_unreachable (); + } - emit_insn (gen_cmpxchg_rel_<mode> (dst, operands[1], ccv, operands[3])); - emit_insn (gen_memory_barrier ()); + if (<MODE>mode != DImode) + emit_move_insn (operands[1], gen_lowpart (<MODE>mode, dval)); - if (dst != operands[0]) - emit_move_insn (operands[0], gen_lowpart (<MODE>mode, dst)); + emit_insn (gen_cstoredi4 (operands[0], gen_rtx_EQ (DImode, dval, eval), + dval, eval)); DONE; }) -(define_insn "cmpxchg_rel_<mode>" +(define_insn "cmpxchg_acq_<mode>" [(set (match_operand:DI 0 "gr_register_operand" "=r") (zero_extend:DI (match_operand:I124MODE 1 "not_postinc_memory_operand" "+S"))) @@ -154,10 +153,24 @@ (match_operand:I124MODE 3 "gr_reg_or_0_operand" "rO")] UNSPEC_CMPXCHG_ACQ))] "" + "cmpxchg<modesuffix>.acq %0 = %1, %r3, %2" + [(set_attr "itanium_class" "sem")]) + +(define_insn "cmpxchg_rel_<mode>" + [(set (match_operand:DI 0 "gr_register_operand" "=r") + (zero_extend:DI + (match_operand:I124MODE 1 "not_postinc_memory_operand" "+S"))) + (set (match_dup 1) + (unspec:I124MODE + [(match_dup 1) + (match_operand:DI 2 "ar_ccv_reg_operand" "") + (match_operand:I124MODE 3 "gr_reg_or_0_operand" "rO")] + UNSPEC_CMPXCHG_REL))] + "" "cmpxchg<modesuffix>.rel %0 = %1, %r3, %2" [(set_attr "itanium_class" "sem")]) -(define_insn "cmpxchg_rel_di" +(define_insn "cmpxchg_acq_di" [(set (match_operand:DI 0 "gr_register_operand" "=r") (match_operand:DI 1 "not_postinc_memory_operand" "+S")) (set (match_dup 1) @@ -166,10 +179,50 @@ (match_operand:DI 3 "gr_reg_or_0_operand" "rO")] UNSPEC_CMPXCHG_ACQ))] "" + "cmpxchg8.acq %0 = %1, %r3, %2" + [(set_attr "itanium_class" "sem")]) + +(define_insn "cmpxchg_rel_di" + [(set (match_operand:DI 0 "gr_register_operand" "=r") + (match_operand:DI 1 "not_postinc_memory_operand" "+S")) + (set (match_dup 1) + (unspec:DI [(match_dup 1) + (match_operand:DI 2 "ar_ccv_reg_operand" "") + (match_operand:DI 3 "gr_reg_or_0_operand" "rO")] + UNSPEC_CMPXCHG_REL))] + "" "cmpxchg8.rel %0 = %1, %r3, %2" [(set_attr "itanium_class" "sem")]) -(define_insn "sync_lock_test_and_set<mode>" +(define_expand "atomic_exchange<mode>" + [(match_operand:IMODE 0 "gr_register_operand" "") ;; output + (match_operand:IMODE 1 "not_postinc_memory_operand" "") ;; memory + (match_operand:IMODE 2 "gr_reg_or_0_operand" "") ;; input + (match_operand:SI 3 "const_int_operand" "")] ;; succ model + "" +{ + enum memmodel model = (enum memmodel) INTVAL (operands[3]); + + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_ACQUIRE: + case MEMMODEL_CONSUME: + break; + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_memory_barrier ()); + break; + default: + gcc_unreachable (); + } + emit_insn (gen_xchg_acq_<mode> (operands[0], operands[1], operands[2])); + DONE; +}) + +;; Note that XCHG is always memory model acquire. +(define_insn "xchg_acq_<mode>" [(set (match_operand:IMODE 0 "gr_register_operand" "=r") (match_operand:IMODE 1 "not_postinc_memory_operand" "+S")) (set (match_dup 1) @@ -178,10 +231,101 @@ "xchg<modesuffix> %0 = %1, %r2" [(set_attr "itanium_class" "sem")]) -(define_expand "sync_lock_release<mode>" +(define_expand "atomic_<fetchop_name><mode>" [(set (match_operand:IMODE 0 "memory_operand" "") - (match_operand:IMODE 1 "gr_reg_or_0_operand" ""))] + (FETCHOP:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" ""))) + (use (match_operand:SI 2 "const_int_operand" ""))] + "" +{ + ia64_expand_atomic_op (<CODE>, operands[0], operands[1], NULL, NULL, + (enum memmodel) INTVAL (operands[2])); + DONE; +}) + +(define_expand "atomic_nand<mode>" + [(set (match_operand:IMODE 0 "memory_operand" "") + (not:IMODE + (and:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "")))) + (use (match_operand:SI 2 "const_int_operand" ""))] + "" +{ + ia64_expand_atomic_op (NOT, operands[0], operands[1], NULL, NULL, + (enum memmodel) INTVAL (operands[2])); + DONE; +}) + +(define_expand "atomic_fetch_<fetchop_name><mode>" + [(set (match_operand:IMODE 0 "gr_register_operand" "") + (FETCHOP:IMODE + (match_operand:IMODE 1 "memory_operand" "") + (match_operand:IMODE 2 "nonmemory_operand" ""))) + (use (match_operand:SI 3 "const_int_operand" ""))] "" { - gcc_assert (MEM_VOLATILE_P (operands[0])); + ia64_expand_atomic_op (<CODE>, operands[1], operands[2], operands[0], NULL, + (enum memmodel) INTVAL (operands[3])); + DONE; +}) + +(define_expand "atomic_fetch_nand<mode>" + [(set (match_operand:IMODE 0 "gr_register_operand" "") + (not:IMODE + (and:IMODE (match_operand:IMODE 1 "memory_operand" "") + (match_operand:IMODE 2 "nonmemory_operand" "")))) + (use (match_operand:SI 3 "const_int_operand" ""))] + "" +{ + ia64_expand_atomic_op (NOT, operands[1], operands[2], operands[0], NULL, + (enum memmodel) INTVAL (operands[3])); + DONE; }) + +(define_expand "atomic_<fetchop_name>_fetch<mode>" + [(set (match_operand:IMODE 0 "gr_register_operand" "") + (FETCHOP:IMODE + (match_operand:IMODE 1 "memory_operand" "") + (match_operand:IMODE 2 "nonmemory_operand" ""))) + (use (match_operand:SI 3 "const_int_operand" ""))] + "" +{ + ia64_expand_atomic_op (<CODE>, operands[1], operands[2], NULL, operands[0], + (enum memmodel) INTVAL (operands[3])); + DONE; +}) + +(define_expand "atomic_nand_fetch<mode>" + [(set (match_operand:IMODE 0 "gr_register_operand" "") + (not:IMODE + (and:IMODE (match_operand:IMODE 1 "memory_operand" "") + (match_operand:IMODE 2 "nonmemory_operand" "")))) + (use (match_operand:SI 3 "const_int_operand" ""))] + "" +{ + ia64_expand_atomic_op (NOT, operands[1], operands[2], NULL, operands[0], + (enum memmodel) INTVAL (operands[3])); + DONE; +}) + +(define_insn "fetchadd_acq_<mode>" + [(set (match_operand:I48MODE 0 "gr_register_operand" "=r") + (match_operand:I48MODE 1 "not_postinc_memory_operand" "+S")) + (set (match_dup 1) + (unspec:I48MODE [(match_dup 1) + (match_operand:I48MODE 2 "fetchadd_operand" "n")] + UNSPEC_FETCHADD_ACQ))] + "" + "fetchadd<modesuffix>.acq %0 = %1, %2" + [(set_attr "itanium_class" "sem")]) + +(define_insn "fetchadd_rel_<mode>" + [(set (match_operand:I48MODE 0 "gr_register_operand" "=r") + (match_operand:I48MODE 1 "not_postinc_memory_operand" "+S")) + (set (match_dup 1) + (unspec:I48MODE [(match_dup 1) + (match_operand:I48MODE 2 "fetchadd_operand" "n")] + UNSPEC_FETCHADD_REL))] + "" + "fetchadd<modesuffix>.rel %0 = %1, %2" + [(set_attr "itanium_class" "sem")]) diff --git a/gcc/config/m68k/linux.h b/gcc/config/m68k/linux.h index 70738d221bf..325faf73a75 100644 --- a/gcc/config/m68k/linux.h +++ b/gcc/config/m68k/linux.h @@ -235,3 +235,7 @@ along with GCC; see the file COPYING3. If not see #undef WCHAR_TYPE_SIZE #define WCHAR_TYPE_SIZE BITS_PER_WORD + +/* Install the __sync libcalls. */ +#undef TARGET_INIT_LIBFUNCS +#define TARGET_INIT_LIBFUNCS m68k_init_sync_libfuncs diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index 51a04ed9c15..61267a80497 100644 --- a/gcc/config/m68k/m68k.c +++ b/gcc/config/m68k/m68k.c @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see #include "insn-codes.h" #include "ggc.h" #include "opts.h" +#include "optabs.h" enum reg_class regno_reg_class[] = { @@ -164,6 +165,7 @@ static rtx m68k_function_arg (cumulative_args_t, enum machine_mode, const_tree, bool); static bool m68k_cannot_force_const_mem (enum machine_mode mode, rtx x); static bool m68k_output_addr_const_extra (FILE *, rtx); +static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; /* Initialize the GCC target structure. */ @@ -6524,4 +6526,10 @@ m68k_conditional_register_usage (void) fixed_regs[PIC_REG] = call_used_regs[PIC_REG] = 1; } +static void +m68k_init_sync_libfuncs (void) +{ + init_sync_libfuncs (UNITS_PER_WORD); +} + #include "gt-m68k.h" diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md index 672ef0db6c0..e4b4b59afc2 100644 --- a/gcc/config/m68k/m68k.md +++ b/gcc/config/m68k/m68k.md @@ -124,6 +124,10 @@ (define_constants [(UNSPECV_BLOCKAGE 0) + (UNSPECV_CAS_1 1) + (UNSPECV_CAS_2 2) + (UNSPECV_TAS_1 3) + (UNSPECV_TAS_2 4) ]) ;; Registers by name. @@ -255,6 +259,10 @@ (const_int 0)] (const_int 1))) +;; Mode macros for integer operations. +(define_mode_iterator I [QI HI SI]) +(define_mode_attr sz [(QI "%.b") (HI "%.w") (SI "%.l")]) + ;; Mode macros for floating point operations. ;; Valid floating point modes (define_mode_iterator FP [SF DF (XF "TARGET_68881")]) @@ -7806,3 +7814,4 @@ [(set_attr "type" "ib")]) (include "cf.md") +(include "sync.md") diff --git a/gcc/config/m68k/sync.md b/gcc/config/m68k/sync.md new file mode 100644 index 00000000000..9a5bcda4c04 --- /dev/null +++ b/gcc/config/m68k/sync.md @@ -0,0 +1,80 @@ +;; GCC machine description for m68k synchronization instructions. +;; Copyright (C) 2011 +;; Free Software Foundation, Inc. +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + + +(define_expand "atomic_compare_and_swap<mode>" + [(match_operand:QI 0 "register_operand" "") ;; bool success output + (match_operand:I 1 "register_operand" "") ;; oldval output + (match_operand:I 2 "memory_operand" "") ;; memory + (match_operand:I 3 "register_operand" "") ;; expected input + (match_operand:I 4 "register_operand" "") ;; newval input + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; success model + (match_operand:SI 7 "const_int_operand" "")] ;; failure model + "TARGET_68020 || TARGET_68040" +{ + emit_insn (gen_atomic_compare_and_swap<mode>_1 + (operands[0], operands[1], operands[2], + operands[3], operands[4])); + emit_insn (gen_negqi2 (operands[0], operands[0])); + DONE; +}) + +(define_insn "atomic_compare_and_swap<mode>_1" + [(set (match_operand:I 1 "register_operand" "=d") + (unspec_volatile:I + [(match_operand:I 2 "memory_operand" "+m") + (match_operand:I 3 "register_operand" "0") + (match_operand:I 4 "register_operand" "d")] + UNSPECV_CAS_1)) + (set (match_dup 2) + (unspec_volatile:I + [(match_dup 2) (match_dup 3) (match_dup 4)] + UNSPECV_CAS_2)) + (set (match_operand:QI 0 "register_operand" "=d") + (unspec_volatile:QI + [(match_dup 2) (match_dup 3) (match_dup 4)] + UNSPECV_CAS_2))] + "TARGET_68020 || TARGET_68040" + ;; Elide the seq if operands[0] is dead. + "cas<sz> %1,%4,%2\;seq %0") + +(define_expand "sync_test_and_setqi" + [(match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "memory_operand" "") + (match_operand:QI 2 "general_operand" "")] + "!(TARGET_68020 || TARGET_68040)" +{ + if (operands[2] != const1_rtx) + FAIL; + emit_insn (gen_sync_test_and_setqi_1 (operands[0], operands[1])); + emit_insn (gen_negqi2 (operands[0], operands[0])); + DONE; +}) + +(define_insn "sync_test_and_setqi_1" + [(set (match_operand:QI 0 "register_operand" "=d") + (unspec_volatile:QI + [(match_operand:QI 1 "memory_operand" "+m")] + UNSPECV_TAS_1)) + (set (match_dup 1) + (unspec_volatile:QI [(match_dup 1)] UNSPECV_TAS_2))] + "!(TARGET_68020 || TARGET_68040)" + "tas %1\;sne %0") diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index b28b0b39165..dbabdffaef0 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -191,6 +191,9 @@ extern int mips_split_const_insns (rtx); extern int mips_load_store_insns (rtx, rtx); extern int mips_idiv_insns (void); extern rtx mips_emit_move (rtx, rtx); +#ifdef RTX_CODE +extern void mips_emit_binary (enum rtx_code, rtx, rtx, rtx); +#endif extern rtx mips_pic_base_register (rtx); extern rtx mips_got_load (rtx, rtx, enum mips_symbol_type); extern bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index ff72e28a35c..d3fd7097f57 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -2163,7 +2163,7 @@ static bool mips16_unextended_reference_p (enum machine_mode mode, rtx base, unsigned HOST_WIDE_INT offset) { - if (offset % GET_MODE_SIZE (mode) == 0) + if (mode != BLKmode && offset % GET_MODE_SIZE (mode) == 0) { if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx) return offset < 256U * GET_MODE_SIZE (mode); @@ -2398,7 +2398,7 @@ mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0) /* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */ -static void +void mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1) { emit_insn (gen_rtx_SET (VOIDmode, target, @@ -3777,6 +3777,16 @@ mips_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED, return false; case ZERO_EXTEND: + if (outer_code == SET + && ISA_HAS_BADDU + && (GET_CODE (XEXP (x, 0)) == TRUNCATE + || GET_CODE (XEXP (x, 0)) == SUBREG) + && GET_MODE (XEXP (x, 0)) == QImode + && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS) + { + *total = set_src_cost (XEXP (XEXP (x, 0), 0), speed); + return true; + } *total = mips_zero_extend_cost (mode, XEXP (x, 0)); return false; @@ -11218,9 +11228,13 @@ mips_init_libfuncs (void) } /* The MIPS16 ISA does not have an encoding for "sync", so we rely - on an external non-MIPS16 routine to implement __sync_synchronize. */ + on an external non-MIPS16 routine to implement __sync_synchronize. + Similarly for the rest of the ll/sc libfuncs. */ if (TARGET_MIPS16) - synchronize_libfunc = init_one_libfunc ("__sync_synchronize"); + { + synchronize_libfunc = init_one_libfunc ("__sync_synchronize"); + init_sync_libfuncs (UNITS_PER_WORD); + } } /* Build up a multi-insn sequence that loads label TARGET into $AT. */ @@ -15246,6 +15260,11 @@ mips_set_mips16_mode (int mips16_p) /* MIPS16 has no BAL instruction. */ target_flags &= ~MASK_RELAX_PIC_CALLS; + /* The R4000 errata don't apply to any known MIPS16 cores. + It's simpler to make the R4000 fixes and MIPS16 mode + mutually exclusive. */ + target_flags &= ~MASK_FIX_R4000; + if (flag_pic && !TARGET_OLDABI) sorry ("MIPS16 PIC for ABIs other than o32 and o64"); @@ -15831,38 +15850,33 @@ mips_conditional_register_usage (void) global_regs[CCDSP_PO_REGNUM] = 1; global_regs[CCDSP_SC_REGNUM] = 1; } - else - { - int regno; + else + AND_COMPL_HARD_REG_SET (accessible_reg_set, + reg_class_contents[(int) DSP_ACC_REGS]); - for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++) - fixed_regs[regno] = call_used_regs[regno] = 1; - } if (!TARGET_HARD_FLOAT) { - int regno; - - for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) - fixed_regs[regno] = call_used_regs[regno] = 1; - for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++) - fixed_regs[regno] = call_used_regs[regno] = 1; + AND_COMPL_HARD_REG_SET (accessible_reg_set, + reg_class_contents[(int) FP_REGS]); + AND_COMPL_HARD_REG_SET (accessible_reg_set, + reg_class_contents[(int) ST_REGS]); } - else if (! ISA_HAS_8CC) + else if (!ISA_HAS_8CC) { - int regno; - /* We only have a single condition-code register. We implement this by fixing all the condition-code registers and generating RTL that refers directly to ST_REG_FIRST. */ - for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++) - fixed_regs[regno] = call_used_regs[regno] = 1; + AND_COMPL_HARD_REG_SET (accessible_reg_set, + reg_class_contents[(int) ST_REGS]); + SET_HARD_REG_BIT (accessible_reg_set, FPSW_REGNUM); + fixed_regs[FPSW_REGNUM] = call_used_regs[FPSW_REGNUM] = 1; } - /* In MIPS16 mode, we permit the $t temporary registers to be used - for reload. We prohibit the unused $s registers, since they - are call-saved, and saving them via a MIPS16 register would - probably waste more time than just reloading the value. */ if (TARGET_MIPS16) { + /* In MIPS16 mode, we permit the $t temporary registers to be used + for reload. We prohibit the unused $s registers, since they + are call-saved, and saving them via a MIPS16 register would + probably waste more time than just reloading the value. */ fixed_regs[18] = call_used_regs[18] = 1; fixed_regs[19] = call_used_regs[19] = 1; fixed_regs[20] = call_used_regs[20] = 1; @@ -15872,6 +15886,12 @@ mips_conditional_register_usage (void) fixed_regs[26] = call_used_regs[26] = 1; fixed_regs[27] = call_used_regs[27] = 1; fixed_regs[30] = call_used_regs[30] = 1; + + /* Do not allow HI and LO to be treated as register operands. + There are no MTHI or MTLO instructions (or any real need + for them) and one-way registers cannot easily be reloaded. */ + AND_COMPL_HARD_REG_SET (operand_reg_set, + reg_class_contents[(int) MD_REGS]); } /* $f20-$f23 are call-clobbered for n64. */ if (mips_abi == ABI_64) @@ -16057,12 +16077,20 @@ mips_mulsidi3_gen_fn (enum rtx_code ext_code) case we still expand mulsidi3 for DMUL. */ if (ISA_HAS_DMUL3) return signed_p ? gen_mulsidi3_64bit_dmul : NULL; + if (TARGET_MIPS16) + return (signed_p + ? gen_mulsidi3_64bit_mips16 + : gen_umulsidi3_64bit_mips16); if (TARGET_FIX_R4000) return NULL; return signed_p ? gen_mulsidi3_64bit : gen_umulsidi3_64bit; } else { + if (TARGET_MIPS16) + return (signed_p + ? gen_mulsidi3_32bit_mips16 + : gen_umulsidi3_32bit_mips16); if (TARGET_FIX_R4000 && !ISA_HAS_DSP) return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000; return signed_p ? gen_mulsidi3_32bit : gen_umulsidi3_32bit; diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index c2211a3c170..55b7fffb665 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -1331,11 +1331,19 @@ (match_operand:GPR 2 "register_operand")))] "" { + rtx lo; + if (TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A) emit_insn (gen_mul<mode>3_mul3_loongson (operands[0], operands[1], operands[2])); else if (ISA_HAS_<D>MUL3) emit_insn (gen_mul<mode>3_mul3 (operands[0], operands[1], operands[2])); + else if (TARGET_MIPS16) + { + lo = gen_rtx_REG (<MODE>mode, LO_REGNUM); + emit_insn (gen_mul<mode>3_internal (lo, operands[1], operands[2])); + emit_move_insn (operands[0], lo); + } else if (TARGET_FIX_R4000) emit_insn (gen_mul<mode>3_r4000 (operands[0], operands[1], operands[2])); else @@ -1398,7 +1406,7 @@ (clobber (match_dup 0))])]) (define_insn "mul<mode>3_internal" - [(set (match_operand:GPR 0 "register_operand" "=l") + [(set (match_operand:GPR 0 "muldiv_target_operand" "=l") (mult:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand:GPR 2 "register_operand" "d")))] "!TARGET_FIX_R4000" @@ -1575,7 +1583,7 @@ ;; Patterns generated by the define_peephole2 below. (define_insn "*macc2" - [(set (match_operand:SI 0 "register_operand" "=l") + [(set (match_operand:SI 0 "muldiv_target_operand" "=l") (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d") (match_operand:SI 2 "register_operand" "d")) (match_dup 0))) @@ -1589,7 +1597,7 @@ (set_attr "mode" "SI")]) (define_insn "*msac2" - [(set (match_operand:SI 0 "register_operand" "=l") + [(set (match_operand:SI 0 "muldiv_target_operand" "=l") (minus:SI (match_dup 0) (mult:SI (match_operand:SI 1 "register_operand" "d") (match_operand:SI 2 "register_operand" "d")))) @@ -1744,11 +1752,25 @@ DONE; }) +(define_expand "<u>mulsidi3_32bit_mips16" + [(set (match_operand:DI 0 "register_operand") + (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand")) + (any_extend:DI (match_operand:SI 2 "register_operand"))))] + "!TARGET_64BIT && TARGET_MIPS16" +{ + rtx hilo; + + hilo = gen_rtx_REG (DImode, MD_REG_FIRST); + emit_insn (gen_<u>mulsidi3_32bit (hilo, operands[1], operands[2])); + emit_move_insn (operands[0], hilo); + DONE; +}) + ;; As well as being named patterns, these instructions are used by the ;; __builtin_mips_mult<u>() functions. We must always make those functions ;; available if !TARGET_64BIT && ISA_HAS_DSP. (define_insn "<u>mulsidi3_32bit" - [(set (match_operand:DI 0 "register_operand" "=ka") + [(set (match_operand:DI 0 "muldiv_target_operand" "=ka") (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))] "!TARGET_64BIT && (!TARGET_FIX_R4000 || ISA_HAS_DSP)" @@ -1766,20 +1788,27 @@ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) (any_extend:DI (match_operand:SI 2 "register_operand" "d")))) (clobber (match_scratch:DI 3 "=x"))] - "!TARGET_64BIT && TARGET_FIX_R4000" + "!TARGET_64BIT && TARGET_FIX_R4000 && !ISA_HAS_DSP" "mult<u>\t%1,%2\;mflo\t%L0\;mfhi\t%M0" [(set_attr "type" "imul") (set_attr "mode" "SI") (set_attr "length" "12")]) -(define_insn "<u>mulsidi3_64bit" +(define_insn_and_split "<u>mulsidi3_64bit" [(set (match_operand:DI 0 "register_operand" "=d") (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) (any_extend:DI (match_operand:SI 2 "register_operand" "d")))) (clobber (match_scratch:TI 3 "=x")) (clobber (match_scratch:DI 4 "=d"))] - "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_DMUL3" + "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_DMUL3 && !TARGET_MIPS16" "#" + "&& reload_completed" + [(const_int 0)] +{ + emit_insn (gen_<u>mulsidi3_64bit_split (operands[0], operands[1], + operands[2], operands[4])); + DONE; +} [(set_attr "type" "imul") (set_attr "mode" "SI") (set (attr "length") @@ -1787,63 +1816,52 @@ (const_int 16) (const_int 28)))]) -(define_split - [(set (match_operand:DI 0 "d_operand") - (mult:DI (any_extend:DI (match_operand:SI 1 "d_operand")) - (any_extend:DI (match_operand:SI 2 "d_operand")))) - (clobber (match_operand:TI 3 "hilo_operand")) - (clobber (match_operand:DI 4 "d_operand"))] - "TARGET_64BIT && !TARGET_FIX_R4000 && ISA_HAS_EXT_INS && reload_completed" - [(set (match_dup 3) - (unspec:TI [(mult:DI (any_extend:DI (match_dup 1)) - (any_extend:DI (match_dup 2)))] - UNSPEC_SET_HILO)) - - ;; OP0 <- LO, OP4 <- HI - (set (match_dup 0) (match_dup 5)) - (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI)) - - (set (zero_extract:DI (match_dup 0) (const_int 32) (const_int 32)) - (match_dup 4))] - { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); }) +(define_expand "<u>mulsidi3_64bit_mips16" + [(set (match_operand:DI 0 "register_operand") + (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand")) + (any_extend:DI (match_operand:SI 2 "register_operand"))))] + "TARGET_64BIT && TARGET_MIPS16" +{ + emit_insn (gen_<u>mulsidi3_64bit_split (operands[0], operands[1], + operands[2], gen_reg_rtx (DImode))); + DONE; +}) -(define_split - [(set (match_operand:DI 0 "d_operand") - (mult:DI (any_extend:DI (match_operand:SI 1 "d_operand")) - (any_extend:DI (match_operand:SI 2 "d_operand")))) - (clobber (match_operand:TI 3 "hilo_operand")) - (clobber (match_operand:DI 4 "d_operand"))] - "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_EXT_INS && reload_completed" - [(set (match_dup 3) - (unspec:TI [(mult:DI (any_extend:DI (match_dup 1)) - (any_extend:DI (match_dup 2)))] - UNSPEC_SET_HILO)) - - ;; OP0 <- LO, OP4 <- HI - (set (match_dup 0) (match_dup 5)) - (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI)) - - ;; Zero-extend OP0. - (set (match_dup 0) - (ashift:DI (match_dup 0) - (const_int 32))) - (set (match_dup 0) - (lshiftrt:DI (match_dup 0) - (const_int 32))) +(define_expand "<u>mulsidi3_64bit_split" + [(set (match_operand:DI 0 "register_operand") + (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand")) + (any_extend:DI (match_operand:SI 2 "register_operand")))) + (clobber (match_operand:DI 3 "register_operand"))] + "" +{ + rtx hilo; - ;; Shift OP4 into place. - (set (match_dup 4) - (ashift:DI (match_dup 4) - (const_int 32))) + hilo = gen_rtx_REG (TImode, MD_REG_FIRST); + emit_insn (gen_<u>mulsidi3_64bit_hilo (hilo, operands[1], operands[2])); - ;; OR the two halves together - (set (match_dup 0) - (ior:DI (match_dup 0) - (match_dup 4)))] - { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); }) + emit_move_insn (operands[0], gen_rtx_REG (DImode, LO_REGNUM)); + emit_insn (gen_mfhidi_ti (operands[3], hilo)); + + if (ISA_HAS_EXT_INS) + emit_insn (gen_insvdi (operands[0], GEN_INT (32), GEN_INT (32), + operands[3])); + else + { + /* Zero-extend the low part. */ + mips_emit_binary (ASHIFT, operands[0], operands[0], GEN_INT (32)); + mips_emit_binary (LSHIFTRT, operands[0], operands[0], GEN_INT (32)); + + /* Shift the high part into place. */ + mips_emit_binary (ASHIFT, operands[3], operands[3], GEN_INT (32)); + + /* OR the two halves together. */ + mips_emit_binary (IOR, operands[0], operands[0], operands[3]); + } + DONE; +}) (define_insn "<u>mulsidi3_64bit_hilo" - [(set (match_operand:TI 0 "register_operand" "=x") + [(set (match_operand:TI 0 "muldiv_target_operand" "=x") (unspec:TI [(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) @@ -1867,7 +1885,7 @@ ;; Widening multiply with negation. (define_insn "*muls<u>_di" - [(set (match_operand:DI 0 "register_operand" "=x") + [(set (match_operand:DI 0 "muldiv_target_operand" "=x") (neg:DI (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) @@ -1885,9 +1903,9 @@ ;; in GENERATE_MADD_MSUB for -mno-dsp, but always ignore them for -mdsp, ;; even if !ISA_HAS_DSP_MULT. (define_insn "<u>msubsidi4" - [(set (match_operand:DI 0 "register_operand" "=ka") + [(set (match_operand:DI 0 "muldiv_target_operand" "=ka") (minus:DI - (match_operand:DI 3 "register_operand" "0") + (match_operand:DI 3 "muldiv_target_operand" "0") (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) (any_extend:DI (match_operand:SI 2 "register_operand" "d")))))] @@ -1918,6 +1936,9 @@ emit_insn (gen_<su>mulsi3_highpart_mulhi_internal (operands[0], operands[1], operands[2])); + else if (TARGET_MIPS16) + emit_insn (gen_<su>mulsi3_highpart_split (operands[0], operands[1], + operands[2])); else emit_insn (gen_<su>mulsi3_highpart_internal (operands[0], operands[1], operands[2])); @@ -1932,11 +1953,28 @@ (any_extend:DI (match_operand:SI 2 "register_operand" "d"))) (const_int 32)))) (clobber (match_scratch:SI 3 "=l"))] - "!ISA_HAS_MULHI" + "!ISA_HAS_MULHI && !TARGET_MIPS16" { return TARGET_FIX_R4000 ? "mult<u>\t%1,%2\n\tmfhi\t%0" : "#"; } "&& reload_completed && !TARGET_FIX_R4000" [(const_int 0)] { + emit_insn (gen_<su>mulsi3_highpart_split (operands[0], operands[1], + operands[2])); + DONE; +} + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "8")]) + +(define_expand "<su>mulsi3_highpart_split" + [(set (match_operand:SI 0 "register_operand") + (truncate:SI + (lshiftrt:DI + (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand")) + (any_extend:DI (match_operand:SI 2 "register_operand"))) + (const_int 32))))] + "" +{ rtx hilo; if (TARGET_64BIT) @@ -1952,10 +1990,7 @@ emit_insn (gen_mfhisi_di (operands[0], hilo)); } DONE; -} - [(set_attr "type" "imul") - (set_attr "mode" "SI") - (set_attr "length" "8")]) +}) (define_insn "<su>mulsi3_highpart_mulhi_internal" [(set (match_operand:SI 0 "register_operand" "=d") @@ -1989,7 +2024,25 @@ ;; Disable unsigned multiplication for -mfix-vr4120. This is for VR4120 ;; errata MD(0), which says that dmultu does not always produce the ;; correct result. -(define_insn_and_split "<su>muldi3_highpart" +(define_expand "<su>muldi3_highpart" + [(set (match_operand:DI 0 "register_operand") + (truncate:DI + (lshiftrt:TI + (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand")) + (any_extend:TI (match_operand:DI 2 "register_operand"))) + (const_int 64))))] + "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)" +{ + if (TARGET_MIPS16) + emit_insn (gen_<su>muldi3_highpart_split (operands[0], operands[1], + operands[2])); + else + emit_insn (gen_<su>muldi3_highpart_internal (operands[0], operands[1], + operands[2])); + DONE; +}) + +(define_insn_and_split "<su>muldi3_highpart_internal" [(set (match_operand:DI 0 "register_operand" "=d") (truncate:DI (lshiftrt:TI @@ -1997,21 +2050,37 @@ (any_extend:TI (match_operand:DI 2 "register_operand" "d"))) (const_int 64)))) (clobber (match_scratch:DI 3 "=l"))] - "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)" + "TARGET_64BIT + && !TARGET_MIPS16 + && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)" { return TARGET_FIX_R4000 ? "dmult<u>\t%1,%2\n\tmfhi\t%0" : "#"; } "&& reload_completed && !TARGET_FIX_R4000" [(const_int 0)] { + emit_insn (gen_<su>muldi3_highpart_split (operands[0], operands[1], + operands[2])); + DONE; +} + [(set_attr "type" "imul") + (set_attr "mode" "DI") + (set_attr "length" "8")]) + +(define_expand "<su>muldi3_highpart_split" + [(set (match_operand:DI 0 "register_operand") + (truncate:DI + (lshiftrt:TI + (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand")) + (any_extend:TI (match_operand:DI 2 "register_operand"))) + (const_int 64))))] + "" +{ rtx hilo; hilo = gen_rtx_REG (TImode, MD_REG_FIRST); emit_insn (gen_<u>mulditi3_internal (hilo, operands[1], operands[2])); emit_insn (gen_mfhidi_ti (operands[0], hilo)); DONE; -} - [(set_attr "type" "imul") - (set_attr "mode" "DI") - (set_attr "length" "8")]) +}) (define_expand "<u>mulditi3" [(set (match_operand:TI 0 "register_operand") @@ -2019,7 +2088,15 @@ (any_extend:TI (match_operand:DI 2 "register_operand"))))] "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)" { - if (TARGET_FIX_R4000) + rtx hilo; + + if (TARGET_MIPS16) + { + hilo = gen_rtx_REG (TImode, MD_REG_FIRST); + emit_insn (gen_<u>mulditi3_internal (hilo, operands[1], operands[2])); + emit_move_insn (operands[0], hilo); + } + else if (TARGET_FIX_R4000) emit_insn (gen_<u>mulditi3_r4000 (operands[0], operands[1], operands[2])); else emit_insn (gen_<u>mulditi3_internal (operands[0], operands[1], @@ -2028,7 +2105,7 @@ }) (define_insn "<u>mulditi3_internal" - [(set (match_operand:TI 0 "register_operand" "=x") + [(set (match_operand:TI 0 "muldiv_target_operand" "=x") (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand" "d")) (any_extend:TI (match_operand:DI 2 "register_operand" "d"))))] "TARGET_64BIT @@ -2067,11 +2144,11 @@ ;; See the comment above <u>msubsidi4 for the relationship between ;; ISA_HAS_DSP and ISA_HAS_DSP_MULT. (define_insn "<u>maddsidi4" - [(set (match_operand:DI 0 "register_operand" "=ka") + [(set (match_operand:DI 0 "muldiv_target_operand" "=ka") (plus:DI (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d")) (any_extend:DI (match_operand:SI 2 "register_operand" "d"))) - (match_operand:DI 3 "register_operand" "0")))] + (match_operand:DI 3 "muldiv_target_operand" "0")))] "(TARGET_MAD || ISA_HAS_MACC || GENERATE_MADD_MSUB || ISA_HAS_DSP) && !TARGET_64BIT" { @@ -2311,72 +2388,113 @@ ;; VR4120 errata MD(A1): signed division instructions do not work correctly ;; with negative operands. We use special libgcc functions instead. -(define_insn_and_split "divmod<mode>4" - [(set (match_operand:GPR 0 "register_operand" "=l") +(define_expand "divmod<mode>4" + [(set (match_operand:GPR 0 "register_operand") + (div:GPR (match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "register_operand"))) + (set (match_operand:GPR 3 "register_operand") + (mod:GPR (match_dup 1) + (match_dup 2)))] + "!TARGET_FIX_VR4120" +{ + if (TARGET_MIPS16) + { + emit_insn (gen_divmod<mode>4_split (operands[3], operands[1], + operands[2])); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, LO_REGNUM)); + } + else + emit_insn (gen_divmod<mode>4_internal (operands[0], operands[1], + operands[2], operands[3])); + DONE; +}) + +(define_insn_and_split "divmod<mode>4_internal" + [(set (match_operand:GPR 0 "muldiv_target_operand" "=l") (div:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand:GPR 2 "register_operand" "d"))) (set (match_operand:GPR 3 "register_operand" "=d") (mod:GPR (match_dup 1) (match_dup 2)))] - "!TARGET_FIX_VR4120" + "!TARGET_FIX_VR4120 && !TARGET_MIPS16" "#" "&& reload_completed" [(const_int 0)] { - rtx hilo; - - if (TARGET_64BIT) - { - hilo = gen_rtx_REG (TImode, MD_REG_FIRST); - emit_insn (gen_divmod<mode>4_hilo_ti (hilo, operands[1], operands[2])); - emit_insn (gen_mfhi<mode>_ti (operands[3], hilo)); - } - else - { - hilo = gen_rtx_REG (DImode, MD_REG_FIRST); - emit_insn (gen_divmod<mode>4_hilo_di (hilo, operands[1], operands[2])); - emit_insn (gen_mfhi<mode>_di (operands[3], hilo)); - } + emit_insn (gen_divmod<mode>4_split (operands[3], operands[1], operands[2])); DONE; } [(set_attr "type" "idiv") (set_attr "mode" "<MODE>") (set_attr "length" "8")]) -(define_insn_and_split "udivmod<mode>4" - [(set (match_operand:GPR 0 "register_operand" "=l") +(define_expand "udivmod<mode>4" + [(set (match_operand:GPR 0 "register_operand") + (udiv:GPR (match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "register_operand"))) + (set (match_operand:GPR 3 "register_operand") + (umod:GPR (match_dup 1) + (match_dup 2)))] + "" +{ + if (TARGET_MIPS16) + { + emit_insn (gen_udivmod<mode>4_split (operands[3], operands[1], + operands[2])); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, LO_REGNUM)); + } + else + emit_insn (gen_udivmod<mode>4_internal (operands[0], operands[1], + operands[2], operands[3])); + DONE; +}) + +(define_insn_and_split "udivmod<mode>4_internal" + [(set (match_operand:GPR 0 "muldiv_target_operand" "=l") (udiv:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand:GPR 2 "register_operand" "d"))) (set (match_operand:GPR 3 "register_operand" "=d") (umod:GPR (match_dup 1) (match_dup 2)))] - "" + "!TARGET_MIPS16" "#" "reload_completed" [(const_int 0)] { + emit_insn (gen_udivmod<mode>4_split (operands[3], operands[1], operands[2])); + DONE; +} + [(set_attr "type" "idiv") + (set_attr "mode" "<MODE>") + (set_attr "length" "8")]) + +(define_expand "<u>divmod<mode>4_split" + [(set (match_operand:GPR 0 "register_operand") + (any_mod:GPR (match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "register_operand")))] + "" +{ rtx hilo; if (TARGET_64BIT) { hilo = gen_rtx_REG (TImode, MD_REG_FIRST); - emit_insn (gen_udivmod<mode>4_hilo_ti (hilo, operands[1], operands[2])); - emit_insn (gen_mfhi<mode>_ti (operands[3], hilo)); + emit_insn (gen_<u>divmod<mode>4_hilo_ti (hilo, operands[1], + operands[2])); + emit_insn (gen_mfhi<mode>_ti (operands[0], hilo)); } else { hilo = gen_rtx_REG (DImode, MD_REG_FIRST); - emit_insn (gen_udivmod<mode>4_hilo_di (hilo, operands[1], operands[2])); - emit_insn (gen_mfhi<mode>_di (operands[3], hilo)); + emit_insn (gen_<u>divmod<mode>4_hilo_di (hilo, operands[1], + operands[2])); + emit_insn (gen_mfhi<mode>_di (operands[0], hilo)); } DONE; -} - [(set_attr "type" "idiv") - (set_attr "mode" "<MODE>") - (set_attr "length" "8")]) +}) (define_insn "<u>divmod<GPR:mode>4_hilo_<HILO:mode>" - [(set (match_operand:HILO 0 "register_operand" "=x") + [(set (match_operand:HILO 0 "muldiv_target_operand" "=x") (unspec:HILO [(any_div:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand:GPR 2 "register_operand" "d"))] @@ -4590,7 +4708,7 @@ ;; and the errata related to -mfix-vr4130. (define_insn "mfhi<GPR:mode>_<HILO:mode>" [(set (match_operand:GPR 0 "register_operand" "=d") - (unspec:GPR [(match_operand:HILO 1 "register_operand" "x")] + (unspec:GPR [(match_operand:HILO 1 "hilo_operand" "x")] UNSPEC_MFHI))] "" { return ISA_HAS_MACCHI ? "<GPR:d>macchi\t%0,%.,%." : "mfhi\t%0"; } diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md index dd5148067cd..5e9398e69f3 100644 --- a/gcc/config/mips/predicates.md +++ b/gcc/config/mips/predicates.md @@ -127,6 +127,11 @@ (and (match_code "reg,subreg") (match_test "ST_REG_P (true_regnum (op))"))) +(define_predicate "muldiv_target_operand" + (if_then_else (match_test "TARGET_MIPS16") + (match_operand 0 "hilo_operand") + (match_operand 0 "register_operand"))) + (define_special_predicate "pc_or_label_operand" (match_code "pc,label_ref")) @@ -189,7 +194,9 @@ }) (define_predicate "move_operand" - (match_operand 0 "general_operand") + ;; Allow HI and LO to be used as the source of a MIPS16 move. + (ior (match_operand 0 "general_operand") + (match_operand 0 "hilo_operand")) { enum mips_symbol_type symbol_type; diff --git a/gcc/config/pa/pa-linux.h b/gcc/config/pa/pa-linux.h index addc0e18c8b..6cc68a5c6e1 100644 --- a/gcc/config/pa/pa-linux.h +++ b/gcc/config/pa/pa-linux.h @@ -133,7 +133,6 @@ along with GCC; see the file COPYING3. If not see } \ while (0) -/* Linux always uses gas. */ #undef TARGET_GAS #define TARGET_GAS 1 diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index 23d2d2aa389..af4c95488de 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -103,13 +103,9 @@ extern rtx rs6000_emit_set_const (rtx, enum machine_mode, rtx, int); extern int rs6000_emit_cmove (rtx, rtx, rtx, rtx); extern int rs6000_emit_vector_cond_expr (rtx, rtx, rtx, rtx, rtx, rtx); extern void rs6000_emit_minmax (rtx, enum rtx_code, rtx, rtx); -extern void rs6000_emit_sync (enum rtx_code, enum machine_mode, - rtx, rtx, rtx, rtx, bool); -extern void rs6000_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx); -extern void rs6000_split_compare_and_swap (rtx, rtx, rtx, rtx, rtx); -extern void rs6000_expand_compare_and_swapqhi (rtx, rtx, rtx, rtx); -extern void rs6000_split_compare_and_swapqhi (rtx, rtx, rtx, rtx, rtx, rtx); -extern void rs6000_split_lock_test_and_set (rtx, rtx, rtx, rtx); +extern void rs6000_expand_atomic_compare_and_swap (rtx op[]); +extern void rs6000_expand_atomic_exchange (rtx op[]); +extern void rs6000_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx); extern void rs6000_emit_swdiv (rtx, rtx, rtx, bool); extern void rs6000_emit_swrsqrt (rtx, rtx); extern void output_toc (FILE *, rtx, int, enum machine_mode); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 87c84d9844e..f01353b8942 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -17133,199 +17133,6 @@ rs6000_emit_minmax (rtx dest, enum rtx_code code, rtx op0, rtx op1) emit_move_insn (dest, target); } -/* Emit instructions to perform a load-reserved/store-conditional operation. - The operation performed is an atomic - (set M (CODE:MODE M OP)) - If not NULL, BEFORE is atomically set to M before the operation, and - AFTER is set to M after the operation (that is, (CODE:MODE M OP)). - If SYNC_P then a memory barrier is emitted before the operation. - Either OP or M may be wrapped in a NOT operation. */ - -void -rs6000_emit_sync (enum rtx_code code, enum machine_mode mode, - rtx m, rtx op, rtx before_param, rtx after_param, - bool sync_p) -{ - enum machine_mode used_mode; - rtx the_op, set_before, set_after, set_atomic, cc_scratch, before, after; - rtx used_m; - rtvec vec; - HOST_WIDE_INT imask = GET_MODE_MASK (mode); - rtx shift = NULL_RTX; - - if (sync_p) - emit_insn (gen_lwsync ()); - - used_m = m; - - /* If this is smaller than SImode, we'll have to use SImode with - adjustments. */ - if (mode == QImode || mode == HImode) - { - rtx newop, oldop; - - if (MEM_ALIGN (used_m) >= 32) - { - int ishift = 0; - if (BYTES_BIG_ENDIAN) - ishift = GET_MODE_BITSIZE (SImode) - GET_MODE_BITSIZE (mode); - - shift = GEN_INT (ishift); - used_m = change_address (used_m, SImode, 0); - } - else - { - rtx addrSI, aligned_addr; - int shift_mask = mode == QImode ? 0x18 : 0x10; - - addrSI = gen_lowpart_common (SImode, - force_reg (Pmode, XEXP (used_m, 0))); - addrSI = force_reg (SImode, addrSI); - shift = gen_reg_rtx (SImode); - - emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3), - GEN_INT (shift_mask))); - emit_insn (gen_xorsi3 (shift, shift, GEN_INT (shift_mask))); - - aligned_addr = expand_binop (Pmode, and_optab, - XEXP (used_m, 0), - GEN_INT (-4), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - used_m = change_address (used_m, SImode, aligned_addr); - set_mem_align (used_m, 32); - } - /* It's safe to keep the old alias set of USED_M, because - the operation is atomic and only affects the original - USED_M. */ - m = used_m; - - if (GET_CODE (op) == NOT) - { - oldop = lowpart_subreg (SImode, XEXP (op, 0), mode); - oldop = gen_rtx_NOT (SImode, oldop); - } - else - oldop = lowpart_subreg (SImode, op, mode); - - switch (code) - { - case IOR: - case XOR: - newop = expand_binop (SImode, and_optab, - oldop, GEN_INT (imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_ashlsi3 (newop, newop, shift)); - break; - - case NOT: /* NAND */ - newop = expand_binop (SImode, ior_optab, - oldop, GEN_INT (~imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_rotlsi3 (newop, newop, shift)); - break; - - case AND: - newop = expand_binop (SImode, ior_optab, - oldop, GEN_INT (~imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_rotlsi3 (newop, newop, shift)); - break; - - case PLUS: - case MINUS: - { - rtx mask; - - newop = expand_binop (SImode, and_optab, - oldop, GEN_INT (imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_ashlsi3 (newop, newop, shift)); - - mask = gen_reg_rtx (SImode); - emit_move_insn (mask, GEN_INT (imask)); - emit_insn (gen_ashlsi3 (mask, mask, shift)); - - if (code == PLUS) - newop = gen_rtx_PLUS (SImode, m, newop); - else - newop = gen_rtx_MINUS (SImode, m, newop); - newop = gen_rtx_AND (SImode, newop, mask); - newop = gen_rtx_IOR (SImode, newop, - gen_rtx_AND (SImode, - gen_rtx_NOT (SImode, mask), - m)); - break; - } - - default: - gcc_unreachable (); - } - - op = newop; - used_mode = SImode; - before = gen_reg_rtx (used_mode); - after = gen_reg_rtx (used_mode); - } - else - { - used_mode = mode; - before = before_param; - after = after_param; - - if (before == NULL_RTX) - before = gen_reg_rtx (used_mode); - if (after == NULL_RTX) - after = gen_reg_rtx (used_mode); - } - - if ((code == PLUS || code == MINUS) - && used_mode != mode) - the_op = op; /* Computed above. */ - else if (GET_CODE (op) == NOT && GET_CODE (m) != NOT) - the_op = gen_rtx_fmt_ee (code, used_mode, op, m); - else if (code == NOT) - the_op = gen_rtx_fmt_ee (IOR, used_mode, - gen_rtx_NOT (used_mode, m), - gen_rtx_NOT (used_mode, op)); - else - the_op = gen_rtx_fmt_ee (code, used_mode, m, op); - - set_after = gen_rtx_SET (VOIDmode, after, the_op); - set_before = gen_rtx_SET (VOIDmode, before, used_m); - set_atomic = gen_rtx_SET (VOIDmode, used_m, - gen_rtx_UNSPEC (used_mode, - gen_rtvec (1, the_op), - UNSPEC_SYNC_OP)); - cc_scratch = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (CCmode)); - - if ((code == PLUS || code == MINUS) && used_mode != mode) - vec = gen_rtvec (5, set_after, set_before, set_atomic, cc_scratch, - gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))); - else - vec = gen_rtvec (4, set_after, set_before, set_atomic, cc_scratch); - emit_insn (gen_rtx_PARALLEL (VOIDmode, vec)); - - /* Shift and mask the return values properly. */ - if (used_mode != mode && before_param) - { - emit_insn (gen_lshrsi3 (before, before, shift)); - convert_move (before_param, before, 1); - } - - if (used_mode != mode && after_param) - { - emit_insn (gen_lshrsi3 (after, after, shift)); - convert_move (after_param, after, 1); - } - - /* The previous sequence will end with a branch that's dependent on - the conditional store, so placing an isync will ensure that no - other instructions (especially, no load or store instructions) - can start before the atomic operation completes. */ - if (sync_p) - emit_insn (gen_isync ()); -} - /* A subroutine of the atomic operation splitters. Jump to LABEL if COND is true. Mark the jump as unlikely to be taken. */ @@ -17347,10 +17154,18 @@ static void emit_load_locked (enum machine_mode mode, rtx reg, rtx mem) { rtx (*fn) (rtx, rtx) = NULL; - if (mode == SImode) - fn = gen_load_locked_si; - else if (mode == DImode) - fn = gen_load_locked_di; + + switch (mode) + { + case SImode: + fn = gen_load_lockedsi; + break; + case DImode: + fn = gen_load_lockeddi; + break; + default: + gcc_unreachable (); + } emit_insn (fn (reg, mem)); } @@ -17361,214 +17176,406 @@ static void emit_store_conditional (enum machine_mode mode, rtx res, rtx mem, rtx val) { rtx (*fn) (rtx, rtx, rtx) = NULL; - if (mode == SImode) - fn = gen_store_conditional_si; - else if (mode == DImode) - fn = gen_store_conditional_di; + + switch (mode) + { + case SImode: + fn = gen_store_conditionalsi; + break; + case DImode: + fn = gen_store_conditionaldi; + break; + default: + gcc_unreachable (); + } /* Emit sync before stwcx. to address PPC405 Erratum. */ if (PPC405_ERRATUM77) - emit_insn (gen_memory_barrier ()); + emit_insn (gen_hwsync ()); emit_insn (fn (res, mem, val)); } -/* Expand an atomic fetch-and-operate pattern. CODE is the binary operation - to perform. MEM is the memory on which to operate. VAL is the second - operand of the binary operator. BEFORE and AFTER are optional locations to - return the value of MEM either before of after the operation. SCRATCH is - a scratch register. */ +/* Expand barriers before and after a load_locked/store_cond sequence. */ -void -rs6000_split_atomic_op (enum rtx_code code, rtx mem, rtx val, - rtx before, rtx after, rtx scratch) +static void +rs6000_pre_atomic_barrier (enum memmodel model) { - enum machine_mode mode = GET_MODE (mem); - rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_ACQUIRE: + break; + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + emit_insn (gen_lwsync ()); + break; + case MEMMODEL_SEQ_CST: + emit_insn (gen_hwsync ()); + break; + default: + gcc_unreachable (); + } +} - emit_insn (gen_lwsync ()); +static void +rs6000_post_atomic_barrier (enum memmodel model) +{ + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_RELEASE: + break; + case MEMMODEL_ACQUIRE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + emit_insn (gen_isync ()); + break; + default: + gcc_unreachable (); + } +} - label = gen_label_rtx (); - emit_label (label); - label = gen_rtx_LABEL_REF (VOIDmode, label); +/* A subroutine of the various atomic expanders. For sub-word operations, + we must adjust things to operate on SImode. Given the original MEM, + return a new aligned memory. Also build and return the quantities by + which to shift and mask. */ - if (before == NULL_RTX) - before = scratch; - emit_load_locked (mode, before, mem); +static rtx +rs6000_adjust_atomic_subword (rtx orig_mem, rtx *pshift, rtx *pmask) +{ + rtx addr, align, shift, mask, mem; + HOST_WIDE_INT shift_mask; + enum machine_mode mode = GET_MODE (orig_mem); - if (code == NOT) - x = gen_rtx_IOR (mode, - gen_rtx_NOT (mode, before), - gen_rtx_NOT (mode, val)); - else if (code == AND) - x = gen_rtx_UNSPEC (mode, gen_rtvec (2, before, val), UNSPEC_AND); - else - x = gen_rtx_fmt_ee (code, mode, before, val); + /* For smaller modes, we have to implement this via SImode. */ + shift_mask = (mode == QImode ? 0x18 : 0x10); - if (after != NULL_RTX) - emit_insn (gen_rtx_SET (VOIDmode, after, copy_rtx (x))); - emit_insn (gen_rtx_SET (VOIDmode, scratch, x)); + addr = XEXP (orig_mem, 0); + addr = force_reg (GET_MODE (addr), addr); - emit_store_conditional (mode, cond, mem, scratch); + /* Aligned memory containing subword. Generate a new memory. We + do not want any of the existing MEM_ATTR data, as we're now + accessing memory outside the original object. */ + align = expand_simple_binop (Pmode, AND, addr, GEN_INT (-4), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + mem = gen_rtx_MEM (SImode, align); + MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem); + if (MEM_ALIAS_SET (orig_mem) == ALIAS_SET_MEMORY_BARRIER) + set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); - x = gen_rtx_NE (VOIDmode, cond, const0_rtx); - emit_unlikely_jump (x, label); + /* Shift amount for subword relative to aligned word. */ + shift = gen_reg_rtx (SImode); + addr = gen_lowpart (SImode, addr); + emit_insn (gen_rlwinm (shift, addr, GEN_INT (3), GEN_INT (shift_mask))); + shift = expand_simple_binop (SImode, XOR, shift, GEN_INT (shift_mask), + shift, 1, OPTAB_LIB_WIDEN); + *pshift = shift; + + /* Mask for insertion. */ + mask = expand_simple_binop (SImode, ASHIFT, GEN_INT (GET_MODE_MASK (mode)), + shift, NULL_RTX, 1, OPTAB_LIB_WIDEN); + *pmask = mask; - emit_insn (gen_isync ()); + return mem; } -/* Expand an atomic compare and swap operation. MEM is the memory on which - to operate. OLDVAL is the old value to be compared. NEWVAL is the new - value to be stored. SCRATCH is a scratch GPR. */ +/* A subroutine of the various atomic expanders. For sub-word operands, + combine OLDVAL and NEWVAL via MASK. Returns a new pseduo. */ + +static rtx +rs6000_mask_atomic_subword (rtx oldval, rtx newval, rtx mask) +{ + rtx x; + + x = gen_reg_rtx (SImode); + emit_insn (gen_rtx_SET (VOIDmode, x, + gen_rtx_AND (SImode, + gen_rtx_NOT (SImode, mask), + oldval))); + + x = expand_simple_binop (SImode, IOR, newval, x, x, 1, OPTAB_LIB_WIDEN); + + return x; +} + +/* A subroutine of the various atomic expanders. For sub-word operands, + extract WIDE to NARROW via SHIFT. */ + +static void +rs6000_finish_atomic_subword (rtx narrow, rtx wide, rtx shift) +{ + wide = expand_simple_binop (SImode, LSHIFTRT, wide, shift, + wide, 1, OPTAB_LIB_WIDEN); + emit_move_insn (narrow, gen_lowpart (GET_MODE (narrow), wide)); +} + +/* Expand an atomic compare and swap operation. */ void -rs6000_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval, - rtx scratch) +rs6000_expand_atomic_compare_and_swap (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); - rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); + rtx boolval, retval, mem, oldval, newval, cond; + rtx label1, label2, x, mask, shift; + enum machine_mode mode; + enum memmodel mod_s, mod_f; + bool is_weak; + + boolval = operands[0]; + retval = operands[1]; + mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + is_weak = (INTVAL (operands[5]) != 0); + mod_s = (enum memmodel) INTVAL (operands[6]); + mod_f = (enum memmodel) INTVAL (operands[7]); + mode = GET_MODE (mem); + + mask = shift = NULL_RTX; + if (mode == QImode || mode == HImode) + { + mem = rs6000_adjust_atomic_subword (mem, &shift, &mask); + + /* Shift and mask OLDVAL into position with the word. */ + oldval = convert_modes (SImode, mode, oldval, 1); + oldval = expand_simple_binop (SImode, ASHIFT, oldval, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); - emit_insn (gen_lwsync ()); + /* Shift and mask NEWVAL into position within the word. */ + newval = convert_modes (SImode, mode, newval, 1); + newval = expand_simple_binop (SImode, ASHIFT, newval, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); - label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + /* Prepare to adjust the return value. */ + retval = gen_reg_rtx (SImode); + mode = SImode; + } + + rs6000_pre_atomic_barrier (mod_s); + + emit_move_insn (boolval, const0_rtx); + + label1 = NULL_RTX; + if (!is_weak) + { + label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label1, 0)); + } label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); - emit_label (XEXP (label1, 0)); emit_load_locked (mode, retval, mem); - x = gen_rtx_COMPARE (CCmode, retval, oldval); - emit_insn (gen_rtx_SET (VOIDmode, cond, x)); + x = retval; + if (mask) + { + x = expand_simple_binop (SImode, AND, retval, mask, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } - x = gen_rtx_NE (VOIDmode, cond, const0_rtx); + x = gen_rtx_NE (VOIDmode, x, oldval); + x = rs6000_generate_compare (x, mode); emit_unlikely_jump (x, label2); - emit_move_insn (scratch, newval); - emit_store_conditional (mode, cond, mem, scratch); + x = newval; + if (mask) + x = rs6000_mask_atomic_subword (retval, newval, mask); - x = gen_rtx_NE (VOIDmode, cond, const0_rtx); - emit_unlikely_jump (x, label1); + cond = gen_reg_rtx (CCmode); + emit_store_conditional (mode, cond, mem, x); + + if (is_weak) + { + /* ??? It's either this or an unlikely jump over (set bool 1). */ + x = gen_rtx_EQ (SImode, cond, const0_rtx); + emit_insn (gen_rtx_SET (VOIDmode, boolval, x)); + } + else + { + x = gen_rtx_NE (VOIDmode, cond, const0_rtx); + emit_unlikely_jump (x, label1); + emit_move_insn (boolval, const1_rtx); + } + + if (mod_f != MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); - emit_insn (gen_isync ()); - emit_label (XEXP (label2, 0)); + rs6000_post_atomic_barrier (mod_s); + + if (mod_f == MEMMODEL_RELAXED) + emit_label (XEXP (label2, 0)); + + if (shift) + rs6000_finish_atomic_subword (operands[1], retval, shift); } -/* Expand an atomic test and set operation. MEM is the memory on which - to operate. VAL is the value set. SCRATCH is a scratch GPR. */ +/* Expand an atomic exchange operation. */ void -rs6000_split_lock_test_and_set (rtx retval, rtx mem, rtx val, rtx scratch) +rs6000_expand_atomic_exchange (rtx operands[]) { - enum machine_mode mode = GET_MODE (mem); - rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); + rtx retval, mem, val, cond; + enum machine_mode mode; + enum memmodel model; + rtx label, x, mask, shift; + + retval = operands[0]; + mem = operands[1]; + val = operands[2]; + model = (enum memmodel) INTVAL (operands[3]); + mode = GET_MODE (mem); + + mask = shift = NULL_RTX; + if (mode == QImode || mode == HImode) + { + mem = rs6000_adjust_atomic_subword (mem, &shift, &mask); + + /* Shift and mask VAL into position with the word. */ + val = convert_modes (SImode, mode, val, 1); + val = expand_simple_binop (SImode, ASHIFT, val, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + /* Prepare to adjust the return value. */ + retval = gen_reg_rtx (SImode); + mode = SImode; + } + + rs6000_pre_atomic_barrier (model); label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); emit_label (XEXP (label, 0)); emit_load_locked (mode, retval, mem); - emit_move_insn (scratch, val); - emit_store_conditional (mode, cond, mem, scratch); + + x = val; + if (mask) + x = rs6000_mask_atomic_subword (retval, val, mask); + + cond = gen_reg_rtx (CCmode); + emit_store_conditional (mode, cond, mem, x); x = gen_rtx_NE (VOIDmode, cond, const0_rtx); emit_unlikely_jump (x, label); - emit_insn (gen_isync ()); + rs6000_post_atomic_barrier (model); + + if (shift) + rs6000_finish_atomic_subword (operands[0], retval, shift); } +/* Expand an atomic fetch-and-operate pattern. CODE is the binary operation + to perform. MEM is the memory on which to operate. VAL is the second + operand of the binary operator. BEFORE and AFTER are optional locations to + return the value of MEM either before of after the operation. MODEL_RTX + is a CONST_INT containing the memory model to use. */ + void -rs6000_expand_compare_and_swapqhi (rtx dst, rtx mem, rtx oldval, rtx newval) +rs6000_expand_atomic_op (enum rtx_code code, rtx mem, rtx val, + rtx orig_before, rtx orig_after, rtx model_rtx) { + enum memmodel model = (enum memmodel) INTVAL (model_rtx); enum machine_mode mode = GET_MODE (mem); - rtx addrSI, align, wdst, shift, mask; - HOST_WIDE_INT shift_mask = mode == QImode ? 0x18 : 0x10; - HOST_WIDE_INT imask = GET_MODE_MASK (mode); + rtx label, x, cond, mask, shift; + rtx before = orig_before, after = orig_after; - /* Shift amount for subword relative to aligned word. */ - addrSI = force_reg (GET_MODE (XEXP (mem, 0)), XEXP (mem, 0)); - addrSI = force_reg (SImode, gen_lowpart_common (SImode, addrSI)); - shift = gen_reg_rtx (SImode); - emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3), - GEN_INT (shift_mask))); - emit_insn (gen_xorsi3 (shift, shift, GEN_INT (shift_mask))); - - /* Shift and mask old value into position within word. */ - oldval = convert_modes (SImode, mode, oldval, 1); - oldval = expand_binop (SImode, and_optab, - oldval, GEN_INT (imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_ashlsi3 (oldval, oldval, shift)); - - /* Shift and mask new value into position within word. */ - newval = convert_modes (SImode, mode, newval, 1); - newval = expand_binop (SImode, and_optab, - newval, GEN_INT (imask), NULL_RTX, - 1, OPTAB_LIB_WIDEN); - emit_insn (gen_ashlsi3 (newval, newval, shift)); + mask = shift = NULL_RTX; + if (mode == QImode || mode == HImode) + { + mem = rs6000_adjust_atomic_subword (mem, &shift, &mask); - /* Mask for insertion. */ - mask = gen_reg_rtx (SImode); - emit_move_insn (mask, GEN_INT (imask)); - emit_insn (gen_ashlsi3 (mask, mask, shift)); + /* Shift and mask VAL into position with the word. */ + val = convert_modes (SImode, mode, val, 1); + val = expand_simple_binop (SImode, ASHIFT, val, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); - /* Address of aligned word containing subword. */ - align = expand_binop (Pmode, and_optab, XEXP (mem, 0), GEN_INT (-4), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - mem = change_address (mem, SImode, align); - set_mem_align (mem, 32); - MEM_VOLATILE_P (mem) = 1; + switch (code) + { + case IOR: + case XOR: + /* We've already zero-extended VAL. That is sufficient to + make certain that it does not affect other bits. */ + mask = NULL; + break; - wdst = gen_reg_rtx (SImode); - emit_insn (gen_sync_compare_and_swapqhi_internal (wdst, mask, - oldval, newval, mem)); + case AND: + /* If we make certain that all of the other bits in VAL are + set, that will be sufficient to not affect other bits. */ + x = gen_rtx_NOT (SImode, mask); + x = gen_rtx_IOR (SImode, x, val); + emit_insn (gen_rtx_SET (VOIDmode, val, x)); + mask = NULL; + break; - /* Shift the result back. */ - emit_insn (gen_lshrsi3 (wdst, wdst, shift)); + case NOT: + case PLUS: + case MINUS: + /* These will all affect bits outside the field and need + adjustment via MASK within the loop. */ + break; - emit_move_insn (dst, gen_lowpart (mode, wdst)); -} + default: + gcc_unreachable (); + } -void -rs6000_split_compare_and_swapqhi (rtx dest, rtx mask, - rtx oldval, rtx newval, rtx mem, - rtx scratch) -{ - rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); + /* Prepare to adjust the return value. */ + before = gen_reg_rtx (SImode); + if (after) + after = gen_reg_rtx (SImode); + mode = SImode; + } - emit_insn (gen_lwsync ()); - label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); - label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); - emit_label (XEXP (label1, 0)); + rs6000_pre_atomic_barrier (model); - emit_load_locked (SImode, scratch, mem); + label = gen_label_rtx (); + emit_label (label); + label = gen_rtx_LABEL_REF (VOIDmode, label); - /* Mask subword within loaded value for comparison with oldval. - Use UNSPEC_AND to avoid clobber.*/ - emit_insn (gen_rtx_SET (SImode, dest, - gen_rtx_UNSPEC (SImode, - gen_rtvec (2, scratch, mask), - UNSPEC_AND))); + if (before == NULL_RTX) + before = gen_reg_rtx (mode); - x = gen_rtx_COMPARE (CCmode, dest, oldval); - emit_insn (gen_rtx_SET (VOIDmode, cond, x)); + emit_load_locked (mode, before, mem); - x = gen_rtx_NE (VOIDmode, cond, const0_rtx); - emit_unlikely_jump (x, label2); + if (code == NOT) + { + x = expand_simple_binop (mode, AND, before, val, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + after = expand_simple_unop (mode, NOT, x, after, 1); + } + else + { + after = expand_simple_binop (mode, code, before, val, + after, 1, OPTAB_LIB_WIDEN); + } - /* Clear subword within loaded value for insertion of new value. */ - emit_insn (gen_rtx_SET (SImode, scratch, - gen_rtx_AND (SImode, - gen_rtx_NOT (SImode, mask), scratch))); - emit_insn (gen_iorsi3 (scratch, scratch, newval)); - emit_store_conditional (SImode, cond, mem, scratch); + x = after; + if (mask) + { + x = expand_simple_binop (SImode, AND, after, mask, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + x = rs6000_mask_atomic_subword (before, x, mask); + } + + cond = gen_reg_rtx (CCmode); + emit_store_conditional (mode, cond, mem, x); x = gen_rtx_NE (VOIDmode, cond, const0_rtx); - emit_unlikely_jump (x, label1); + emit_unlikely_jump (x, label); - emit_insn (gen_isync ()); - emit_label (XEXP (label2, 0)); -} + rs6000_post_atomic_barrier (model); + if (shift) + { + if (orig_before) + rs6000_finish_atomic_subword (orig_before, before, shift); + if (orig_after) + rs6000_finish_atomic_subword (orig_after, after, shift); + } + else if (orig_after && after != orig_after) + emit_move_insn (orig_after, after); +} - /* Emit instructions to move SRC to DST. Called by splitters for +/* Emit instructions to move SRC to DST. Called by splitters for multi-register moves. It will emit at most one instruction for each register that is accessed; that is, it won't emit li/lis pairs (or equivalent for 64-bit code). One of SRC or DST must be a hard @@ -25777,7 +25784,7 @@ rs6000_xcoff_section_type_flags (tree decl, const char *name, int reloc) unsigned int flags = default_section_type_flags (decl, name, reloc); /* Align to at least UNIT size. */ - if (flags & SECTION_CODE) + if (flags & SECTION_CODE || !decl) align = MIN_UNITS_PER_WORD; else /* Increase alignment of large objects if not already stricter. */ diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 93b0b6c70be..22207bbf2b2 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -106,6 +106,7 @@ UNSPEC_SP_SET UNSPEC_SP_TEST UNSPEC_SYNC + UNSPEC_LWSYNC UNSPEC_SYNC_OP UNSPEC_ATOMIC UNSPEC_CMPXCHG @@ -138,7 +139,6 @@ UNSPECV_PROBE_STACK_RANGE ; probe range of stack addresses UNSPECV_EH_RR ; eh_reg_restore UNSPECV_ISYNC ; isync instruction - UNSPECV_LWSYNC ; lwsync ]) diff --git a/gcc/config/rs6000/sync.md b/gcc/config/rs6000/sync.md index c3fbd9ee028..38bd8bac354 100644 --- a/gcc/config/rs6000/sync.md +++ b/gcc/config/rs6000/sync.md @@ -28,12 +28,32 @@ (define_code_attr fetchop_pred [(plus "add_operand") (minus "gpc_reg_operand") (ior "logical_operand") (xor "logical_operand") (and "and_operand")]) -(define_code_attr fetchopsi_constr - [(plus "rIL") (minus "r") (ior "rKL") (xor "rKL") (and "rTKL")]) -(define_code_attr fetchopdi_constr - [(plus "rIL") (minus "r") (ior "rKJF") (xor "rKJF") (and "rSTKJ")]) -(define_expand "memory_barrier" +(define_expand "mem_thread_fence" + [(match_operand:SI 0 "const_int_operand" "")] ;; model + "" +{ + enum memmodel model = (enum memmodel) INTVAL (operands[0]); + switch (model) + { + case MEMMODEL_RELAXED: + break; + case MEMMODEL_CONSUME: + case MEMMODEL_ACQUIRE: + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + emit_insn (gen_lwsync ()); + break; + case MEMMODEL_SEQ_CST: + emit_insn (gen_hwsync ()); + break; + default: + gcc_unreachable (); + } + DONE; +}) + +(define_expand "hwsync" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_SYNC))] "" @@ -42,582 +62,224 @@ MEM_VOLATILE_P (operands[0]) = 1; }) -(define_insn "*sync_internal" +(define_insn "*hwsync" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_SYNC))] "" "{dcs|sync}" [(set_attr "type" "sync")]) -(define_insn "load_locked_<mode>" - [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") - (unspec_volatile:GPR - [(match_operand:GPR 1 "memory_operand" "Z")] UNSPECV_LL))] - "TARGET_POWERPC" - "<larx> %0,%y1" - [(set_attr "type" "load_l")]) - -(define_insn "store_conditional_<mode>" - [(set (match_operand:CC 0 "cc_reg_operand" "=x") - (unspec_volatile:CC [(const_int 0)] UNSPECV_SC)) - (set (match_operand:GPR 1 "memory_operand" "=Z") - (match_operand:GPR 2 "gpc_reg_operand" "r"))] - "TARGET_POWERPC" - "<stcx> %2,%y1" - [(set_attr "type" "store_c")]) - -(define_insn_and_split "sync_compare_and_swap<mode>" - [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") - (match_operand:GPR 1 "memory_operand" "+Z")) - (set (match_dup 1) - (unspec:GPR - [(match_operand:GPR 2 "reg_or_short_operand" "rI") - (match_operand:GPR 3 "gpc_reg_operand" "r")] - UNSPEC_CMPXCHG)) - (clobber (match_scratch:GPR 4 "=&r")) - (clobber (match_scratch:CC 5 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] +(define_expand "lwsync" + [(set (match_dup 0) + (unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))] + "" { - rs6000_split_compare_and_swap (operands[0], operands[1], operands[2], - operands[3], operands[4]); - DONE; + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; }) -(define_expand "sync_compare_and_swaphi" - [(match_operand:HI 0 "gpc_reg_operand" "") - (match_operand:HI 1 "memory_operand" "") - (match_operand:HI 2 "gpc_reg_operand" "") - (match_operand:HI 3 "gpc_reg_operand" "")] - "TARGET_POWERPC" +(define_insn "*lwsync" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_LWSYNC))] + "" { - rs6000_expand_compare_and_swapqhi (operands[0], operands[1], - operands[2], operands[3]); - DONE; -}) + /* Some AIX assemblers don't accept lwsync, so we use a .long. */ + if (TARGET_NO_LWSYNC) + return "sync"; + else if (TARGET_LWSYNC_INSTRUCTION) + return "lwsync"; + else + return ".long 0x7c2004ac"; +} + [(set_attr "type" "sync")]) -(define_expand "sync_compare_and_swapqi" - [(match_operand:QI 0 "gpc_reg_operand" "") - (match_operand:QI 1 "memory_operand" "") - (match_operand:QI 2 "gpc_reg_operand" "") - (match_operand:QI 3 "gpc_reg_operand" "")] - "TARGET_POWERPC" -{ - rs6000_expand_compare_and_swapqhi (operands[0], operands[1], - operands[2], operands[3]); - DONE; -}) +(define_insn "isync" + [(unspec_volatile:BLK [(const_int 0)] UNSPECV_ISYNC)] + "" + "{ics|isync}" + [(set_attr "type" "isync")]) -(define_insn_and_split "sync_compare_and_swapqhi_internal" - [(set (match_operand:SI 0 "gpc_reg_operand" "=&r") - (match_operand:SI 4 "memory_operand" "+Z")) - (set (match_dup 4) - (unspec:SI - [(match_operand:SI 1 "gpc_reg_operand" "r") - (match_operand:SI 2 "gpc_reg_operand" "r") - (match_operand:SI 3 "gpc_reg_operand" "r")] - UNSPEC_CMPXCHG)) - (clobber (match_scratch:SI 5 "=&r")) - (clobber (match_scratch:CC 6 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] +;; The control dependency used for load dependency described +;; in B.2.3 of the Power ISA 2.06B. +(define_insn "loadsync" + [(unspec_volatile:BLK [(match_operand 0 "register_operand" "r")] + UNSPECV_ISYNC) + (clobber (match_scratch:CC 1 "=y"))] + "" + "cmpw %1,%0,%0\;bne- %1,$+4\;isync" + [(set_attr "type" "isync") + (set_attr "length" "12")]) + +(define_expand "atomic_load<mode>" + [(set (match_operand:INT 0 "register_operand" "") ;; output + (match_operand:INT 1 "memory_operand" "")) ;; memory + (use (match_operand:SI 2 "const_int_operand" ""))] ;; model + "" { - rs6000_split_compare_and_swapqhi (operands[0], operands[1], - operands[2], operands[3], operands[4], - operands[5]); - DONE; -}) + enum memmodel model = (enum memmodel) INTVAL (operands[2]); -(define_insn_and_split "sync_lock_test_and_set<mode>" - [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") - (match_operand:GPR 1 "memory_operand" "+Z")) - (set (match_dup 1) - (unspec:GPR - [(match_operand:GPR 2 "reg_or_short_operand" "rL")] - UNSPEC_XCHG)) - (clobber (match_scratch:GPR 3 "=&r")) - (clobber (match_scratch:CC 4 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] -{ - rs6000_split_lock_test_and_set (operands[0], operands[1], operands[2], - operands[3]); - DONE; -}) + if (model == MEMMODEL_SEQ_CST) + emit_insn (gen_hwsync ()); -(define_expand "sync_<fetchop_name><mode>" - [(parallel [(set (match_operand:INT1 0 "memory_operand" "") - (unspec:INT1 - [(FETCHOP:INT1 (match_dup 0) - (match_operand:INT1 1 "<fetchop_pred>" ""))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] - "TARGET_POWERPC" - " -{ - if (<MODE>mode != SImode && <MODE>mode != DImode) + emit_move_insn (operands[0], operands[1]); + + switch (model) { - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (<CODE>, <MODE>mode, operands[0], operands[1], - NULL_RTX, NULL_RTX, true); - DONE; + case MEMMODEL_RELAXED: + break; + case MEMMODEL_CONSUME: + case MEMMODEL_ACQUIRE: + case MEMMODEL_SEQ_CST: + emit_insn (gen_loadsync (operands[0])); + break; + default: + gcc_unreachable (); } -}") - -(define_insn_and_split "*sync_<fetchop_name>si_internal" - [(set (match_operand:SI 0 "memory_operand" "+Z") - (unspec:SI - [(FETCHOP:SI (match_dup 0) - (match_operand:SI 1 "<fetchop_pred>" "<fetchopsi_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:SI 2 "=&b")) - (clobber (match_scratch:CC 3 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] -{ - rs6000_split_atomic_op (<CODE>, operands[0], operands[1], - NULL_RTX, NULL_RTX, operands[2]); DONE; }) -(define_insn_and_split "*sync_<fetchop_name>di_internal" - [(set (match_operand:DI 0 "memory_operand" "+Z") - (unspec:DI - [(FETCHOP:DI (match_dup 0) - (match_operand:DI 1 "<fetchop_pred>" "<fetchopdi_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:DI 2 "=&b")) - (clobber (match_scratch:CC 3 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] +(define_expand "atomic_store<mode>" + [(set (match_operand:INT 0 "memory_operand" "") ;; memory + (match_operand:INT 1 "register_operand" "")) ;; input + (use (match_operand:SI 2 "const_int_operand" ""))] ;; model + "" { - rs6000_split_atomic_op (<CODE>, operands[0], operands[1], - NULL_RTX, NULL_RTX, operands[2]); + enum memmodel model = (enum memmodel) INTVAL (operands[2]); + switch (model) + { + case MEMMODEL_RELAXED: + break; + case MEMMODEL_RELEASE: + emit_insn (gen_lwsync ()); + break; + case MEMMODEL_SEQ_CST: + emit_insn (gen_hwsync ()); + break; + default: + gcc_unreachable (); + } + emit_move_insn (operands[0], operands[1]); DONE; }) -(define_expand "sync_nand<mode>" - [(parallel [(set (match_operand:INT1 0 "memory_operand" "") - (unspec:INT1 - [(ior:INT1 (not:INT1 (match_dup 0)) - (not:INT1 (match_operand:INT1 1 "gpc_reg_operand" "")))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] - "TARGET_POWERPC" - " -{ - if (<MODE>mode != SImode && <MODE>mode != DImode) - { - FAIL; - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (NOT, <MODE>mode, operands[0], operands[1], - NULL_RTX, NULL_RTX, true); - DONE; - } -}") +;; ??? Power ISA 2.06B says that there *is* a load-{byte,half}-and-reserve +;; opcode that is "phased-in". Not implemented as of Power7, so not yet used, +;; but let's prepare the macros anyway. + +(define_mode_iterator ATOMIC [SI (DI "TARGET_64BIT")]) -(define_insn_and_split "*sync_nand<mode>_internal" - [(set (match_operand:GPR 0 "memory_operand" "+Z") - (unspec:GPR - [(ior:GPR (not:GPR (match_dup 0)) - (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:GPR 2 "=&r")) - (clobber (match_scratch:CC 3 "=&x"))] +(define_insn "load_locked<mode>" + [(set (match_operand:ATOMIC 0 "gpc_reg_operand" "=r") + (unspec_volatile:ATOMIC + [(match_operand:ATOMIC 1 "memory_operand" "Z")] UNSPECV_LL))] "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] -{ - rs6000_split_atomic_op (NOT, operands[0], operands[1], - NULL_RTX, NULL_RTX, operands[2]); - DONE; -}) + "<larx> %0,%y1" + [(set_attr "type" "load_l")]) -(define_expand "sync_old_<fetchop_name><mode>" - [(parallel [(set (match_operand:INT1 0 "gpc_reg_operand" "") - (match_operand:INT1 1 "memory_operand" "")) - (set (match_dup 1) - (unspec:INT1 - [(FETCHOP:INT1 (match_dup 1) - (match_operand:INT1 2 "<fetchop_pred>" ""))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] +(define_insn "store_conditional<mode>" + [(set (match_operand:CC 0 "cc_reg_operand" "=x") + (unspec_volatile:CC [(const_int 0)] UNSPECV_SC)) + (set (match_operand:ATOMIC 1 "memory_operand" "=Z") + (match_operand:ATOMIC 2 "gpc_reg_operand" "r"))] "TARGET_POWERPC" - " -{ - if (<MODE>mode != SImode && <MODE>mode != DImode) - { - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (<CODE>, <MODE>mode, operands[1], operands[2], - operands[0], NULL_RTX, true); - DONE; - } -}") + "<stcx> %2,%y1" + [(set_attr "type" "store_c")]) -(define_insn_and_split "*sync_old_<fetchop_name>si_internal" - [(set (match_operand:SI 0 "gpc_reg_operand" "=&r") - (match_operand:SI 1 "memory_operand" "+Z")) - (set (match_dup 1) - (unspec:SI - [(FETCHOP:SI (match_dup 1) - (match_operand:SI 2 "<fetchop_pred>" "<fetchopsi_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:SI 3 "=&b")) - (clobber (match_scratch:CC 4 "=&x"))] +(define_expand "atomic_compare_and_swap<mode>" + [(match_operand:SI 0 "gpc_reg_operand" "") ;; bool out + (match_operand:INT1 1 "gpc_reg_operand" "") ;; val out + (match_operand:INT1 2 "memory_operand" "") ;; memory + (match_operand:INT1 3 "reg_or_short_operand" "") ;; expected + (match_operand:INT1 4 "gpc_reg_operand" "") ;; desired + (match_operand:SI 5 "const_int_operand" "") ;; is_weak + (match_operand:SI 6 "const_int_operand" "") ;; model succ + (match_operand:SI 7 "const_int_operand" "")] ;; model fail "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] { - rs6000_split_atomic_op (<CODE>, operands[1], operands[2], - operands[0], NULL_RTX, operands[3]); + rs6000_expand_atomic_compare_and_swap (operands); DONE; }) -(define_insn_and_split "*sync_old_<fetchop_name>di_internal" - [(set (match_operand:DI 0 "gpc_reg_operand" "=&r") - (match_operand:DI 1 "memory_operand" "+Z")) - (set (match_dup 1) - (unspec:DI - [(FETCHOP:DI (match_dup 1) - (match_operand:DI 2 "<fetchop_pred>" "<fetchopdi_constr>"))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:DI 3 "=&b")) - (clobber (match_scratch:CC 4 "=&x"))] +(define_expand "atomic_exchange<mode>" + [(match_operand:INT1 0 "gpc_reg_operand" "") ;; output + (match_operand:INT1 1 "memory_operand" "") ;; memory + (match_operand:INT1 2 "gpc_reg_operand" "") ;; input + (match_operand:SI 3 "const_int_operand" "")] ;; model "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] { - rs6000_split_atomic_op (<CODE>, operands[1], operands[2], - operands[0], NULL_RTX, operands[3]); + rs6000_expand_atomic_exchange (operands); DONE; }) -(define_expand "sync_old_nand<mode>" - [(parallel [(set (match_operand:INT1 0 "gpc_reg_operand" "") - (match_operand:INT1 1 "memory_operand" "")) - (set (match_dup 1) - (unspec:INT1 - [(ior:INT1 (not:INT1 (match_dup 1)) - (not:INT1 (match_operand:INT1 2 "gpc_reg_operand" "")))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] - "TARGET_POWERPC" - " -{ - if (<MODE>mode != SImode && <MODE>mode != DImode) - { - FAIL; - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (NOT, <MODE>mode, operands[1], operands[2], - operands[0], NULL_RTX, true); - DONE; - } -}") - -(define_insn_and_split "*sync_old_nand<mode>_internal" - [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") - (match_operand:GPR 1 "memory_operand" "+Z")) - (set (match_dup 1) - (unspec:GPR - [(ior:GPR (not:GPR (match_dup 1)) - (not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r")))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:GPR 3 "=&r")) - (clobber (match_scratch:CC 4 "=&x"))] +(define_expand "atomic_<fetchop_name><mode>" + [(match_operand:INT1 0 "memory_operand" "") ;; memory + (FETCHOP:INT1 (match_dup 0) + (match_operand:INT1 1 "<fetchop_pred>" "")) ;; operand + (match_operand:SI 2 "const_int_operand" "")] ;; model "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] { - rs6000_split_atomic_op (NOT, operands[1], operands[2], - operands[0], NULL_RTX, operands[3]); + rs6000_expand_atomic_op (<CODE>, operands[0], operands[1], + NULL_RTX, NULL_RTX, operands[2]); DONE; }) -(define_expand "sync_new_<fetchop_name><mode>" - [(parallel [(set (match_operand:INT1 0 "gpc_reg_operand" "") - (FETCHOP:INT1 - (match_operand:INT1 1 "memory_operand" "") - (match_operand:INT1 2 "<fetchop_pred>" ""))) - (set (match_dup 1) - (unspec:INT1 - [(FETCHOP:INT1 (match_dup 1) (match_dup 2))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] +(define_expand "atomic_nand<mode>" + [(match_operand:INT1 0 "memory_operand" "") ;; memory + (match_operand:INT1 1 "gpc_reg_operand" "") ;; operand + (match_operand:SI 2 "const_int_operand" "")] ;; model "TARGET_POWERPC" - " { - if (<MODE>mode != SImode && <MODE>mode != DImode) - { - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (<CODE>, <MODE>mode, operands[1], operands[2], - NULL_RTX, operands[0], true); - DONE; - } -}") - -(define_insn_and_split "*sync_new_<fetchop_name>si_internal" - [(set (match_operand:SI 0 "gpc_reg_operand" "=&r") - (FETCHOP:SI - (match_operand:SI 1 "memory_operand" "+Z") - (match_operand:SI 2 "<fetchop_pred>" "<fetchopsi_constr>"))) - (set (match_dup 1) - (unspec:SI - [(FETCHOP:SI (match_dup 1) (match_dup 2))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:SI 3 "=&b")) - (clobber (match_scratch:CC 4 "=&x"))] - "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] -{ - rs6000_split_atomic_op (<CODE>, operands[1], operands[2], - NULL_RTX, operands[0], operands[3]); + rs6000_expand_atomic_op (NOT, operands[0], operands[1], + NULL_RTX, NULL_RTX, operands[2]); DONE; }) -(define_insn_and_split "*sync_new_<fetchop_name>di_internal" - [(set (match_operand:DI 0 "gpc_reg_operand" "=&r") - (FETCHOP:DI - (match_operand:DI 1 "memory_operand" "+Z") - (match_operand:DI 2 "<fetchop_pred>" "<fetchopdi_constr>"))) - (set (match_dup 1) - (unspec:DI - [(FETCHOP:DI (match_dup 1) (match_dup 2))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:DI 3 "=&b")) - (clobber (match_scratch:CC 4 "=&x"))] +(define_expand "atomic_fetch_<fetchop_name><mode>" + [(match_operand:INT1 0 "gpc_reg_operand" "") ;; output + (match_operand:INT1 1 "memory_operand" "") ;; memory + (FETCHOP:INT1 (match_dup 1) + (match_operand:INT1 2 "<fetchop_pred>" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] -{ - rs6000_split_atomic_op (<CODE>, operands[1], operands[2], - NULL_RTX, operands[0], operands[3]); +{ + rs6000_expand_atomic_op (<CODE>, operands[1], operands[2], + operands[0], NULL_RTX, operands[3]); DONE; }) -(define_expand "sync_new_nand<mode>" - [(parallel [(set (match_operand:INT1 0 "gpc_reg_operand" "") - (ior:INT1 - (not:INT1 (match_operand:INT1 1 "memory_operand" "")) - (not:INT1 (match_operand:INT1 2 "gpc_reg_operand" "")))) - (set (match_dup 1) - (unspec:INT1 - [(ior:INT1 (not:INT1 (match_dup 1)) - (not:INT1 (match_dup 2)))] - UNSPEC_ATOMIC)) - (clobber (scratch:INT1)) - (clobber (scratch:CC))])] +(define_expand "atomic_fetch_nand<mode>" + [(match_operand:INT1 0 "gpc_reg_operand" "") ;; output + (match_operand:INT1 1 "memory_operand" "") ;; memory + (match_operand:INT1 2 "gpc_reg_operand" "") ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model "TARGET_POWERPC" - " { - if (<MODE>mode != SImode && <MODE>mode != DImode) - { - FAIL; - if (PPC405_ERRATUM77) - FAIL; - rs6000_emit_sync (NOT, <MODE>mode, operands[1], operands[2], - NULL_RTX, operands[0], true); - DONE; - } -}") + rs6000_expand_atomic_op (NOT, operands[1], operands[2], + operands[0], NULL_RTX, operands[3]); + DONE; +}) -(define_insn_and_split "*sync_new_nand<mode>_internal" - [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") - (ior:GPR - (not:GPR (match_operand:GPR 1 "memory_operand" "+Z")) - (not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r")))) - (set (match_dup 1) - (unspec:GPR - [(ior:GPR (not:GPR (match_dup 1)) (not:GPR (match_dup 2)))] - UNSPEC_ATOMIC)) - (clobber (match_scratch:GPR 3 "=&r")) - (clobber (match_scratch:CC 4 "=&x"))] +(define_expand "atomic_<fetchop_name>_fetch<mode>" + [(match_operand:INT1 0 "gpc_reg_operand" "") ;; output + (match_operand:INT1 1 "memory_operand" "") ;; memory + (FETCHOP:INT1 (match_dup 1) + (match_operand:INT1 2 "<fetchop_pred>" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model "TARGET_POWERPC" - "#" - "&& reload_completed" - [(const_int 0)] { - rs6000_split_atomic_op (NOT, operands[1], operands[2], - NULL_RTX, operands[0], operands[3]); + rs6000_expand_atomic_op (<CODE>, operands[1], operands[2], + NULL_RTX, operands[0], operands[3]); DONE; }) -; and<mode> without cr0 clobber to avoid generation of additional clobber -; in atomic splitters causing internal consistency failure. -; cr0 already clobbered by larx/stcx. -(define_insn "*atomic_andsi" - [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r,r,r") - (unspec:SI [(match_operand:SI 1 "gpc_reg_operand" "%r,r,r,r") - (match_operand:SI 2 "and_operand" "?r,T,K,L")] - UNSPEC_AND))] - "" - "@ - and %0,%1,%2 - {rlinm|rlwinm} %0,%1,0,%m2,%M2 - {andil.|andi.} %0,%1,%b2 - {andiu.|andis.} %0,%1,%u2" - [(set_attr "type" "*,*,compare,compare")]) - -(define_insn "*atomic_anddi" - [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r,r,r,r") - (unspec:DI [(match_operand:DI 1 "gpc_reg_operand" "%r,r,r,r,r") - (match_operand:DI 2 "and_operand" "?r,S,T,K,J")] - UNSPEC_AND))] - "TARGET_POWERPC64" - "@ - and %0,%1,%2 - rldic%B2 %0,%1,0,%S2 - rlwinm %0,%1,0,%m2,%M2 - andi. %0,%1,%b2 - andis. %0,%1,%u2" - [(set_attr "type" "*,*,*,compare,compare") - (set_attr "length" "4,4,4,4,4")]) - -; the sync_*_internal patterns all have these operands: -; 0 - memory location -; 1 - operand -; 2 - value in memory after operation -; 3 - value in memory immediately before operation - -(define_insn "*sync_addshort_internal" - [(set (match_operand:SI 2 "gpc_reg_operand" "=&r") - (ior:SI (and:SI (plus:SI (match_operand:SI 0 "memory_operand" "+Z") - (match_operand:SI 1 "add_operand" "rI")) - (match_operand:SI 4 "gpc_reg_operand" "r")) - (and:SI (not:SI (match_dup 4)) (match_dup 0)))) - (set (match_operand:SI 3 "gpc_reg_operand" "=&b") (match_dup 0)) - (set (match_dup 0) - (unspec:SI [(ior:SI (and:SI (plus:SI (match_dup 0) (match_dup 1)) - (match_dup 4)) - (and:SI (not:SI (match_dup 4)) (match_dup 0)))] - UNSPEC_SYNC_OP)) - (clobber (match_scratch:CC 5 "=&x")) - (clobber (match_scratch:SI 6 "=&r"))] - "TARGET_POWERPC && !PPC405_ERRATUM77" - "lwarx %3,%y0\n\tadd%I1 %2,%3,%1\n\tandc %6,%3,%4\n\tand %2,%2,%4\n\tor %2,%2,%6\n\tstwcx. %2,%y0\n\tbne- $-24" - [(set_attr "length" "28")]) - -(define_insn "*sync_subshort_internal" - [(set (match_operand:SI 2 "gpc_reg_operand" "=&r") - (ior:SI (and:SI (minus:SI (match_operand:SI 0 "memory_operand" "+Z") - (match_operand:SI 1 "add_operand" "rI")) - (match_operand:SI 4 "gpc_reg_operand" "r")) - (and:SI (not:SI (match_dup 4)) (match_dup 0)))) - (set (match_operand:SI 3 "gpc_reg_operand" "=&b") (match_dup 0)) - (set (match_dup 0) - (unspec:SI [(ior:SI (and:SI (minus:SI (match_dup 0) (match_dup 1)) - (match_dup 4)) - (and:SI (not:SI (match_dup 4)) (match_dup 0)))] - UNSPEC_SYNC_OP)) - (clobber (match_scratch:CC 5 "=&x")) - (clobber (match_scratch:SI 6 "=&r"))] - "TARGET_POWERPC && !PPC405_ERRATUM77" - "lwarx %3,%y0\n\tsubf %2,%1,%3\n\tandc %6,%3,%4\n\tand %2,%2,%4\n\tor %2,%2,%6\n\tstwcx. %2,%y0\n\tbne- $-24" - [(set_attr "length" "28")]) - -(define_insn "*sync_andsi_internal" - [(set (match_operand:SI 2 "gpc_reg_operand" "=&r,&r,&r,&r") - (and:SI (match_operand:SI 0 "memory_operand" "+Z,Z,Z,Z") - (match_operand:SI 1 "and_operand" "r,T,K,L"))) - (set (match_operand:SI 3 "gpc_reg_operand" "=&b,&b,&b,&b") (match_dup 0)) - (set (match_dup 0) - (unspec:SI [(and:SI (match_dup 0) (match_dup 1))] - UNSPEC_SYNC_OP)) - (clobber (match_scratch:CC 4 "=&x,&x,&x,&x"))] - "TARGET_POWERPC && !PPC405_ERRATUM77" - "@ - lwarx %3,%y0\n\tand %2,%3,%1\n\tstwcx. %2,%y0\n\tbne- $-12 - lwarx %3,%y0\n\trlwinm %2,%3,0,%m1,%M1\n\tstwcx. %2,%y0\n\tbne- $-12 - lwarx %3,%y0\n\tandi. %2,%3,%b1\n\tstwcx. %2,%y0\n\tbne- $-12 - lwarx %3,%y0\n\tandis. %2,%3,%u1\n\tstwcx. %2,%y0\n\tbne- $-12" - [(set_attr "length" "16,16,16,16")]) - -(define_insn "*sync_boolsi_internal" - [(set (match_operand:SI 2 "gpc_reg_operand" "=&r,&r,&r") - (match_operator:SI 4 "boolean_or_operator" - [(match_operand:SI 0 "memory_operand" "+Z,Z,Z") - (match_operand:SI 1 "logical_operand" "r,K,L")])) - (set (match_operand:SI 3 "gpc_reg_operand" "=&b,&b,&b") (match_dup 0)) - (set (match_dup 0) (unspec:SI [(match_dup 4)] UNSPEC_SYNC_OP)) - (clobber (match_scratch:CC 5 "=&x,&x,&x"))] - "TARGET_POWERPC && !PPC405_ERRATUM77" - "@ - lwarx %3,%y0\n\t%q4 %2,%3,%1\n\tstwcx. %2,%y0\n\tbne- $-12 - lwarx %3,%y0\n\t%q4i %2,%3,%b1\n\tstwcx. %2,%y0\n\tbne- $-12 - lwarx %3,%y0\n\t%q4is %2,%3,%u1\n\tstwcx. %2,%y0\n\tbne- $-12" - [(set_attr "length" "16,16,16")]) - -; This pattern could also take immediate values of operand 1, -; since the non-NOT version of the operator is used; but this is not -; very useful, since in practice operand 1 is a full 32-bit value. -; Likewise, operand 5 is in practice either <= 2^16 or it is a register. -(define_insn "*sync_boolcshort_internal" - [(set (match_operand:SI 2 "gpc_reg_operand" "=&r") - (match_operator:SI 4 "boolean_or_operator" - [(xor:SI (not:SI (match_operand:SI 0 "memory_operand" "+Z")) - (not:SI (match_operand:SI 5 "logical_operand" "rK"))) - (match_operand:SI 1 "gpc_reg_operand" "r")])) - (set (match_operand:SI 3 "gpc_reg_operand" "=&b") (match_dup 0)) - (set (match_dup 0) (unspec:SI [(match_dup 4)] UNSPEC_SYNC_OP)) - (clobber (match_scratch:CC 6 "=&x"))] - "TARGET_POWERPC && !PPC405_ERRATUM77" - "lwarx %3,%y0\n\txor%I2 %2,%3,%5\n\t%q4 %2,%2,%1\n\tstwcx. %2,%y0\n\tbne- $-16" - [(set_attr "length" "20")]) - -(define_insn "isync" - [(set (mem:BLK (match_scratch 0 "X")) - (unspec_volatile:BLK [(mem:BLK (match_scratch 1 "X"))] UNSPECV_ISYNC))] - "" - "{ics|isync}" - [(set_attr "type" "isync")]) - -(define_expand "sync_lock_release<mode>" - [(set (match_operand:INT 0 "memory_operand") - (match_operand:INT 1 "any_operand"))] - "" - " +(define_expand "atomic_nand_fetch<mode>" + [(match_operand:INT1 0 "gpc_reg_operand" "") ;; output + (match_operand:INT1 1 "memory_operand" "") ;; memory + (match_operand:INT1 2 "gpc_reg_operand" "") ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model + "TARGET_POWERPC" { - emit_insn (gen_lwsync ()); - emit_move_insn (operands[0], operands[1]); + rs6000_expand_atomic_op (NOT, operands[1], operands[2], + NULL_RTX, operands[0], operands[3]); DONE; -}") - -; Some AIX assemblers don't accept lwsync, so we use a .long. -(define_insn "lwsync" - [(set (mem:BLK (match_scratch 0 "X")) - (unspec_volatile:BLK [(mem:BLK (match_scratch 1 "X"))] UNSPECV_LWSYNC))] - "" -{ - if (TARGET_NO_LWSYNC) - return "sync"; - else - return (TARGET_LWSYNC_INSTRUCTION) ? "lwsync" : ".long 0x7c2004ac"; -} - [(set_attr "type" "sync")]) - +}) diff --git a/gcc/config/sparc/sparc-protos.h b/gcc/config/sparc/sparc-protos.h index ccf20b1bcaa..10fa5edbb66 100644 --- a/gcc/config/sparc/sparc-protos.h +++ b/gcc/config/sparc/sparc-protos.h @@ -109,6 +109,8 @@ extern void sparc_expand_vector_init (rtx, rtx); extern void sparc_expand_vec_perm_bmask(enum machine_mode, rtx); extern bool sparc_expand_conditional_move (enum machine_mode, rtx *); extern void sparc_expand_vcond (enum machine_mode, rtx *, int, int); +unsigned int sparc_regmode_natural_size (enum machine_mode); +bool sparc_modes_tieable_p (enum machine_mode, enum machine_mode); #endif /* RTX_CODE */ #endif /* __SPARC_PROTOS_H__ */ diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 55759a03bd1..b315698bc94 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -11616,4 +11616,69 @@ sparc_expand_vcond (enum machine_mode mode, rtx *operands, int ccode, int fcode) emit_insn (gen_rtx_SET (VOIDmode, operands[0], bshuf)); } +/* On sparc, any mode which naturally allocates into the float + registers should return 4 here. */ + +unsigned int +sparc_regmode_natural_size (enum machine_mode mode) +{ + int size = UNITS_PER_WORD; + + if (TARGET_ARCH64) + { + enum mode_class mclass = GET_MODE_CLASS (mode); + + if (mclass == MODE_FLOAT || mclass == MODE_VECTOR_INT) + size = 4; + } + + return size; +} + +/* Return TRUE if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be FALSE for correct output. + + For V9 we have to deal with the fact that only the lower 32 floating + point registers are 32-bit addressable. */ + +bool +sparc_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2) +{ + enum mode_class mclass1, mclass2; + unsigned short size1, size2; + + if (mode1 == mode2) + return true; + + mclass1 = GET_MODE_CLASS (mode1); + mclass2 = GET_MODE_CLASS (mode2); + if (mclass1 != mclass2) + return false; + + if (! TARGET_V9) + return true; + + /* Classes are the same and we are V9 so we have to deal with upper + vs. lower floating point registers. If one of the modes is a + 4-byte mode, and the other is not, we have to mark them as not + tieable because only the lower 32 floating point register are + addressable 32-bits at a time. + + We can't just test explicitly for SFmode, otherwise we won't + cover the vector mode cases properly. */ + + if (mclass1 != MODE_FLOAT && mclass1 != MODE_VECTOR_INT) + return true; + + size1 = GET_MODE_SIZE (mode1); + size2 = GET_MODE_SIZE (mode2); + if ((size1 > 4 && size2 == 4) + || (size2 > 4 && size1 == 4)) + return false; + + return true; +} + #include "gt-sparc.h" diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h index e8707f50577..32f8c1074ae 100644 --- a/gcc/config/sparc/sparc.h +++ b/gcc/config/sparc/sparc.h @@ -716,8 +716,7 @@ extern enum cmodel sparc_cmodel; /* Due to the ARCH64 discrepancy above we must override this next macro too. */ -#define REGMODE_NATURAL_SIZE(MODE) \ - ((TARGET_ARCH64 && FLOAT_MODE_P (MODE)) ? 4 : UNITS_PER_WORD) +#define REGMODE_NATURAL_SIZE(MODE) sparc_regmode_natural_size (MODE) /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. See sparc.c for how we initialize this. */ @@ -735,20 +734,7 @@ extern int sparc_mode_class[]; register window instruction in the prologue. */ #define HARD_REGNO_RENAME_OK(FROM, TO) ((FROM) != 1) -/* Value is 1 if it is a good idea to tie two pseudo registers - when one has mode MODE1 and one has mode MODE2. - If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, - for any hard reg, then this must be 0 for correct output. - - For V9: SFmode can't be combined with other float modes, because they can't - be allocated to the %d registers. Also, DFmode won't fit in odd %f - registers, but SFmode will. */ -#define MODES_TIEABLE_P(MODE1, MODE2) \ - ((MODE1) == (MODE2) \ - || (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2) \ - && (! TARGET_V9 \ - || (GET_MODE_CLASS (MODE1) != MODE_FLOAT \ - || (MODE1 != SFmode && MODE2 != SFmode))))) +#define MODES_TIEABLE_P(MODE1, MODE2) sparc_modes_tieable_p (MODE1, MODE2) /* Specify the registers used for certain standard purposes. The values of these macros are register numbers. */ diff --git a/gcc/config/vms/vms-c.c b/gcc/config/vms/vms-c.c index eb4c63577a9..b3e0d955f19 100644 --- a/gcc/config/vms/vms-c.c +++ b/gcc/config/vms/vms-c.c @@ -227,6 +227,42 @@ vms_pragma_message (cpp_reader *pfile ATTRIBUTE_UNUSED) #endif } +/* Handle '#pragma __extern_prefix' */ + +static GTY(()) tree saved_extern_prefix; + +static void +vms_pragma_extern_prefix (cpp_reader * ARG_UNUSED (dummy)) +{ + enum cpp_ttype tok; + tree x; + + tok = pragma_lex (&x); + if (tok == CPP_NAME) + { + const char *op = IDENTIFIER_POINTER (x); + + if (!strcmp (op, "__save")) + saved_extern_prefix = pragma_extern_prefix; + else if (!strcmp (op, "__restore")) + pragma_extern_prefix = saved_extern_prefix; + else + warning (OPT_Wpragmas, + "malformed '#pragma __extern_prefix', ignoring"); + return; + } + else if (tok != CPP_STRING) + { + warning (OPT_Wpragmas, + "malformed '#pragma __extern_prefix', ignoring"); + } + else + { + /* Note that the length includes the null terminator. */ + pragma_extern_prefix = (TREE_STRING_LENGTH (x) > 1 ? x : NULL); + } +} + /* Add vms-specific pragma. */ void @@ -245,4 +281,52 @@ vms_c_register_pragma (void) c_register_pragma (NULL, "__extern_model", vms_pragma_extern_model); c_register_pragma (NULL, "extern_model", vms_pragma_extern_model); c_register_pragma (NULL, "__message", vms_pragma_message); + c_register_pragma (NULL, "__extern_prefix", vms_pragma_extern_prefix); +} + +/* Standard modules list. */ +static const char * const vms_std_modules[] = { "rtldef", "starlet_c", NULL }; + +/* Find include modules in the include path. */ + +void +vms_c_register_includes (const char *sysroot, + const char *iprefix ATTRIBUTE_UNUSED, int stdinc) +{ + static const char dir_separator_str[] = { DIR_SEPARATOR, 0 }; + struct cpp_dir *dir; + + /* Add on standard include pathes. */ + if (!stdinc) + return; + + for (dir = get_added_cpp_dirs (SYSTEM); dir != NULL; dir = dir->next) + { + const char * const *lib; + for (lib = vms_std_modules; *lib != NULL; lib++) + { + char *path; + struct stat st; + + if (sysroot != NULL) + path = concat (sysroot, dir->name, dir_separator_str, *lib, NULL); + else + path = concat (dir->name, dir_separator_str, *lib, NULL); + + if (stat (path, &st) == 0 && S_ISDIR (st.st_mode)) + { + cpp_dir *p; + + p = XNEW (cpp_dir); + p->next = NULL; + p->name = path; + p->sysp = 1; + p->construct = 0; + p->user_supplied_p = 0; + add_cpp_dir_path (p, SYSTEM); + } + else + free (path); + } + } } diff --git a/gcc/config/vms/vms.h b/gcc/config/vms/vms.h index 0da9d85a89c..62e8636cae4 100644 --- a/gcc/config/vms/vms.h +++ b/gcc/config/vms/vms.h @@ -34,6 +34,9 @@ along with GCC; see the file COPYING3. If not see } \ } while (0) +extern void vms_c_register_includes (const char *, const char *, int); +#define TARGET_EXTRA_INCLUDES vms_c_register_includes + /* Tell compiler we want to support VMS pragmas */ #define REGISTER_TARGET_PRAGMAS() vms_c_register_pragma () |
