alloc_diag.ll raw
1 ; Diagnostic override for runtime.alloc - tracks allocation/free/scope stats.
2 ; Link with --override= to replace dealloc_freelist.bc's runtime.alloc/free/scopePush/scopeFreeTo.
3 ; Prints summary to stderr on exit via a destructor.
4
5 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
6 target triple = "x86_64-unknown-linux-musleabihf"
7
8 @runtime.zeroSizedAlloc = external hidden global i8
9
10 declare ptr @calloc(i64, i64)
11 declare void @free(ptr)
12 declare i64 @write(i32, ptr, i64)
13 declare void @exit(i32) noreturn
14 declare i32 @snprintf(ptr, i64, ptr, ...)
15 declare ptr @llvm.returnaddress(i32)
16
17 ; counters
18 @diag.alloc_count = internal global i64 0, align 8
19 @diag.alloc_bytes = internal global i64 0, align 8
20 @diag.free_count = internal global i64 0, align 8
21 @diag.free_bytes = internal global i64 0, align 8
22 @diag.peak_bytes = internal global i64 0, align 8
23 @diag.live_bytes = internal global i64 0, align 8
24 @diag.scope_push = internal global i64 0, align 8
25 @diag.scope_free = internal global i64 0, align 8
26 @diag.huge_count = internal global i64 0, align 8
27 @diag.huge_bytes = internal global i64 0, align 8
28
29 @diag.buf = internal global [512 x i8] zeroinitializer
30 @diag.fmt = internal constant [201 x i8] c"\0A=== ALLOC DIAG ===\0Aallocs: %lu (%lu bytes)\0Afrees: %lu (%lu bytes)\0Alive: %lu bytes\0Apeak: %lu bytes\0Ascope_push: %lu\0Ascope_free: %lu\0Ahuge(>1MB): %lu (%lu bytes)\0A===================\0A\00"
31 @diag.hugefmt = internal constant [33 x i8] c"[HUGE ALLOC] size=%lu caller=%p\0A\00"
32
33 define hidden noalias nonnull ptr @runtime.alloc(i64 %size, ptr %layout, ptr %context) #0 {
34 entry:
35 %zero = icmp eq i64 %size, 0
36 br i1 %zero, label %ret_zero, label %track
37
38 ret_zero:
39 ret ptr @runtime.zeroSizedAlloc
40
41 track:
42 ; update counters
43 %ac = load i64, ptr @diag.alloc_count, align 8
44 %ac1 = add i64 %ac, 1
45 store i64 %ac1, ptr @diag.alloc_count, align 8
46
47 %aligned = add i64 %size, 15
48 %size16 = and i64 %aligned, -16
49
50 %ab = load i64, ptr @diag.alloc_bytes, align 8
51 %ab1 = add i64 %ab, %size16
52 store i64 %ab1, ptr @diag.alloc_bytes, align 8
53
54 %lb = load i64, ptr @diag.live_bytes, align 8
55 %lb1 = add i64 %lb, %size16
56 store i64 %lb1, ptr @diag.live_bytes, align 8
57
58 %pk = load i64, ptr @diag.peak_bytes, align 8
59 %higher = icmp ugt i64 %lb1, %pk
60 br i1 %higher, label %update_peak, label %check_huge
61
62 update_peak:
63 store i64 %lb1, ptr @diag.peak_bytes, align 8
64 br label %check_huge
65
66 check_huge:
67 %is_huge = icmp ugt i64 %size16, 1048576
68 br i1 %is_huge, label %log_huge, label %do_alloc
69
70 log_huge:
71 %hc = load i64, ptr @diag.huge_count, align 8
72 store i64 0, ptr @diag.huge_count, align 8
73 %hc1 = add i64 %hc, 1
74 store i64 %hc1, ptr @diag.huge_count, align 8
75 %hb = load i64, ptr @diag.huge_bytes, align 8
76 %hb1 = add i64 %hb, %size16
77 store i64 %hb1, ptr @diag.huge_bytes, align 8
78 ; print to stderr
79 %ra = call ptr @llvm.returnaddress(i32 0)
80 %n = call i32 (ptr, i64, ptr, ...) @snprintf(
81 ptr getelementptr ([512 x i8], ptr @diag.buf, i64 0, i64 0),
82 i64 512,
83 ptr getelementptr ([33 x i8], ptr @diag.hugefmt, i64 0, i64 0),
84 i64 %size16, ptr %ra)
85 %n64 = sext i32 %n to i64
86 call i64 @write(i32 2, ptr getelementptr ([512 x i8], ptr @diag.buf, i64 0, i64 0), i64 %n64)
87 br label %do_alloc
88
89 do_alloc:
90 %total = add i64 %size16, 16
91 %base = call ptr @calloc(i64 1, i64 %total)
92 %ok = icmp ne ptr %base, null
93 br i1 %ok, label %done, label %oom
94
95 oom:
96 call void @exit(i32 134) noreturn
97 unreachable
98
99 done:
100 ; store magic + size in header for free tracking
101 store i64 -6872988504188559820, ptr %base, align 8
102 %szslot = getelementptr i8, ptr %base, i64 8
103 store i64 %size16, ptr %szslot, align 8
104 %user = getelementptr i8, ptr %base, i64 16
105 ret ptr %user
106 }
107
108 define hidden void @runtime.free(ptr %ptr, ptr %context) {
109 entry:
110 %isnil = icmp eq ptr %ptr, null
111 br i1 %isnil, label %done, label %check
112 check:
113 %base = getelementptr i8, ptr %ptr, i64 -16
114 %magic = load i64, ptr %base, align 8
115 %is_ours = icmp eq i64 %magic, -6872988504188559820
116 br i1 %is_ours, label %dofree, label %done
117 dofree:
118 ; read size from header
119 %szslot = getelementptr i8, ptr %ptr, i64 -8
120 %sz = load i64, ptr %szslot, align 8
121 ; update counters
122 %fc = load i64, ptr @diag.free_count, align 8
123 store i64 0, ptr @diag.free_count, align 8
124 %fc1 = add i64 %fc, 1
125 store i64 %fc1, ptr @diag.free_count, align 8
126 %fb = load i64, ptr @diag.free_bytes, align 8
127 %fb1 = add i64 %fb, %sz
128 store i64 %fb1, ptr @diag.free_bytes, align 8
129 %lb = load i64, ptr @diag.live_bytes, align 8
130 %lb1 = sub i64 %lb, %sz
131 store i64 %lb1, ptr @diag.live_bytes, align 8
132 ; clear magic and free
133 store i64 0, ptr %base, align 8
134 call void @free(ptr %base)
135 br label %done
136 done:
137 ret void
138 }
139
140 define hidden void @runtime.scopePush(ptr %payload, ptr %headSlot) {
141 entry:
142 %isnil = icmp eq ptr %payload, null
143 br i1 %isnil, label %done, label %push
144 push:
145 %sp = load i64, ptr @diag.scope_push, align 8
146 %sp1 = add i64 %sp, 1
147 store i64 %sp1, ptr @diag.scope_push, align 8
148 %node = call ptr @calloc(i64 1, i64 16)
149 %ok = icmp ne ptr %node, null
150 br i1 %ok, label %fill, label %done
151 fill:
152 store ptr %payload, ptr %node, align 8
153 %nextSlot = getelementptr i8, ptr %node, i64 8
154 %oldHead = load ptr, ptr %headSlot, align 8
155 store ptr %oldHead, ptr %nextSlot, align 8
156 store ptr %node, ptr %headSlot, align 8
157 br label %done
158 done:
159 ret void
160 }
161
162 define hidden ptr @runtime.scopeFreeTo(ptr %head, ptr %sentinel) {
163 entry:
164 br label %loop
165 loop:
166 %cur = phi ptr [ %head, %entry ], [ %next, %freeself ]
167 %done = icmp eq ptr %cur, %sentinel
168 br i1 %done, label %ret, label %checknull
169 checknull:
170 %isnull = icmp eq ptr %cur, null
171 br i1 %isnull, label %ret, label %freenode
172 freenode:
173 %sf = load i64, ptr @diag.scope_free, align 8
174 %sf1 = add i64 %sf, 1
175 store i64 %sf1, ptr @diag.scope_free, align 8
176 %payload = load ptr, ptr %cur, align 8
177 %nextSlot = getelementptr i8, ptr %cur, i64 8
178 %next0 = load ptr, ptr %nextSlot, align 8
179 %paynil = icmp eq ptr %payload, null
180 br i1 %paynil, label %freeself, label %freepay
181 freepay:
182 call void @runtime.free(ptr %payload, ptr undef)
183 br label %freeself
184 freeself:
185 %next = phi ptr [ %next0, %freenode ], [ %next0, %freepay ]
186 call void @free(ptr %cur)
187 br label %loop
188 ret:
189 ret ptr %sentinel
190 }
191
192 define hidden void @runtime.scopeEscapeReturn(ptr %payload, ptr %headSlot) {
193 entry:
194 %isnil = icmp eq ptr %payload, null
195 br i1 %isnil, label %done, label %start
196 start:
197 %head = load ptr, ptr %headSlot, align 8
198 br label %loop
199 loop:
200 %cur = phi ptr [ %head, %start ], [ %next, %nomatch ]
201 %curnil = icmp eq ptr %cur, null
202 br i1 %curnil, label %done, label %check
203 check:
204 %curPay = load ptr, ptr %cur, align 8
205 %match = icmp eq ptr %curPay, %payload
206 br i1 %match, label %nullify, label %nomatch
207 nullify:
208 store ptr null, ptr %cur, align 8
209 br label %done
210 nomatch:
211 %nextSlot = getelementptr i8, ptr %cur, i64 8
212 %next = load ptr, ptr %nextSlot, align 8
213 br label %loop
214 done:
215 ret void
216 }
217
218 ; print summary at exit
219 @llvm.global_ctors = appending global [0 x { i32, ptr, ptr }] zeroinitializer
220 @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @diag.report, ptr null }]
221
222 define internal void @diag.report() {
223 %ac = load i64, ptr @diag.alloc_count, align 8
224 %ab = load i64, ptr @diag.alloc_bytes, align 8
225 %fc = load i64, ptr @diag.free_count, align 8
226 %fb = load i64, ptr @diag.free_bytes, align 8
227 %lb = load i64, ptr @diag.live_bytes, align 8
228 %pk = load i64, ptr @diag.peak_bytes, align 8
229 %sp = load i64, ptr @diag.scope_push, align 8
230 %sf = load i64, ptr @diag.scope_free, align 8
231 %hc = load i64, ptr @diag.huge_count, align 8
232 %hb = load i64, ptr @diag.huge_bytes, align 8
233 %n = call i32 (ptr, i64, ptr, ...) @snprintf(
234 ptr getelementptr ([512 x i8], ptr @diag.buf, i64 0, i64 0),
235 i64 512,
236 ptr getelementptr ([201 x i8], ptr @diag.fmt, i64 0, i64 0),
237 i64 %ac, i64 %ab,
238 i64 %fc, i64 %fb,
239 i64 %lb,
240 i64 %pk,
241 i64 %sp,
242 i64 %sf,
243 i64 %hc, i64 %hb)
244 %n64 = sext i32 %n to i64
245 call i64 @write(i32 2, ptr getelementptr ([512 x i8], ptr @diag.buf, i64 0, i64 0), i64 %n64)
246 ret void
247 }
248
249 attributes #0 = { noinline nounwind }
250 attributes #1 = { nounwind }
251