@@ -499,6 +499,7 @@ void asm_thumb_store_reg_reg_offset(asm_thumb_t *as, uint reg_src, uint reg_base
499
499
#define OP_BW_HI (byte_offset ) (0xf000 | (((byte_offset) >> 12) & 0x07ff))
500
500
#define OP_BW_LO (byte_offset ) (0xb800 | (((byte_offset) >> 1) & 0x07ff))
501
501
502
+ // In Thumb1 mode, this may clobber r1.
502
503
void asm_thumb_b_label (asm_thumb_t * as , uint label ) {
503
504
mp_uint_t dest = get_label_dest (as , label );
504
505
mp_int_t rel = dest - as -> base .code_offset ;
@@ -518,19 +519,40 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) {
518
519
if (asm_thumb_allow_armv7m (as )) {
519
520
asm_thumb_op32 (as , OP_BW_HI (rel ), OP_BW_LO (rel ));
520
521
} else {
522
+ // this code path has to be the same instruction size irrespective of the value of rel
523
+ bool need_align = as -> base .code_offset & 2u ;
521
524
if (SIGNED_FIT12 (rel )) {
522
- // this code path has to be the same number of instructions irrespective of rel
523
525
asm_thumb_op16 (as , OP_B_N (rel ));
524
- } else {
525
526
asm_thumb_op16 (as , ASM_THUMB_OP_NOP );
526
- if (dest != (mp_uint_t )- 1 ) {
527
- // we have an actual branch > 12 bits; this is not handled yet
528
- mp_raise_NotImplementedError (MP_ERROR_TEXT ("native method too big" ));
527
+ asm_thumb_op16 (as , ASM_THUMB_OP_NOP );
528
+ asm_thumb_op16 (as , ASM_THUMB_OP_NOP );
529
+ if (need_align ) {
530
+ asm_thumb_op16 (as , ASM_THUMB_OP_NOP );
529
531
}
532
+ } else {
533
+ // do a large jump using:
534
+ // (nop)
535
+ // ldr r1, [pc, _data]
536
+ // add pc, r1
537
+ // _data: .word rel
538
+ //
539
+ // note: can't use r0 as a temporary because native code can have the return value
540
+ // in that register and use a large jump to get to the exit point of the function
541
+
542
+ rel -= 2 ; // account for the "ldr r1, [pc, _data]"
543
+ if (need_align ) {
544
+ asm_thumb_op16 (as , ASM_THUMB_OP_NOP );
545
+ rel -= 2 ; // account for this nop
546
+ }
547
+ asm_thumb_ldr_rlo_pcrel_i8 (as , ASM_THUMB_REG_R1 , 0 );
548
+ asm_thumb_add_reg_reg (as , ASM_THUMB_REG_R15 , ASM_THUMB_REG_R1 );
549
+ asm_thumb_op16 (as , rel & 0xffff );
550
+ asm_thumb_op16 (as , rel >> 16 );
530
551
}
531
552
}
532
553
}
533
554
555
+ // In Thumb1 mode, this may clobber r1.
534
556
void asm_thumb_bcc_label (asm_thumb_t * as , int cond , uint label ) {
535
557
mp_uint_t dest = get_label_dest (as , label );
536
558
mp_int_t rel = dest - as -> base .code_offset ;
@@ -551,8 +573,15 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) {
551
573
asm_thumb_op32 (as , OP_BCC_W_HI (cond , rel ), OP_BCC_W_LO (rel ));
552
574
} else {
553
575
// reverse the sense of the branch to jump over a longer branch
554
- asm_thumb_op16 (as , OP_BCC_N (cond ^ 1 , 0 ));
576
+ size_t code_offset_start = as -> base .code_offset ;
577
+ byte * c = asm_thumb_get_cur_to_write_bytes (as , 2 );
555
578
asm_thumb_b_label (as , label );
579
+ size_t bytes_to_skip = as -> base .code_offset - code_offset_start ;
580
+ uint16_t op = OP_BCC_N (cond ^ 1 , bytes_to_skip - 4 );
581
+ if (c != NULL ) {
582
+ c [0 ] = op ;
583
+ c [1 ] = op >> 8 ;
584
+ }
556
585
}
557
586
}
558
587
0 commit comments