Skip to content

Commit d6c4a20

Browse files
committed
riscv32: several fixes to li implementation
Signed-off-by: Paul Guyot <[email protected]>
1 parent 2d89f74 commit d6c4a20

File tree

3 files changed

+130
-41
lines changed

3 files changed

+130
-41
lines changed

libs/jit/src/jit_riscv32.erl

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,19 +1689,8 @@ set_registers_args0(
16891689
AvailGP,
16901690
StackOffset
16911691
) when is_integer(Value) ->
1692-
LowPartUnsigned = Value band 16#FFFFFFFF,
1693-
HighPartUnsigned = (Value bsr 32) band 16#FFFFFFFF,
1694-
% Convert to signed 32-bit values for RISC-V li instruction
1695-
LowPart =
1696-
if
1697-
LowPartUnsigned > 16#7FFFFFFF -> LowPartUnsigned - 16#100000000;
1698-
true -> LowPartUnsigned
1699-
end,
1700-
HighPart =
1701-
if
1702-
HighPartUnsigned > 16#7FFFFFFF -> HighPartUnsigned - 16#100000000;
1703-
true -> HighPartUnsigned
1704-
end,
1692+
LowPart = Value band 16#FFFFFFFF,
1693+
HighPart = (Value bsr 32) band 16#FFFFFFFF,
17051694
set_registers_args0(
17061695
State, [LowPart, HighPart | ArgsT], [imm | ArgsRegs], ParamRegs, AvailGP, StackOffset
17071696
);
@@ -2833,6 +2822,7 @@ rewrite_cp_offset(
28332822
% Pad with NOP if needed to maintain alignment
28342823
PaddedInstr =
28352824
case byte_size(NewMoveInstr) of
2825+
2 -> <<NewMoveInstr/binary, (jit_riscv32_asm:nop())/binary, (jit_riscv32_asm:c_nop())/binary>>;
28362826
4 -> <<NewMoveInstr/binary, (jit_riscv32_asm:nop())/binary>>;
28372827
6 -> <<NewMoveInstr/binary, (jit_riscv32_asm:c_nop())/binary>>;
28382828
8 -> NewMoveInstr

libs/jit/src/jit_riscv32_asm.erl

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,20 +1001,30 @@ li(Rd, Imm) when Rd =/= zero, Imm >= -32, Imm =< 31 ->
10011001
li(Rd, Imm) when Imm >= -2048, Imm =< 2047 ->
10021002
% Small immediate: addi rd, x0, imm
10031003
addi(Rd, zero, Imm);
1004-
li(Rd, Imm) when Imm >= -16#80000000, Imm =< 16#7FFFFFFF ->
1005-
% Large immediate: lui + addi
1004+
% Handle unsigned values that represent small signed values (e.g., 0xFFFFFFFF = -1)
1005+
li(Rd, Imm) when Imm > 16#7FFFFFFF, Imm - 16#100000000 >= -2048 ->
1006+
% This unsigned value fits in 12-bit signed range when normalized
1007+
addi(Rd, zero, Imm - 16#100000000);
1008+
li(Rd, Imm) when Imm >= -16#80000000, Imm =< 16#FFFFFFFF ->
1009+
% Large immediate: lui or lui + addi
10061010
% Split into upper 20 bits and lower 12 bits
10071011
% Need to account for sign extension of lower 12 bits
1008-
Lower = Imm band 16#FFF,
1012+
% Work with unsigned values to avoid issues with arithmetic right shift
1013+
UnsignedImm = if
1014+
Imm < 0 -> Imm + 16#100000000;
1015+
true -> Imm
1016+
end,
1017+
Lower = UnsignedImm band 16#FFF,
10091018
% If lower 12 bits has sign bit set, we need to add 1 to upper
1019+
% because addi will sign-extend the immediate
10101020
UpperRaw =
10111021
if
10121022
Lower >= 16#800 ->
1013-
(Imm bsr 12) + 1;
1023+
(UnsignedImm bsr 12) + 1;
10141024
true ->
1015-
Imm bsr 12
1025+
UnsignedImm bsr 12
10161026
end,
1017-
% Mask to 20 bits first, then sign extend if needed
1027+
% Mask to 20 bits first, then sign extend if needed for lui instruction
10181028
UpperMasked = UpperRaw band 16#FFFFF,
10191029
Upper =
10201030
if
@@ -1026,17 +1036,24 @@ li(Rd, Imm) when Imm >= -16#80000000, Imm =< 16#7FFFFFFF ->
10261036
% Positive value
10271037
UpperMasked
10281038
end,
1029-
% Sign extend lower 12 bits
1030-
LowerSigned =
1031-
if
1032-
Lower >= 16#800 -> Lower - 16#1000;
1033-
true -> Lower
1034-
end,
1039+
% Only emit addi if lower bits are non-zero
10351040
LuiInstr = lui(Rd, Upper),
1036-
AddiInstr = addi(Rd, Rd, LowerSigned),
1037-
<<LuiInstr/binary, AddiInstr/binary>>;
1041+
if
1042+
Lower =:= 0 ->
1043+
% Just lui is sufficient when lower 12 bits are zero
1044+
LuiInstr;
1045+
true ->
1046+
% Sign extend lower 12 bits
1047+
LowerSigned =
1048+
if
1049+
Lower >= 16#800 -> Lower - 16#1000;
1050+
true -> Lower
1051+
end,
1052+
AddiInstr = addi(Rd, Rd, LowerSigned),
1053+
<<LuiInstr/binary, AddiInstr/binary>>
1054+
end;
10381055
li(_Rd, Imm) ->
1039-
error({immediate_out_of_range, Imm, -16#80000000, 16#7FFFFFFF}).
1056+
error({immediate_out_of_range, Imm, -16#80000000, 16#FFFFFFFF}).
10401057

10411058
%% MV - Move (copy register)
10421059
%% Expands to: addi rd, rs, 0 or c.mv rd, rs

tests/libs/jit/jit_riscv32_asm_tests.erl

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,16 @@ beq_test_() ->
326326
?_assertAsmEqual(
327327
<<16#feb50ee3:32/little>>, "beq a0, a1, .-4", jit_riscv32_asm:beq(a0, a1, -4)
328328
),
329+
% Test c.beqz (compressed reg with zero)
329330
?_assertAsmEqual(
330331
<<16#c101:16/little>>, "beq a0, zero, .", jit_riscv32_asm:beq(a0, zero, 0)
332+
),
333+
?_assertAsmEqual(
334+
<<16#c9c9:16/little>>, "beq a5, zero, .+24", jit_riscv32_asm:beq(a5, zero, 24)
335+
),
336+
% Test beq with non-compressed reg and zero (falls through to encode_b_type)
337+
?_assertAsmEqual(
338+
<<16#00030463:32/little>>, "beq t1, zero, .+8", jit_riscv32_asm:beq(t1, zero, 8)
331339
)
332340
].
333341

@@ -338,6 +346,17 @@ bne_test_() ->
338346
),
339347
?_assertAsmEqual(
340348
<<16#feb51ee3:32/little>>, "bne a0, a1, .-4", jit_riscv32_asm:bne(a0, a1, -4)
349+
),
350+
% Test c.bnez (compressed reg with zero)
351+
?_assertAsmEqual(
352+
<<16#e901:16/little>>, "bne a2, zero, .+20", jit_riscv32_asm:bne(a2, zero, 20)
353+
),
354+
?_assertAsmEqual(
355+
<<16#f3f5:16/little>>, "bne a5, zero, .-2", jit_riscv32_asm:bne(a5, zero, -2)
356+
),
357+
% Test bne with non-compressed reg and zero (falls through to encode_b_type)
358+
?_assertAsmEqual(
359+
<<16#00031463:32/little>>, "bne t1, zero, .+8", jit_riscv32_asm:bne(t1, zero, 8)
341360
)
342361
].
343362

@@ -393,6 +412,28 @@ jal_test_() ->
393412
?_assertAsmEqual(
394413
<<16#3ff5:16/little>>, "jal .-4", jit_riscv32_asm:jal(ra, -4)
395414
),
415+
% Test c.j (jal zero, offset)
416+
?_assertAsmEqual(
417+
<<16#a011:16/little>>, "j .+4", jit_riscv32_asm:jal(zero, 4)
418+
),
419+
?_assertAsmEqual(
420+
<<16#bffd:16/little>>, "j .-2", jit_riscv32_asm:jal(zero, -2)
421+
),
422+
% Test full J-type encoding (encode_j_type) with larger offsets
423+
?_assertAsmEqual(
424+
<<16#008005ef:32/little>>, "jal a1, .+8", jit_riscv32_asm:jal(a1, 8)
425+
),
426+
?_assertAsmEqual(
427+
<<16#ffdffa6f:32/little>>, "jal s4, .-4", jit_riscv32_asm:jal(s4, -4)
428+
),
429+
% Test with maximum positive offset (1048574)
430+
?_assertAsmEqual(
431+
<<16#7ffff56f:32/little>>, "jal a0, .+1048574", jit_riscv32_asm:jal(a0, 1048574)
432+
),
433+
% Test with maximum negative offset (-1048576)
434+
?_assertAsmEqual(
435+
<<16#800005ef:32/little>>, "jal a1, .-1048576", jit_riscv32_asm:jal(a1, -1048576)
436+
),
396437
?_assertAsmEqual(
397438
<<16#00000517:32/little, 16#9502:16/little>>,
398439
"auipc a0, 0\njalr a0",
@@ -439,32 +480,73 @@ nop_test_() ->
439480
?_assertAsmEqual(<<16#00000013:32/little>>, ".option norvc\nnop", jit_riscv32_asm:nop())
440481
].
441482

442-
li_small_test_() ->
483+
li_test_() ->
443484
[
444485
?_assertAsmEqual(<<16#4529:16/little>>, "li a0, 10", jit_riscv32_asm:li(a0, 10)),
445486
?_assertAsmEqual(<<16#557d:16/little>>, "li a0, -1", jit_riscv32_asm:li(a0, -1)),
446-
?_assertAsmEqual(<<16#7ff00513:32/little>>, "li a0, 2047", jit_riscv32_asm:li(a0, 2047))
447-
].
487+
?_assertAsmEqual(<<16#7ff00513:32/little>>, "li a0, 2047", jit_riscv32_asm:li(a0, 2047)),
448488

449-
li_large_test_() ->
450-
[
451489
% 0x12345 = 74565 - requires lui + addi
452490
?_assertAsmEqual(
453491
<<16#6549:16/little, 16#34550513:32/little>>,
454-
"lui a0, 0x12\naddi a0, a0, 0x345",
492+
"li a0, 0x12345",
455493
jit_riscv32_asm:li(a0, 16#12345)
456494
),
457-
% 0x80000000 = -2147483648 (minimum 32-bit signed)
458-
?_assertAsmEqual(
459-
<<16#800005b7:32/little, 16#0581:16/little>>,
460-
"lui a1, 0x80000\nc.addi a1, 0",
461-
jit_riscv32_asm:li(a1, -16#80000000)
462-
),
463495
% 0x7FFFFFFF = 2147483647 (maximum 32-bit signed)
464496
?_assertAsmEqual(
465497
<<16#80000537:32/little, 16#157d:16/little>>,
466-
"lui a0, 0x80000\naddi a0, a0, -1",
498+
"li a0, 0x7fffffff",
467499
jit_riscv32_asm:li(a0, 16#7FFFFFFF)
500+
),
501+
% 0xFFFFFFFF = 4294967295 (maximum 32-bit unsigned, interpreted as -1 signed)
502+
?_assertAsmEqual(
503+
<<16#fff00513:32/little>>,
504+
"li a0, 0xffffffff",
505+
jit_riscv32_asm:li(a0, 16#FFFFFFFF)
506+
),
507+
% Test lui-only cases (lower 12 bits are zero)
508+
% 0x80000000 = 2147483648 (unsigned), -2147483648 (signed)
509+
?_assertAsmEqual(
510+
<<16#800005b7:32/little>>,
511+
"li a1, 0x80000000",
512+
jit_riscv32_asm:li(a1, 16#80000000)
513+
),
514+
% Same value as signed negative
515+
?_assertAsmEqual(
516+
<<16#800005b7:32/little>>,
517+
"li a1, -0x80000000",
518+
jit_riscv32_asm:li(a1, -16#80000000)
519+
),
520+
% 0x100000 = 1048576 (lower 12 bits zero)
521+
?_assertAsmEqual(
522+
<<16#00100537:32/little>>,
523+
"li a0, 0x100000",
524+
jit_riscv32_asm:li(a0, 16#100000)
525+
),
526+
% Test c.lui cases (2 bytes, -32 to 31, lower 12 bits zero)
527+
% 0x1000 = 4096 (upper = 1)
528+
?_assertAsmEqual(
529+
<<16#6505:16/little>>,
530+
"li a0, 0x1000",
531+
jit_riscv32_asm:li(a0, 16#1000)
532+
),
533+
% 0x1f000 = 126976 (upper = 31, max for c.lui)
534+
?_assertAsmEqual(
535+
<<16#657d:16/little>>,
536+
"li a0, 0x1f000",
537+
jit_riscv32_asm:li(a0, 16#1f000)
538+
),
539+
% 0xfffffffffffe0000 = -131072 (upper = -32, min for c.lui)
540+
?_assertAsmEqual(
541+
<<16#7501:16/little>>,
542+
"li a0, -0x20000",
543+
jit_riscv32_asm:li(a0, -16#20000)
544+
),
545+
% 0x20000 = 131072 (upper = 32, just outside c.lui range, needs full lui)
546+
?_assertAsmEqual(
547+
<<16#00020537:32/little>>,
548+
"li a0, 0x20000",
549+
jit_riscv32_asm:li(a0, 16#20000)
468550
)
469551
].
470552

0 commit comments

Comments
 (0)