diff options
author | Richard Sandiford <richard.sandiford@arm.com> | 2020-12-17 00:15:09 +0000 |
---|---|---|
committer | Richard Sandiford <richard.sandiford@arm.com> | 2020-12-17 00:15:09 +0000 |
commit | 04ee46ed1c06cbbffcd8eb0626b4ab6c3a5082d9 (patch) | |
tree | e013d7869c88e489c212f733856b114a91375a97 /gcc/rtlanal.c | |
parent | a240ea024dd8533d898c8cd779dedd0747bbbf2e (diff) | |
download | gcc-04ee46ed1c06cbbffcd8eb0626b4ab6c3a5082d9.zip gcc-04ee46ed1c06cbbffcd8eb0626b4ab6c3a5082d9.tar.gz gcc-04ee46ed1c06cbbffcd8eb0626b4ab6c3a5082d9.tar.bz2 |
rtlanal: Add some new helper classes
This patch adds some classes for gathering the list of registers
and memory that are read and written by an instruction, along
with various properties about the accesses. In some ways it's
similar to the information that DF collects for registers,
but extended to memory. The main reason for using it instead
of DF is that it can analyse tentative changes to instructions
before they've been committed.
The classes also collect general information about the instruction,
since it's cheap to do and helps to avoid multiple walks of the same
RTL pattern.
I've tried to optimise the code quite a bit, since with later patches
it becomes relatively performance-sensitive. See the discussion in
the comments for the trade-offs involved.
I put the declarations in a new rtlanal.h header file since it
seemed a bit excessive to put so much new inline stuff in rtl.h.
gcc/
* rtlanal.h: New file.
(MEM_REGNO): New constant.
(rtx_obj_flags): New namespace.
(rtx_obj_reference, rtx_properties): New classes.
(growing_rtx_properties, vec_rtx_properties_base): Likewise.
(vec_rtx_properties): New alias.
* rtlanal.c: Include it.
(rtx_properties::try_to_add_reg): New function.
(rtx_properties::try_to_add_dest): Likewise.
(rtx_properties::try_to_add_src): Likewise.
(rtx_properties::try_to_add_pattern): Likewise.
(rtx_properties::try_to_add_insn): Likewise.
(vec_rtx_properties_base::grow): Likewise.
Diffstat (limited to 'gcc/rtlanal.c')
-rw-r--r-- | gcc/rtlanal.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 30d5b0c..404813b 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "backend.h" #include "target.h" #include "rtl.h" +#include "rtlanal.h" #include "tree.h" #include "predict.h" #include "df.h" @@ -2049,6 +2050,287 @@ note_uses (rtx *pbody, void (*fun) (rtx *, void *), void *data) return; } } + +/* Try to add a description of REG X to this object, stopping once + the REF_END limit has been reached. FLAGS is a bitmask of + rtx_obj_reference flags that describe the context. */ + +void +rtx_properties::try_to_add_reg (const_rtx x, unsigned int flags) +{ + if (REG_NREGS (x) != 1) + flags |= rtx_obj_flags::IS_MULTIREG; + machine_mode mode = GET_MODE (x); + unsigned int start_regno = REGNO (x); + unsigned int end_regno = END_REGNO (x); + for (unsigned int regno = start_regno; regno < end_regno; ++regno) + if (ref_iter != ref_end) + *ref_iter++ = rtx_obj_reference (regno, flags, mode, + regno - start_regno); +} + +/* Add a description of destination X to this object. FLAGS is a bitmask + of rtx_obj_reference flags that describe the context. + + This routine accepts all rtxes that can legitimately appear in a + SET_DEST. */ + +void +rtx_properties::try_to_add_dest (const_rtx x, unsigned int flags) +{ + /* If we have a PARALLEL, SET_DEST is a list of EXPR_LIST expressions, + each of whose first operand is a register. */ + if (__builtin_expect (GET_CODE (x) == PARALLEL, 0)) + { + for (int i = XVECLEN (x, 0) - 1; i >= 0; --i) + if (rtx dest = XEXP (XVECEXP (x, 0, i), 0)) + try_to_add_dest (dest, flags); + return; + } + + unsigned int base_flags = flags & rtx_obj_flags::STICKY_FLAGS; + flags |= rtx_obj_flags::IS_WRITE; + for (;;) + if (GET_CODE (x) == ZERO_EXTRACT) + { + try_to_add_src (XEXP (x, 1), base_flags); + try_to_add_src (XEXP (x, 2), base_flags); + flags |= rtx_obj_flags::IS_READ; + x = XEXP (x, 0); + } + else if (GET_CODE (x) == STRICT_LOW_PART) + { + flags |= rtx_obj_flags::IS_READ; + x = XEXP (x, 0); + } + else if (GET_CODE (x) == SUBREG) + { + flags |= rtx_obj_flags::IN_SUBREG; + if (read_modify_subreg_p (x)) + flags |= rtx_obj_flags::IS_READ; + x = SUBREG_REG (x); + } + else + break; + + if (MEM_P (x)) + { + if (ref_iter != ref_end) + *ref_iter++ = rtx_obj_reference (MEM_REGNO, flags, GET_MODE (x)); + + unsigned int addr_flags = base_flags | rtx_obj_flags::IN_MEM_STORE; + if (flags & rtx_obj_flags::IS_READ) + addr_flags |= rtx_obj_flags::IN_MEM_LOAD; + try_to_add_src (XEXP (x, 0), addr_flags); + return; + } + + if (__builtin_expect (REG_P (x), 1)) + { + /* We want to keep sp alive everywhere - by making all + writes to sp also use sp. */ + if (REGNO (x) == STACK_POINTER_REGNUM) + flags |= rtx_obj_flags::IS_READ; + try_to_add_reg (x, flags); + return; + } +} + +/* Try to add a description of source X to this object, stopping once + the REF_END limit has been reached. FLAGS is a bitmask of + rtx_obj_reference flags that describe the context. + + This routine accepts all rtxes that can legitimately appear in a SET_SRC. */ + +void +rtx_properties::try_to_add_src (const_rtx x, unsigned int flags) +{ + unsigned int base_flags = flags & rtx_obj_flags::STICKY_FLAGS; + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, x, NONCONST) + { + const_rtx x = *iter; + rtx_code code = GET_CODE (x); + if (code == REG) + try_to_add_reg (x, flags | rtx_obj_flags::IS_READ); + else if (code == MEM) + { + if (MEM_VOLATILE_P (x)) + has_volatile_refs = true; + + if (!MEM_READONLY_P (x) && ref_iter != ref_end) + { + auto mem_flags = flags | rtx_obj_flags::IS_READ; + *ref_iter++ = rtx_obj_reference (MEM_REGNO, mem_flags, + GET_MODE (x)); + } + + try_to_add_src (XEXP (x, 0), + base_flags | rtx_obj_flags::IN_MEM_LOAD); + iter.skip_subrtxes (); + } + else if (code == SUBREG) + { + try_to_add_src (SUBREG_REG (x), flags | rtx_obj_flags::IN_SUBREG); + iter.skip_subrtxes (); + } + else if (code == UNSPEC_VOLATILE) + has_volatile_refs = true; + else if (code == ASM_INPUT || code == ASM_OPERANDS) + { + has_asm = true; + if (MEM_VOLATILE_P (x)) + has_volatile_refs = true; + } + else if (code == PRE_INC + || code == PRE_DEC + || code == POST_INC + || code == POST_DEC + || code == PRE_MODIFY + || code == POST_MODIFY) + { + has_pre_post_modify = true; + + unsigned int addr_flags = (base_flags + | rtx_obj_flags::IS_PRE_POST_MODIFY + | rtx_obj_flags::IS_READ); + try_to_add_dest (XEXP (x, 0), addr_flags); + if (code == PRE_MODIFY || code == POST_MODIFY) + iter.substitute (XEXP (XEXP (x, 1), 1)); + else + iter.skip_subrtxes (); + } + else if (code == CALL) + has_call = true; + } +} + +/* Try to add a description of instruction pattern PAT to this object, + stopping once the REF_END limit has been reached. */ + +void +rtx_properties::try_to_add_pattern (const_rtx pat) +{ + switch (GET_CODE (pat)) + { + case COND_EXEC: + try_to_add_src (COND_EXEC_TEST (pat)); + try_to_add_pattern (COND_EXEC_CODE (pat)); + break; + + case PARALLEL: + { + int last = XVECLEN (pat, 0) - 1; + for (int i = 0; i < last; ++i) + try_to_add_pattern (XVECEXP (pat, 0, i)); + try_to_add_pattern (XVECEXP (pat, 0, last)); + break; + } + + case ASM_OPERANDS: + for (int i = 0, len = ASM_OPERANDS_INPUT_LENGTH (pat); i < len; ++i) + try_to_add_src (ASM_OPERANDS_INPUT (pat, i)); + break; + + case CLOBBER: + try_to_add_dest (XEXP (pat, 0), rtx_obj_flags::IS_CLOBBER); + break; + + case SET: + try_to_add_dest (SET_DEST (pat)); + try_to_add_src (SET_SRC (pat)); + break; + + default: + /* All the other possibilities never store and can use a normal + rtx walk. This includes: + + - USE + - TRAP_IF + - PREFETCH + - UNSPEC + - UNSPEC_VOLATILE. */ + try_to_add_src (pat); + break; + } +} + +/* Try to add a description of INSN to this object, stopping once + the REF_END limit has been reached. INCLUDE_NOTES is true if the + description should include REG_EQUAL and REG_EQUIV notes; all such + references will then be marked with rtx_obj_flags::IN_NOTE. + + For calls, this description includes all accesses in + CALL_INSN_FUNCTION_USAGE. It also include all implicit accesses + to global registers by the target function. However, it does not + include clobbers performed by the target function; callers that want + this information should instead use the function_abi interface. */ + +void +rtx_properties::try_to_add_insn (const rtx_insn *insn, bool include_notes) +{ + if (CALL_P (insn)) + { + /* Adding the global registers first removes a situation in which + a fixed-form clobber of register R could come before a real set + of register R. */ + if (!hard_reg_set_empty_p (global_reg_set)) + { + unsigned int flags = (rtx_obj_flags::IS_READ + | rtx_obj_flags::IS_WRITE); + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + if (global_regs[regno] && ref_iter != ref_end) + *ref_iter++ = rtx_obj_reference (regno, flags, + reg_raw_mode[regno], 0); + } + if (ref_iter != ref_end && !RTL_CONST_CALL_P (insn)) + { + auto mem_flags = rtx_obj_flags::IS_READ; + if (!RTL_PURE_CALL_P (insn)) + mem_flags |= rtx_obj_flags::IS_WRITE; + *ref_iter++ = rtx_obj_reference (MEM_REGNO, mem_flags, BLKmode); + } + try_to_add_pattern (PATTERN (insn)); + for (rtx link = CALL_INSN_FUNCTION_USAGE (insn); link; + link = XEXP (link, 1)) + { + rtx x = XEXP (link, 0); + if (GET_CODE (x) == CLOBBER) + try_to_add_dest (XEXP (x, 0), rtx_obj_flags::IS_CLOBBER); + else if (GET_CODE (x) == USE) + try_to_add_src (XEXP (x, 0)); + } + } + else + try_to_add_pattern (PATTERN (insn)); + + if (include_notes) + for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_EQUAL + || REG_NOTE_KIND (note) == REG_EQUIV) + try_to_add_note (XEXP (note, 0)); +} + +/* Grow the storage by a bit while keeping the contents of the first + START elements. */ + +void +vec_rtx_properties_base::grow (ptrdiff_t start) +{ + /* The same heuristic that vec uses. */ + ptrdiff_t new_elems = (ref_end - ref_begin) * 3 / 2; + if (ref_begin == m_storage) + { + ref_begin = XNEWVEC (rtx_obj_reference, new_elems); + if (start) + memcpy (ref_begin, m_storage, start * sizeof (rtx_obj_reference)); + } + else + ref_begin = reinterpret_cast<rtx_obj_reference *> + (xrealloc (ref_begin, new_elems * sizeof (rtx_obj_reference))); + ref_iter = ref_begin + start; + ref_end = ref_begin + new_elems; +} /* Return nonzero if X's old contents don't survive after INSN. This will be true if X is (cc0) or if X is a register and |