Skip to content

Commit 52a94ed

Browse files
committed
riscv32: Implement and use C extension
Signed-off-by: Paul Guyot <[email protected]>
1 parent c35b871 commit 52a94ed

File tree

5 files changed

+2719
-1519
lines changed

5 files changed

+2719
-1519
lines changed

libs/jit/src/jit_riscv32.erl

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ flush(#state{stream_module = StreamModule, stream = Stream0} = State) ->
317317
%%-----------------------------------------------------------------------------
318318
-spec debugger(state()) -> state().
319319
debugger(#state{stream_module = StreamModule, stream = Stream0} = State) ->
320-
Stream1 = StreamModule:append(Stream0, jit_riscv32_asm:bkpt(0)),
320+
Stream1 = StreamModule:append(Stream0, jit_riscv32_asm:c_ebreak()),
321321
State#state{stream = Stream1}.
322322

323323
%%-----------------------------------------------------------------------------
@@ -416,12 +416,7 @@ jump_table0(
416416
% Create jump table entry: AUIPC + JALR (8 bytes total)
417417
% This will be patched later in update_branches/2
418418
Offset = StreamModule:offset(Stream0),
419-
% Placeholder: Load PC + upper20 bits
420-
I1 = jit_riscv32_asm:auipc(a3, 0),
421-
% Placeholder: Jump to a3 + lower12 bits
422-
I2 = jit_riscv32_asm:jalr(zero, a3, 0),
423-
424-
JumpEntry = <<I1/binary, I2/binary>>,
419+
JumpEntry = <<16#FFFFFFFF:32, 16#FFFFFFFF:32>>,
425420
Stream1 = StreamModule:append(Stream0, JumpEntry),
426421

427422
% Record both AUIPC and JALR offsets for patching
@@ -451,30 +446,49 @@ update_branches(
451446
Rel = LabelOffset - Offset,
452447
NewInstr =
453448
case Type of
454-
{adr, Reg} when Rel rem 4 =:= 0 -> pc_relative_address(Reg, Rel);
455-
{adr, Reg} when Rel rem 4 =:= 2 -> pc_relative_address(Reg, Rel + 2);
456-
{far_branch, Size, TempReg} ->
449+
{adr, Reg} when Rel rem 4 =:= 0 ->
450+
% Generate pc_relative_address and pad to 8 bytes with NOP
451+
I = pc_relative_address(Reg, Rel),
452+
case byte_size(I) of
453+
4 -> <<I/binary, (jit_riscv32_asm:nop())/binary>>;
454+
6 -> <<I/binary, (jit_riscv32_asm:c_nop())/binary>>;
455+
8 -> I
456+
end;
457+
{adr, Reg} when Rel rem 4 =:= 2; Rel rem 4 =:= -2 ->
458+
% Handle 2-byte aligned offsets and pad to 8 bytes
459+
% Handle both positive and negative offsets (Erlang rem can be negative)
460+
I = pc_relative_address(Reg, Rel),
461+
case byte_size(I) of
462+
4 -> <<I/binary, (jit_riscv32_asm:nop())/binary>>;
463+
6 -> <<I/binary, (jit_riscv32_asm:c_nop())/binary>>;
464+
8 -> I
465+
end;
466+
{far_branch, TempReg} ->
457467
% Check if branch can now be optimized to near branch
458468
if
459469
Rel >= -1048576 andalso Rel =< 1048574 andalso (Rel rem 2) =:= 0 ->
460470
% RISC-V jal has ±1MB range
461471
% Optimize to near branch: jal + nops to fill original size
462472
DirectBranch = jit_riscv32_asm:jal(zero, Rel),
463-
% Fill remaining bytes with NOPs (RISC-V instructions are 4 bytes)
464-
NopCount = (Size - 4) div 4,
465-
Nops = <<
466-
<<(jit_riscv32_asm:nop())/binary>>
467-
|| _ <- lists:seq(1, NopCount)
468-
>>,
469-
<<DirectBranch/binary, Nops/binary>>;
473+
case byte_size(DirectBranch) of
474+
2 ->
475+
<<DirectBranch/binary, (jit_riscv32_asm:c_nop())/binary,
476+
(jit_riscv32_asm:nop())/binary>>;
477+
4 ->
478+
<<DirectBranch/binary, (jit_riscv32_asm:nop())/binary>>
479+
end;
470480
true ->
471481
% Keep far branch sequence: auipc + jalr (PC-relative, 8 bytes)
472482
% Split the relative offset into upper 20 bits and lower 12 bits
473483
Hi20 = (Rel + 16#800) bsr 12,
474484
Lo12 = Rel - (Hi20 bsl 12),
475485
I1 = jit_riscv32_asm:auipc(TempReg, Hi20),
476486
I2 = jit_riscv32_asm:jalr(zero, TempReg, Lo12),
477-
<<I1/binary, I2/binary>>
487+
Entry = <<I1/binary, I2/binary>>,
488+
case byte_size(Entry) of
489+
6 -> <<Entry/binary, (jit_riscv32_asm:c_nop())/binary>>;
490+
8 -> Entry
491+
end
478492
end;
479493
jump_table_auipc_jalr ->
480494
% Calculate PC-relative offset from AUIPC instruction to target
@@ -498,7 +512,12 @@ update_branches(
498512
% Encode AUIPC and JALR with computed offsets
499513
I1 = jit_riscv32_asm:auipc(a3, Upper20),
500514
I2 = jit_riscv32_asm:jalr(zero, a3, Lower12Signed),
501-
<<I1/binary, I2/binary>>
515+
% Map to 8 bytes
516+
JumpTableEntry = <<I1/binary, I2/binary>>,
517+
case byte_size(JumpTableEntry) of
518+
6 -> <<JumpTableEntry/binary, (jit_riscv32_asm:c_nop())/binary>>;
519+
8 -> JumpTableEntry
520+
end
502521
end,
503522
Stream1 = StreamModule:replace(Stream0, Offset, NewInstr),
504523
update_branches(State#state{stream = Stream1, branches = BranchesT}).
@@ -783,13 +802,10 @@ branch_to_label_code(
783802
% RISC-V: Far branch sequence using PC-relative auipc + jalr (8 bytes)
784803

785804
% Placeholder: auipc TempReg, 0
786-
I1 = jit_riscv32_asm:auipc(TempReg, 0),
787805
% Placeholder: jalr zero, TempReg, 0
788-
I2 = jit_riscv32_asm:jalr(zero, TempReg, 0),
789-
CodeBlock = <<I1/binary, I2/binary>>,
790-
SequenceSize = byte_size(CodeBlock),
806+
CodeBlock = <<16#FFFFFFFF:32, 16#FFFFFFFF:32>>,
791807
% Add relocation entry
792-
Reloc = {Label, Offset, {far_branch, SequenceSize, TempReg}},
808+
Reloc = {Label, Offset, {far_branch, TempReg}},
793809
State1 = State0#state{branches = [Reloc | Branches]},
794810
{State1, CodeBlock};
795811
branch_to_label_code(
@@ -799,13 +815,10 @@ branch_to_label_code(
799815
% Far branch sequence using PC-relative auipc + jalr (8 bytes)
800816

801817
% Placeholder: auipc t6, 0
802-
I1 = jit_riscv32_asm:auipc(t6, 0),
803818
% Placeholder: jalr zero, t6, 0
804-
I2 = jit_riscv32_asm:jalr(zero, t6, 0),
805-
CodeBlock = <<I1/binary, I2/binary>>,
806-
SequenceSize = byte_size(CodeBlock),
819+
CodeBlock = <<16#FFFFFFFF:32, 16#FFFFFFFF:32>>,
807820
% Add relocation entry
808-
Reloc = {Label, Offset, {far_branch, SequenceSize, t6}},
821+
Reloc = {Label, Offset, {far_branch, t6}},
809822
State1 = State0#state{branches = [Reloc | Branches]},
810823
{State1, CodeBlock};
811824
branch_to_label_code(#state{available_regs = []}, _Offset, _Label, _LabelLookup) ->
@@ -2356,8 +2369,9 @@ set_continuation_to_label(
23562369
% resolved to point directly to the label's actual address (not the jump table entry)
23572370
Offset = StreamModule:offset(Stream0),
23582371
% Emit placeholder for pc_relative_address (auipc + addi)
2372+
% Reserve 8 bytes (2 x 32-bit instructions) with all-1s placeholder for flash programming
23592373
% The relocation will replace these with the correct offset
2360-
I1 = pc_relative_address(Temp, 4),
2374+
I1 = <<16#FFFFFFFF:32/little, 16#FFFFFFFF:32/little>>,
23612375
Reloc = {Label, Offset, {adr, Temp}},
23622376
% Store continuation (jit_state is in a1)
23632377
I2 = jit_riscv32_asm:sw(?JITSTATE_REG, Temp, ?JITSTATE_CONTINUATION_OFFSET),
@@ -2379,7 +2393,8 @@ set_continuation_to_offset(
23792393
) ->
23802394
OffsetRef = make_ref(),
23812395
Offset = StreamModule:offset(Stream0),
2382-
I1 = pc_relative_address(Temp, 4),
2396+
% Reserve 8 bytes with all-1s placeholder for flash programming
2397+
I1 = <<16#FFFFFFFF:32/little, 16#FFFFFFFF:32/little>>,
23832398
Reloc = {OffsetRef, Offset, {adr, Temp}},
23842399
% Store continuation (jit_state is in a1)
23852400
I2 = jit_riscv32_asm:sw(?JITSTATE_REG, Temp, ?JITSTATE_CONTINUATION_OFFSET),
@@ -2659,8 +2674,11 @@ decrement_reductions_and_maybe_schedule_next(
26592674
NewI5 =
26602675
case pc_relative_address(Temp, NewI5Offset) of
26612676
I when byte_size(I) =:= 4 ->
2662-
% Only auipc, pad with NOP
2677+
% Only auipc, pad with NOP (4 bytes)
26632678
<<I/binary, (jit_riscv32_asm:nop())/binary>>;
2679+
I when byte_size(I) =:= 6 ->
2680+
% auipc + c.addi, pad with c.nop (2 bytes)
2681+
<<I/binary, (jit_riscv32_asm:c_nop())/binary>>;
26642682
I when byte_size(I) =:= 8 ->
26652683
% auipc + addi, no padding needed
26662684
I
@@ -2798,10 +2816,11 @@ rewrite_cp_offset(
27982816
CPValue = NewOffset bsl 2,
27992817
NewMoveInstr = jit_riscv32_asm:li(TempReg, CPValue),
28002818
% We reserved 8 bytes (2 instructions) for the CP value
2801-
% If li generates only 4 bytes, pad with a NOP to maintain alignment
2819+
% Pad with NOP if needed to maintain alignment
28022820
PaddedInstr =
28032821
case byte_size(NewMoveInstr) of
28042822
4 -> <<NewMoveInstr/binary, (jit_riscv32_asm:nop())/binary>>;
2823+
6 -> <<NewMoveInstr/binary, (jit_riscv32_asm:c_nop())/binary>>;
28052824
8 -> NewMoveInstr
28062825
end,
28072826
Stream1 = StreamModule:replace(Stream0, RewriteOffset, PaddedInstr),
@@ -2841,13 +2860,22 @@ return_labels_and_lines(
28412860
|| {Label, LabelOffset} <- Labels, is_integer(Label)
28422861
]),
28432862

2844-
I1 = pc_relative_address(a0, 12),
28452863
I2 = jit_riscv32_asm:ret(),
2864+
% Assume total size is 10 bytes (8-byte I1 + 2-byte c.ret)
2865+
% If actual is 8 bytes (6-byte I1 + 2-byte c.ret), we'll pad with 2 bytes
2866+
I1 = pc_relative_address(a0, 10),
2867+
Prologue = <<I1/binary, I2/binary>>,
2868+
ProloguePadded =
2869+
case byte_size(Prologue) of
2870+
10 -> Prologue;
2871+
% 2-byte padding
2872+
8 -> <<Prologue/binary, 16#FFFF:16>>
2873+
end,
28462874
LabelsTable = <<<<Label:16, Offset:32>> || {Label, Offset} <- SortedLabels>>,
28472875
LinesTable = <<<<Line:16, Offset:32>> || {Line, Offset} <- SortedLines>>,
28482876
Stream1 = StreamModule:append(
28492877
Stream0,
2850-
<<I1/binary, I2/binary, (length(SortedLabels)):16, LabelsTable/binary,
2878+
<<ProloguePadded/binary, (length(SortedLabels)):16, LabelsTable/binary,
28512879
(length(SortedLines)):16, LinesTable/binary>>
28522880
),
28532881
State#state{stream = Stream1}.
@@ -3005,7 +3033,7 @@ args_regs(Args) ->
30053033
).
30063034

30073035
%%-----------------------------------------------------------------------------
3008-
%% @doc Add a label at the current offset. Eventually align it with a nop.
3036+
%% @doc Add a label at the current offset.
30093037
%% @end
30103038
%% @param State current backend state
30113039
%% @param Label the label number or reference
@@ -3014,15 +3042,7 @@ args_regs(Args) ->
30143042
-spec add_label(state(), integer() | reference()) -> state().
30153043
add_label(#state{stream_module = StreamModule, stream = Stream0} = State0, Label) ->
30163044
Offset0 = StreamModule:offset(Stream0),
3017-
{State1, Offset1} =
3018-
if
3019-
Offset0 rem 4 =:= 0 ->
3020-
{State0, Offset0};
3021-
true ->
3022-
Stream1 = StreamModule:append(Stream0, jit_riscv32_asm:nop()),
3023-
{State0#state{stream = Stream1}, Offset0 + 2}
3024-
end,
3025-
add_label(State1, Label, Offset1).
3045+
add_label(State0, Label, Offset0).
30263046

30273047
%%-----------------------------------------------------------------------------
30283048
%% @doc Add a label at a specific offset

0 commit comments

Comments
 (0)