Skip to content

Commit 933af32

Browse files
committed
armv6m: use literal pool to reduce binary size
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent f99f1dc commit 933af32

File tree

4 files changed

+379
-333
lines changed

4 files changed

+379
-333
lines changed

libs/estdlib/src/code_server.erl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,12 @@ load(Module) ->
174174
BackendModule,
175175
BackendState0
176176
),
177-
Stream1 = BackendModule:stream(BackendState1),
177+
BackendState2 = BackendModule:flush(BackendState1),
178+
Stream1 = BackendModule:stream(BackendState2),
178179
code_server:set_native_code(Module, LabelsCount, Stream1),
179180
End = erlang:system_time(millisecond),
180181
io:format("~B ms (bytecode: ~B bytes, native code: ~B bytes)\n", [
181-
End - Start, byte_size(Code), BackendModule:offset(BackendState1)
182+
End - Start, byte_size(Code), BackendModule:offset(BackendState2)
182183
])
183184
catch
184185
T:V:S ->

libs/jit/src/jit_armv6m.erl

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@
139139
available_regs :: [armv6m_register()],
140140
used_regs :: [armv6m_register()],
141141
labels :: [{integer() | reference(), integer()}],
142-
variant :: non_neg_integer()
142+
variant :: non_neg_integer(),
143+
literal_pool :: [{non_neg_integer(), armv6m_register(), non_neg_integer()}]
143144
}).
144145

145146
-type state() :: #state{}.
@@ -252,7 +253,8 @@ new(Variant, StreamModule, Stream) ->
252253
available_regs = ?AVAILABLE_REGS,
253254
used_regs = [],
254255
labels = [],
255-
variant = Variant
256+
variant = Variant,
257+
literal_pool = []
256258
}.
257259

