Three microMIPS jump instruction encodings (namely J32, JAL32, JALS32) shift their 26-bit immediate field by 1 instead of 2, allowing a jump only within the 128MB aligned block rather than the more common 256MB aligned block.
This wasn't being taken into account when masking the address of the delay slot in gen_compute_branch(), resulting in bit 27 of the PC being cleared. This meant that any such jump in an odd 128MB block incorrectly jumped to the even 128MB block before it. For example this jals jumped to 0x17000010: 1f00000a: 7780 0008 jals 1f000010 <.L11> And this jal jumped to 0x17000022: 1f00001a: f780 0011 jal 1f000022 <.L12> And this j jumped to 0x17000030: 1f00002a: d780 0018 j 1f000030 <.L13> All three encodings pass OPC_J or OPC_JAL to gen_compute_branch(), so allow the mask applied to the delay slot address to be changed from 0xF0000000 to 0xF8000000 for these opcodes, but only when microMIPS is in use. Fixes: 3c824109da07 ("target-mips: microMIPS ASE support") Signed-off-by: James Hogan <james.ho...@imgtec.com> Cc: Yongbok Kim <yongbok....@imgtec.com> Cc: Aurelien Jarno <aurel...@aurel32.net> Cc: Nathan Froyd <froy...@codesourcery.com> --- target/mips/translate.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/mips/translate.c b/target/mips/translate.c index 3022f349cb2a..7071b124344a 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -4248,6 +4248,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, int bcond_compute = 0; TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); + int32_t seg_mask = 0xF0000000; /* 256 MB-aligned region */ if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS @@ -4303,9 +4304,15 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, break; case OPC_J: case OPC_JAL: + /* microMIPS J32, JAL32 & JALS32 offsets are in 128 MB region not 256 */ + if ((ctx->hflags & MIPS_HFLAG_M16) && + (ctx->insn_flags & ASE_MICROMIPS)) { + seg_mask = 0xF8000000; /* 128 MB-aligned region */ + } + /* fall through */ case OPC_JALX: /* Jump to immediate */ - btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; + btgt = ((ctx->pc + insn_bytes) & seg_mask) | (uint32_t)offset; break; case OPC_JR: case OPC_JALR: -- git-series 0.8.10