Index: llvm/lib/Transforms/Scalar/LICM.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LICM.cpp +++ llvm/lib/Transforms/Scalar/LICM.cpp @@ -1219,6 +1219,14 @@ if (CI->isConvergent()) return false; + // FIXME: We don't handle the semantics of thread local well. So that the + // address of thread locals are fake constants in coroutines. So We forbid + // to treat onlyReadsMemory call in coroutines as constants now. Note that + // it is possible to hide a thread local access in a onlyReadsMemory call. + // Remove this check after we handle the semantics of thread locals well. + if (CI->onlyReadsMemory() && CI->getFunction()->isPresplitCoroutine()) + return false; + using namespace PatternMatch; if (match(CI, m_Intrinsic())) // Assumes don't actually alias anything or throw Index: llvm/test/Transforms/LICM/sink-with-coroutine.ll =================================================================== --- llvm/test/Transforms/LICM/sink-with-coroutine.ll +++ llvm/test/Transforms/LICM/sink-with-coroutine.ll @@ -48,5 +48,63 @@ ret i64 0 } +@tls = thread_local global i32 0 + +define i64 @hoist_threadlocal() presplitcoroutine { +; CHECK-LABEL: @hoist_threadlocal +; CHECK: loop.preheader: +; CHECK-NEXT: br label %for.body +; CHECK: for.body: +; CHECK-NEXT: %i = phi i64 +; CHECK-NEXT: %i.next = add i64 %i +; CHECK-NEXT: %thread_local.0 = call ptr @llvm.threadlocal.address +; CHECK-NEXT: %readonly.0 = call ptr @readonly_funcs() +; CHECK-NEXT: %suspend = call i8 @llvm.coro.suspend +; CHECK-NEXT: switch i8 %suspend, label %exit +; CHECK-NEXT: i8 0, label %await.ready +; CHECK: await.ready: +; CHECK-NEXT: %thread_local.1 = call ptr @llvm.threadlocal.address +; CHECK-NEXT: %readonly.1 = call ptr @readonly_funcs() +entry: + %p = alloca i64 + br label %loop.preheader + +loop.preheader: + br label %for.body + +for.body: + %i = phi i64 [ 0, %loop.preheader ], [ %i.next, %loop.end ] + %i.next = add i64 %i, 1 + %thread_local.0 = call ptr @llvm.threadlocal.address(ptr @tls) + %readonly.0 = call ptr @readonly_funcs() + %suspend = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %suspend, label %exit [ + i8 0, label %await.ready + ] + +await.ready: + %thread_local.1 = call ptr @llvm.threadlocal.address(ptr @tls) + %readonly.1 = call ptr @readonly_funcs() + %cmp.0 = icmp eq ptr %thread_local.0, %thread_local.1 + %cmp.1 = icmp eq ptr %readonly.0, %readonly.1 + %cmp = and i1 %cmp.0, %cmp.1 + br i1 %cmp, label %not.reachable, label %loop.end + +not.reachable: + call void @not.reachable() + br label %loop.end + +loop.end: + %loop.end.cond = icmp ugt i64 %i.next, 5 + br i1 %cmp, label %exit, label %for.body + +exit: + %res = call i1 @llvm.coro.end(ptr null, i1 false) + ret i64 0 +} + declare i8 @llvm.coro.suspend(token, i1) declare i1 @llvm.coro.end(ptr, i1) +declare nonnull ptr @readonly_funcs() readonly +declare nonnull ptr @llvm.threadlocal.address(ptr nonnull) nounwind readnone willreturn +declare void @not.reachable()