JIT bs_create_bin: badarg on <<V:0>>, codegen crashes on unit and little utf

Open
#2,442 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
1-2 days
Newbie friendliness
54/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
erlang
Domain
backend

Research direction

Start with first_pass_bs_create_bin_compute_size and first_pass_bs_create_bin_insert_value in libs/jit/src/jit.erl, then trace the backend calls named in the report. Reproduce the three cases with the JIT and compare against the interpreter or BEAM. Done means all reported cases compile and behave like the interpreter across the affected backends.

Written by the indexing model from the issue text.

Description

Three independent problems in the JIT's bs_create_bin, found while reviewing #2357. All are pre-existing: they reproduce with the JIT compiler of release-0.7 at 18b35870 (the base of #2357) and at the #2357 head. The interpreter and BEAM agree on every example; only the JIT differs. Values go through an id/1 so nothing is constant-folded.

1. A zero-size integer segment raises badarg
V = id(5),
<<V:0>>         %% <<>> on BEAM and the interpreter, badarg on the JIT
<<V:0, 255:8>>  %% <<255>> vs. badarg
<<"", V:8>>     %% <<5>> vs. badarg: the compiler emits "" as a 0:0 integer segment

Cause: first_pass_bs_create_bin_compute_size(integer, ...) in libs/jit/src/jit.erl only accepts a literal size when SizeValue > 0. A zero-width segment is valid in BEAM, and bitstring_insert_integer already handles n = 0. The string clause has the same test.

2. A literal size with a unit other than 1 crashes code generation
f(V) -> <<V:2/unit:8>>.           %% function_clause in jit_x86_64:shift_left/3 (jit_aarch64:shift_left/3 on aarch64)
g(V) -> <<V:18/integer-unit:3>>.  %% function_clause in jit_x86_64_asm:imulq/2 (jit_aarch64_asm:lsl/3 on aarch64)

The compiler keeps the unit for a literal size. In first_pass_bs_create_bin_insert_value(integer, ...), term_to_int(Size, ...) returns a plain integer for a literal, which is passed to MMod:mul/3, and every backend's mul/3 expects a register. The size pass already folds SizeValue * SegmentUnit for literals; the insert pass should too. The module cannot be JIT-compiled.

3. utf16-little or utf32-little after a runtime-sized segment runs out of registers (x86_64, armv6m)
f(A, B) -> <<A/utf8, B/utf16-little>>.   %% x86_64: function_clause in jit_regs:first_set/3, armv6m: no_available_registers
g(V, N, C) -> <<V:N, C/utf32-little>>.   %% same

It compiles with a literal-size first segment (<<1:1, B/utf16-little>>) or a big-endian second segment (<<A/utf8, B/utf16>>), and aarch64, arm32, riscv32, riscv64, xtensa and wasm32 compile all of these. The failure is in term_to_int(Src, ...) of the utf16/utf32 insert clause, which needs a scratch register while the created binary, the offset, the runtime size accumulator, the flags and the source are all live. The module cannot be JIT-compiled.

Dominant language
Erlang
Stars
2.3k
Forks
171
Avg merge
4d 11h
Merged PRs (30d)
20

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from atomvm/AtomVM

All issues in atomvm/AtomVM

Similar issues

More Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.