;; Predicate definitions for TI PRU. ;; Copyright (C) 2014-2020 Free Software Foundation, Inc. ;; Contributed by Dimitar Dimitrov dimitar@dinux.eu ;; ;; 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_predicate “const_1_operand” (and (match_code “const_int”) (match_test “INTVAL (op) == 1”)))

; Note: Always pass a valid mode! (define_predicate “const_ubyte_operand” (match_code “const_int”) { gcc_assert (mode != VOIDmode); return IN_RANGE (INTVAL (op) & GET_MODE_MASK (mode), 0, 0xff); })

(define_predicate “const_uhword_operand” (match_code “const_int”) { gcc_assert (mode != VOIDmode); return IN_RANGE (INTVAL (op) & GET_MODE_MASK (mode), 0, 0xffff); })

; TRUE for comparisons we support. (define_predicate “pru_cmp_operator” (match_code “eq,ne,leu,ltu,geu,gtu”))

; TRUE for signed comparisons that need special handling for PRU. (define_predicate “pru_signed_cmp_operator” (match_code “ge,gt,le,lt”))

;; FP Comparisons handled by pru_expand_pru_compare. (define_predicate “pru_fp_comparison_operator” (match_code “eq,ne,lt,gt,le,ge”))

;; Return true if OP is a constant that contains only one 1 in its ;; binary representation. (define_predicate “single_one_operand” (and (match_code “const_int”) (match_test “exact_log2 (INTVAL (op) & GET_MODE_MASK (mode)) >= 0”)))

;; Return true if OP is a constant that contains only one 0 in its ;; binary representation. (define_predicate “single_zero_operand” (and (match_code “const_int”) (match_test “exact_log2 (~INTVAL (op) & GET_MODE_MASK (mode)) >= 0”)))

(define_predicate “pru_muldst_operand” (match_code “subreg,reg”) { if (register_operand (op, mode)) { int regno;

  if (REG_P (op))
regno = REGNO (op);
  else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
regno = REGNO (SUBREG_REG (op));
  else
return 0;

  return REGNO_REG_CLASS (regno) == MULDST_REGS
     || regno >= FIRST_PSEUDO_REGISTER;
}

return 0; })

(define_predicate “pru_mulsrc0_operand” (match_code “subreg,reg”) { if (register_operand (op, mode)) { int regno;

  if (REG_P (op))
regno = REGNO (op);
  else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
regno = REGNO (SUBREG_REG (op));
  else
return 0;

  return REGNO_REG_CLASS (regno) == MULSRC0_REGNUM
     || regno >= FIRST_PSEUDO_REGISTER;
}

return 0; })

(define_predicate “pru_mulsrc1_operand” (match_code “subreg,reg”) { if (register_operand (op, mode)) { int regno;

  if (REG_P (op))
regno = REGNO (op);
  else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
regno = REGNO (SUBREG_REG (op));
  else
return 0;

  return REGNO_REG_CLASS (regno) == MULSRC1_REGNUM
     || regno >= FIRST_PSEUDO_REGISTER;
}

return 0; })

(define_predicate “reg_or_const_int_operand” (ior (match_operand 0 “const_int_operand”) (match_operand 0 “register_operand”)))

(define_predicate “reg_or_ubyte_operand” (ior (match_operand 0 “const_ubyte_operand”) (match_operand 0 “register_operand”)))

(define_predicate “reg_or_const_1_operand” (ior (match_operand 0 “const_1_operand”) (match_operand 0 “register_operand”)))

(define_predicate “const_shift_operand” (and (match_code “const_int”) (match_test “SHIFT_INT (INTVAL (op))”)))

(define_predicate “shift_operand” (ior (match_operand 0 “const_shift_operand”) (match_operand 0 “register_operand”)))

(define_predicate “ctable_addr_operand” (and (match_code “const_int”) (match_test “pru_get_ctable_base_index (INTVAL (op)) >= 0”)))

(define_predicate “ctable_base_operand” (and (match_code “const_int”) (match_test “pru_get_ctable_exact_base_index (INTVAL (op)) >= 0”)))