258260
%%-----------------------------------------------------------------------------
@@ -637,7 +639,8 @@ call_primitive_last(
637639
State2 = set_registers_args(State1, ArgsForTailCall, 0),
638640
tail_call_with_jit_state_registers_only(State2, Temp)
639641
end,
640-
State4#state{available_regs = ?AVAILABLE_REGS, used_regs = []}.
642+
State5 = State4#state{available_regs = ?AVAILABLE_REGS, used_regs = []},
643+
flush_literal_pool(State5).
641644

642645
%%-----------------------------------------------------------------------------
643646
%% @doc Tail call to address in register, restoring prolog registers including
@@ -730,13 +733,15 @@ jump_to_label(
730733
Offset = StreamModule:offset(Stream0),
731734
{State1, CodeBlock} = branch_to_label_code(State0, Offset, Label, LabelLookupResult),
732735
Stream1 = StreamModule:append(Stream0, CodeBlock),
733-
State1#state{stream = Stream1}.
736+
State2 = State1#state{stream = Stream1},
737+
flush_literal_pool(State2).
734738

735739
jump_to_offset(#state{stream_module = StreamModule, stream = Stream0} = State, TargetOffset) ->
736740
Offset = StreamModule:offset(Stream0),
737741
CodeBlock = branch_to_offset_code(State, Offset, TargetOffset),
738742
Stream1 = StreamModule:append(Stream0, CodeBlock),
739-
State#state{stream = Stream1}.
743+
State2 = State#state{stream = Stream1},
744+
flush_literal_pool(State2).
740745

741746
%%-----------------------------------------------------------------------------
742747
%% @doc Jump to address in continuation pointer register
@@ -798,7 +803,8 @@ jump_to_continuation(
798803
Code = <<I3/binary, I4/binary, I5/binary, I6/binary, I7/binary>>,
799804
Stream2 = StreamModule:append(State1#state.stream, Code),
800805
% Free all registers as this is a terminal instruction
801-
State1#state{stream = Stream2, available_regs = ?AVAILABLE_REGS, used_regs = []}.
806+
State2 = State1#state{stream = Stream2, available_regs = ?AVAILABLE_REGS, used_regs = []},
807+
flush_literal_pool(State2).
802808

803809
branch_to_offset_code(_State, Offset, TargetOffset) when
804810
TargetOffset - Offset =< 2050, TargetOffset - Offset >= -2044
@@ -1741,7 +1747,7 @@ set_registers_args(
17411747
UsedRegs,
17421748
Args
17431749
),
1744-
State0#state{
1750+
State1#state{
17451751
stream = Stream1,
17461752
available_regs = ?AVAILABLE_REGS -- ParamRegs -- NewUsedRegs,
17471753
used_regs = ParamRegs ++ (NewUsedRegs -- ParamRegs)
@@ -2625,41 +2631,42 @@ mov_immediate(#state{stream_module = StreamModule, stream = Stream0} = State, Re
26252631
I2 = jit_armv6m_asm:negs(Reg, Reg),
26262632
Stream1 = StreamModule:append(Stream0, <<I1/binary, I2/binary>>),
26272633
State#state{stream = Stream1};
2628-
mov_immediate(#state{stream_module = StreamModule, stream = Stream0} = State, Reg, Val) ->
2629-
%% Use a literal pool with a branch instruction (branch-over pattern)
2630-
%% Calculate where literal will be placed (must be word-aligned)
2631-
%% After LDR (2 bytes) + Branch (2 bytes) = 4 bytes from current position
2632-
CurrentOffset = StreamModule:offset(Stream0),
2633-
OffsetAfterInstructions = CurrentOffset + 4,
2634-
%% Find next word-aligned position for literal
2635-
LiteralPosition =
2636-
case OffsetAfterInstructions rem 4 of
2637-
% Already aligned
2638-
0 -> OffsetAfterInstructions;
2639-
% Add 2 bytes padding to align
2640-
_ -> OffsetAfterInstructions + 2
2634+
mov_immediate(
2635+
#state{stream_module = StreamModule, stream = Stream0, literal_pool = LP} = State, Reg, Val
2636+
) ->
2637+
LdrInstructionAddr = StreamModule:offset(Stream0),
2638+
I1 = jit_armv6m_asm:ldr(Reg, {pc, 0}),
2639+
Stream1 = StreamModule:append(Stream0, <<I1/binary>>),
2640+
State#state{stream = Stream1, literal_pool = [{LdrInstructionAddr, Reg, Val} | LP]}.
2641+
2642+
flush_literal_pool(#state{literal_pool = []} = State) ->
2643+
State;
2644+
flush_literal_pool(
2645+
#state{stream_module = StreamModule, stream = Stream0, literal_pool = LP} = State
2646+
) ->
2647+
% Align
2648+
Offset = StreamModule:offset(Stream0),
2649+
Stream1 =
2650+
if
2651+
Offset rem 4 =:= 0 -> Stream0;
2652+
true -> StreamModule:append(Stream0, <<0:16>>)
26412653
end,
2642-
PaddingNeeded = LiteralPosition - OffsetAfterInstructions,
2643-
2644-
%% Calculate LDR PC-relative offset
2645-
%% PC = (current_instruction_address & ~3) + 4
2646-
LdrInstructionAddr = CurrentOffset,
2647-
LdrPC = (LdrInstructionAddr band (bnot 3)) + 4,
2648-
LiteralOffset = LiteralPosition - LdrPC,
2649-
2650-
%% Generate: ldr rTemp, [pc, #LiteralOffset] ; Load from literal
2651-
I1 = jit_armv6m_asm:ldr(Reg, {pc, LiteralOffset}),
2652-
%% Calculate branch offset
2653-
%% Branch is at CurrentOffset + 2, need to jump past literal
2654-
BranchPosition = CurrentOffset + 2,
2655-
% After the 4-byte literal
2656-
TargetPosition = LiteralPosition + 4,
2657-
BranchOffset = TargetPosition - BranchPosition,
2658-
I2 = jit_armv6m_asm:b(BranchOffset),
2659-
%% Generate padding if needed (just zeros)
2660-
Padding = <<0:(PaddingNeeded * 8)>>,
2661-
Stream1 = StreamModule:append(Stream0, <<I1/binary, I2/binary, Padding/binary, Val:32/little>>),
2662-
State#state{stream = Stream1}.
2654+
% Lay all values and update ldr instructions
2655+
Stream2 = lists:foldl(
2656+
fun({LdrInstructionAddr, Reg, Val}, AccStream) ->
2657+
LiteralPosition = StreamModule:offset(AccStream),
2658+
LdrPC = (LdrInstructionAddr band (bnot 3)) + 4,
2659+
LiteralOffset = LiteralPosition - LdrPC,
2660+
LdrInstruction = jit_armv6m_asm:ldr(Reg, {pc, LiteralOffset}),
2661+
AccStream1 = StreamModule:append(AccStream, <<Val:32/little>>),
2662+
StreamModule:replace(
2663+
AccStream1, LdrInstructionAddr, LdrInstruction
2664+
)
2665+
end,
2666+
Stream1,
2667+
lists:reverse(LP)
2668+
),
2669+
State#state{stream = Stream2, literal_pool = []}.
26632670

26642671
sub(#state{stream_module = StreamModule, stream = Stream0} = State, Reg, Val) when
26652672
(Val >= 0 andalso Val =< 255) orelse is_atom(Val)

0 commit comments

Comments
 (0)