summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--py/asmxtensa.c32
1 files changed, 28 insertions, 4 deletions
diff --git a/py/asmxtensa.c b/py/asmxtensa.c
index 2b36a56ea..fd71dedb1 100644
--- a/py/asmxtensa.c
+++ b/py/asmxtensa.c
@@ -41,6 +41,7 @@
#endif
#define WORD_SIZE (4)
+#define SIGNED_FIT6(x) ((((x) & 0xffffffe0) == 0) || (((x) & 0xffffffe0) == 0xffffffe0))
#define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80))
#define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800))
#define SIGNED_FIT18(x) ((((x) & 0xfffe0000) == 0) || (((x) & 0xfffe0000) == 0xfffe0000))
@@ -158,13 +159,36 @@ void asm_xtensa_j_label(asm_xtensa_t *as, uint label) {
asm_xtensa_op_j(as, rel);
}
+static bool calculate_branch_displacement(asm_xtensa_t *as, uint label, ptrdiff_t *displacement) {
+ assert(displacement != NULL && "Displacement pointer is NULL");
+
+ uint32_t label_offset = get_label_dest(as, label);
+ *displacement = (ptrdiff_t)(label_offset - as->base.code_offset - 4);
+ return (label_offset != (uint32_t)-1) && (*displacement < 0);
+}
+
void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) {
- uint32_t dest = get_label_dest(as, label);
- int32_t rel = dest - as->base.code_offset - 4;
- if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) {
+ ptrdiff_t rel = 0;
+ bool can_emit_short_jump = calculate_branch_displacement(as, label, &rel);
+
+ if (can_emit_short_jump && SIGNED_FIT12(rel)) {
+ // Backwards BCCZ opcodes with an offset that fits in 12 bits can
+ // be emitted without any change.
+ asm_xtensa_op_bccz(as, cond, reg, rel);
+ return;
+ }
+
+ // Range is effectively extended to 18 bits, as a more complex jump code
+ // sequence is emitted.
+ if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT18(rel - 6)) {
mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bccz);
}
- asm_xtensa_op_bccz(as, cond, reg, rel);
+
+ // ~BCCZ skip ; +0 <- Condition is flipped here (EQ -> NE, etc.)
+ // J addr ; +3
+ // skip: ; +6
+ asm_xtensa_op_bccz(as, cond ^ 1, reg, 6 - 4);
+ asm_xtensa_op_j(as, rel - 3);
}
void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) {