Skip to content

JIT arm32: assemble push and pop with single reg as gnu as#2276

Merged
bettio merged 1 commit intoatomvm:release-0.7from
pguyot:w16/arm32-fixup
Apr 20, 2026
Merged

JIT arm32: assemble push and pop with single reg as gnu as#2276
bettio merged 1 commit intoatomvm:release-0.7from
pguyot:w16/arm32-fixup

Conversation

@pguyot
Copy link
Copy Markdown
Collaborator

@pguyot pguyot commented Apr 19, 2026

This fixes test if gnu as is found and expectations are tested against its output

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@petermm
Copy link
Copy Markdown
Contributor

petermm commented Apr 19, 2026

PR Review: JIT arm32 — assemble push and pop with single reg as GNU as

Commit: af50c7b9a53840213d19b530da1701feb8dd6b6f

Summary

Single-register push/pop now emit STR Rd, [SP, #-4]! / LDR Rd, [SP], #4 instead of STMDB SP! / LDMIA SP!, matching GNU as canonical encoding per the ARM Architecture Reference Manual.

Verdict

Correct. Encodings, register field placement (RegNum bsl 12 for Rd bits [15:12]), and GNU as alignment are all verified.

Edge-Case Concern: singleton sp

push([sp]) would emit STR sp, [sp, #-4]! and pop([sp]) would emit LDR sp, [sp], #4. Both are CONSTRAINED UNPREDICTABLE per the ARM spec (writeback with Rn == Rt).

Suggested fix

Add a guard clause to reject or fall back for sp:

%% in push/1 — add before the existing push([Reg]) clause:
push([sp]) ->
    error({invalid_push_register, sp});
%% in pop/1 — add before the existing pop([Reg]) clause:
pop([sp]) ->
    error({invalid_pop_register, sp});

Missing tests for the sp guard

%% Add to push_test_/0:
?_assertError({invalid_push_register, sp}, jit_arm32_asm:push([sp]))

%% Add to pop_test_/0:
?_assertError({invalid_pop_register, sp}, jit_arm32_asm:pop([sp]))

Minor: comment drift

The section comments above push/1 and pop/1 still say:

%% PUSH {reglist} = STMDB SP!, {reglist}
%% POP  {reglist} = LDMIA SP!, {reglist}

These are no longer fully accurate. Consider updating to:

%% PUSH {reglist}
%%   single register: STR Rd, [SP, #-4]!  (matches GNU as)
%%   multi register:  STMDB SP!, {reglist}
%% POP {reglist}
%%   single register: LDR Rd, [SP], #4  (matches GNU as)
%%   multi register:  LDMIA SP!, {reglist}

Pre-existing (not introduced by this commit)

push([]) / pop([]) fall through to reglist_to_mask([]) which folds over an empty list and returns 0 — encoding an instruction with no registers set (invalid).

Suggested fix

Guard in reglist_to_mask/1 itself:

-reglist_to_mask(RegList) ->
+reglist_to_mask([]) ->
+    error(empty_register_list);
+reglist_to_mask(RegList) ->
     lists:foldl(
         fun(Reg, Acc) ->
             Acc bor (1 bsl reg_to_num(Reg))
         end,
         0,
         RegList
     ).

Missing tests

%% Add to push_test_/0:
?_assertError(empty_register_list, jit_arm32_asm:push([]))

%% Add to pop_test_/0:
?_assertError(empty_register_list, jit_arm32_asm:pop([]))

This fixes test if gnu as is found and expectations are tested against
its output

Signed-off-by: Paul Guyot <pguyot@kallisys.net>
@pguyot
Copy link
Copy Markdown
Collaborator Author

pguyot commented Apr 19, 2026

We don't push or pop sp but for completeness I implemented it.

@bettio bettio merged commit 90fc73d into atomvm:release-0.7 Apr 20, 2026
231 of 238 checks passed
@pguyot pguyot deleted the w16/arm32-fixup branch April 20, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants