Skip to content

Commit 1fffaae

Browse files
committed
JIT compile-time optimizations
Replace O(n) list lookups with O(log n) maps for hot data structures in the JIT compiler, and batch consecutive small instruction emits into single stream appends to reduce NIF dispatch overhead. Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent b93752a commit 1fffaae

10 files changed

Lines changed: 457 additions & 347 deletions

libs/jit/src/jit.erl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,22 @@
9999
tail_cache :: tail_cache()
100100
}).
101101

102-
-type tail_cache() :: [{tuple(), non_neg_integer()}] | disabled.
102+
-type tail_cache() :: #{tuple() => non_neg_integer()} | disabled.
103103
-type stream() :: any().
104104

105105
%%-define(TRACE(Fmt, Args), io:format(Fmt, Args)).
106106
-define(TRACE(Fmt, Args), ok).
107107

108-
tail_cache_find(_Key, disabled) -> false;
109-
tail_cache_find(Key, TC) -> lists:keyfind(Key, 1, TC).
108+
tail_cache_find(_Key, disabled) ->
109+
false;
110+
tail_cache_find(Key, TC) ->
111+
case TC of
112+
#{Key := Value} -> {Key, Value};
113+
_ -> false
114+
end.
110115

111116
tail_cache_store(_Key, _Value, disabled) -> disabled;
112-
tail_cache_store(Key, Value, TC) -> [{Key, Value} | TC].
117+
tail_cache_store(Key, Value, TC) -> TC#{Key => Value}.
113118

114119
%%-define(ASSERT_ALL_NATIVE_FREE(St), MMod:assert_all_native_free(St)).
115120
%%-define(ASSERT(Expr), true = Expr).
@@ -154,11 +159,11 @@ compile(
154159
case erlang:function_exported(MMod, supports_tail_cache, 0) of
155160
true ->
156161
case MMod:supports_tail_cache() of
157-
true -> [];
162+
true -> #{};
158163
false -> disabled
159164
end;
160165
false ->
161-
[]
166+
#{}
162167
end
163168
},
164169
MSt1 = MMod:jump_table(MSt0, LabelsCount),

