Skip to content

Commit 24de8e8

Browse files
committed
Add Thumb-2 variant for ARMv6-M JIT backend
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 36f1c90 commit 24de8e8

26 files changed

Lines changed: 580 additions & 91 deletions

.github/workflows/build-and-test.yaml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,17 +266,26 @@ jobs:
266266
arch: "armhf"
267267
library-arch: arm-linux-gnueabihf
268268

269+
# JIT armv6m build (Thumb-1 only JIT code)
269270
- cc: "arm-linux-gnueabihf-gcc"
270271
cxx: "arm-linux-gnueabihf-g++"
271-
# -D_FILE_OFFSET_BITS=64 is required for making atomvm:posix_readdir/1 test work
272-
# otherwise readdir will fail due to 64 bits inode numbers with 32 bit ino_t
273272
cflags: "-mcpu=cortex-a7 -mfloat-abi=hard -O3 -mthumb -mthumb-interwork -D_FILE_OFFSET_BITS=64"
274273
cmake_opts_other: "-DAVM_DISABLE_JIT=OFF -DAVM_JIT_TARGET_ARCH=armv6m -DCMAKE_TOOLCHAIN_FILE=${RUNNER_TEMP}/armhf_toolchain.cmake"
275274
compiler_pkgs: "crossbuild-essential-armhf libc6-dbg:armhf zlib1g-dev:armhf libmbedtls-dev:armhf qemu-user qemu-user-binfmt binfmt-support"
276275
arch: "armhf"
277276
library-arch: arm-linux-gnueabihf
278277
jit_target_arch: "armv6m"
279278

279+
# JIT armv6m+thumb2 build (Thumb-2 JIT code)
280+
- cc: "arm-linux-gnueabihf-gcc"
281+
cxx: "arm-linux-gnueabihf-g++"
282+
cflags: "-mcpu=cortex-a7 -mfloat-abi=hard -O3 -mthumb -mthumb-interwork -D_FILE_OFFSET_BITS=64"
283+
cmake_opts_other: "-DAVM_DISABLE_JIT=OFF -DAVM_JIT_TARGET_ARCH=armv6m+thumb2 -DCMAKE_TOOLCHAIN_FILE=${RUNNER_TEMP}/armhf_toolchain.cmake"
284+
compiler_pkgs: "crossbuild-essential-armhf libc6-dbg:armhf zlib1g-dev:armhf libmbedtls-dev:armhf qemu-user qemu-user-binfmt binfmt-support"
285+
arch: "armhf"
286+
library-arch: arm-linux-gnueabihf
287+
jit_target_arch: "armv6m+thumb2"
288+
280289
# JIT ARM32 (ARM mode) build
281290
- cc: "arm-linux-gnueabihf-gcc"
282291
cxx: "arm-linux-gnueabihf-g++"
@@ -301,6 +310,20 @@ jobs:
301310
library-arch: arm-linux-gnueabihf
302311
jit_target_arch: "armv6m"
303312

313+
# JIT + DWARF build (armv6m+thumb2)
314+
- os: "ubuntu-24.04"
315+
cc: "arm-linux-gnueabihf-gcc"
316+
cxx: "arm-linux-gnueabihf-g++"
317+
cflags: "-mcpu=cortex-a7 -mfloat-abi=hard -O2 -mthumb -mthumb-interwork -D_FILE_OFFSET_BITS=64"
318+
otp: "28"
319+
elixir_version: "1.17"
320+
rebar3_version: "3.24.0"
321+
cmake_opts_other: "-DAVM_DISABLE_JIT=OFF -DAVM_DISABLE_JIT_DWARF=OFF -DAVM_JIT_TARGET_ARCH=armv6m+thumb2 -DCMAKE_TOOLCHAIN_FILE=${RUNNER_TEMP}/armhf_toolchain.cmake"
322+
compiler_pkgs: "crossbuild-essential-armhf libc6-dbg:armhf zlib1g-dev:armhf libmbedtls-dev:armhf qemu-user qemu-user-binfmt binfmt-support"
323+
arch: "armhf"
324+
library-arch: arm-linux-gnueabihf
325+
jit_target_arch: "armv6m+thumb2"
326+
304327
# JIT + DWARF build (arm32)
305328
- os: "ubuntu-24.04"
306329
cc: "arm-linux-gnueabihf-gcc"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Added DWARF debug information support for JIT-compiled code
1717
- Added I2C and SPI APIs to rp2 platform
1818
- Added `code:get_object_code/1`
19+
- Added Thumb-2 support to armv6m JIT backend, optimizing code for ARMv7-M and later cores
1920

