| ; RUN: llc -mtriple=bpfel -mcpu=v4 -filetype=asm -o - %s | FileCheck --check-prefix=ASM %s |
| ; RUN: llc -mtriple=bpfel -mcpu=v4 -filetype=obj -o %t %s |
| ; RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECTIONS %s |
| ; RUN: llvm-readelf -r %t | FileCheck --check-prefix=RELOCS %s |
| |
| ; Verify that invoke/landingpad with cleanup produces a .bpf_cleanup section |
| ; containing (call_site, landing_pad) pairs with R_BPF_64_NODYLD32 relocations. |
| ; Models a Rust BTreeMap insertion that may fail, with Drop cleanup |
| ; calling bpf_free to release allocated nodes. |
| |
| declare ptr @bpf_alloc(i64, i64) |
| declare void @bpf_free(ptr) |
| declare void @btree_insert(ptr, ptr, i64) |
| declare i32 @rust_personality(i32, i64, ptr, ptr) |
| |
| ; SECTIONS: .bpf_cleanup |
| |
| define void @stat_inc(ptr %map, ptr %key, i64 %val) personality ptr @rust_personality { |
| entry: |
| %node = call ptr @bpf_alloc(i64 256, i64 0) |
| %null = icmp eq ptr %node, null |
| br i1 %null, label %oom, label %insert |
| |
| insert: |
| invoke void @btree_insert(ptr %map, ptr %node, i64 %val) |
| to label %done unwind label %cleanup |
| |
| done: |
| ret void |
| |
| cleanup: |
| %lp = landingpad { ptr, i32 } cleanup |
| call void @bpf_free(ptr %node) |
| resume { ptr, i32 } %lp |
| |
| oom: |
| ret void |
| } |
| |
| ; ASM-LABEL: stat_inc: |
| ; ASM: r1 = 256 |
| ; ASM: call bpf_alloc |
| ; ASM: if r0 == 0 goto |
| ; Normal path: call btree_insert then exit |
| ; ASM: .Ltmp0: |
| ; ASM: call btree_insert |
| ; ASM: .Ltmp1: |
| ; ASM: exit |
| ; Cleanup landing pad: free the node, then resume unwinding |
| ; ASM: .Ltmp2: |
| ; ASM: r1 = r7 |
| ; ASM: call bpf_free |
| ; ASM: call _Unwind_Resume |
| ; .bpf_cleanup entry: [.Ltmp0, .Ltmp1) -> .Ltmp2 |
| ; ASM: .section .bpf_cleanup,"a",@progbits |
| ; ASM-NEXT: .long .Ltmp0 |
| ; ASM-NEXT: .long .Ltmp1 |
| ; ASM-NEXT: .long .Ltmp2 |
| |
| define void @two_allocs(ptr %map) personality ptr @rust_personality { |
| entry: |
| %n1 = call ptr @bpf_alloc(i64 256, i64 0) |
| invoke void @btree_insert(ptr %map, ptr %n1, i64 1) |
| to label %second unwind label %clean1 |
| |
| second: |
| %n2 = call ptr @bpf_alloc(i64 512, i64 0) |
| invoke void @btree_insert(ptr %map, ptr %n2, i64 2) |
| to label %done unwind label %clean2 |
| |
| done: |
| ret void |
| |
| clean2: |
| ; Must free both n2 and n1 |
| %lp2 = landingpad { ptr, i32 } cleanup |
| call void @bpf_free(ptr %n2) |
| call void @bpf_free(ptr %n1) |
| resume { ptr, i32 } %lp2 |
| |
| clean1: |
| ; Only n1 allocated at this point |
| %lp1 = landingpad { ptr, i32 } cleanup |
| call void @bpf_free(ptr %n1) |
| resume { ptr, i32 } %lp1 |
| } |
| |
| ; ASM-LABEL: two_allocs: |
| ; First alloc + invoke |
| ; ASM: r1 = 256 |
| ; ASM: call bpf_alloc |
| ; ASM: .Ltmp3: |
| ; ASM: call btree_insert |
| ; ASM: .Ltmp4: |
| ; Second alloc + invoke |
| ; ASM: r1 = 512 |
| ; ASM: call bpf_alloc |
| ; ASM: .Ltmp6: |
| ; ASM: call btree_insert |
| ; ASM: .Ltmp7: |
| ; ASM: exit |
| ; clean2: free n2, then fall through to free n1 |
| ; ASM: .Ltmp8: |
| ; ASM: call bpf_free |
| ; clean1 entry point, shared tail: free n1 then resume |
| ; ASM: .Ltmp5: |
| ; ASM: call bpf_free |
| ; ASM: call _Unwind_Resume |
| ; .bpf_cleanup entries: two triples |
| ; ASM: .section .bpf_cleanup,"a",@progbits |
| ; ASM-NEXT: .long .Ltmp3 |
| ; ASM-NEXT: .long .Ltmp4 |
| ; ASM-NEXT: .long .Ltmp5 |
| ; ASM-NEXT: .long .Ltmp6 |
| ; ASM-NEXT: .long .Ltmp7 |
| ; ASM-NEXT: .long .Ltmp8 |
| |
| ; Models std::panic::catch_unwind: catch the exception, free resources, |
| ; and return normally. The landing pad uses "catch ptr null" (catch-all) |
| ; instead of "cleanup", and does NOT call _Unwind_Resume — unwinding stops here. |
| |
| define i64 @with_catch_unwind(ptr %map, i64 %val) personality ptr @rust_personality { |
| entry: |
| %node = call ptr @bpf_alloc(i64 128, i64 0) |
| invoke void @btree_insert(ptr %map, ptr %node, i64 %val) |
| to label %ok unwind label %caught |
| |
| ok: |
| ret i64 0 |
| |
| caught: |
| %lp = landingpad { ptr, i32 } |
| catch ptr null |
| call void @bpf_free(ptr %node) |
| ret i64 1 |
| } |
| |
| ; ASM-LABEL: with_catch_unwind: |
| ; ASM: r1 = 128 |
| ; ASM: call bpf_alloc |
| ; ASM: .Ltmp9: |
| ; ASM: call btree_insert |
| ; ASM: .Ltmp10: |
| ; ASM: r0 = 0 |
| ; ASM: exit |
| ; Catch block: free the node, return 1 (no _Unwind_Resume) |
| ; ASM: .Ltmp11: |
| ; ASM: call bpf_free |
| ; ASM: r0 = 1 |
| ; ASM: exit |
| ; .bpf_cleanup entry for the catch |
| ; ASM: .section .bpf_cleanup,"a",@progbits |
| ; ASM-NEXT: .long .Ltmp9 |
| ; ASM-NEXT: .long .Ltmp10 |
| ; ASM-NEXT: .long .Ltmp11 |
| |
| ; .bpf_cleanup should have R_BPF_64_NODYLD32 relocations |
| ; 4 entries x 3 fields = 12 relocs |
| ; RELOCS: .rel.bpf_cleanup |
| ; RELOCS-COUNT-12: R_BPF_64_NODYLD32 {{.*}} .text |