Hi, just tested the clang eBPF backend. Very cool thing :)
But I've noticed that the codegen creates really odd things which I cannot explain from my understanding of the ISA. My test is attached and was compiled via: clang -O2 -target bpf -x c -c ebpf_opimizer_problems.o -o ebpf_opimizer_problems.o llvm-objdump -S ebpf_opimizer_problems.o First thing is the odd way how 8 bit loads to an uint8_t are handled (see bug1_sec): 0: bf 16 00 00 00 00 00 00 r6 = r1 1: 30 00 00 00 00 00 00 00 r0 = *(u8 *)skb[0] 2: 57 00 00 00 ff ff 00 00 r0 &= 65535 3: 95 00 00 00 00 00 00 00 exit I have no idea why the second instruction (line "2:") would be required when there was just a load of 8 bit to the 64 bit register. This isn't generated when it is stored in an uint32_t or uint64_t variable (but also when storing it in an uint16_t). This can be seen in ok1_sec, ok1_too_a_sec and ok1_too_b_sec. The other problem is the chaining of tests which results in an extra store for the temporary output register (r7, see bug2_sec): 0: bf 16 00 00 00 00 00 00 r6 = r1 1: b7 07 00 00 00 00 00 00 r7 = 0 2: 30 00 00 00 00 00 00 00 r0 = *(u8 *)skb[0] 3: 55 00 04 00 01 00 00 00 if r0 != 1 goto 4 4: 30 00 00 00 01 00 00 00 r0 = *(u8 *)skb[1] 5: b7 07 00 00 15 00 00 00 r7 = 21 6: 15 00 01 00 01 00 00 00 if r0 == 1 goto 1 7: b7 07 00 00 00 00 00 00 r7 = 0 LBB4_3: 8: bf 70 00 00 00 00 00 00 r0 = r7 9: 95 00 00 00 00 00 00 00 exit For unknown reasons, the line "6:" was changed from an JNE to an JEQ. This makes it necessary to introduce link "5:" and line "7:". It would have been better (in my eyes) to use keep it an JNE, move line "5:" after the comparison and drop line "7:". This would look like this (removed the bytecode to make it easier for me): 0: r6 = r1 1: r7 = 0 2: r0 = *(u8 *)skb[0] 3: if r0 != 1 goto 3 4: r0 = *(u8 *)skb[1] 5: if r0 != 1 goto 1 6: r7 = 21 LBB4_3: 7: r0 = r7 8: exit Or a different version (not so easily readable because it is asymmetric) would have been: 0: bf 16 00 00 00 00 00 00 r6 = r1 1: 30 00 00 00 00 00 00 00 r0 = *(u8 *)skb[0] 2: 55 00 03 00 01 00 00 00 if r0 != 1 goto 3 3: b7 07 00 00 15 00 00 00 r7 = 21 4: 30 00 00 00 01 00 00 00 r0 = *(u8 *)skb[1] 5: 15 00 01 00 01 00 00 00 if r0 == 1 goto 1 LBB4_2: 6: b7 07 00 00 00 00 00 00 r7 = 0 LBB4_3: 7: bf 70 00 00 00 00 00 00 r0 = r7 8: 95 00 00 00 00 00 00 00 exit The used version (Debian buster amd64) is: clang version 5.0.1-2 (tags/RELEASE_501/final) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin
#include <stddef.h> #include <stdint.h> struct __sk_buff; unsigned long long load_byte(void *skb, unsigned long long off) asm("llvm.bpf.load.byte"); /* FIRST eBPF optimizer "BUG" */ __attribute__((section("bug1_sec"), used)) int bug1(struct __sk_buff *skb) { uint8_t t = load_byte(skb, 0); return t; } __attribute__((section("ok1_sec"), used)) int ok1(struct __sk_buff *skb) { return load_byte(skb, 0); } __attribute__((section("ok1_too_a_sec"), used)) int ok1_too_a(struct __sk_buff *skb) { uint32_t t = load_byte(skb, 0); return t; } __attribute__((section("ok1_too_b_sec"), used)) int ok1_too_b(struct __sk_buff *skb) { uint64_t t = load_byte(skb, 0); return t; } /* SECOND eBPF optimizer "BUG" */ __attribute__((section("bug2_sec"), used)) int bug2(struct __sk_buff *skb) { uint64_t t; t = load_byte(skb, 0); if (t != 1) return 0; t = load_byte(skb, 1); if (t != 1) return 0; return 21; }
_______________________________________________ cfe-users mailing list cfe-users@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-users