libs/jit/src/jit_aarch64.erl

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,11 @@
154154
stream_module :: module(),
155155
stream :: stream(),
156156
offset :: non_neg_integer(),
157-
branches :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}],
157+
branches :: #{integer() | reference() => [{non_neg_integer(), non_neg_integer()}]},
158158
jump_table_start :: non_neg_integer(),
159159
available_regs :: non_neg_integer(),
160160
used_regs :: non_neg_integer(),
161-
labels :: [{integer() | reference(), integer()}],
161+
labels :: #{integer() | reference() => integer()},
162162
variant :: non_neg_integer(),
163163
regs :: jit_regs:regs()
164164
}).
@@ -286,12 +286,12 @@ new(Variant, StreamModule, Stream) ->
286286
#state{
287287
stream_module = StreamModule,
288288
stream = Stream,
289-
branches = [],
289+
branches = #{},
290290
jump_table_start = 0,
291291
offset = StreamModule:offset(Stream),
292292
available_regs = ?AVAILABLE_REGS_MASK,
293293
used_regs = 0,
294-
labels = [],
294+
labels = #{},
295295
variant = Variant,
296296
regs = jit_regs:new()
297297
}.
@@ -465,27 +465,25 @@ patch_branch(StreamModule, Stream, Offset, Type, LabelOffset) ->
465465
-spec patch_branches_for_label(
466466
module(),
467467
stream(),
468-
integer(),
468+
integer() | reference(),
469469
non_neg_integer(),
470-
[{integer(), non_neg_integer(), any()}]
471-
) -> {stream(), [{integer(), non_neg_integer(), any()}]}.
470+
#{integer() | reference() => [{non_neg_integer(), non_neg_integer()}]}
471+
) ->
472+
{stream(), #{integer() | reference() => [{non_neg_integer(), non_neg_integer()}]}}.
472473
patch_branches_for_label(StreamModule, Stream, TargetLabel, LabelOffset, Branches) ->
473-
patch_branches_for_label(StreamModule, Stream, TargetLabel, LabelOffset, Branches, []).
474-
475-
patch_branches_for_label(_StreamModule, Stream, _TargetLabel, _LabelOffset, [], Acc) ->
476-
{Stream, lists:reverse(Acc)};
477-
patch_branches_for_label(
478-
StreamModule,
479-
Stream0,
480-
TargetLabel,
481-
LabelOffset,
482-
[{Label, Offset, Type} | Rest],
483-
Acc
484-
) when Label =:= TargetLabel ->
485-
Stream1 = patch_branch(StreamModule, Stream0, Offset, Type, LabelOffset),
486-
patch_branches_for_label(StreamModule, Stream1, TargetLabel, LabelOffset, Rest, Acc);
487-
patch_branches_for_label(StreamModule, Stream, TargetLabel, LabelOffset, [Branch | Rest], Acc) ->
488-
patch_branches_for_label(StreamModule, Stream, TargetLabel, LabelOffset, Rest, [Branch | Acc]).
474+
case Branches of
475+
#{TargetLabel := BrList} ->
476+
Stream1 = lists:foldl(
477+
fun({Offset, Type}, AccStream) ->
478+
patch_branch(StreamModule, AccStream, Offset, Type, LabelOffset)
479+
end,
480+
Stream,
481+
BrList
482+
),
483+
{Stream1, maps:remove(TargetLabel, Branches)};
484+
_ ->
485+
{Stream, Branches}
486+
end.
489487

490488
%%-----------------------------------------------------------------------------
491489
%% @doc Rewrite stream to update all branches for labels.
@@ -494,19 +492,29 @@ patch_branches_for_label(StreamModule, Stream, TargetLabel, LabelOffset, [Branch
494492
%% @return Updated backend state
495493
%%-----------------------------------------------------------------------------
496494
-spec update_branches(state()) -> state().
497-
update_branches(#state{branches = []} = State) ->
498-
State;
499495
update_branches(
500496
#state{
501497
stream_module = StreamModule,
502498
stream = Stream0,
503-
branches = [{Label, Offset, Type} | BranchesT],
499+
branches = Branches,
504500
labels = Labels
505501
} = State
506502
) ->
507-
{Label, LabelOffset} = lists:keyfind(Label, 1, Labels),
508-
Stream1 = patch_branch(StreamModule, Stream0, Offset, Type, LabelOffset),
509-
update_branches(State#state{stream = Stream1, branches = BranchesT}).
503+
Stream1 = maps:fold(
504+
fun(Label, BrList, AccStream) ->
505+
#{Label := LabelOffset} = Labels,
506+
lists:foldl(
507+
fun({Offset, Type}, AccStream2) ->
508+
patch_branch(StreamModule, AccStream2, Offset, Type, LabelOffset)
509+
end,
510+
AccStream,
511+
BrList
512+
)
513+
end,
514+
Stream0,
515+
Branches
516+
),
517+
State#state{stream = Stream1, branches = #{}}.
510518

511519
%%-----------------------------------------------------------------------------
512520
%% @doc Emit a call (call with return) to a primitive with arguments. This
@@ -650,21 +658,22 @@ jump_to_label(
650658
Label
651659
) ->
652660
Offset = StreamModule:offset(Stream0),
653-
case lists:keyfind(Label, 1, Labels) of
654-
{Label, LabelOffset} ->
661+
case Labels of
662+
#{Label := LabelOffset} ->
655663
% Label is already known, emit direct branch without relocation
656664
Rel = LabelOffset - Offset,
657665
I1 = jit_aarch64_asm:b(Rel),
658666
Stream1 = StreamModule:append(Stream0, I1),
659667
State#state{stream = Stream1, regs = jit_regs:unreachable(State#state.regs)};
660-
false ->
668+
_ ->
661669
% Label not yet known, emit placeholder and add relocation
662670
I1 = jit_aarch64_asm:b(0),
663-
Reloc = {Label, Offset, b},
671+
BrEntry = {Offset, b},
672+
ExistingBrs = maps:get(Label, AccBranches, []),
664673
Stream1 = StreamModule:append(Stream0, I1),
665674
State#state{
666675
stream = Stream1,
667-
branches = [Reloc | AccBranches],
676+
branches = AccBranches#{Label => [BrEntry | ExistingBrs]},
668677
regs = jit_regs:unreachable(State#state.regs)
669678
}
670679
end.
@@ -2354,21 +2363,26 @@ set_continuation_to_label(
23542363
Temp = first_avail(Avail),
23552364
Offset = StreamModule:offset(Stream0),
23562365
Regs1 = jit_regs:invalidate_reg(Regs0, Temp),
2357-
case lists:keyfind(Label, 1, Labels) of
2358-
{Label, LabelOffset} ->
2366+
case Labels of
2367+
#{Label := LabelOffset} ->
23592368
Rel = LabelOffset - Offset,
23602369
I1 = jit_aarch64_asm:adr(Temp, Rel),
23612370
I2 = jit_aarch64_asm:str(Temp, ?JITSTATE_CONTINUATION),
23622371
Code = <<I1/binary, I2/binary>>,
23632372
Stream1 = StreamModule:append(Stream0, Code),
23642373
State#state{stream = Stream1, regs = Regs1};
2365-
false ->
2374+
_ ->
23662375
I1 = jit_aarch64_asm:adr(Temp, 0),
2367-
Reloc = {Label, Offset, {adr, Temp}},
2376+
BrEntry = {Offset, {adr, Temp}},
23682377
I2 = jit_aarch64_asm:str(Temp, ?JITSTATE_CONTINUATION),
23692378
Code = <<I1/binary, I2/binary>>,
23702379
Stream1 = StreamModule:append(Stream0, Code),
2371-
State#state{stream = Stream1, branches = [Reloc | Branches], regs = Regs1}
2380+
ExistingBrs = maps:get(Label, Branches, []),
2381+
State#state{
2382+
stream = Stream1,
2383+
branches = Branches#{Label => [BrEntry | ExistingBrs]},
2384+
regs = Regs1
2385+
}
23722386
end.
23732387

23742388
%%-----------------------------------------------------------------------------
@@ -2393,12 +2407,19 @@ set_continuation_to_offset(
23932407
OffsetRef = make_ref(),
23942408
Offset = StreamModule:offset(Stream0),
23952409
I1 = jit_aarch64_asm:adr(Temp, 0),
2396-
Reloc = {OffsetRef, Offset, {adr, Temp}},
2410+
BrEntry = {Offset, {adr, Temp}},
23972411
I2 = jit_aarch64_asm:str(Temp, ?JITSTATE_CONTINUATION),
23982412
Code = <<I1/binary, I2/binary>>,
23992413
Stream1 = StreamModule:append(Stream0, Code),
24002414
Regs1 = jit_regs:invalidate_reg(Regs0, Temp),
2401-
{State#state{stream = Stream1, branches = [Reloc | Branches], regs = Regs1}, OffsetRef}.
2415+
{
2416+
State#state{
2417+
stream = Stream1,
2418+
branches = Branches#{OffsetRef => [BrEntry]},
2419+
regs = Regs1
2420+
},
2421+
OffsetRef
2422+
}.
24022423

24032424
%%-----------------------------------------------------------------------------
24042425
%% @doc Implement a continuation entry point. On AArch64 this is a nop
@@ -2783,20 +2804,24 @@ call_only_or_schedule_next(
27832804
Stream1 = StreamModule:append(Stream0, <<I1/binary, I2/binary, I3/binary>>),
27842805
BNEOffset = StreamModule:offset(Stream1),
27852806

2786-
case lists:keyfind(Label, 1, Labels) of
2787-
{Label, LabelOffset} ->
2807+
case Labels of
2808+
#{Label := LabelOffset} ->
27882809
% Label is already known, emit direct branch with calculated offset
27892810
% Calculate relative offset (must be 4-byte aligned)
27902811
Rel = LabelOffset - BNEOffset,
27912812
I4 = jit_aarch64_asm:bcc(ne, Rel),
27922813
Stream2 = StreamModule:append(Stream1, I4),
27932814
State1 = State0#state{stream = Stream2};
2794-
false ->
2815+
_ ->
27952816
% Label not yet known, emit placeholder and add relocation
27962817
I4 = jit_aarch64_asm:bcc(ne, 0),
2797-
Reloc1 = {Label, BNEOffset, {bcc, ne}},
2818+
BrEntry = {BNEOffset, {bcc, ne}},
2819+
ExistingBrs = maps:get(Label, Branches, []),
27982820
Stream2 = StreamModule:append(Stream1, I4),
2799-
State1 = State0#state{stream = Stream2, branches = [Reloc1 | Branches]}
2821+
State1 = State0#state{
2822+
stream = Stream2,
2823+
branches = Branches#{Label => [BrEntry | ExistingBrs]}
2824+
}
28002825
end,
28012826
State2 = set_continuation_to_label(State1, Label),
28022827
call_primitive_last(State2, ?PRIM_SCHEDULE_NEXT_CP, [ctx, jit_state]).
@@ -2894,7 +2919,7 @@ return_labels_and_lines(
28942919
) ->
28952920
SortedLabels = lists:keysort(2, [
28962921
{Label, LabelOffset}
2897-
|| {Label, LabelOffset} <- Labels, is_integer(Label)
2922+
|| {Label, LabelOffset} <- maps:to_list(Labels), is_integer(Label)
28982923
]),
28992924

29002925
I1 = jit_aarch64_asm:adr(r0, 8),
@@ -3049,10 +3074,10 @@ add_label(
30493074
),
30503075

30513076
State#state{
3052-
stream = Stream2, branches = RemainingBranches, labels = [{Label, LabelOffset} | Labels]
3077+
stream = Stream2, branches = RemainingBranches, labels = Labels#{Label => LabelOffset}
30533078
};
30543079
add_label(#state{labels = Labels} = State, Label, Offset) ->
3055-
State#state{labels = [{Label, Offset} | Labels]}.
3080+
State#state{labels = Labels#{Label => Offset}}.
30563081

30573082
-ifdef(JIT_DWARF).
30583083
%%-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)