| /* Dependency checks for instruction scheduling, shared between ARM and |
| AARCH64. |
| |
| Copyright (C) 1991-2014 Free Software Foundation, Inc. |
| Contributed by ARM Ltd. |
| |
| 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/>. */ |
| |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tm_p.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "c-family/c-common.h" |
| #include "rtl.h" |
| |
| /* In ARMv8-A there's a general expectation that AESE/AESMC |
| and AESD/AESIMC sequences of the form: |
| |
| AESE Vn, _ |
| AESMC Vn, Vn |
| |
| will issue both instructions in a single cycle on super-scalar |
| implementations. This function identifies such pairs. */ |
| |
| int |
| aarch_crypto_can_dual_issue (rtx producer, rtx consumer) |
| { |
| rtx producer_src, consumer_src; |
| |
| producer = single_set (producer); |
| consumer = single_set (consumer); |
| |
| producer_src = producer ? SET_SRC (producer) : NULL; |
| consumer_src = consumer ? SET_SRC (consumer) : NULL; |
| |
| if (producer_src && consumer_src |
| && GET_CODE (producer_src) == UNSPEC && GET_CODE (consumer_src) == UNSPEC |
| && ((XINT (producer_src, 1) == UNSPEC_AESE |
| && XINT (consumer_src, 1) == UNSPEC_AESMC) |
| || (XINT (producer_src, 1) == UNSPEC_AESD |
| && XINT (consumer_src, 1) == UNSPEC_AESIMC))) |
| { |
| unsigned int regno = REGNO (SET_DEST (producer)); |
| |
| return REGNO (SET_DEST (consumer)) == regno |
| && REGNO (XVECEXP (consumer_src, 0, 0)) == regno; |
| } |
| |
| return 0; |
| } |
| |
| typedef struct |
| { |
| rtx_code search_code; |
| rtx search_result; |
| bool find_any_shift; |
| } search_term; |
| |
| /* Return TRUE if X is either an arithmetic shift left, or |
| is a multiplication by a power of two. */ |
| bool |
| arm_rtx_shift_left_p (rtx x) |
| { |
| enum rtx_code code = GET_CODE (x); |
| |
| if (code == MULT && CONST_INT_P (XEXP (x, 1)) |
| && exact_log2 (INTVAL (XEXP (x, 1))) > 0) |
| return true; |
| |
| if (code == ASHIFT) |
| return true; |
| |
| return false; |
| } |
| |
| static rtx_code shift_rtx_codes[] = |
| { ASHIFT, ROTATE, ASHIFTRT, LSHIFTRT, |
| ROTATERT, ZERO_EXTEND, SIGN_EXTEND }; |
| |
| /* Callback function for arm_find_sub_rtx_with_code. |
| DATA is safe to treat as a SEARCH_TERM, ST. This will |
| hold a SEARCH_CODE. PATTERN is checked to see if it is an |
| RTX with that code. If it is, write SEARCH_RESULT in ST |
| and return 1. Otherwise, or if we have been passed a NULL_RTX |
| return 0. If ST.FIND_ANY_SHIFT then we are interested in |
| anything which can reasonably be described as a SHIFT RTX. */ |
| static int |
| arm_find_sub_rtx_with_search_term (rtx *pattern, void *data) |
| { |
| search_term *st = (search_term *) data; |
| rtx_code pattern_code; |
| int found = 0; |
| |
| gcc_assert (pattern); |
| gcc_assert (st); |
| |
| /* Poorly formed patterns can really ruin our day. */ |
| if (*pattern == NULL_RTX) |
| return 0; |
| |
| pattern_code = GET_CODE (*pattern); |
| |
| if (st->find_any_shift) |
| { |
| unsigned i = 0; |
| |
| /* Left shifts might have been canonicalized to a MULT of some |
| power of two. Make sure we catch them. */ |
| if (arm_rtx_shift_left_p (*pattern)) |
| found = 1; |
| else |
| for (i = 0; i < ARRAY_SIZE (shift_rtx_codes); i++) |
| if (pattern_code == shift_rtx_codes[i]) |
| found = 1; |
| } |
| |
| if (pattern_code == st->search_code) |
| found = 1; |
| |
| if (found) |
| st->search_result = *pattern; |
| |
| return found; |
| } |
| |
| /* Traverse PATTERN looking for a sub-rtx with RTX_CODE CODE. */ |
| static rtx |
| arm_find_sub_rtx_with_code (rtx pattern, rtx_code code, bool find_any_shift) |
| { |
| search_term st; |
| int result = 0; |
| |
| gcc_assert (pattern != NULL_RTX); |
| st.search_code = code; |
| st.search_result = NULL_RTX; |
| st.find_any_shift = find_any_shift; |
| result = for_each_rtx (&pattern, arm_find_sub_rtx_with_search_term, &st); |
| if (result) |
| return st.search_result; |
| else |
| return NULL_RTX; |
| } |
| |
| /* Traverse PATTERN looking for any sub-rtx which looks like a shift. */ |
| static rtx |
| arm_find_shift_sub_rtx (rtx pattern) |
| { |
| return arm_find_sub_rtx_with_code (pattern, ASHIFT, true); |
| } |
| |
| /* PRODUCER and CONSUMER are two potentially dependant RTX. PRODUCER |
| (possibly) contains a SET which will provide a result we can access |
| using the SET_DEST macro. We will place the RTX which would be |
| written by PRODUCER in SET_SOURCE. |
| Similarly, CONSUMER (possibly) contains a SET which has an operand |
| we can access using SET_SRC. We place this operand in |
| SET_DESTINATION. |
| |
| Return nonzero if we found the SET RTX we expected. */ |
| static int |
| arm_get_set_operands (rtx producer, rtx consumer, |
| rtx *set_source, rtx *set_destination) |
| { |
| rtx set_producer = arm_find_sub_rtx_with_code (producer, SET, false); |
| rtx set_consumer = arm_find_sub_rtx_with_code (consumer, SET, false); |
| |
| if (set_producer && set_consumer) |
| { |
| *set_source = SET_DEST (set_producer); |
| *set_destination = SET_SRC (set_consumer); |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Return nonzero if the CONSUMER instruction (a load) does need |
| PRODUCER's value to calculate the address. */ |
| int |
| arm_early_load_addr_dep (rtx producer, rtx consumer) |
| { |
| rtx value, addr; |
| |
| if (!arm_get_set_operands (producer, consumer, &value, &addr)) |
| return 0; |
| |
| return reg_overlap_mentioned_p (value, addr); |
| } |
| |
| /* Return nonzero if the CONSUMER instruction (an ALU op) does not |
| have an early register shift value or amount dependency on the |
| result of PRODUCER. */ |
| int |
| arm_no_early_alu_shift_dep (rtx producer, rtx consumer) |
| { |
| rtx value, op; |
| rtx early_op; |
| |
| if (!arm_get_set_operands (producer, consumer, &value, &op)) |
| return 0; |
| |
| if ((early_op = arm_find_shift_sub_rtx (op))) |
| { |
| if (REG_P (early_op)) |
| early_op = op; |
| |
| return !reg_overlap_mentioned_p (value, early_op); |
| } |
| |
| return 0; |
| } |
| |
| /* Return nonzero if the CONSUMER instruction (an ALU op) does not |
| have an early register shift value dependency on the result of |
| PRODUCER. */ |
| int |
| arm_no_early_alu_shift_value_dep (rtx producer, rtx consumer) |
| { |
| rtx value, op; |
| rtx early_op; |
| |
| if (!arm_get_set_operands (producer, consumer, &value, &op)) |
| return 0; |
| |
| if ((early_op = arm_find_shift_sub_rtx (op))) |
| /* We want to check the value being shifted. */ |
| if (!reg_overlap_mentioned_p (value, XEXP (early_op, 0))) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Return nonzero if the CONSUMER (a mul or mac op) does not |
| have an early register mult dependency on the result of |
| PRODUCER. */ |
| int |
| arm_no_early_mul_dep (rtx producer, rtx consumer) |
| { |
| rtx value, op; |
| |
| if (!arm_get_set_operands (producer, consumer, &value, &op)) |
| return 0; |
| |
| if (GET_CODE (op) == PLUS || GET_CODE (op) == MINUS) |
| { |
| if (GET_CODE (XEXP (op, 0)) == MULT) |
| return !reg_overlap_mentioned_p (value, XEXP (op, 0)); |
| else |
| return !reg_overlap_mentioned_p (value, XEXP (op, 1)); |
| } |
| |
| return 0; |
| } |
| |
| /* Return nonzero if the CONSUMER instruction (a store) does not need |
| PRODUCER's value to calculate the address. */ |
| |
| int |
| arm_no_early_store_addr_dep (rtx producer, rtx consumer) |
| { |
| rtx value = arm_find_sub_rtx_with_code (producer, SET, false); |
| rtx addr = arm_find_sub_rtx_with_code (consumer, SET, false); |
| |
| if (value) |
| value = SET_DEST (value); |
| |
| if (addr) |
| addr = SET_DEST (addr); |
| |
| if (!value || !addr) |
| return 0; |
| |
| return !reg_overlap_mentioned_p (value, addr); |
| } |
| |
| /* Return nonzero if the CONSUMER instruction (a store) does need |
| PRODUCER's value to calculate the address. */ |
| |
| int |
| arm_early_store_addr_dep (rtx producer, rtx consumer) |
| { |
| return !arm_no_early_store_addr_dep (producer, consumer); |
| } |
| |
| /* Return non-zero iff the consumer (a multiply-accumulate or a |
| multiple-subtract instruction) has an accumulator dependency on the |
| result of the producer and no other dependency on that result. It |
| does not check if the producer is multiply-accumulate instruction. */ |
| int |
| arm_mac_accumulator_is_result (rtx producer, rtx consumer) |
| { |
| rtx result; |
| rtx op0, op1, acc; |
| |
| producer = PATTERN (producer); |
| consumer = PATTERN (consumer); |
| |
| if (GET_CODE (producer) == COND_EXEC) |
| producer = COND_EXEC_CODE (producer); |
| if (GET_CODE (consumer) == COND_EXEC) |
| consumer = COND_EXEC_CODE (consumer); |
| |
| if (GET_CODE (producer) != SET) |
| return 0; |
| |
| result = XEXP (producer, 0); |
| |
| if (GET_CODE (consumer) != SET) |
| return 0; |
| |
| /* Check that the consumer is of the form |
| (set (...) (plus (mult ...) (...))) |
| or |
| (set (...) (minus (...) (mult ...))). */ |
| if (GET_CODE (XEXP (consumer, 1)) == PLUS) |
| { |
| if (GET_CODE (XEXP (XEXP (consumer, 1), 0)) != MULT) |
| return 0; |
| |
| op0 = XEXP (XEXP (XEXP (consumer, 1), 0), 0); |
| op1 = XEXP (XEXP (XEXP (consumer, 1), 0), 1); |
| acc = XEXP (XEXP (consumer, 1), 1); |
| } |
| else if (GET_CODE (XEXP (consumer, 1)) == MINUS) |
| { |
| if (GET_CODE (XEXP (XEXP (consumer, 1), 1)) != MULT) |
| return 0; |
| |
| op0 = XEXP (XEXP (XEXP (consumer, 1), 1), 0); |
| op1 = XEXP (XEXP (XEXP (consumer, 1), 1), 1); |
| acc = XEXP (XEXP (consumer, 1), 0); |
| } |
| else |
| return 0; |
| |
| return (reg_overlap_mentioned_p (result, acc) |
| && !reg_overlap_mentioned_p (result, op0) |
| && !reg_overlap_mentioned_p (result, op1)); |
| } |
| |
| /* Return non-zero if the consumer (a multiply-accumulate instruction) |
| has an accumulator dependency on the result of the producer (a |
| multiplication instruction) and no other dependency on that result. */ |
| int |
| arm_mac_accumulator_is_mul_result (rtx producer, rtx consumer) |
| { |
| rtx mul = PATTERN (producer); |
| rtx mac = PATTERN (consumer); |
| rtx mul_result; |
| rtx mac_op0, mac_op1, mac_acc; |
| |
| if (GET_CODE (mul) == COND_EXEC) |
| mul = COND_EXEC_CODE (mul); |
| if (GET_CODE (mac) == COND_EXEC) |
| mac = COND_EXEC_CODE (mac); |
| |
| /* Check that mul is of the form (set (...) (mult ...)) |
| and mla is of the form (set (...) (plus (mult ...) (...))). */ |
| if ((GET_CODE (mul) != SET || GET_CODE (XEXP (mul, 1)) != MULT) |
| || (GET_CODE (mac) != SET || GET_CODE (XEXP (mac, 1)) != PLUS |
| || GET_CODE (XEXP (XEXP (mac, 1), 0)) != MULT)) |
| return 0; |
| |
| mul_result = XEXP (mul, 0); |
| mac_op0 = XEXP (XEXP (XEXP (mac, 1), 0), 0); |
| mac_op1 = XEXP (XEXP (XEXP (mac, 1), 0), 1); |
| mac_acc = XEXP (XEXP (mac, 1), 1); |
| |
| return (reg_overlap_mentioned_p (mul_result, mac_acc) |
| && !reg_overlap_mentioned_p (mul_result, mac_op0) |
| && !reg_overlap_mentioned_p (mul_result, mac_op1)); |
| } |