Re: [PATCH 08/14] bpf/tests: Add tests for ALU operations implemented with function calls

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





On 7/29/21 2:17 PM, Johan Almbladh wrote:
On Thu, Jul 29, 2021 at 1:52 AM Yonghong Song <yhs@xxxxxx> wrote:
+             /*
+              * Register (non-)clobbering test, in the case where a 32-bit
+              * JIT implements complex ALU64 operations via function calls.
+              */
+             "INT: Register clobbering, R1 updated",
+             .u.insns_int = {
+                     BPF_ALU32_IMM(BPF_MOV, R0, 0),
+                     BPF_ALU32_IMM(BPF_MOV, R1, 123456789),
+                     BPF_ALU32_IMM(BPF_MOV, R2, 2),
+                     BPF_ALU32_IMM(BPF_MOV, R3, 3),
+                     BPF_ALU32_IMM(BPF_MOV, R4, 4),
+                     BPF_ALU32_IMM(BPF_MOV, R5, 5),
+                     BPF_ALU32_IMM(BPF_MOV, R6, 6),
+                     BPF_ALU32_IMM(BPF_MOV, R7, 7),
+                     BPF_ALU32_IMM(BPF_MOV, R8, 8),
+                     BPF_ALU32_IMM(BPF_MOV, R9, 9),
+                     BPF_ALU64_IMM(BPF_DIV, R1, 123456789),
+                     BPF_JMP_IMM(BPF_JNE, R0, 0, 10),
+                     BPF_JMP_IMM(BPF_JNE, R1, 1, 9),
+                     BPF_JMP_IMM(BPF_JNE, R2, 2, 8),
+                     BPF_JMP_IMM(BPF_JNE, R3, 3, 7),
+                     BPF_JMP_IMM(BPF_JNE, R4, 4, 6),
+                     BPF_JMP_IMM(BPF_JNE, R5, 5, 5),
+                     BPF_JMP_IMM(BPF_JNE, R6, 6, 4),
+                     BPF_JMP_IMM(BPF_JNE, R7, 7, 3),
+                     BPF_JMP_IMM(BPF_JNE, R8, 8, 2),
+                     BPF_JMP_IMM(BPF_JNE, R9, 9, 1),
+                     BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                     BPF_EXIT_INSN(),
+             },
+             INTERNAL,
+             { },
+             { { 0, 1 } }
+     },
+     {
+             "INT: Register clobbering, R2 updated",
+             .u.insns_int = {
+                     BPF_ALU32_IMM(BPF_MOV, R0, 0),
+                     BPF_ALU32_IMM(BPF_MOV, R1, 1),
+                     BPF_ALU32_IMM(BPF_MOV, R2, 2 * 123456789),
+                     BPF_ALU32_IMM(BPF_MOV, R3, 3),
+                     BPF_ALU32_IMM(BPF_MOV, R4, 4),
+                     BPF_ALU32_IMM(BPF_MOV, R5, 5),
+                     BPF_ALU32_IMM(BPF_MOV, R6, 6),
+                     BPF_ALU32_IMM(BPF_MOV, R7, 7),
+                     BPF_ALU32_IMM(BPF_MOV, R8, 8),
+                     BPF_ALU32_IMM(BPF_MOV, R9, 9),
+                     BPF_ALU64_IMM(BPF_DIV, R2, 123456789),
+                     BPF_JMP_IMM(BPF_JNE, R0, 0, 10),
+                     BPF_JMP_IMM(BPF_JNE, R1, 1, 9),
+                     BPF_JMP_IMM(BPF_JNE, R2, 2, 8),
+                     BPF_JMP_IMM(BPF_JNE, R3, 3, 7),
+                     BPF_JMP_IMM(BPF_JNE, R4, 4, 6),
+                     BPF_JMP_IMM(BPF_JNE, R5, 5, 5),
+                     BPF_JMP_IMM(BPF_JNE, R6, 6, 4),
+                     BPF_JMP_IMM(BPF_JNE, R7, 7, 3),
+                     BPF_JMP_IMM(BPF_JNE, R8, 8, 2),
+                     BPF_JMP_IMM(BPF_JNE, R9, 9, 1),
+                     BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                     BPF_EXIT_INSN(),
+             },
+             INTERNAL,
+             { },
+             { { 0, 1 } }
+     },

It looks like the above two tests, "R1 updated" and "R2 updated" should
be very similar and the only difference is one immediate is 123456789
and another is 2 * 123456789. But for generated code, they all just have
the final immediate. Could you explain what the difference in terms of
jit for the above two tests?

When a BPF_CALL instruction is executed, the eBPF assembler have
already saved any caller-saved registers that must be preserved, put
the arguments in R1-R5, and expects a return value in R0. It is just
for the JIT to emit the call.

Not so when an eBPF instruction is implemented by a function call,
like ALU64 DIV in a 32-bit JIT. In this case, the function call is
unexpected by the eBPF assembler, and must be invisible to it. Now the
JIT must take care of saving all caller-saved registers on stack, put
the operands in the right argument registers, put the return value in
the destination register, and finally restore all caller-saved
registers without overwriting the computed result.

The test checks that all other registers retain their value after such
a hidden function call. However, one register will contain the result.
In order to verify that all registers are saved and restored properly,
we must vary the destination and run it two times. It is not the
result of the operation that its tested, it is absence of possible
side effects.

I can put a more elaborate description in the comment to explain this.

Indeed, an elaborate description as comments will be great.



+     {
+             /*
+              * Test 32-bit JITs that implement complex ALU64 operations as
+              * function calls R0 = f(R1, R2), and must re-arrange operands.
+              */
[...]



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux