Skip to content

Commit 069e576

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

File tree

3 files changed

+364
-307
lines changed

3 files changed

+364
-307
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
@@ -134,7 +134,8 @@
134134
available_regs :: [armv6m_register()],
135135
used_regs :: [armv6m_register()],
136136
labels :: [{integer() | reference(), integer()}],
137-
variant :: non_neg_integer()
137+
variant :: non_neg_integer(),
138+
literal_pool :: [{non_neg_integer(), armv6m_register(), non_neg_integer()}]
138139
}).
139140

140141
-type state() :: #state{}.
@@ -247,7 +248,8 @@ new(Variant, StreamModule, Stream) ->
247248
available_regs = ?AVAILABLE_REGS,
248249
used_regs = [],
249250
labels = [],
250-
variant = Variant
251+
variant = Variant,
252+
literal_pool = []
251253
}.
252254

253255
%%-----------------------------------------------------------------------------
@@ -632,7 +634,8 @@ call_primitive_last(
632634
State2 = set_registers_args(State1, ArgsForTailCall, 0),
633635
tail_call_with_jit_state_registers_only(State2, Temp)
634636
end,
635-
State4#state{available_regs = ?AVAILABLE_REGS, used_regs = []}.
637+
State5 = State4#state{available_regs = ?AVAILABLE_REGS, used_regs = []},
638+
flush_literal_pool(State5).
636639

637640
%%-----------------------------------------------------------------------------
638641
%% @doc Tail call to address in register, restoring prolog registers including
@@ -725,13 +728,15 @@ jump_to_label(
725728
Offset = StreamModule:offset(Stream0),
726729
{State1, CodeBlock} = branch_to_label_code(State0, Offset, Label, LabelLookupResult),
727730
Stream1 = StreamModule:append(Stream0, CodeBlock),
728-
State1#state{stream = Stream1}.
731+
State2 = State1#state{stream = Stream1},
732+
flush_literal_pool(State2).
729733

730734
jump_to_offset(#state{stream_module = StreamModule, stream = Stream0} = State, TargetOffset) ->
731735
Offset = StreamModule:offset(Stream0),
732736
CodeBlock = branch_to_offset_code(State, Offset, TargetOffset),
733737
Stream1 = StreamModule:append(Stream0, CodeBlock),
734-
State#state{stream = Stream1}.
738+
State2 = State#state{stream = Stream1},
739+
flush_literal_pool(State2).
735740

736741
%%-----------------------------------------------------------------------------
737742
%% @doc Jump to address in continuation pointer register
@@ -793,7 +798,8 @@ jump_to_continuation(
793798
Code = <<I3/binary, I4/binary, I5/binary, I6/binary, I7/binary>>,
794799
Stream2 = StreamModule:append(State1#state.stream, Code),
795800
% Free all registers as this is a terminal instruction
796-
State1#state{stream = Stream2, available_regs = ?AVAILABLE_REGS, used_regs = []}.
801+
State2 = State1#state{stream = Stream2, available_regs = ?AVAILABLE_REGS, used_regs = []},
802+
flush_literal_pool(State2).
797803

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

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

0 commit comments

Comments
 (0)