| ; 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 |