Skip to content

Commit 5a24621

Browse files
committed
JIT: add register value caching across all backends
Add register value tracking (jit_regs) to all JIT backends (x86-64, aarch64, armv6m, riscv32). This enables optimization of redundant loads when a register already contains the needed value. Factor out shared value_to_contents and vm_dest_to_contents helpers into jit_regs module. Add regs_to_mask/2 to replace per-backend duplicated regs_to_mask/1 functions. Add invalidate_volatile/2 for correct cache management after function calls. Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent c805acc commit 5a24621

15 files changed

+2896
-913
lines changed

libs/jit/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ include(BuildErlang)
2525
set(ERLANG_MODULES
2626
jit
2727
jit_precompile
28+
jit_regs
2829
jit_stream_binary
2930
jit_stream_flash
3031
jit_stream_mmap

libs/jit/src/jit.erl

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -666,17 +666,20 @@ first_pass(<<?OP_IS_INTEGER, Rest0/binary>>, MMod, MSt0, State0) ->
666666
{MSt1, Arg1, Rest2} = decode_compact_term(Rest1, MMod, MSt0, State0),
667667
?TRACE("OP_IS_INTEGER ~p, ~p\n", [Label, Arg1]),
668668
MSt2 = verify_is_any_integer({free, Arg1}, Label, MMod, MSt1),
669-
?ASSERT_ALL_NATIVE_FREE(MSt2),
670-
first_pass(Rest2, MMod, MSt2, State0);
669+
%% After successful type guard, record the known type
670+
MSt3 = set_type_after_guard(MMod, MSt2, Arg1, any_integer),
671+
?ASSERT_ALL_NATIVE_FREE(MSt3),
672+
first_pass(Rest2, MMod, MSt3, State0);
671673
% 46
672674
first_pass(<<?OP_IS_FLOAT, Rest0/binary>>, MMod, MSt0, State0) ->
673675
?ASSERT_ALL_NATIVE_FREE(MSt0),
674676
{Label, Rest1} = decode_label(Rest0),
675677
{MSt1, Arg1, Rest2} = decode_compact_term(Rest1, MMod, MSt0, State0),
676678
?TRACE("OP_IS_FLOAT ~p, ~p\n", [Label, Arg1]),
677679
MSt2 = verify_is_boxed_with_tag(Label, Arg1, ?TERM_BOXED_FLOAT, MMod, MSt1),
678-
?ASSERT_ALL_NATIVE_FREE(MSt2),
679-
first_pass(Rest2, MMod, MSt2, State0);
680+
MSt3 = set_type_after_guard(MMod, MSt2, Arg1, float),
681+
?ASSERT_ALL_NATIVE_FREE(MSt3),
682+
first_pass(Rest2, MMod, MSt3, State0);
680683
% 47
681684
first_pass(<<?OP_IS_NUMBER, Rest0/binary>>, MMod, MSt0, State0) ->
682685
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -692,8 +695,9 @@ first_pass(<<?OP_IS_NUMBER, Rest0/binary>>, MMod, MSt0, State0) ->
692695
MMod,
693696
MSt1
694697
),
695-
?ASSERT_ALL_NATIVE_FREE(MSt2),
696-
first_pass(Rest2, MMod, MSt2, State0);
698+
MSt3 = set_type_after_guard(MMod, MSt2, Arg1, number),
699+
?ASSERT_ALL_NATIVE_FREE(MSt3),
700+
first_pass(Rest2, MMod, MSt3, State0);
697701
% 48
698702
first_pass(<<?OP_IS_ATOM, Rest0/binary>>, MMod, MSt0, State0) ->
699703
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -704,8 +708,9 @@ first_pass(<<?OP_IS_ATOM, Rest0/binary>>, MMod, MSt0, State0) ->
704708
MSt3 = cond_jump_to_label(
705709
{{free, Reg}, '&', ?TERM_IMMED2_TAG_MASK, '!=', ?TERM_IMMED2_ATOM}, Label, MMod, MSt2
706710
),
707-
?ASSERT_ALL_NATIVE_FREE(MSt3),
708-
first_pass(Rest2, MMod, MSt3, State0);
711+
MSt4 = set_type_after_guard(MMod, MSt3, Arg1, atom),
712+
?ASSERT_ALL_NATIVE_FREE(MSt4),
713+
first_pass(Rest2, MMod, MSt4, State0);
709714
% 49
710715
first_pass(<<?OP_IS_PID, Rest0/binary>>, MMod, MSt0, State0) ->
711716
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -715,8 +720,9 @@ first_pass(<<?OP_IS_PID, Rest0/binary>>, MMod, MSt0, State0) ->
715720
MSt2 = verify_is_immediate_or_boxed(
716721
{free, Arg1}, ?TERM_PID_TAG, ?TERM_BOXED_EXTERNAL_PID, Label, MMod, MSt1
717722
),
718-
?ASSERT_ALL_NATIVE_FREE(MSt2),
719-
first_pass(Rest2, MMod, MSt2, State0);
723+
MSt3 = set_type_after_guard(MMod, MSt2, Arg1, pid),
724+
?ASSERT_ALL_NATIVE_FREE(MSt3),
725+
first_pass(Rest2, MMod, MSt3, State0);
720726
% 50
721727
first_pass(<<?OP_IS_REFERENCE, Rest0/binary>>, MMod, MSt0, State0) ->
722728
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -759,8 +765,9 @@ first_pass(<<?OP_IS_NIL, Rest0/binary>>, MMod, MSt0, State0) ->
759765
{MSt2, Reg} = MMod:move_to_native_register(MSt1, Arg1),
760766
MSt3 = cond_jump_to_label({Reg, '!=', ?TERM_NIL}, Label, MMod, MSt2),
761767
MSt4 = MMod:free_native_registers(MSt3, [Reg]),
762-
?ASSERT_ALL_NATIVE_FREE(MSt4),
763-
first_pass(Rest2, MMod, MSt4, State0);
768+
MSt5 = set_type_after_guard(MMod, MSt4, Arg1, nil),
769+
?ASSERT_ALL_NATIVE_FREE(MSt5),
770+
first_pass(Rest2, MMod, MSt5, State0);
764771
% 53
765772
first_pass(<<?OP_IS_BINARY, Rest0/binary>>, MMod, MSt0, State0) ->
766773
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -769,8 +776,9 @@ first_pass(<<?OP_IS_BINARY, Rest0/binary>>, MMod, MSt0, State0) ->
769776
?TRACE("OP_IS_BINARY ~p, ~p\n", [Label, Arg1]),
770777
MSt2 = verify_is_binary(Arg1, Label, MMod, MSt1),
771778
MSt3 = MMod:free_native_registers(MSt2, [Arg1]),
772-
?ASSERT_ALL_NATIVE_FREE(MSt3),
773-
first_pass(Rest2, MMod, MSt3, State0);
779+
MSt4 = set_type_after_guard(MMod, MSt3, Arg1, binary),
780+
?ASSERT_ALL_NATIVE_FREE(MSt4),
781+
first_pass(Rest2, MMod, MSt4, State0);
774782
% 55
775783
first_pass(<<?OP_IS_LIST, Rest0/binary>>, MMod, MSt0, State0) ->
776784
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -787,8 +795,9 @@ first_pass(<<?OP_IS_LIST, Rest0/binary>>, MMod, MSt0, State0) ->
787795
MMod,
788796
MSt2
789797
),
790-
?ASSERT_ALL_NATIVE_FREE(MSt3),
791-
first_pass(Rest2, MMod, MSt3, State0);
798+
MSt4 = set_type_after_guard(MMod, MSt3, Arg1, list),
799+
?ASSERT_ALL_NATIVE_FREE(MSt4),
800+
first_pass(Rest2, MMod, MSt4, State0);
792801
% 56
793802
first_pass(<<?OP_IS_NONEMPTY_LIST, Rest0/binary>>, MMod, MSt0, State0) ->
794803
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -799,17 +808,19 @@ first_pass(<<?OP_IS_NONEMPTY_LIST, Rest0/binary>>, MMod, MSt0, State0) ->
799808
MSt3 = cond_jump_to_label(
800809
{{free, Reg}, '&', ?TERM_PRIMARY_MASK, '!=', ?TERM_PRIMARY_LIST}, Label, MMod, MSt2
801810
),
802-
?ASSERT_ALL_NATIVE_FREE(MSt3),
803-
first_pass(Rest2, MMod, MSt3, State0);
811+
MSt4 = set_type_after_guard(MMod, MSt3, Arg1, nonempty_list),
812+
?ASSERT_ALL_NATIVE_FREE(MSt4),
813+
first_pass(Rest2, MMod, MSt4, State0);
804814
% 57
805815
first_pass(<<?OP_IS_TUPLE, Rest0/binary>>, MMod, MSt0, State0) ->
806816
?ASSERT_ALL_NATIVE_FREE(MSt0),
807817
{Label, Rest1} = decode_label(Rest0),
808818
{MSt1, Arg1, Rest2} = decode_compact_term(Rest1, MMod, MSt0, State0),
809819
?TRACE("OP_IS_TUPLE ~p, ~p\n", [Label, Arg1]),
810820
MSt2 = verify_is_boxed_with_tag(Label, Arg1, ?TERM_BOXED_TUPLE, MMod, MSt1),
811-
?ASSERT_ALL_NATIVE_FREE(MSt2),
812-
first_pass(Rest2, MMod, MSt2, State0);
821+
MSt3 = set_type_after_guard(MMod, MSt2, Arg1, tuple),
822+
?ASSERT_ALL_NATIVE_FREE(MSt3),
823+
first_pass(Rest2, MMod, MSt3, State0);
813824
% 58
814825
first_pass(<<?OP_TEST_ARITY, Rest0/binary>>, MMod, MSt0, State0) ->
815826
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -3391,6 +3402,16 @@ op_gc_bif2_sub(MMod, MSt0, FailLabel, Live, Bif, Arg1, Arg2, Dest, Range1, Range
33913402
unwrap_typed({typed, Arg, _Type}) -> Arg;
33923403
unwrap_typed(Arg) -> Arg.
33933404

3405+
%% @doc Record type information for a VM register after a successful type guard.
3406+
%% Only records type for x_reg and y_reg sources (not intermediates or immediates).
3407+
set_type_after_guard(MMod, MSt, {x_reg, X}, Type) when is_integer(X) ->
3408+
MMod:set_type_tracking(MSt, {x_reg, X}, Type);
3409+
set_type_after_guard(MMod, MSt, {y_reg, Y}, Type) ->
3410+
MMod:set_type_tracking(MSt, {y_reg, Y}, Type);
3411+
set_type_after_guard(_MMod, MSt, _Arg, _Type) ->
3412+
%% Argument is not a VM register (immediate, native reg, etc.) - nothing to track
3413+
MSt.
3414+
33943415
% Optimized >= comparison for typed integers
33953416
% Test if Arg1 >= Arg2, jump to Label if false (i.e., if Arg1 < Arg2)
33963417
op_is_ge(MMod, MSt0, Label, Arg1, {typed, Arg2, {t_integer, _Range}}) when is_integer(Arg1) ->

0 commit comments

Comments
 (0)