Skip to content

Commit 40ce601

Browse files
committed
Harden xtensa call_primitive_last and align encode_rrr with the ISA
call_primitive_last only supports args of the form [_, jit_state | _] (the windowed-ABI tail call rewrites the second argument to jit_state_tail_call). Reject every other shape in the function head, before any register allocation or stream writes, so a scratch-exhausting argument list fails with a descriptive error rather than an opaque first_avail/1 function_clause. Also rename encode_rrr's op1/op2 parameters and align the RRR comments (including SLLI) with the Xtensa ISA field order; emitted bytes are unchanged, verified by the jit_xtensa_asm round-trip tests (73) and codegen tests (145). Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent f7594af commit 40ce601

3 files changed

Lines changed: 32 additions & 15 deletions

File tree

libs/jit/src/jit_xtensa.erl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ call_primitive_last(
671671
stream = Stream0
672672
} = State0,
673673
Primitive,
674-
Args
674+
[_, jit_state | _] = Args
675675
) ->
676676
%% Xtensa windowed ABI: CALLX8 to the primitive, move its return value
677677
%% from a10 (our view of callee's a2) into our a2, then RETW to C.
@@ -716,7 +716,12 @@ call_primitive_last(
716716
used_regs = 0,
717717
regs = jit_regs:unreachable(State2#state.regs)
718718
}
719-
end.
719+
end;
720+
%% The windowed-ABI tail-call sequence rewrites the second argument
721+
%% (jit_state -> jit_state_tail_call), so reject every other shape in the
722+
%% function head before allocating registers or appending to the stream.
723+
call_primitive_last(_State, _Primitive, Args) ->
724+
error({unsupported_call_primitive_last_args, Args}).
720725

721726
%%-----------------------------------------------------------------------------
722727
%% @doc Emit a return of a value if it's not equal to ctx.

libs/jit/src/jit_xtensa_asm.erl

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,12 @@ reg_to_num(a15) -> 15.
144144
%% 24-bit Instruction Format Encoders
145145
%%=============================================================================
146146

147-
%% RRR format: op1[23:20] | op2[19:16] | r[15:12] | s[11:8] | t[7:4] | op0[3:0]
147+
%% RRR format: op2[23:20] | op1[19:16] | r[15:12] | s[11:8] | t[7:4] | op0[3:0]
148+
%% Op2/Op1 are ordered high-to-low to match the ISA field layout above; the
149+
%% emitted bytes are unchanged from before this parameter rename.
148150
-spec encode_rrr(integer(), integer(), integer(), integer(), integer(), integer()) -> binary().
149-
encode_rrr(Op0, T, S, R, Op1, Op2) ->
150-
Instr = (Op1 bsl 20) bor (Op2 bsl 16) bor (R bsl 12) bor (S bsl 8) bor (T bsl 4) bor Op0,
151+
encode_rrr(Op0, T, S, R, Op2, Op1) ->
152+
Instr = (Op2 bsl 20) bor (Op1 bsl 16) bor (R bsl 12) bor (S bsl 8) bor (T bsl 4) bor Op0,
151153
<<Instr:24/little>>.
152154

153155
%% RRI8 format: imm8[23:16] | r[15:12] | s[11:8] | t[7:4] | op0[3:0]
@@ -196,31 +198,31 @@ encode_rrrn(Op0, T, S, R) ->
196198
%%=============================================================================
197199

198200
%% ADD: AR[r] = AR[s] + AR[t]
199-
%% op0=0, op1=8, op2=0
201+
%% op0=0, op1=0, op2=8
200202
-spec add(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
201203
add(Ar, As, At) ->
202204
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#8, 0).
203205

204206
%% SUB: AR[r] = AR[s] - AR[t]
205-
%% op0=0, op1=12, op2=0
207+
%% op0=0, op1=0, op2=12
206208
-spec sub(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
207209
sub(Ar, As, At) ->
208210
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#C, 0).
209211

210212
%% AND: AR[r] = AR[s] & AR[t]
211-
%% op0=0, op1=1, op2=0
213+
%% op0=0, op1=0, op2=1
212214
-spec and_(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
213215
and_(Ar, As, At) ->
214216
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#1, 0).
215217

216218
%% OR: AR[r] = AR[s] | AR[t]
217-
%% op0=0, op1=2, op2=0
219+
%% op0=0, op1=0, op2=2
218220
-spec or_(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
219221
or_(Ar, As, At) ->
220222
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#2, 0).
221223

222224
%% XOR: AR[r] = AR[s] ^ AR[t]
223-
%% op0=0, op1=3, op2=0
225+
%% op0=0, op1=0, op2=3
224226
-spec xor_(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
225227
xor_(Ar, As, At) ->
226228
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#3, 0).
@@ -243,14 +245,14 @@ srl(Ar, _As, At) ->
243245
encode_rrr(0, reg_to_num(At), 0, reg_to_num(Ar), 16#9, 16#1).
244246

245247
%% SSR: Set SAR for right shift. SAR = AR[s][4:0]
246-
%% op0=0, op1=4, op2=0, r=0, t=0
248+
%% op0=0, op1=0, op2=4, r=0, t=0
247249
-spec ssr(xtensa_register()) -> binary().
248250
ssr(As) ->
249251
encode_rrr(0, 0, reg_to_num(As), 0, 16#4, 0).
250252

251253
%% SLLI: AR[r] = AR[s] << sa (1..31)
252254
%% op0=0, RRR format with shift amount encoded as (32 - sa).
253-
%% The encoded value split: sa_enc[4] at bits[23:20], op=1 at bits[19:16],
255+
%% The encoded value split: sa_enc[4] at bits[23:20], op1=1 at bits[19:16],
254256
%% r at bits[15:12], s at bits[11:8], sa_enc[3:0] at bits[7:4].
255257
-spec slli(xtensa_register(), xtensa_register(), 1..31) -> binary().
256258
slli(Ar, As, Sa) when Sa >= 1, Sa =< 31 ->
@@ -284,19 +286,19 @@ srai(Ar, At, Sa) when Sa >= 0, Sa =< 31 ->
284286
%%=============================================================================
285287

286288
%% MULL: AR[r] = AR[s] * AR[t] (low 32 bits)
287-
%% op0=0, op1=8, op2=2
289+
%% op0=0, op1=2, op2=8
288290
-spec mull(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
289291
mull(Ar, As, At) ->
290292
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#8, 16#2).
291293

292294
%% QUOS: AR[r] = AR[s] / AR[t] (signed)
293-
%% op0=0, op1=13, op2=2
295+
%% op0=0, op1=2, op2=13
294296
-spec quos(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
295297
quos(Ar, As, At) ->
296298
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#D, 16#2).
297299

298300
%% REMS: AR[r] = AR[s] % AR[t] (signed)
299-
%% op0=0, op1=15, op2=2
301+
%% op0=0, op1=2, op2=15
300302
-spec rems(xtensa_register(), xtensa_register(), xtensa_register()) -> binary().
301303
rems(Ar, As, At) ->
302304
encode_rrr(0, reg_to_num(At), reg_to_num(As), reg_to_num(Ar), 16#F, 16#2).

tests/libs/jit/jit_xtensa_tests.erl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,16 @@ call_primitive_last_test() ->
290290
>>,
291291
?assertStream(xtensa, Dump, Stream).
292292

293+
% An argument list that exhausts the scratch registers must still be rejected
294+
% with a descriptive error, not an opaque first_avail/1 function_clause.
295+
call_primitive_last_unsupported_args_test() ->
296+
State = ?BACKEND:new(?JIT_VARIANT_PIC, jit_stream_binary, jit_stream_binary:new(0)),
297+
Args = [ctx, a9, a7, a6, a5, 0],
298+
?assertError(
299+
{unsupported_call_primitive_last_args, Args},
300+
?BACKEND:call_primitive_last(State, 0, Args)
301+
).
302+
293303
shift_right_test() ->
294304
State0 = ?BACKEND:new(?JIT_VARIANT_PIC, jit_stream_binary, jit_stream_binary:new(0)),
295305
{State1, RegA} = ?BACKEND:move_to_native_register(State0, {x_reg, 0}),

0 commit comments

Comments
 (0)