diff options
author | Richard Henderson <rth@twiddle.net> | 2013-08-31 06:30:45 -0700 |
---|---|---|
committer | Richard Henderson <rth@twiddle.net> | 2013-09-25 07:46:32 -0700 |
commit | b0940da012c4c80145fdcf1730620f28ce80c2d8 (patch) | |
tree | 61f7231c6d50e338f06da07885800c3d7000bc5e /tcg/ppc64 | |
parent | d40f3cb1128208d901b6224b52ff36ff05680d28 (diff) | |
download | qemu-b0940da012c4c80145fdcf1730620f28ce80c2d8.zip |
tcg-ppc64: Look through a constant function descriptor
Especially in the user-only configurations, a direct branch into
the executable may be in range.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'tcg/ppc64')
-rw-r--r-- | tcg/ppc64/tcg-target.c | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index 8eb04060e6..0659dd6e66 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -173,14 +173,17 @@ static const int tcg_target_callee_save_regs[] = { TCG_REG_R31 }; +static inline bool in_range_b(tcg_target_long target) +{ + return target == sextract64(target, 0, 26); +} + static uint32_t reloc_pc24_val(void *pc, tcg_target_long target) { tcg_target_long disp; disp = target - (tcg_target_long)pc; - if ((disp << 38) >> 38 != disp) { - tcg_abort(); - } + assert(in_range_b(disp)); return disp & 0x3fffffc; } @@ -694,7 +697,7 @@ static void tcg_out_b(TCGContext *s, int mask, tcg_target_long target) tcg_target_long disp; disp = target - (tcg_target_long)s->code_ptr; - if ((disp << 38) >> 38 == disp) { + if (in_range_b(disp)) { tcg_out32(s, B | (disp & 0x3fffffc) | mask); } else { tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, (tcg_target_long)target); @@ -717,6 +720,18 @@ static void tcg_out_call(TCGContext *s, tcg_target_long arg, int const_arg) int ofs = 0; if (const_arg) { + /* Look through the descriptor. If the branch is in range, and we + don't have to spend too much effort on building the toc. */ + intptr_t tgt = ((intptr_t *)arg)[0]; + intptr_t toc = ((intptr_t *)arg)[1]; + intptr_t diff = tgt - (intptr_t)s->code_ptr; + + if (in_range_b(diff) && toc == (uint32_t)toc) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, toc); + tcg_out_b(s, LK, tgt); + return; + } + /* Fold the low bits of the constant into the addresses below. */ ofs = (int16_t)arg; if (ofs + 8 < 0x8000) { |