2021
### Changed
2122
- ~10% binary size reduction by rewriting module loading logic

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if (NOT AVM_DISABLE_JIT AND NOT DEFINED AVM_JIT_TARGET_ARCH)
6666
endif()
6767
endif()
6868

69-
set(AVM_PRECOMPILED_TARGETS "x86_64;aarch64;arm32;armv6m;armv6m+float32;riscv32;riscv64" CACHE STRING "Targets to precompile code to if AVM_DISABLE_JIT is OFF or AVM_ENABLE_PRECOMPILED is ON")
69+
set(AVM_PRECOMPILED_TARGETS "x86_64;aarch64;arm32;armv6m;armv6m+float32;armv6m+thumb2;armv6m+thumb2+float32;riscv32;riscv64" CACHE STRING "Targets to precompile code to if AVM_DISABLE_JIT is OFF or AVM_ENABLE_PRECOMPILED is ON")
7070

7171
if((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") OR
7272
(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") OR

CMakeModules/BuildErlang.cmake

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ macro(pack_precompiled_archive avm_name)
103103
${CMAKE_BINARY_DIR}/libs/jit/src/beams/jit_${jit_target_arch}.beam
104104
${CMAKE_BINARY_DIR}/libs/jit/src/beams/jit_${jit_target_arch}_asm.beam
105105
)
106+
if("${jit_target_arch_variant}" MATCHES "thumb2")
107+
list(APPEND jit_compiler_modules
108+
${CMAKE_BINARY_DIR}/libs/jit/src/beams/jit_armv7m_asm.beam
109+
)
110+
endif()
106111

107112
if (NOT AVM_DISABLE_JIT_DWARF)
108113
set(jit_precompile_dwarf_flag "dwarf")

doc/src/atomvm-internals.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Following BEAM, there are two flavors of the emulator: jit and emu, but eventual
137137
- Native: the VM only runs native code and all code must be precompiled on the desktop using the JIT compiler (which effectively is a AOT or Ahead-of-Time compiler). In this mode, it is not necessary to bundle the jit compiler on the embedded target.
138138
- Hybrid: the VM can run native code as well as emulated BEAM code and some code is precompiled on the desktop.
139139

140-
JIT is available on some platforms (currently x86_64, aarch64, arm32, armv6m, riscv32 and riscv64) and compiles Erlang bytecode at runtime. Erlang bytecode is never interpreted. EMU is available on all platforms and Erlang bytecode is interpreted.
140+
JIT is available on some platforms (currently x86_64, aarch64, arm32, armv6m, armv6m+thumb2, riscv32 and riscv64) and compiles Erlang bytecode at runtime. Erlang bytecode is never interpreted. EMU is available on all platforms and Erlang bytecode is interpreted.
141141

142142
Modules can include precompiled code in a dedicated beam chunk with name 'avmN'. The chunk can contain native code for several architectures, however it may only contain native code for a given version of the native interface. Current version is 1. This native code is executed by the jit-flavor of the emulator as well as the emu flavor if execution of precompiled is enabled.
143143

@@ -158,7 +158,7 @@ A backend implementation is required for each architecture. The backend is calle
158158
- `jit_x86_64` for System V X86 64 ABI
159159
- `jit_aarch64` for AArch64 ABI
160160
- `jit_arm32` for ARM32 (AArch32 ARM mode) ABI
161-
- `jit_armv6m` for ARMv6-M (AArch32 Thumb mode) ABI
161+
- `jit_armv6m` for ARMv6-M (AArch32 Thumb mode) ABI, with an ARMv7-M or later variant using Thumb-2 32-bit encodings for Cortex-M3+ targets (Raspberry Pi Pico 2, STM32 with Cortex-M3/M4/M7/M33)
162162
- `jit_riscv32` for rv32imc ilp32 ABI
163163
- `jit_riscv64` for rv64gc lp64 ABI.
164164

@@ -169,7 +169,7 @@ A stream implementation is responsible for streaming the machine code, especiall
169169

170170
### Embedded JIT and Native
171171

172-
On embedded devices, Native mode means the code is precompiled on the desktop and executed natively on the device. This currently works on all ARMv6M devices (Pico and STM32).
172+
On embedded devices, Native mode means the code is precompiled on the desktop and executed natively on the device. This currently works on all ARMv6-M devices (Pico and STM32 with Cortex-M0/M0+) as well as ARMv7-M devices using the Thumb-2 variant (Pico 2 and STM32 with Cortex-M3/M4/M7/M33).
173173

174174
The default partition scheme on all platforms is optimized for the Emulated VM which is larger than the JIT or Native VM, and for the Emulated atomvmlib (with no native code for estdlib and no jit library) which is smaller than the JIT atomvmlib (that includes native code for estdlib and jit library).
175175

doc/src/jit.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ The JIT compiler supports the following target architectures:
2121
* `x86_64` — 64-bit x86 (Linux, macOS, FreeBSD)
2222
* `aarch64` — 64-bit ARM (Linux, macOS)
2323
* `arm32` — 32-bit ARM (Linux)
24-
* `armv6m` — ARM Cortex-M0+ (Raspberry Pi Pico, STM32)
24+
* `armv6m` — ARM Cortex-M0+ (Raspberry Pi Pico, STM32 with Cortex-M0/M0+)
25+
* `armv6m+thumb2` — ARM Cortex-M3+ with Thumb-2 support, ARMv7-M or later (Raspberry Pi Pico 2, STM32 with Cortex-M3/M4/M7/M33)
2526
* `riscv32` — 32-bit RISC-V
2627
* `riscv64` — 64-bit RISC-V
2728

@@ -176,5 +177,5 @@ $ riscv64-elf-objdump -d module.elf
176177
|--------|---------|-------------|
177178
| `AVM_DISABLE_JIT` | `ON` | Disable JIT compilation |
178179
| `AVM_DISABLE_JIT_DWARF` | `ON` | Disable DWARF debug information in JIT |
179-
| `AVM_JIT_TARGET_ARCH` | auto-detected | Target architecture (`x86_64`, `aarch64`, `arm32`, `armv6m`, `riscv32`, `riscv64`) |
180+
| `AVM_JIT_TARGET_ARCH` | auto-detected | Target architecture (`x86_64`, `aarch64`, `arm32`, `armv6m`, `armv6m+thumb2`, `riscv32`, `riscv64`) |
180181
| `AVM_DISABLE_SMP` | `OFF` | Disable SMP support |

libs/jit/include/jit.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333

3434
-define(JIT_VARIANT_PIC, 1).
3535
-define(JIT_VARIANT_FLOAT32, 2).
36+
-define(JIT_VARIANT_THUMB2, 4).
3637

3738
-define(MAX_REG, 16).

libs/jit/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ set(ERLANG_MODULES
3737
jit_arm32_asm
3838
jit_armv6m
3939
jit_armv6m_asm
40+
jit_armv7m_asm
4041
jit_riscv32
4142
jit_riscv32_asm
4243
jit_riscv64

libs/jit/src/jit_armv6m.erl

Lines changed: 109 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -146,19 +146,24 @@
146146
).
147147

148148
-type stream() :: any().
149+
-type branch_type() ::
150+
{adr, armv6m_register()} | b_w | {far_branch, non_neg_integer(), armv6m_register()}.
149151

150152
-record(state, {
151153
stream_module :: module(),
152154
stream :: stream(),
153155
offset :: non_neg_integer(),
154-
branches :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}],
156+
branches :: [{non_neg_integer(), non_neg_integer(), branch_type()}],
155157
jump_table_start :: non_neg_integer(),
156158
available_regs :: non_neg_integer(),
157159
used_regs :: non_neg_integer(),
158160
labels :: [{integer() | reference(), integer()}],
159161
variant :: non_neg_integer(),
160162
literal_pool :: [{non_neg_integer(), armv6m_register(), non_neg_integer()}],
161-
regs :: jit_regs:regs()
163+
%% Register value tracking for optimization
164+
regs :: jit_regs:regs(),
165+
%% Whether Thumb-2 instructions are available (ARMv7-M / ARMv8-M)
166+
thumb2 :: boolean()
162167
}).
163168

