diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst index 0e6b49c84ace..9fa403e68e13 100644 --- a/llvm/docs/Coroutines.rst +++ b/llvm/docs/Coroutines.rst @@ -2252,4 +2252,7 @@ Areas Requiring Attention #. Make required changes to make sure that coroutine optimizations work with LTO. +#. In Windows EH, exception objects must be allocated on the stack (see :ref:wineh for details). + We identify an exception object as an alloca that has `catchpad` users. + #. More tests, more tests, more tests diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 13883883d398..e0e79bf07d58 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -14012,8 +14012,9 @@ ensures that each ``catchpad`` has exactly one predecessor block, and it always terminates in a ``catchswitch``. The ``args`` correspond to whatever information the personality routine -requires to determine if this is an appropriate handler for the exception. Control -will transfer to the ``catchpad`` if this is the first appropriate handler for +requires to determine if this is an appropriate handler for the exception. +Each operand must be an alloca or a constant. +Control will transfer to the ``catchpad`` if this is the first appropriate handler for the exception. The ``resultval`` has the type :ref:`token ` and is used to match the diff --git a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp index b5946b98bb12..4d1d6d6cc0a7 100644 --- a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp +++ b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp @@ -344,6 +344,11 @@ static bool isCompatibleReplacement(const Instruction *I, const Use &Operand, return false; return !Callee->hasParamAttribute(OperandNo, Attribute::ImmArg); } + case Instruction::CatchPad: + // Argument operand must be alloca or constant + if (!isa(Replacement) && !isa(Replacement)) + return false; + break; default: break; } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index f4d4f81c1212..5909738b0c90 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -4976,6 +4976,13 @@ void Verifier::visitCatchPadInst(CatchPadInst &CPI) { Check(&*BB->getFirstNonPHIIt() == &CPI, "CatchPadInst not the first non-PHI instruction in the block.", &CPI); + Check(llvm::all_of(CPI.arg_operands(), + [](Use &U) { + auto *V = U.get(); + return isa(V) || isa(V); + }), + "Argument operand must be alloca or constant.", &CPI); + visitEHPadPredecessors(CPI); visitFuncletPadInst(CPI); } diff --git a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp index 81fe0c9acd41..05abccf0f9a9 100644 --- a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp +++ b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp @@ -180,6 +180,13 @@ struct AllocaUseVisitor : PtrUseVisitor { handleAlias(I); } + void visitCatchPadInst(CatchPadInst &I) { + // Windows EH requires exception objects allocated on the stack, + // shortcut the traversal and keep it on stack. + ShouldLiveOnFrame = false; + Base::Worklist.clear(); + } + void visitInsertElementInst(InsertElementInst &I) { enqueueUsers(I); handleAlias(I); diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-10.ll b/llvm/test/Transforms/Coroutines/coro-alloca-10.ll new file mode 100644 index 000000000000..12e2a921aa76 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-alloca-10.ll @@ -0,0 +1,66 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; Test that catchpad is specially handled. Do not collect exception object into coroutine frame. +; RUN: opt < %s -passes='coro-split,simplifycfg,early-cse' -S | FileCheck %s + +define void @fn() presplitcoroutine personality i32 0 { +; CHECK-LABEL: define void @fn() personality i32 0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[EXCEPTION_OBJ_RELOAD_ADDR:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[ID:%.*]] = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr @fn.resumers) +; CHECK-NEXT: [[MEM:%.*]] = call noalias nonnull ptr @malloc(i64 24) +; CHECK-NEXT: [[HDL:%.*]] = call noalias nonnull ptr @llvm.coro.begin(token [[ID]], ptr [[MEM]]) +; CHECK-NEXT: store ptr @fn.resume, ptr [[HDL]], align 8 +; CHECK-NEXT: [[DESTROY_ADDR:%.*]] = getelementptr inbounds i8, ptr [[HDL]], i64 8 +; CHECK-NEXT: store ptr @fn.destroy, ptr [[DESTROY_ADDR]], align 8 +; CHECK-NEXT: store ptr null, ptr [[EXCEPTION_OBJ_RELOAD_ADDR]], align 8 +; CHECK-NEXT: [[INDEX_ADDR4:%.*]] = getelementptr inbounds i8, ptr [[HDL]], i64 16 +; CHECK-NEXT: store i1 false, ptr [[INDEX_ADDR4]], align 1 +; CHECK-NEXT: ret void +; +entry: + %exception.obj = alloca ptr, align 8 + %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null) + %size = call i64 @llvm.coro.size.i64() + %mem = call noalias nonnull ptr @malloc(i64 %size) + %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem) + store ptr null, ptr %exception.obj, align 8 + br label %while + +while: + %save = call token @llvm.coro.save(ptr null) + %suspend = call i8 @llvm.coro.suspend(token %save, i1 false) + switch i8 %suspend, label %coro.ret [ + i8 0, label %await.ready + ] + +await.ready: + invoke void @throw() + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: + %switch = catchswitch within none [label %catch] unwind label %ehcleanup + +catch: + %pad = catchpad within %switch [ptr null, i32 8, ptr %exception.obj] + invoke void @use(ptr %exception.obj) [ "funclet"(token %pad) ] + to label %catch.ret unwind label %ehcleanup + +catch.ret: + catchret from %pad to label %while + +ehcleanup: + %cleanup = cleanuppad within none [] + call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token %cleanup) ] + cleanupret from %cleanup unwind to caller + +coro.ret: + call void @llvm.coro.end(ptr null, i1 false, token none) + ret void + +unreachable: + unreachable +} + +declare ptr @malloc(i64) +declare void @throw() +declare void @use(ptr) diff --git a/llvm/unittests/FuzzMutate/StrategiesTest.cpp b/llvm/unittests/FuzzMutate/StrategiesTest.cpp index fd91066b994a..278cce20119c 100644 --- a/llvm/unittests/FuzzMutate/StrategiesTest.cpp +++ b/llvm/unittests/FuzzMutate/StrategiesTest.cpp @@ -733,11 +733,12 @@ TEST(AllStrategies, SkipEHPad) { StringRef Source = "\n\ define void @f(i32 %x) personality ptr @__CxxFrameHandler3 { \n\ entry: \n\ + %I = alloca i32, align 4 \n\ invoke void @g() to label %try.cont unwind label %catch.dispatch \n\ catch.dispatch: \n\ %0 = catchswitch within none [label %catch] unwind to caller \n\ catch: \n\ - %1 = catchpad within %0 [ptr null, i32 64, ptr null] \n\ + %1 = catchpad within %0 [ptr null, i32 64, ptr %I] \n\ catchret from %1 to label %try.cont \n\ try.cont: \n\ ret void \n\