Compiler projects using llvm
; RUN: llc -mtriple=x86_64-unknown-unknown -O2 --relocation-model=pic --tls-load-hoist=true --stop-after=tlshoist -o - %s | FileCheck %s
; RUN: llc -mtriple=x86_64-unknown-unknown -O2 --relocation-model=pic --stop-after=tlshoist -o - %s | FileCheck %s

; This test come from compiling clang/test/CodeGen/intel/tls_loads.cpp with:
; (clang tls_loads.cpp -fPIC -ftls-model=global-dynamic -O2 -S -emit-llvm)

; // Variable declaration and definition:
; thread_local int thl_x;
; thread_local int thl_x2;
;
; struct SS {
;   char thl_c;
;   int num;
; };
;
; int gfunc();
; int gfunc2(int);

; // First function (@_Z2f1i):
; int f1(int c) {
;   while (c)
;     c++;
;
;   int *px = &thl_x;
;   c -= gfunc();
;
;   while(c++) {
;     c = gfunc();
;     while (c--)
;       *px += gfunc2(thl_x2);
;   }
;   return *px;
; }

$_ZTW5thl_x = comdat any

$_ZTW6thl_x2 = comdat any

@thl_x = thread_local global i32 0, align 4
@thl_x2 = thread_local global i32 0, align 4
@_ZZ2f2iE2st.0 = internal thread_local unnamed_addr global i8 0, align 4
@_ZZ2f2iE2st.1 = internal thread_local unnamed_addr global i32 0, align 4

; Function Attrs: mustprogress uwtable
define noundef i32 @_Z2f1i(i32 noundef %c) local_unnamed_addr #0 {
; CHECK-LABEL: _Z2f1i
; CHECK:      entry:
; CHECK-NEXT:   %call = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %phi.cmp = icmp eq i32 %call, 0
; CHECK-NEXT:   %tls_bitcast1 = bitcast i32* @thl_x to i32*
; CHECK-NEXT:   br i1 %phi.cmp, label %while.end11, label %while.body4.preheader

; CHECK:      while.body4.preheader:
; CHECK-NEXT:   %tls_bitcast = bitcast i32* @thl_x2 to i32*
; CHECK-NEXT:   br label %while.body4

; CHECK:      while.body4:
; CHECK-NEXT:   %call5 = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %tobool7.not18 = icmp eq i32 %call5, 0
; CHECK-NEXT:   br i1 %tobool7.not18, label %while.body4.backedge, label %while.body8.preheader

; CHECK:      while.body8.preheader:
; CHECK-NEXT:   br label %while.body8

; CHECK:      while.body4.backedge.loopexit:
; CHECK-NEXT:   br label %while.body4.backedge

; CHECK:      while.body4.backedge:
; CHECK-NEXT:   br label %while.body4, !llvm.loop !4

; CHECK:      while.body8:
; CHECK-NEXT:   %c.addr.219 = phi i32 [ %dec, %while.body8 ], [ %call5, %while.body8.preheader ]
; CHECK-NEXT:   %dec = add i32 %c.addr.219, -1
; CHECK-NEXT:   %0 = load i32, i32* %tls_bitcast, align 4
; CHECK-NEXT:   %call9 = tail call noundef i32 @_Z6gfunc2i(i32 noundef %0)
; CHECK-NEXT:   %1 = load i32, i32* %tls_bitcast1, align 4
; CHECK-NEXT:   %add = add nsw i32 %1, %call9
; CHECK-NEXT:   store i32 %add, i32* %tls_bitcast1, align 4
; CHECK-NEXT:   %tobool7.not = icmp eq i32 %dec, 0
; CHECK-NEXT:   br i1 %tobool7.not, label %while.body4.backedge.loopexit, label %while.body8, !llvm.loop !4

; CHECK:      while.end11:
; CHECK-NEXT:   %2 = load i32, i32* %tls_bitcast1, align 4
; CHECK-NEXT:   ret i32 %2

