blob: af8fc9996fabebadb2aab07e4123945c9b9560af [file] [log] [blame]
/* 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));
}