diff options
author | wehle <wehle@138bc75d-0d04-0410-961f-82ee72b054a4> | 2001-12-07 16:10:03 +0000 |
---|---|---|
committer | wehle <wehle@138bc75d-0d04-0410-961f-82ee72b054a4> | 2001-12-07 16:10:03 +0000 |
commit | 29a40d6d2d1a7debbdcd3554138069fee3270555 (patch) | |
tree | 91871aa733ca0ae38e63395948c525a3d872a6d7 /gcc/rtlanal.c | |
parent | eb3b751b644ea760d742ed54c780a6d290592747 (diff) | |
download | ppe42-gcc-29a40d6d2d1a7debbdcd3554138069fee3270555.tar.gz ppe42-gcc-29a40d6d2d1a7debbdcd3554138069fee3270555.zip |
* rtl.h (get_jump_table_offset): Declare.
* rtlanal.c (get_jump_table_offset): Implement.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@47756 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/rtlanal.c')
-rw-r--r-- | gcc/rtlanal.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 07c467659cd..f105950d985 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -339,6 +339,147 @@ get_related_value (x) return 0; } +/* Given a tablejump insn INSN, return the RTL expression for the offset + into the jump table. If the offset cannot be determined, then return + NULL_RTX. + + If EARLIEST is non-zero, it is a pointer to a place where the earliest + insn used in locating the offset was found. */ + +rtx +get_jump_table_offset (insn, earliest) + rtx insn; + rtx *earliest; +{ + rtx label; + rtx table; + rtx set; + rtx old_insn; + rtx x; + rtx old_x; + rtx y; + rtx old_y; + int i; + int j; + + if (GET_CODE (insn) != JUMP_INSN + || ! (label = JUMP_LABEL (insn)) + || ! (table = NEXT_INSN (label)) + || GET_CODE (table) != JUMP_INSN + || (GET_CODE (PATTERN (table)) != ADDR_VEC + && GET_CODE (PATTERN (table)) != ADDR_DIFF_VEC) + || ! (set = single_set (insn))) + return NULL_RTX; + + x = SET_SRC (set); + + /* Some targets (eg, ARM) emit a tablejump that also + contains the out-of-range target. */ + if (GET_CODE (x) == IF_THEN_ELSE + && GET_CODE (XEXP (x, 2)) == LABEL_REF) + x = XEXP (x, 1); + + /* Search backwards and locate the expression stored in X. */ + for (old_x = NULL_RTX; GET_CODE (x) == REG && x != old_x; + old_x = x, x = find_last_value (x, &insn, NULL_RTX, 0)) + ; + + /* If X is an expression using a relative address then strip + off the addition / subtraction of PC, PIC_OFFSET_TABLE_REGNUM, + or the jump table label. */ + if (GET_CODE (PATTERN (table)) == ADDR_DIFF_VEC + && (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS)) + { + for (i = 0; i < 2; i++) + { + old_insn = insn; + y = XEXP (x, i); + + if (y == pc_rtx || y == pic_offset_table_rtx) + break; + + for (old_y = NULL_RTX; GET_CODE (y) == REG && y != old_y; + old_y = y, y = find_last_value (y, &old_insn, NULL_RTX, 0)) + ; + + if ((GET_CODE (y) == LABEL_REF && XEXP (y, 0) == label)) + break; + } + + if (i >= 2) + return NULL_RTX; + + x = XEXP (x, 1 - i); + + for (old_x = NULL_RTX; GET_CODE (x) == REG && x != old_x; + old_x = x, x = find_last_value (x, &insn, NULL_RTX, 0)) + ; + } + + /* Strip off any sign or zero extension. */ + if (GET_CODE (x) == SIGN_EXTEND || GET_CODE (x) == ZERO_EXTEND) + { + x = XEXP (x, 0); + + for (old_x = NULL_RTX; GET_CODE (x) == REG && x != old_x; + old_x = x, x = find_last_value (x, &insn, NULL_RTX, 0)) + ; + } + + /* If X isn't a MEM then this isn't a tablejump we understand. */ + if (GET_CODE (x) != MEM) + return NULL_RTX; + + /* Strip off the MEM. */ + x = XEXP (x, 0); + + for (old_x = NULL_RTX; GET_CODE (x) == REG && x != old_x; + old_x = x, x = find_last_value (x, &insn, NULL_RTX, 0)) + ; + + /* If X isn't a PLUS than this isn't a tablejump we understand. */ + if (GET_CODE (x) != PLUS) + return NULL_RTX; + + /* At this point we should have an expression representing the jump table + plus an offset. Examine each operand in order to determine which one + represents the jump table. Knowing that tells us that the other operand + must represent the offset. */ + for (i = 0; i < 2; i++) + { + old_insn = insn; + y = XEXP (x, i); + + for (old_y = NULL_RTX; GET_CODE (y) == REG && y != old_y; + old_y = y, y = find_last_value (y, &old_insn, NULL_RTX, 0)) + ; + + if ((GET_CODE (y) == CONST || GET_CODE (y) == LABEL_REF) + && reg_mentioned_p (label, y)) + break; + } + + if (i >= 2) + return NULL_RTX; + + x = XEXP (x, 1 - i); + + /* Strip off the addition / subtraction of PIC_OFFSET_TABLE_REGNUM. */ + if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS) + for (i = 0; i < 2; i++) + if (XEXP (x, i) == pic_offset_table_rtx) + { + x = XEXP (x, 1 - i); + break; + } + + if (earliest) + *earliest = insn; + + /* Return the RTL expression representing the offset. */ + return x; +} + /* Return the number of places FIND appears within X. If COUNT_DEST is zero, we do not count occurrences inside the destination of a SET. */ |