entry:
  %call = tail call noundef i32 @_Z5gfuncv()
  %phi.cmp = icmp eq i32 %call, 0
  br i1 %phi.cmp, label %while.end11, label %while.body4

while.body4:                                      ; preds = %entry, %while.body4.backedge
  %call5 = tail call noundef i32 @_Z5gfuncv()
  %tobool7.not18 = icmp eq i32 %call5, 0
  br i1 %tobool7.not18, label %while.body4.backedge, label %while.body8

while.body4.backedge:                             ; preds = %while.body8, %while.body4
  br label %while.body4, !llvm.loop !4

while.body8:                                      ; preds = %while.body4, %while.body8
  %c.addr.219 = phi i32 [ %dec, %while.body8 ], [ %call5, %while.body4 ]
  %dec = add nsw i32 %c.addr.219, -1
  %0 = load i32, i32* @thl_x2, align 4
  %call9 = tail call noundef i32 @_Z6gfunc2i(i32 noundef %0)
  %1 = load i32, i32* @thl_x, align 4
  %add = add nsw i32 %1, %call9
  store i32 %add, i32* @thl_x, align 4
  %tobool7.not = icmp eq i32 %dec, 0
  br i1 %tobool7.not, label %while.body4.backedge, label %while.body8, !llvm.loop !4

while.end11:                                      ; preds = %entry
  %2 = load i32, i32* @thl_x, align 4
  ret i32 %2
}

; // Sencond function (@_Z2f2i):
; int f2(int c) {
;   thread_local struct SS st;
;   c += gfunc();
;   while (c--) {
;     thl_x += gfunc();
;     st.thl_c += (char)gfunc();
;     st.num += gfunc();
;   }
;   return thl_x;
; }
declare noundef i32 @_Z5gfuncv() local_unnamed_addr #1

declare noundef i32 @_Z6gfunc2i(i32 noundef) local_unnamed_addr #1

; Function Attrs: mustprogress uwtable
define noundef i32 @_Z2f2i(i32 noundef %c) local_unnamed_addr #0 {
; CHECK-LABEL: _Z2f2i
; CHECK:      entry:
; CHECK-NEXT:   %call = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %add = add nsw i32 %call, %c
; CHECK-NEXT:   %tobool.not12 = icmp eq i32 %add, 0
; CHECK-NEXT:   %tls_bitcast = bitcast i32* @thl_x to i32*
; CHECK-NEXT:   br i1 %tobool.not12, label %while.end, label %while.body.preheader

; CHECK:      while.body.preheader:
; CHECK-NEXT:   %tls_bitcast1 = bitcast i8* @_ZZ2f2iE2st.0 to i8*
; CHECK-NEXT:   %tls_bitcast2 = bitcast i32* @_ZZ2f2iE2st.1 to i32*
; CHECK-NEXT:   br label %while.body

; CHECK:      while.body:
; CHECK-NEXT:   %c.addr.013 = phi i32 [ %dec, %while.body ], [ %add, %while.body.preheader ]
; CHECK-NEXT:   %dec = add i32 %c.addr.013, -1
; CHECK-NEXT:   %call1 = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %0 = load i32, i32* %tls_bitcast, align 4
; CHECK-NEXT:   %add2 = add nsw i32 %0, %call1
; CHECK-NEXT:   store i32 %add2, i32* %tls_bitcast, align 4
; CHECK-NEXT:   %call3 = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %1 = load i8, i8* %tls_bitcast1, align 4
; CHECK-NEXT:   %2 = trunc i32 %call3 to i8
; CHECK-NEXT:   %conv7 = add i8 %1, %2
; CHECK-NEXT:   store i8 %conv7, i8* %tls_bitcast1, align 4
; CHECK-NEXT:   %call8 = tail call noundef i32 @_Z5gfuncv()
; CHECK-NEXT:   %3 = load i32, i32* %tls_bitcast2, align 4
; CHECK-NEXT:   %add9 = add nsw i32 %3, %call8
; CHECK-NEXT:   store i32 %add9, i32* %tls_bitcast2, align 4
; CHECK-NEXT:   %tobool.not = icmp eq i32 %dec, 0
; CHECK-NEXT:   br i1 %tobool.not, label %while.end.loopexit, label %while.body

