summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Gatti <a.gatti@frob.it>2025-06-08 21:51:30 +0200
committerDamien George <damien@micropython.org>2025-06-10 12:28:29 +1000
commit80b823bca15d846de940d08a6e8144290514f8a2 (patch)
treec0dd708e9011ba005f5b8a649c180defebe2986b
parent0da22b2c30aac8a843c504f907b0675190ee0c89 (diff)
py/asmxtensa: Extend BCCZ range to 18 bits.
This commit lets the native emitter backend extends the range of the BCCZ family of opcodes (BEQZ, BNEZ, BLTZ, BGEZ) from 12 bits to 18 bits. The test suite contains some test files that, when compiled into native code, would require BCCZ jumps outside the (signed) 12 bits range. In this case either the MicroPython interpreter or mpy-cross would raise an exception, not running the test when using the "--via-mpy --emit native" command line options with the test runner. This comes with a 3 bytes penalty on each forward jump, bringing the footprint of those jumps to 6 bytes each, as a longer opcode sequence has to be emitted to let jumps access a larger range. However, this is slightly offset by the fact that backward jumps can be emitted with a single opcode if the range is small enough (3 bytes for a 12-bits offset). Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
-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) {