github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/transform/testdata/gc-stackslots.ll (about) 1 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 2 target triple = "wasm32-unknown-unknown-wasm" 3 4 @runtime.stackChainStart = external global ptr 5 @someGlobal = global i8 3 6 @ptrGlobal = global ptr null 7 8 declare void @runtime.trackPointer(ptr nocapture readonly) 9 10 declare noalias nonnull ptr @runtime.alloc(i32, ptr) 11 12 ; Generic function that returns a pointer (that must be tracked). 13 define ptr @getPointer() { 14 ret ptr @someGlobal 15 } 16 17 define ptr @needsStackSlots() { 18 ; Tracked pointer. Although, in this case the value is immediately returned 19 ; so tracking it is not really necessary. 20 %ptr = call ptr @runtime.alloc(i32 4, ptr null) 21 call void @runtime.trackPointer(ptr %ptr) 22 call void @someArbitraryFunction() 23 %val = load i8, ptr @someGlobal 24 ret ptr %ptr 25 } 26 27 ; Check some edge cases of pointer tracking. 28 define ptr @needsStackSlots2() { 29 ; Only one stack slot should be created for this (but at the moment, one is 30 ; created for each call to runtime.trackPointer). 31 %ptr1 = call ptr @getPointer() 32 call void @runtime.trackPointer(ptr %ptr1) 33 call void @runtime.trackPointer(ptr %ptr1) 34 call void @runtime.trackPointer(ptr %ptr1) 35 36 ; Create a pointer that does not need to be tracked (but is tracked). 37 %ptr2 = getelementptr i8, ptr @someGlobal, i32 0 38 call void @runtime.trackPointer(ptr %ptr2) 39 40 ; Here is finally the point where an allocation happens. 41 %unused = call ptr @runtime.alloc(i32 4, ptr null) 42 call void @runtime.trackPointer(ptr %unused) 43 44 ret ptr %ptr1 45 } 46 47 ; Return a pointer from a caller. Because it doesn't allocate, no stack objects 48 ; need to be created. 49 define ptr @noAllocatingFunction() { 50 %ptr = call ptr @getPointer() 51 call void @runtime.trackPointer(ptr %ptr) 52 ret ptr %ptr 53 } 54 55 define ptr @fibNext(ptr %x, ptr %y) { 56 %x.val = load i8, ptr %x 57 %y.val = load i8, ptr %y 58 %out.val = add i8 %x.val, %y.val 59 %out.alloc = call ptr @runtime.alloc(i32 1, ptr null) 60 call void @runtime.trackPointer(ptr %out.alloc) 61 store i8 %out.val, ptr %out.alloc 62 ret ptr %out.alloc 63 } 64 65 define ptr @allocLoop() { 66 entry: 67 %entry.x = call ptr @runtime.alloc(i32 1, ptr null) 68 call void @runtime.trackPointer(ptr %entry.x) 69 %entry.y = call ptr @runtime.alloc(i32 1, ptr null) 70 call void @runtime.trackPointer(ptr %entry.y) 71 store i8 1, ptr %entry.y 72 br label %loop 73 74 loop: 75 %prev.y = phi ptr [ %entry.y, %entry ], [ %prev.x, %loop ] 76 %prev.x = phi ptr [ %entry.x, %entry ], [ %next.x, %loop ] 77 call void @runtime.trackPointer(ptr %prev.x) 78 call void @runtime.trackPointer(ptr %prev.y) 79 %next.x = call ptr @fibNext(ptr %prev.x, ptr %prev.y) 80 call void @runtime.trackPointer(ptr %next.x) 81 %next.x.val = load i8, ptr %next.x 82 %loop.done = icmp ult i8 40, %next.x.val 83 br i1 %loop.done, label %end, label %loop 84 85 end: 86 ret ptr %next.x 87 } 88 89 declare ptr @arrayAlloc() 90 91 define void @testGEPBitcast() { 92 %arr = call ptr @arrayAlloc() 93 %arr.bitcast = getelementptr [32 x i8], ptr %arr, i32 0, i32 0 94 call void @runtime.trackPointer(ptr %arr.bitcast) 95 %other = call ptr @runtime.alloc(i32 1, ptr null) 96 call void @runtime.trackPointer(ptr %other) 97 ret void 98 } 99 100 define void @someArbitraryFunction() { 101 ret void 102 } 103 104 define void @earlyPopRegression() { 105 %x.alloc = call ptr @runtime.alloc(i32 4, ptr null) 106 call void @runtime.trackPointer(ptr %x.alloc) 107 ; At this point the pass used to pop the stack chain, resulting in a potential use-after-free during allocAndSave. 108 musttail call void @allocAndSave(ptr %x.alloc) 109 ret void 110 } 111 112 define void @allocAndSave(ptr %x) { 113 %y = call ptr @runtime.alloc(i32 4, ptr null) 114 call void @runtime.trackPointer(ptr %y) 115 store ptr %y, ptr %x 116 store ptr %x, ptr @ptrGlobal 117 ret void 118 }