; CHECK:      while.end.loopexit:
; CHECK-NEXT:   br label %while.end

; CHECK:      while.end:
; CHECK-NEXT:   %4 = load i32, i32* %tls_bitcast, align 4
; CHECK-NEXT:   ret i32 %4
entry:
  %call = tail call noundef i32 @_Z5gfuncv()
  %add = add nsw i32 %call, %c
  %tobool.not12 = icmp eq i32 %add, 0
  br i1 %tobool.not12, label %while.end, label %while.body

while.body:                                       ; preds = %entry, %while.body
  %c.addr.013 = phi i32 [ %dec, %while.body ], [ %add, %entry ]
  %dec = add nsw i32 %c.addr.013, -1
  %call1 = tail call noundef i32 @_Z5gfuncv()
  %0 = load i32, i32* @thl_x, align 4
  %add2 = add nsw i32 %0, %call1
  store i32 %add2, i32* @thl_x, align 4
  %call3 = tail call noundef i32 @_Z5gfuncv()
  %1 = load i8, i8* @_ZZ2f2iE2st.0, align 4
  %2 = trunc i32 %call3 to i8
  %conv7 = add i8 %1, %2
  store i8 %conv7, i8* @_ZZ2f2iE2st.0, align 4
  %call8 = tail call noundef i32 @_Z5gfuncv()
  %3 = load i32, i32* @_ZZ2f2iE2st.1, align 4
  %add9 = add nsw i32 %3, %call8
  store i32 %add9, i32* @_ZZ2f2iE2st.1, align 4
  %tobool.not = icmp eq i32 %dec, 0
  br i1 %tobool.not, label %while.end, label %while.body

while.end:                                        ; preds = %while.body, %entry
  %4 = load i32, i32* @thl_x, align 4
  ret i32 %4
}

; // Third function (@_Z2f3i):
; int f3(int c) {
;   int *px = &thl_x;
;   gfunc2(*px);
;   gfunc2(*px);
;   return 1;
; }

; Function Attrs: mustprogress uwtable
define noundef i32 @_Z2f3i(i32 noundef %c) local_unnamed_addr #0 {
; CHECK-LABEL: _Z2f3i
; CHECK:      entry:
; CHECK-NEXT:   %tls_bitcast = bitcast i32* @thl_x to i32*
; CHECK-NEXT:   %0 = load i32, i32* %tls_bitcast, align 4
; CHECK-NEXT:   %call = tail call noundef i32 @_Z6gfunc2i(i32 noundef %0)
; CHECK-NEXT:   %1 = load i32, i32* %tls_bitcast, align 4
; CHECK-NEXT:   %call1 = tail call noundef i32 @_Z6gfunc2i(i32 noundef %1)
; CHECK-NEXT:   ret i32 1
entry:
  %0 = load i32, i32* @thl_x, align 4
  %call = tail call noundef i32 @_Z6gfunc2i(i32 noundef %0)
  %1 = load i32, i32* @thl_x, align 4
  %call1 = tail call noundef i32 @_Z6gfunc2i(i32 noundef %1)
  ret i32 1
}

; Function Attrs: uwtable
define weak_odr hidden noundef i32* @_ZTW5thl_x() local_unnamed_addr #2 comdat {
  ret i32* @thl_x
}

; Function Attrs: uwtable
define weak_odr hidden noundef i32* @_ZTW6thl_x2() local_unnamed_addr #2 comdat {
  ret i32* @thl_x2
}

attributes #0 = { mustprogress uwtable "tls-load-hoist" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { uwtable "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 2}
!3 = !{!"clang version 15.0.0"}
!4 = distinct !{!4, !5}
!5 = !{!"llvm.loop.mustprogress"}