Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -2591,7 +2591,8 @@ const Expr *E, const VarDecl *VD) { QualType T = E->getType(); - // If it's thread_local, emit a call to its wrapper function instead. + // If it's a dynamic thread_local, and the ABI requires a wrapper function, + // emit a call to its wrapper function instead. if (VD->getTLSKind() == VarDecl::TLS_Dynamic && CGF.CGM.getCXXABI().usesThreadWrapperFunction(VD)) return CGF.CGM.getCXXABI().EmitThreadLocalVarDeclLValue(CGF, VD, T); @@ -2603,18 +2604,32 @@ return CGF.MakeAddrLValue(Addr, T, AlignmentSource::Decl); } + bool ShouldEmitPrivate = CGF.getLangOpts().OpenMP && + !CGF.getLangOpts().OpenMPSimd && + VD->hasAttr(); + llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD); llvm::Type *RealVarTy = CGF.getTypes().ConvertTypeForMem(VD->getType()); V = EmitBitCastOfLValueToProperType(CGF, V, RealVarTy); + + // Previously, the optimizer could assume the address of a TLS variable is + // same in the same function. The assumption is broken now after we introduced + // coroutines. Mark the TLS variable with llvm.coro.maychange intrinsic to + // block the optimizations before we split coroutines. After the coroutine get + // splitted, the llvm.coro.maychange intrinsics would be removed. Then the + // compiler is free to optimize them. + if (VD->getTLSKind() != VarDecl::TLS_None && !ShouldEmitPrivate && + CGF.isCoroutine()) + V = CGF.Builder.CreateCoroMayChange(V); + CharUnits Alignment = CGF.getContext().getDeclAlign(VD); Address Addr(V, RealVarTy, Alignment); // Emit reference to the private copy of the variable if it is an OpenMP // threadprivate variable. - if (CGF.getLangOpts().OpenMP && !CGF.getLangOpts().OpenMPSimd && - VD->hasAttr()) { + if (ShouldEmitPrivate) return EmitThreadPrivateVarDeclLValue(CGF, VD, T, Addr, RealVarTy, E->getExprLoc()); - } + LValue LV = VD->getType()->isReferenceType() ? CGF.EmitLoadOfReferenceLValue(Addr, VD->getType(), AlignmentSource::Decl) : Index: clang/test/CodeGenCXX/const-init-cxx2a.cpp =================================================================== --- clang/test/CodeGenCXX/const-init-cxx2a.cpp +++ clang/test/CodeGenCXX/const-init-cxx2a.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a -disable-llvm-passes | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a -// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit +// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a -disable-llvm-passes | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit // CHECK: @a ={{.*}} global i32 123, int a = (delete new int, 123); Index: clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp =================================================================== --- clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp +++ clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-linux-gnu -std=c++2a %s -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s -// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-apple-darwin12 -std=c++2a %s -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s +// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-linux-gnu -std=c++2a %s -emit-llvm -o - -disable-llvm-passes | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s +// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-apple-darwin12 -std=c++2a %s -emit-llvm -o - -disable-llvm-passes | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s // Check variable definitions/declarations. Note that on Darwin, typically the // variable's symbol is marked internal, and only the _ZTW function is @@ -31,7 +31,8 @@ // CHECK-LABEL: define{{.*}} i32 @_Z5get_bv() // CHECK-NOT: call -// CHECK: load i32, i32* @b +// CHECK: %0 = call i32* @llvm.coro.maychange.p0i32(i32* @b) +// CHECK: load i32, i32* %0 // CHECK-NOT: call // CHECK: } int get_b() { return b; } @@ -51,8 +52,8 @@ // DARWIN-LABEL: define cxx_fast_tlscc {{.*}} @_ZTW1c() // LINUX-LABEL: define weak_odr {{.*}} @_ZTW1c() // CHECK-NOT: br i1 -// CHECK-NOT: call -// CHECK: ret i32* @c +// CHECK: %[[C:[0-9]+]] = call i32* @llvm.coro.maychange.p0i32(i32* @c) +// CHECK-NEXT: ret i32* %[[C]] // CHECK: } thread_local int c = 0; Index: clang/test/CodeGenCoroutines/coro-tls.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCoroutines/coro-tls.cpp @@ -0,0 +1,59 @@ +// This tests that +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O3 -emit-llvm %s -o - | FileCheck %s + +#include "Inputs/coroutine.h" + +struct awaitable { + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<> h); + void await_resume() {} +}; +awaitable switch_to_new_thread(); + +struct task { + struct promise_type { + task get_return_object() { return {}; } + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; +}; + +thread_local int tls_variable = 0; + +bool non_coroutine() { + auto *i = &tls_variable; + auto *j = &tls_variable; + return i == j; +} + +// CHECK-LABEL: @_Z13non_coroutinev() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i1 true + +void check(int *i, int *j); + +task resuming_on_new_thread() { + auto *i = &tls_variable; + co_await switch_to_new_thread(); + auto *j = &tls_variable; + if (i != j) + check(i, j); +} + +// CHECK-LABEL: define internal fastcc void @_Z22resuming_on_new_threadv.resume +// CHECK: %[[RELOAD:.+]] = load ptr, ptr %[[RELOAD_ADDR:.+reload.addr.*]] +// CHECK: %[[CMP:.+]] = icmp eq ptr %[[RELOAD]], @tls_variable +// CHECK: tail call void @_Z5checkPiS_({{.*}}%[[RELOAD]], {{.*}}@tls_variable) + +task resuming_on_new_thread2() { + co_await switch_to_new_thread(); + auto *j = &tls_variable; + auto *i = &tls_variable; + if (i != j) + check(i, j); +} + +// CHECK-LABEL: _Z23resuming_on_new_thread2v.resume +// CHECK-NOT: call void @_Z5checkPiS_ Index: llvm/docs/Coroutines.rst =================================================================== --- llvm/docs/Coroutines.rst +++ llvm/docs/Coroutines.rst @@ -1690,6 +1690,39 @@ In a yield-once coroutine, it is undefined behavior if the coroutine executes a call to ``llvm.coro.suspend.retcon`` after resuming in any way. +Coroutine Helper Intrinsics +------------------------------ +Intrinsics described in this section are used as a helper to show +the changed properties after we introduced coroutines. + +.. _coro.maychange: + +'llvm.coro.maychange' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + declare ptr @llvm.coro.maychange(ptr) + +Overview: +""""""""" + +The '``llvm.coro.maychange``' intrinsic refers to the value which may +change in coroutine but never change in normal functions. A typicall example +is the address of the TLS variables. The addresses of TLS variables are +thought to be constant in one function. But it is not true in coroutines +due to a coroutine may resume in another thread. + +Arguments: +"""""""""" + +None + +Semantics: +"""""""""" + +The `llvm.coro.maychange` intrinsic would be replaced with its argument +after we lowered all the coroutines. + Coroutine Transformation Passes =============================== CoroEarly Index: llvm/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/include/llvm/IR/IRBuilder.h +++ llvm/include/llvm/IR/IRBuilder.h @@ -738,6 +738,9 @@ /// If the pointer isn't i8* it will be converted. CallInst *CreateInvariantStart(Value *Ptr, ConstantInt *Size = nullptr); + /// Create a call to coro.may_change intrinsic. + CallInst *CreateCoroMayChange(Value *Ptr); + /// Create a call to Masked Load intrinsic CallInst *CreateMaskedLoad(Type *Ty, Value *Ptr, Align Alignment, Value *Mask, Value *PassThru = nullptr, const Twine &Name = ""); Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -1324,6 +1324,9 @@ ReadOnly>, NoCapture>]>; +// Coroutine Lowering Intrinsics to block optimizations. +def int_coro_maychange : Intrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>]>; + ///===-------------------------- Other Intrinsics --------------------------===// // def int_trap : Intrinsic<[], [], [IntrNoReturn, IntrCold]>, Index: llvm/lib/IR/IRBuilder.cpp =================================================================== --- llvm/lib/IR/IRBuilder.cpp +++ llvm/lib/IR/IRBuilder.cpp @@ -499,6 +499,12 @@ return createCallHelper(TheFn, Ops, this); } +CallInst *IRBuilderBase::CreateCoroMayChange(Value *Ptr) { + Type *Ty = Ptr->getType(); + assert(Ty->isPointerTy() && "llvm.coro.maychange is allowed for pointer.\n"); + return CreateIntrinsic(llvm::Intrinsic::coro_maychange, {Ty}, {Ptr}); +} + CallInst * IRBuilderBase::CreateAssumption(Value *Cond, ArrayRef OpBundles) { Index: llvm/lib/Transforms/Coroutines/CoroCleanup.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/CoroCleanup.cpp +++ llvm/lib/Transforms/Coroutines/CoroCleanup.cpp @@ -93,6 +93,9 @@ } else continue; break; + case Intrinsic::coro_maychange: + II->replaceAllUsesWith(II->getOperand(0)); + break; case Intrinsic::coro_async_size_replace: auto *Target = cast( cast(II->getArgOperand(0)->stripPointerCasts()) @@ -111,6 +114,7 @@ Target->replaceAllUsesWith(NewFuncPtrStruct); break; } + II->eraseFromParent(); Changed = true; } @@ -129,7 +133,7 @@ M, {"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.subfn.addr", "llvm.coro.free", "llvm.coro.id", "llvm.coro.id.retcon", "llvm.coro.id.retcon.once", "llvm.coro.async.size.replace", - "llvm.coro.async.resume"}); + "llvm.coro.async.resume", "llvm.coro.maychange"}); } PreservedAnalyses CoroCleanupPass::run(Function &F, Index: llvm/lib/Transforms/Coroutines/Coroutines.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -88,6 +88,7 @@ "llvm.coro.id.async", "llvm.coro.id.retcon", "llvm.coro.id.retcon.once", + "llvm.coro.maychange", "llvm.coro.noop", "llvm.coro.prepare.async", "llvm.coro.prepare.retcon", Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -2124,6 +2124,17 @@ for (Function::iterator BB = FirstNewBlock, E = Caller->end(); BB != E; ++BB) { for (Instruction &I : llvm::make_early_inc_range(*BB)) { + if (Caller->isPresplitCoroutine()) { + for (Use &U : I.operands()) + if (auto *GV = dyn_cast(U.get())) + if (GV->isThreadLocal()) { + auto *MaychangeFn = Intrinsic::getDeclaration(Caller->getParent(), + Intrinsic::coro_maychange); + // FIMXE: Handle PHI Node. + U.set(CallInst::Create(MaychangeFn, {GV}, GV->getName(), &I)); + } + } + CallInst *CI = dyn_cast(&I); if (!CI) continue; Index: llvm/test/Transforms/Coroutines/coro-TLS-01.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-TLS-01.ll @@ -0,0 +1,69 @@ +; Tests that the TLS variables which cross suspend points wouldn't be misoptimized. +; RUN: opt < %s -S -passes=coro-early,sroa,early-cse,coro-split,coro-cleanup,simplifycfg -opaque-pointers | FileCheck %s + +@tls_variable = thread_local global i32 0 + +define ptr @f() "coroutine.presplit"="0" { +entry: + %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) + %size = call i32 @llvm.coro.size.i32() + %alloc = call i8* @malloc(i32 %size) + %i = alloca ptr + %j = alloca ptr + %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc) + %tls_variable = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable, ptr %i + %sus_result = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %sus_result, label %suspend [i8 0, label %resume + i8 1, label %cleanup] +resume: + %tls_variable2 = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable2, ptr %j + %i_value = load ptr, ptr %i + %j_value = load ptr, ptr %j + %cmp = icmp eq ptr %i_value, %j_value + br i1 %cmp, label %same, label %diff + +same: + call void @print_same() + br label %cleanup + +diff: + call void @print_diff() + br label %cleanup + +cleanup: + %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) + call void @free(i8* %mem) + br label %suspend + +suspend: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + ret i8* %hdl +} + +; CHECK-LABEL: f.resume( +; CHECK: br i1 %cmp, label %same, label %diff +; CHECK-EMPTY: +; CHECK-NEXT: same: +; CHECK-NEXT: call void @print_same() +; CHECK-NEXT: br label %cleanup +; CHECK-EMPTY: +; CHECK-NEXT: diff: +; CHECK-NEXT: call void @print_diff() +; CHECK-NEXT: br label %cleanup + +declare ptr @llvm.coro.maychange(ptr) +declare void @print_same() +declare void @print_diff() +declare i8* @llvm.coro.free(token, i8*) +declare i32 @llvm.coro.size.i32() +declare i8 @llvm.coro.suspend(token, i1) + +declare token @llvm.coro.id(i32, i8*, i8*, i8*) +declare i1 @llvm.coro.alloc(token) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.end(i8*, i1) + +declare noalias i8* @malloc(i32) +declare void @free(i8*) Index: llvm/test/Transforms/Coroutines/coro-TLS-02.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-TLS-02.ll @@ -0,0 +1,65 @@ +; Tests that the TLS variables which don't cross suspend points would be optimized correctly. +; RUN: opt < %s -S -passes=coro-early,coro-split,coro-cleanup,sroa,early-cse,simplifycfg -opaque-pointers | FileCheck %s + +@tls_variable = thread_local global i32 0 + +define ptr @f() "coroutine.presplit"="0" { +entry: + %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) + %size = call i32 @llvm.coro.size.i32() + %alloc = call i8* @malloc(i32 %size) + %i = alloca ptr + %j = alloca ptr + %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc) + %sus_result = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %sus_result, label %suspend [i8 0, label %resume + i8 1, label %cleanup] +resume: + %tls_variable = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable, ptr %i + %tls_variable2 = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable2, ptr %j + %i_value = load ptr, ptr %i + %j_value = load ptr, ptr %j + %cmp = icmp eq ptr %i_value, %j_value + br i1 %cmp, label %same, label %diff + +same: + call void @print_same() + br label %cleanup + +diff: + call void @print_diff() + br label %cleanup + +cleanup: + %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) + call void @free(i8* %mem) + br label %suspend + +suspend: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + ret i8* %hdl +} + +; CHECK: void @f.resume +; CHECK-NEXT: entry.resume: +; CHECK-NEXT: call void @print_same() +; CHECK-NEXT: call void @free(ptr %hdl) +; CHECK-NEXT: ret void + +declare ptr @llvm.coro.maychange(ptr) + +declare void @print_same() +declare void @print_diff() +declare i8* @llvm.coro.free(token, i8*) +declare i32 @llvm.coro.size.i32() +declare i8 @llvm.coro.suspend(token, i1) + +declare token @llvm.coro.id(i32, i8*, i8*, i8*) +declare i1 @llvm.coro.alloc(token) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.end(i8*, i1) + +declare noalias i8* @malloc(i32) +declare void @free(i8*) Index: llvm/test/Transforms/Coroutines/coro-TLS-03.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-TLS-03.ll @@ -0,0 +1,70 @@ +; Tests that the TLS variables which cross suspend points wouldn't be misoptimized during O2 pipeline. +; RUN: opt < %s -S -passes='default' -opaque-pointers | FileCheck %s + +@tls_variable = thread_local global i32 0 + +define ptr @f() "coroutine.presplit"="0" { +entry: + %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) + %size = call i32 @llvm.coro.size.i32() + %alloc = call i8* @malloc(i32 %size) + %i = alloca ptr + %j = alloca ptr + %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc) + %tls_variable = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable, ptr %i + %sus_result = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %sus_result, label %suspend [i8 0, label %resume + i8 1, label %cleanup] +resume: + %tls_variable2 = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable2, ptr %j + %i_value = load ptr, ptr %i + %j_value = load ptr, ptr %j + %cmp = icmp eq ptr %i_value, %j_value + br i1 %cmp, label %same, label %diff + +same: + call void @print_same() + br label %cleanup + +diff: + call void @print_diff() + br label %cleanup + +cleanup: + %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) + call void @free(i8* %mem) + br label %suspend + +suspend: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + ret i8* %hdl +} + +; CHECK-LABEL: f.resume( +; CHECK: br i1 %cmp, label %same, label %diff +; CHECK-EMPTY: +; CHECK-NEXT: same: +; CHECK-NEXT: call void @print_same() +; CHECK-NEXT: br label +; CHECK-EMPTY: +; CHECK-NEXT: diff: +; CHECK-NEXT: call void @print_diff() +; CHECK-NEXT: br label + +declare ptr @llvm.coro.maychange(ptr) + +declare void @print_same() +declare void @print_diff() +declare i8* @llvm.coro.free(token, i8*) +declare i32 @llvm.coro.size.i32() +declare i8 @llvm.coro.suspend(token, i1) + +declare token @llvm.coro.id(i32, i8*, i8*, i8*) +declare i1 @llvm.coro.alloc(token) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.end(i8*, i1) + +declare noalias i8* @malloc(i32) +declare void @free(i8*) Index: llvm/test/Transforms/Coroutines/coro-TLS-04.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-TLS-04.ll @@ -0,0 +1,65 @@ +; Tests that the TLS variables which don't cross suspend points would be optimized correctly during O2 pipelines. +; RUN: opt < %s -S -passes='default' -opaque-pointers | FileCheck %s + +@tls_variable = thread_local global i32 0 + +define ptr @f() "coroutine.presplit"="0" { +entry: + %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) + %size = call i32 @llvm.coro.size.i32() + %alloc = call i8* @malloc(i32 %size) + %i = alloca ptr + %j = alloca ptr + %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc) + %sus_result = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %sus_result, label %suspend [i8 0, label %resume + i8 1, label %cleanup] +resume: + %tls_variable = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable, ptr %i + %tls_variable2 = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable2, ptr %j + %i_value = load ptr, ptr %i + %j_value = load ptr, ptr %j + %cmp = icmp eq ptr %i_value, %j_value + br i1 %cmp, label %same, label %diff + +same: + call void @print_same() + br label %cleanup + +diff: + call void @print_diff() + br label %cleanup + +cleanup: + %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) + call void @free(i8* %mem) + br label %suspend + +suspend: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + ret i8* %hdl +} + +; CHECK: void @f.resume +; CHECK-NEXT: resume: +; CHECK-NEXT: call void @print_same( +; CHECK-NEXT: call void @free( +; CHECK-NEXT: ret void + +declare ptr @llvm.coro.maychange(ptr) + +declare void @print_same() +declare void @print_diff() +declare i8* @llvm.coro.free(token, i8*) +declare i32 @llvm.coro.size.i32() +declare i8 @llvm.coro.suspend(token, i1) + +declare token @llvm.coro.id(i32, i8*, i8*, i8*) +declare i1 @llvm.coro.alloc(token) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.end(i8*, i1) + +declare noalias i8* @malloc(i32) +declare void @free(i8*) Index: llvm/test/Transforms/Coroutines/coro-cleanup-maychange.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-cleanup-maychange.ll @@ -0,0 +1,73 @@ +; Test that coro-cleanup would convert llvm.coro.maychange intrinsics +; correctly. +; RUN: opt < %s -S -passes=coro-cleanup -opaque-pointers | FileCheck %s + +%f.Frame = type { ptr, ptr, ptr, ptr, i1 } + +@tls_variable = thread_local global i32 0 +@f.resumers = private constant [3 x ptr] [ptr @f.resume, ptr @f.destroy, ptr @f.cleanup] + +define ptr @f() { +entry: + %id = call token @llvm.coro.id(i32 0, ptr null, ptr @f, ptr @f.resumers) + %alloc = call ptr @malloc(i32 40) + %i = alloca ptr, align 8 + %j = alloca ptr, align 8 + %hdl = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc) + %resume.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 0 + store ptr @f.resume, ptr %resume.addr, align 8 + %destroy.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 1 + store ptr @f.destroy, ptr %destroy.addr, align 8 + %i.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2 + %j.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3 + ; CHECK: store ptr @tls_variable, ptr %i.reload.addr, align 8 + %tls_variable1 = call ptr @llvm.coro.maychange(ptr @tls_variable) + store ptr %tls_variable1, ptr %i.reload.addr, align 8 + %index.addr2 = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 4 + store i1 false, ptr %index.addr2, align 1 + ret ptr %hdl +} + +define internal fastcc void @f.resume(ptr noalias nonnull align 8 dereferenceable(40) %hdl) { +entry.resume: + %i.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2 + %j.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3 + %tls_variable = call ptr @llvm.coro.maychange(ptr @tls_variable) + ; CHECK: store ptr @tls_variable, ptr %j.reload.addr, align 8 + store ptr %tls_variable, ptr %j.reload.addr, align 8 + call void @consume(ptr %i.reload.addr) + call void @consume(ptr %j.reload.addr) + call void @free(ptr %hdl) + ret void +} + +define internal fastcc void @f.destroy(ptr noalias nonnull align 8 dereferenceable(40) %hdl) { +entry.destroy: + %i.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2 + %j.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3 + call void @free(ptr %hdl) + ret void +} + +define internal fastcc void @f.cleanup(ptr noalias nonnull align 8 dereferenceable(40) %hdl) { +entry.cleanup: + %i.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2 + %j.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3 + call void @free(ptr null) + ret void +} + +declare void @consume(ptr) +declare i8* @llvm.coro.free(token, i8*) +declare i32 @llvm.coro.size.i32() +declare i8 @llvm.coro.suspend(token, i1) + +declare token @llvm.coro.id(i32, i8*, i8*, i8*) +declare i1 @llvm.coro.alloc(token) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.end(i8*, i1) + +declare noalias i8* @malloc(i32) +declare void @free(i8*) + +declare ptr @llvm.coro.maychange(ptr)