|
@@ -222,3 +222,148 @@ static void build_prologue(struct jit_ctx *ctx)
|
|
|
static void build_epilogue(struct jit_ctx *ctx)
|
|
|
{
|
|
|
u16 reg_set = saved_regs(ctx);
|
|
|
+
|
|
|
+ if (ctx->seen & SEEN_MEM)
|
|
|
+ emit(ARM_ADD_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx);
|
|
|
+
|
|
|
+ reg_set &= ~(1 << ARM_LR);
|
|
|
+
|
|
|
+#ifdef CONFIG_FRAME_POINTER
|
|
|
+ /* the first instruction of the prologue was: mov ip, sp */
|
|
|
+ reg_set &= ~(1 << ARM_IP);
|
|
|
+ reg_set |= (1 << ARM_SP);
|
|
|
+ emit(ARM_LDM(ARM_SP, reg_set), ctx);
|
|
|
+#else
|
|
|
+ if (reg_set) {
|
|
|
+ if (ctx->seen & SEEN_CALL)
|
|
|
+ reg_set |= 1 << ARM_PC;
|
|
|
+ emit(ARM_POP(reg_set), ctx);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(ctx->seen & SEEN_CALL))
|
|
|
+ emit(ARM_BX(ARM_LR), ctx);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static int16_t imm8m(u32 x)
|
|
|
+{
|
|
|
+ u32 rot;
|
|
|
+
|
|
|
+ for (rot = 0; rot < 16; rot++)
|
|
|
+ if ((x & ~ror32(0xff, 2 * rot)) == 0)
|
|
|
+ return rol32(x, 2 * rot) | (rot << 8);
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+#if __LINUX_ARM_ARCH__ < 7
|
|
|
+
|
|
|
+static u16 imm_offset(u32 k, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ unsigned i = 0, offset;
|
|
|
+ u16 imm;
|
|
|
+
|
|
|
+ /* on the "fake" run we just count them (duplicates included) */
|
|
|
+ if (ctx->target == NULL) {
|
|
|
+ ctx->imm_count++;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ while ((i < ctx->imm_count) && ctx->imms[i]) {
|
|
|
+ if (ctx->imms[i] == k)
|
|
|
+ break;
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ctx->imms[i] == 0)
|
|
|
+ ctx->imms[i] = k;
|
|
|
+
|
|
|
+ /* constants go just after the epilogue */
|
|
|
+ offset = ctx->offsets[ctx->skf->len];
|
|
|
+ offset += ctx->prologue_bytes;
|
|
|
+ offset += ctx->epilogue_bytes;
|
|
|
+ offset += i * 4;
|
|
|
+
|
|
|
+ ctx->target[offset / 4] = k;
|
|
|
+
|
|
|
+ /* PC in ARM mode == address of the instruction + 8 */
|
|
|
+ imm = offset - (8 + ctx->idx * 4);
|
|
|
+
|
|
|
+ return imm;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* __LINUX_ARM_ARCH__ */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Move an immediate that's not an imm8m to a core register.
|
|
|
+ */
|
|
|
+static inline void emit_mov_i_no8m(int rd, u32 val, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+#if __LINUX_ARM_ARCH__ < 7
|
|
|
+ emit(ARM_LDR_I(rd, ARM_PC, imm_offset(val, ctx)), ctx);
|
|
|
+#else
|
|
|
+ emit(ARM_MOVW(rd, val & 0xffff), ctx);
|
|
|
+ if (val > 0xffff)
|
|
|
+ emit(ARM_MOVT(rd, val >> 16), ctx);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static inline void emit_mov_i(int rd, u32 val, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ int imm12 = imm8m(val);
|
|
|
+
|
|
|
+ if (imm12 >= 0)
|
|
|
+ emit(ARM_MOV_I(rd, imm12), ctx);
|
|
|
+ else
|
|
|
+ emit_mov_i_no8m(rd, val, ctx);
|
|
|
+}
|
|
|
+
|
|
|
+#if __LINUX_ARM_ARCH__ < 6
|
|
|
+
|
|
|
+static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R3, r_addr, 1), ctx);
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx);
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 3), ctx);
|
|
|
+ _emit(cond, ARM_LSL_I(ARM_R3, ARM_R3, 16), ctx);
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R0, r_addr, 2), ctx);
|
|
|
+ _emit(cond, ARM_ORR_S(ARM_R3, ARM_R3, ARM_R1, SRTYPE_LSL, 24), ctx);
|
|
|
+ _emit(cond, ARM_ORR_R(ARM_R3, ARM_R3, ARM_R2), ctx);
|
|
|
+ _emit(cond, ARM_ORR_S(r_res, ARM_R3, ARM_R0, SRTYPE_LSL, 8), ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx);
|
|
|
+ _emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 1), ctx);
|
|
|
+ _emit(cond, ARM_ORR_S(r_res, ARM_R2, ARM_R1, SRTYPE_LSL, 8), ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void emit_swap16(u8 r_dst, u8 r_src, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ emit(ARM_LSL_R(ARM_R1, r_src, 8), ctx);
|
|
|
+ emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSL, 8), ctx);
|
|
|
+ emit(ARM_LSL_I(r_dst, r_dst, 8), ctx);
|
|
|
+ emit(ARM_LSL_R(r_dst, r_dst, 8), ctx);
|
|
|
+}
|
|
|
+
|
|
|
+#else /* ARMv6+ */
|
|
|
+
|
|
|
+static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ _emit(cond, ARM_LDR_I(r_res, r_addr, 0), ctx);
|
|
|
+#ifdef __LITTLE_ENDIAN
|
|
|
+ _emit(cond, ARM_REV(r_res, r_res), ctx);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
|
|
|
+{
|
|
|
+ _emit(cond, ARM_LDRH_I(r_res, r_addr, 0), ctx);
|
|
|
+#ifdef __LITTLE_ENDIAN
|
|
|
+ _emit(cond, ARM_REV16(r_res, r_res), ctx);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static inline void emit_swap16(u8 r_dst __maybe_unused,
|
|
|
+ u8 r_src __maybe_unused,
|