164169
-type state() :: #state{}.
@@ -203,8 +208,8 @@
203208
-define(MODULE_INDEX(ModuleReg), {ModuleReg, 0}).
204209

205210
-define(JUMP_TABLE_ENTRY_SIZE, 12).
211+
-define(JUMP_TABLE_ENTRY_SIZE_THUMB2, 6).
206212

207-
% aarch64 ABI specific
208213
%% ARMv6-M register mappings
209214

210215
%% IP can be used as an additional scratch register
@@ -300,7 +305,8 @@ new(Variant, StreamModule, Stream) ->
300305
labels = [],
301306
variant = Variant,
302307
literal_pool = [],
303-
regs = jit_regs:new()
308+
regs = jit_regs:new(),
309+
thumb2 = (Variant band ?JIT_VARIANT_THUMB2) =/= 0
304310
}.
305311

306312
%%-----------------------------------------------------------------------------
@@ -415,7 +421,7 @@ assert_all_native_free(State) ->
415421
%% 0 (special entry for lines and labels information) to LabelsCount included
416422
%% (special entry for OP_INT_CALL_END).
417423
%%
418-
%% On this platform, each jump table entry is 12 bytes.
424+
%% On ARMv6-M (Thumb-1), each jump table entry is 12 bytes:
419425
%% ```
420426
%% ldr r3, pc+4
421427
%% push {r1, r4, r5, r6, r7, lr}
@@ -424,6 +430,12 @@ assert_all_native_free(State) ->
424430
%% offset_to_label0
425431
%% ```
426432
%%
433+
%% On ARMv7-M/ARMv8-M (Thumb-2 variant), each jump table entry is 6 bytes:
434+
%% ```
435+
%% push {r1, r4, r5, r6, r7, lr}
436+
%% b.w offset_to_label0
437+
%% ```
438+
%%
427439
%% @end
428440
%% @param State current backend state
429441
%% @param LabelsCount number of labels in the module.
@@ -436,12 +448,26 @@ jump_table(#state{stream_module = StreamModule, stream = Stream0} = State, Label
436448

437449
jump_table0(State, N, LabelsCount) when N > LabelsCount ->
438450
State;
451+
jump_table0(
452+
#state{stream_module = StreamModule, stream = Stream0, thumb2 = true} = State,
453+
N,
454+
LabelsCount
455+
) ->
456+
% Thumb-2 jump table entry: push + b.w (6 bytes)
457+
I1 = jit_armv6m_asm:push([r1, r4, r5, r6, r7, lr]),
458+
% Placeholder b.w - will be patched by update_branches
459+
I2 = <<16#FFFF:16, 16#FFFF:16>>,
460+
461+
JumpEntry = <<I1/binary, I2/binary>>,
462+
Stream1 = StreamModule:append(Stream0, JumpEntry),
463+
464+
jump_table0(State#state{stream = Stream1}, N + 1, LabelsCount);
439465
jump_table0(
440466
#state{stream_module = StreamModule, stream = Stream0} = State,
441467
N,
442468
LabelsCount
443469
) ->
444-
% Create jump table entry with calculated offsets - all at emit time
470+
% ARMv6-M jump table entry: ldr + push + add pc + nop + literal (12 bytes)
445471
I1 = jit_armv6m_asm:ldr(r3, {pc, 4}),
446472
I2 = jit_armv6m_asm:push([r1, r4, r5, r6, r7, lr]),
447473
I3 = jit_armv6m_asm:add(pc, r3),
@@ -469,6 +495,9 @@ patch_branch(StreamModule, Stream, Offset, Type, LabelOffset) ->
469495
case Type of
470496
{adr, Reg} when Rel rem 4 =:= 0 -> jit_armv6m_asm:adr(Reg, Rel);
471497
{adr, Reg} when Rel rem 4 =:= 2 -> jit_armv6m_asm:adr(Reg, Rel + 2);
498+
b_w ->
499+
% Thumb-2 b.w offset is relative to PC (instruction address + 4)
500+
jit_armv7m_asm:b_w(Rel - 4);
472501
{far_branch, Size, TempReg} ->
473502
% Check if branch can now be optimized to near branch
474503
if
@@ -917,6 +946,11 @@ jump_to_continuation(
917946
State2 = State1#state{stream = Stream2, available_regs = ?AVAILABLE_REGS_MASK, used_regs = 0},
918947
flush_literal_pool(State2).
919948

949+
branch_to_offset_code(#state{thumb2 = true}, Offset, TargetOffset) ->
950+
% Thumb-2: b.w has +-16MB range, always sufficient
951+
% b.w offset is relative to PC (instruction address + 4)
952+
Rel = TargetOffset - (Offset + 4),
953+
jit_armv7m_asm:b_w(Rel);
920954
branch_to_offset_code(_State, Offset, TargetOffset) when
921955
TargetOffset - Offset =< 2050, TargetOffset - Offset >= -2044
922956
->
@@ -952,6 +986,14 @@ branch_to_offset_code(
952986
branch_to_label_code(State, Offset, Label, {Label, LabelOffset}) ->
953987
CodeBlock = branch_to_offset_code(State, Offset, LabelOffset),
954988
{State, CodeBlock};
989+
branch_to_label_code(
990+
#state{branches = Branches, thumb2 = true} = State0, Offset, Label, false
991+
) ->
992+
% Thumb-2: b.w is always 4 bytes, placeholder will be patched later
993+
CodeBlock = <<16#FFFF:16, 16#FFFF:16>>,
994+
Reloc = {Label, Offset, b_w},
995+
State1 = State0#state{branches = [Reloc | Branches]},
996+
{State1, CodeBlock};
955997
branch_to_label_code(
956998
#state{available_regs = Available, branches = Branches} = State0, Offset, Label, false
957999
) when Available =/= 0 ->
@@ -3170,7 +3212,12 @@ set_continuation_to_label(
31703212
Temp1 = first_avail(Avail),
31713213
Temp2 = first_avail(Avail band (bnot reg_bit(Temp1))),
31723214
% Calculate jump table entry offset
3173-
JumpTableEntryOffset = (Label * ?JUMP_TABLE_ENTRY_SIZE) + JumpTableOffset,
3215+
EntrySize =
3216+
case State#state.thumb2 of
3217+
true -> ?JUMP_TABLE_ENTRY_SIZE_THUMB2;
3218+
false -> ?JUMP_TABLE_ENTRY_SIZE
3219+
end,
3220+
JumpTableEntryOffset = (Label * EntrySize) + JumpTableOffset,
31743221

31753222
AdrOffset = StreamModule:offset(Stream0),
31763223
% ADR Temp, +.4 means we're storing PC value in Temp1.
@@ -3494,6 +3541,28 @@ mov_immediate(#state{stream_module = StreamModule, stream = Stream0} = State, Re
34943541
I = jit_armv6m_asm:movs(Reg, Val),
34953542
Stream1 = StreamModule:append(Stream0, I),
34963543
State#state{stream = Stream1};
3544+
%% Thumb-2: use movw for 16-bit unsigned values (256-65535)
3545+
mov_immediate(
3546+
#state{stream_module = StreamModule, stream = Stream0, thumb2 = true} = State, Reg, Val
3547+
) when
3548+
Val > 255 andalso Val =< 65535
3549+
->
3550+
I = jit_armv7m_asm:movw(Reg, Val),
3551+
Stream1 = StreamModule:append(Stream0, I),
3552+
State#state{stream = Stream1};
3553+
%% Thumb-2: use movw+movt for full 32-bit values
3554+
mov_immediate(
3555+
#state{stream_module = StreamModule, stream = Stream0, thumb2 = true} = State, Reg, Val
3556+
) when
3557+
?IS_SIGNED_OR_UNSIGNED_INT32_T(Val)
3558+
->
3559+
UVal = Val band 16#FFFFFFFF,
3560+
Lo16 = UVal band 16#FFFF,
3561+
Hi16 = (UVal bsr 16) band 16#FFFF,
3562+
I1 = jit_armv7m_asm:movw(Reg, Lo16),
3563+
I2 = jit_armv7m_asm:movt(Reg, Hi16),
3564+
Stream1 = StreamModule:append(Stream0, <<I1/binary, I2/binary>>),
3565+
State#state{stream = Stream1};
34973566
mov_immediate(#state{stream_module = StreamModule, stream = Stream0} = State, Reg, Val) when
34983567
Val >= -255 andalso Val < 0
34993568
->
@@ -4192,30 +4261,43 @@ add_label(
41924261
stream = Stream0,
41934262
jump_table_start = JumpTableStart,
41944263
branches = Branches,
4195-
labels = Labels
4264+
labels = Labels,
4265+
thumb2 = Thumb2
41964266
} = State,
41974267
Label,
41984268
LabelOffset
41994269
) when is_integer(Label) ->
4200-
% Patch the jump table entry immediately
4201-
% Each jump table entry is 12 bytes:
4202-
% - ldr r3, [pc, 4] (2 bytes) at offset 0
4203-
% - push {...} (2 bytes) at offset 2
4204-
% - add pc, r3 (2 bytes) at offset 4
4205-
% - nop (2 bytes) at offset 6
4206-
% - data (4 bytes) at offset 8
4207-
JumpTableEntryStart = JumpTableStart + Label * 12,
4208-
DataOffset = JumpTableEntryStart + 8,
4209-
AddInstrOffset = JumpTableEntryStart + 4,
4210-
4211-
% Calculate offset from 'add pc, r3' instruction to target label
4212-
% When 'add pc, r3' executes, PC reads as AddInstrOffset + 4
4213-
% Result goes through BXWritePC, so bit 0 must be 1 for Thumb mode
4214-
AddPC = AddInstrOffset + 4,
4215-
RelativeOffset = LabelOffset - AddPC + 1,
4216-
DataBytes = <<RelativeOffset:32/little>>,
4217-
4218-
Stream1 = StreamModule:replace(Stream0, DataOffset, DataBytes),
4270+
Stream1 =
4271+
case Thumb2 of
4272+
true ->
4273+
% Thumb-2 jump table entry is 6 bytes:
4274+
% - push {...} (2 bytes) at offset 0
4275+
% - b.w <offset> (4 bytes) at offset 2
4276+
JumpTableEntryStart = JumpTableStart + Label * ?JUMP_TABLE_ENTRY_SIZE_THUMB2,
4277+
BranchInstrOffset = JumpTableEntryStart + 2,
4278+
% b.w offset is relative to instruction address + 4
4279+
BranchPC = BranchInstrOffset + 4,
4280+
RelativeOffset = LabelOffset - BranchPC,
4281+
BranchBytes = jit_armv7m_asm:b_w(RelativeOffset),
4282+
StreamModule:replace(Stream0, BranchInstrOffset, BranchBytes);
4283+
false ->
4284+
% ARMv6-M jump table entry is 12 bytes:
4285+
% - ldr r3, [pc, 4] (2 bytes) at offset 0
4286+
% - push {...} (2 bytes) at offset 2
4287+
% - add pc, r3 (2 bytes) at offset 4
4288+
% - nop (2 bytes) at offset 6
4289+
% - data (4 bytes) at offset 8
4290+
JumpTableEntryStart = JumpTableStart + Label * ?JUMP_TABLE_ENTRY_SIZE,
4291+
DataOffset = JumpTableEntryStart + 8,
4292+
AddInstrOffset = JumpTableEntryStart + 4,
4293+
% Calculate offset from 'add pc, r3' instruction to target label
4294+
% When 'add pc, r3' executes, PC reads as AddInstrOffset + 4
4295+
% Result goes through BXWritePC, so bit 0 must be 1 for Thumb mode
4296+
AddPC = AddInstrOffset + 4,
4297+
RelativeOffset = LabelOffset - AddPC + 1,
4298+
DataBytes = <<RelativeOffset:32/little>>,
4299+
StreamModule:replace(Stream0, DataOffset, DataBytes)
4300+
end,
42194301

42204302
% Eagerly patch any branches targeting this label
42214303
{Stream2, RemainingBranches} = patch_branches_for_label(

libs/jit/src/jit_armv6m_asm.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
]).
6060

6161
-export_type([
62+
arm_gpr_register/0,
6263
cc/0
6364
]).
6465

0 commit comments

Comments
 (0)