;; Ideally we should enforce a restriction to all text labels to fit in ;; 16bits, as required by the PRU ISA. But for the time being we'll rely on ;; binutils to catch text segment overflows. (define_predicate “call_operand” (ior (match_operand 0 “immediate_operand”) (match_operand 0 “register_operand”)))

;; Return true if OP is a text segment reference. ;; This is needed for program memory address expressions. Borrowed from AVR. (define_predicate “text_segment_operand” (match_code “code_label,label_ref,symbol_ref,plus,minus”) { poly_int64 offset; rtx base = strip_offset (op, &offset);

switch (GET_CODE (base)) { case CODE_LABEL: /* Why AVR lists this as a valid option? Let's catch it. / gcc_unreachable (); return false; case LABEL_REF: return true; case SYMBOL_REF: return SYMBOL_REF_FUNCTION_P (base); case PLUS: case MINUS: / Handle constructs like (&&label1 - &&label2). See pr70460.c. */ return text_segment_operand (XEXP (op, 0), VOIDmode); default: return false; } })

;; Return true if OP is a load multiple operation. It is known to be a ;; PARALLEL and the first section will be tested.

(define_special_predicate “load_multiple_operation” (match_code “parallel”) { machine_mode elt_mode; int count = XVECLEN (op, 0); unsigned int dest_regno; rtx src_addr, base_reg; poly_int64 base_offs; int i;

/* Perform a quick check so we don't blow up below. */ if (GET_CODE (XVECEXP (op, 0, 0)) != SET || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) return false;

dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); elt_mode = GET_MODE (SET_DEST (XVECEXP (op, 0, 0)));

base_reg = strip_offset (src_addr, &base_offs); if (GET_CODE (base_reg) != REG) return false;

for (i = 1; i < count; i++) { rtx elt_reg; poly_int64 elt_offs; rtx elt = XVECEXP (op, 0, i);

  if (GET_CODE (elt) != SET
  || GET_CODE (SET_DEST (elt)) != REG
  || GET_MODE (SET_DEST (elt)) != elt_mode
  || REGNO (SET_DEST (elt)) != dest_regno + i * GET_MODE_SIZE (elt_mode)
  || GET_CODE (SET_SRC (elt)) != MEM
  || GET_MODE (SET_SRC (elt)) != elt_mode)
return false;

  elt_reg = strip_offset (XEXP (SET_SRC (elt), 0), &elt_offs);

  if (GET_CODE (elt_reg) != REG
  || ! rtx_equal_p (elt_reg, base_reg)
  || elt_offs != base_offs + i * GET_MODE_SIZE (elt_mode))
return false;
}

return true; })

;; Return true if OP is a store multiple operation. It is known to be a ;; PARALLEL and the first section will be tested.

(define_special_predicate “store_multiple_operation” (match_code “parallel”) { machine_mode elt_mode; int count = XVECLEN (op, 0); unsigned int src_regno; rtx dest_addr, base_reg; poly_int64 base_offs; int i;

/* Perform a quick check so we don't blow up below. */ if (GET_CODE (XVECEXP (op, 0, 0)) != SET || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) return false;

src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); elt_mode = GET_MODE (SET_SRC (XVECEXP (op, 0, 0)));

base_reg = strip_offset (dest_addr, &base_offs); if (GET_CODE (base_reg) != REG) return false;

for (i = 1; i < count; i++) { rtx elt_reg; poly_int64 elt_offs; rtx elt = XVECEXP (op, 0, i);

  if (GET_CODE (elt) != SET
  || GET_CODE (SET_SRC (elt)) != REG
  || GET_MODE (SET_SRC (elt)) != elt_mode
  || REGNO (SET_SRC (elt)) != src_regno + i * GET_MODE_SIZE (elt_mode)
  || GET_CODE (SET_DEST (elt)) != MEM
  || GET_MODE (SET_DEST (elt)) != elt_mode)
return false;

  elt_reg = strip_offset (XEXP (SET_DEST (elt), 0), &elt_offs);

  if (GET_CODE (elt_reg) != REG
  || ! rtx_equal_p (elt_reg, base_reg)
  || elt_offs != base_offs + i * GET_MODE_SIZE (elt_mode))
return false;
}

return true; })