From 38a5757c104391ac84d401db9282df21f72a0548 Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Thu, 2 Jul 2026 10:59:00 +0200 Subject: [PATCH 1/4] Properly test arm32 modified immediate constants Co-authored-by: Frank Hunleth --- erts/emulator/beam/jit/arm/32/beam_asm.hpp | 21 ++++++++++++------- .../emulator/beam/jit/arm/32/instr_select.cpp | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/erts/emulator/beam/jit/arm/32/beam_asm.hpp b/erts/emulator/beam/jit/arm/32/beam_asm.hpp index 75090914ae32..2baf58bc3845 100644 --- a/erts/emulator/beam/jit/arm/32/beam_asm.hpp +++ b/erts/emulator/beam/jit/arm/32/beam_asm.hpp @@ -444,7 +444,7 @@ struct BeamAssembler : public BeamAssemblerCommon { } void emit_branch_if_eq(a32::Gp reg, Uint value, Label lbl) { - if (value <= 255) { + if (is_aarch32_imm(value)) { a.cmp(reg, imm(value)); } else { mov_imm(TMP, value); @@ -454,7 +454,7 @@ struct BeamAssembler : public BeamAssemblerCommon { } void emit_branch_if_ne(a32::Gp reg, Uint value, Label lbl) { - if (value <= 255) { + if (is_aarch32_imm(value)) { a.cmp(reg, imm(value)); } else { mov_imm(TMP, value); @@ -505,6 +505,11 @@ struct BeamAssembler : public BeamAssemblerCommon { uint16_t upper16 = (value32 >> 16); a.movt(to, imm(upper16)); } + } + + bool is_aarch32_imm(uint32_t value) { + uint32_t encoded; + return arm::Utils::encodeAArch32Imm(value, &encoded); } void mov_imm(a32::Gp to, std::nullptr_t value) { @@ -512,12 +517,12 @@ struct BeamAssembler : public BeamAssemblerCommon { mov_imm(to, 0); } - void sub(a32::Gp to, a32::Gp src, int64_t val) { + void sub(a32::Gp to, a32::Gp src, int32_t val) { if (val < 0) { add(to, src, -val); } else if (val == 0 && to != src) { a.mov(to, src); - } else if (val <= 255) { + } else if (is_aarch32_imm(val)) { a.sub(to, src, imm(val)); } else { ASSERT(src != TMP); @@ -531,7 +536,7 @@ struct BeamAssembler : public BeamAssemblerCommon { sub(to, src, -val); } else if (val == 0 && to != src) { a.mov(to, src); - } else if (val <= 255) { + } else if (is_aarch32_imm(val)) { a.add(to, src, imm(val)); } else { ASSERT(src != TMP); @@ -540,10 +545,10 @@ struct BeamAssembler : public BeamAssemblerCommon { } } - void subs(a32::Gp to, a32::Gp src, int64_t val) { - if (val >= 0 && val <= 255) { + void subs(a32::Gp to, a32::Gp src, int32_t val) { + if (val >= 0 && is_aarch32_imm(val)) { a.subs(to, src, imm(val)); - } else if (val < 0 && -val <= 255) { + } else if (val < 0 && is_aarch32_imm(-val)) { a.adds(to, src, imm(-val)); } else { ASSERT(src != TMP); diff --git a/erts/emulator/beam/jit/arm/32/instr_select.cpp b/erts/emulator/beam/jit/arm/32/instr_select.cpp index 38f203ed32a6..dee65a5bbd37 100644 --- a/erts/emulator/beam/jit/arm/32/instr_select.cpp +++ b/erts/emulator/beam/jit/arm/32/instr_select.cpp @@ -424,7 +424,7 @@ void BeamModuleAssembler::emit_i_jump_on_val(const ArgSource &Src, a.asr(TMP, TMP, imm(_TAG_IMMED1_SIZE)); if (Base.get() != 0) { - if (Support::isUInt12((Sint)Base.get())) { + if (is_aarch32_imm(Base.get())) { a.sub(TMP, TMP, imm(Base.get())); } else { mov_imm(VAR, Base.get()); From 60800d1d8af5225845d1f918c3c0b76fd61a5212 Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Thu, 2 Jul 2026 13:24:02 +0200 Subject: [PATCH 2/4] Check pending stubs on every segment This binary OP might generate too much code. We cannot wait the next check. We need to act within this emitter. Co-authored-by: Frank Hunleth --- erts/emulator/beam/jit/arm/32/instr_bs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erts/emulator/beam/jit/arm/32/instr_bs.cpp b/erts/emulator/beam/jit/arm/32/instr_bs.cpp index 99a7004b1a5e..23b4ced2866c 100644 --- a/erts/emulator/beam/jit/arm/32/instr_bs.cpp +++ b/erts/emulator/beam/jit/arm/32/instr_bs.cpp @@ -1449,6 +1449,11 @@ void BeamModuleAssembler::emit_i_bs_create_bin(const ArgLabel &Fail, a.str(TMP, TMP_MEM5q); for (auto seg : segments) { +// This emitter may introduce more code then expected. +// This may overshoot the 4KB displacement limit of few constants. +// To avoid it we check the pending stubs between every segment. + check_pending_stubs(); + if (seg.effectiveSize >= 0) { continue; } @@ -1698,6 +1703,9 @@ void BeamModuleAssembler::emit_i_bs_create_bin(const ArgLabel &Fail, a.str(ARG1, TMP_MEM1q); for (auto seg : segments) { + // Safety check, see above + check_pending_stubs(); + switch (seg.type) { case am_append: case am_private_append: From 971d835de4389e37c64aa5b19690ca8b2ac46efd Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Thu, 2 Jul 2026 13:25:41 +0200 Subject: [PATCH 3/4] Do not use small memory in debug builds --- xcomp/erl-xcomp-arm-linux-debug-custom.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcomp/erl-xcomp-arm-linux-debug-custom.conf b/xcomp/erl-xcomp-arm-linux-debug-custom.conf index 1db2b7fd8b96..988a84b58845 100644 --- a/xcomp/erl-xcomp-arm-linux-debug-custom.conf +++ b/xcomp/erl-xcomp-arm-linux-debug-custom.conf @@ -87,7 +87,7 @@ CC="arm-linux-gnueabihf-gcc -march=armv7-a -marm -mfpu=neon --sysroot=$ARM_SYSRO # Skipping warinings that are considered neglegible SKIPPED_WARNINGS="-Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function" # * `CFLAGS' - C compiler flags. -CFLAGS=" -DDEBUG -DJIT_HARD_DEBUG $GDB_FLAGS -O0 -DSMALL_MEMORY --sysroot=$ARM_SYSROOT -Wall $SKIPPED_WARNINGS" +CFLAGS=" -DDEBUG -DJIT_HARD_DEBUG $GDB_FLAGS -O0 --sysroot=$ARM_SYSROOT -Wall $SKIPPED_WARNINGS" # * `STATIC_CFLAGS' - Static C compiler flags. #STATIC_CFLAGS= From 1f8a85037e2e753f14f2274178de2de6332c0e54 Mon Sep 17 00:00:00 2001 From: Luca Succi Date: Thu, 2 Jul 2026 13:26:33 +0200 Subject: [PATCH 4/4] Fix suboptimal literal check in arithmetic operations Co-authored-by: Frank Hunleth --- erts/emulator/beam/jit/arm/32/instr_arith.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erts/emulator/beam/jit/arm/32/instr_arith.cpp b/erts/emulator/beam/jit/arm/32/instr_arith.cpp index 40b0d9b40688..5b5a12a424cf 100644 --- a/erts/emulator/beam/jit/arm/32/instr_arith.cpp +++ b/erts/emulator/beam/jit/arm/32/instr_arith.cpp @@ -148,8 +148,10 @@ void BeamModuleAssembler::emit_i_plus(const ArgLabel &Fail, const ArgSource &LHS, const ArgSource &RHS, const ArgRegister &Dst) { - bool rhs_is_arm_literal = - RHS.isSmall() && Support::isUInt12(RHS.as().get()); + bool rhs_is_arm_literal = RHS.isSmall() && + is_aarch32_imm( + RHS.as().get() & + ~_TAG_IMMED1_MASK); bool is_small_result = is_sum_small_if_args_are_small(LHS, RHS); if (always_small(LHS) && always_small(RHS) && is_small_result) { @@ -341,8 +343,10 @@ void BeamModuleAssembler::emit_i_minus(const ArgLabel &Fail, const ArgSource &LHS, const ArgSource &RHS, const ArgRegister &Dst) { - bool rhs_is_arm_literal = - RHS.isSmall() && Support::isUInt12(RHS.as().get()); + bool rhs_is_arm_literal = RHS.isSmall() && + is_aarch32_imm( + RHS.as().get() & + ~_TAG_IMMED1_MASK); bool is_small_result = is_diff_small_if_args_are_small(LHS, RHS); if (always_small(LHS) && always_small(RHS) && is_small_result) {