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