Author: Andrew Litteken Date: 2020-12-17T19:33:57-06:00 New Revision: cea807602a2fb0b8d79b0eef30ce717ae4645fbc
URL: https://github.com/llvm/llvm-project/commit/cea807602a2fb0b8d79b0eef30ce717ae4645fbc DIFF: https://github.com/llvm/llvm-project/commit/cea807602a2fb0b8d79b0eef30ce717ae4645fbc.diff LOG: [IRSim][IROutliner] Adding InstVisitor to disallow certain operations. This adds a custom InstVisitor to return false on instructions that should not be allowed to be outlined. These match the illegal instructions in the IRInstructionMapper with exception of the addition of the llvm.assume intrinsic. Tests all the tests marked: illegal-*-.ll with a test for each kind of instruction that has been marked as illegal. Reviewers: jroelofs, paquette Differential Revisions: https://reviews.llvm.org/D86976 Added: llvm/test/Transforms/IROutliner/illegal-allocas.ll llvm/test/Transforms/IROutliner/illegal-assumes.ll llvm/test/Transforms/IROutliner/illegal-branches.ll llvm/test/Transforms/IROutliner/illegal-callbr.ll llvm/test/Transforms/IROutliner/illegal-calls.ll llvm/test/Transforms/IROutliner/illegal-catchpad.ll llvm/test/Transforms/IROutliner/illegal-cleanup.ll llvm/test/Transforms/IROutliner/illegal-frozen.ll llvm/test/Transforms/IROutliner/illegal-gep.ll llvm/test/Transforms/IROutliner/illegal-invoke.ll llvm/test/Transforms/IROutliner/illegal-landingpad.ll llvm/test/Transforms/IROutliner/illegal-memcpy.ll llvm/test/Transforms/IROutliner/illegal-memmove.ll llvm/test/Transforms/IROutliner/illegal-memset.ll llvm/test/Transforms/IROutliner/illegal-phi-nodes.ll llvm/test/Transforms/IROutliner/illegal-vaarg.ll llvm/test/Transforms/IROutliner/legal-debug.ll Modified: llvm/include/llvm/Transforms/IPO/IROutliner.h llvm/lib/Transforms/IPO/IROutliner.cpp Removed: ################################################################################ diff --git a/llvm/include/llvm/Transforms/IPO/IROutliner.h b/llvm/include/llvm/Transforms/IPO/IROutliner.h index 80ba40d91ed8..b49970e7056d 100644 --- a/llvm/include/llvm/Transforms/IPO/IROutliner.h +++ b/llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -178,6 +178,61 @@ class IROutliner { /// The memory allocator used to allocate new IRInstructionData. SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; + + /// Custom InstVisitor to classify diff erent instructions for whether it can + /// be analyzed for similarity. This is needed as there may be instruction we + /// can identify as having similarity, but are more complicated to outline. + struct InstructionAllowed + : public InstVisitor<InstructionAllowed, bool> { + InstructionAllowed() {} + + // TODO: Determine a scheme to resolve when the label is similar enough. + bool visitBranchInst(BranchInst &BI) { return false; } + // TODO: Determine a scheme to resolve when the labels are similar enough. + bool visitPHINode(PHINode &PN) { return false; } + // TODO: Handle allocas. + bool visitAllocaInst(AllocaInst &AI) { return false; } + // VAArg instructions are not allowed since this could cause diff iculty when + // diff erentiating between diff erent sets of variable instructions in + // the deduplicated outlined regions. + bool visitVAArgInst(VAArgInst &VI) { return false; } + // We exclude all exception handling cases since they are so context + // dependent. + bool visitLandingPadInst(LandingPadInst &LPI) { return false; } + bool visitFuncletPadInst(FuncletPadInst &FPI) { return false; } + // DebugInfo should be included in the regions, but should not be + // analyzed for similarity as it has no bearing on the outcome of the + // program. + bool visitDbgInfoIntrinsic(DbgInfoIntrinsic &DII) { + return true; + } + // TODO: Handle GetElementPtrInsts + bool visitGetElementPtrInst(GetElementPtrInst &GEPI) { + return false; + } + // TODO: Handle specific intrinsics individually from those that can be + // handled. + bool IntrinsicInst(IntrinsicInst &II) { return false; } + // TODO: Handle CallInsts, there will need to be handling for special kinds + // of calls, as well as calls to intrinsics. + bool visitCallInst(CallInst &CI) { return false; } + // TODO: Handle FreezeInsts. Since a frozen value could be frozen inside + // the outlined region, and then returned as an output, this will have to be + // handled diff erently. + bool visitFreezeInst(FreezeInst &CI) { return false; } + // TODO: We do not current handle similarity that changes the control flow. + bool visitInvokeInst(InvokeInst &II) { return false; } + // TODO: We do not current handle similarity that changes the control flow. + bool visitCallBrInst(CallBrInst &CBI) { return false; } + // TODO: Handle interblock similarity. + bool visitTerminator(Instruction &I) { return false; } + bool visitInstruction(Instruction &I) { + return true; + } + }; + + /// A InstVisitor used to exclude certain instructions from being outlined. + InstructionAllowed InstructionClassifier; }; /// Pass to outline similar regions. diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp index feefeb458bdc..7d39b84b3ca9 100644 --- a/llvm/lib/Transforms/IPO/IROutliner.cpp +++ b/llvm/lib/Transforms/IPO/IROutliner.cpp @@ -180,6 +180,13 @@ void IROutliner::pruneIncompatibleRegions( if (CurrentEndIdx != 0 && StartIdx <= CurrentEndIdx) continue; + bool BadInst = any_of(IRSC, [this](IRInstructionData &ID) { + return !this->InstructionClassifier.visit(ID.Inst); + }); + + if (BadInst) + continue; + OutlinableRegion *OS = new (RegionAllocator.Allocate()) OutlinableRegion(IRSC, CurrentGroup); CurrentGroup.Regions.push_back(OS); @@ -216,10 +223,12 @@ bool IROutliner::extractSection(OutlinableRegion &Region) { // should not be outlined in this round. So marking these as illegal is // allowed. IRInstructionDataList *IDL = Region.Candidate->front()->IDL; - Region.NewFront = new (InstDataAllocator.Allocate()) - IRInstructionData(*RewrittenBB->begin(), false, *IDL); - Region.NewBack = new (InstDataAllocator.Allocate()) - IRInstructionData(*std::prev(std::prev(RewrittenBB->end())), false, *IDL); + Instruction *BeginRewritten = &*RewrittenBB->begin(); + Instruction *EndRewritten = &*RewrittenBB->begin(); + Region.NewFront = new (InstDataAllocator.Allocate()) IRInstructionData( + *BeginRewritten, InstructionClassifier.visit(*BeginRewritten), *IDL); + Region.NewBack = new (InstDataAllocator.Allocate()) IRInstructionData( + *EndRewritten, InstructionClassifier.visit(*EndRewritten), *IDL); // Insert the first IRInstructionData of the new region in front of the // first IRInstructionData of the IRSimilarityCandidate. diff --git a/llvm/test/Transforms/IROutliner/illegal-allocas.ll b/llvm/test/Transforms/IROutliner/illegal-allocas.ll new file mode 100644 index 000000000000..123ce6b24862 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-allocas.ll @@ -0,0 +1,36 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; Show that we do not extract allocas, as outlining allocas may cause +; inconsistencies with the CodeExtractor's algorithm. + +define void @function1() { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + + ret void +} + +define void @function2() { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-assumes.ll b/llvm/test/Transforms/IROutliner/illegal-assumes.ll new file mode 100644 index 000000000000..3bd49b4eab07 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-assumes.ll @@ -0,0 +1,143 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test ensures that we do not include llvm.assumes. There are exceptions +; in the CodeExtractor's algorithm for llvm.assumes, so we ignore it for now. + +define void @outline_assumes() { +; CHECK-LABEL: @outline_assumes( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DL_LOC:%.*]] = alloca i1, align 1 +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4 +; CHECK-NEXT: [[LT_CAST:%.*]] = bitcast i1* [[DL_LOC]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @outline_assumes.outlined.5(i1* [[D]], i1* [[DL_LOC]]) +; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, i1* [[DL_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: [[SPLIT_INST:%.*]] = sub i1 [[DL_RELOAD]], [[DL_RELOAD]] +; CHECK-NEXT: call void @outline_assumes.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]]) +; CHECK-NEXT: call void @outline_assumes.outlined.1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %d = alloca i1, align 4 + store i1 1, i1* %d, align 4 + %dl = load i1, i1* %d + %split_inst = sub i1 %dl, %dl + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @llvm.assume(i1 %dl) + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} + +define void @outline_assumes2() { +; CHECK-LABEL: @outline_assumes2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DL_LOC:%.*]] = alloca i1, align 1 +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4 +; CHECK-NEXT: [[LT_CAST:%.*]] = bitcast i1* [[DL_LOC]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @outline_assumes2.outlined.6(i1* [[D]], i1* [[DL_LOC]]) +; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, i1* [[DL_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @outline_assumes2.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]]) +; CHECK-NEXT: call void @outline_assumes2.outlined.2(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %d = alloca i1, align 4 + store i1 0, i1* %d, align 4 + %dl = load i1, i1* %d + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @llvm.assume(i1 %dl) + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} + +define void @outline_assumes3() { +; CHECK-LABEL: @outline_assumes3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4 +; CHECK-NEXT: store i1 true, i1* [[D]], align 4 +; CHECK-NEXT: [[DL:%.*]] = load i1, i1* [[D]], align 1 +; CHECK-NEXT: [[SPLIT_INST:%.*]] = add i1 [[DL]], [[DL]] +; CHECK-NEXT: call void @outline_assumes3.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[DL]]) +; CHECK-NEXT: call void @outline_assumes3.outlined.3(i32* [[A]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %d = alloca i1, align 4 + store i1 1, i1* %d, align 4 + %dl = load i1, i1* %d + %split_inst = add i1 %dl, %dl + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @llvm.assume(i1 %dl) + %al = load i32, i32* %a + %bl = add i32 %al, %al + ret void +} + +define void @outline_assumes4() { +; CHECK-LABEL: @outline_assumes4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4 +; CHECK-NEXT: store i1 false, i1* [[D]], align 4 +; CHECK-NEXT: [[DL:%.*]] = load i1, i1* [[D]], align 1 +; CHECK-NEXT: [[SPLIT_INST:%.*]] = add i1 [[DL]], [[DL]] +; CHECK-NEXT: call void @outline_assumes4.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[DL]]) +; CHECK-NEXT: call void @outline_assumes4.outlined.4(i32* [[A]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %d = alloca i1, align 4 + store i1 0, i1* %d, align 4 + %dl = load i1, i1* %d + %split_inst = add i1 %dl, %dl + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @llvm.assume(i1 %dl) + %al = load i32, i32* %a + %bl = add i32 %al, %al + ret void +} + +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/IROutliner/illegal-branches.ll b/llvm/test/Transforms/IROutliner/illegal-branches.ll new file mode 100644 index 000000000000..2f00c5986a34 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-branches.ll @@ -0,0 +1,51 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; Show that we do not extract sections with branches as it would require extra +; label and control flow checking. + +define void @function1() { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: br label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + br label %next +next: + ret void +} + +define void @function2() { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: br label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + br label %next +next: + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-callbr.ll b/llvm/test/Transforms/IROutliner/illegal-callbr.ll new file mode 100644 index 000000000000..ee21d3c55b2a --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-callbr.ll @@ -0,0 +1,66 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that we do not outline callbr instruction since as we do not +; outline any control flow change instructions. + + +define i32 @function1(i32 %a, i32 %b) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: bb0: +; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[A:%.*]], 4 +; CHECK-NEXT: call void @function1.outlined(i32 [[B:%.*]]) +; CHECK-NEXT: callbr void asm "xorl $0, $0 +; CHECK-NEXT: to label [[NORMAL:%.*]] [label %fail1] +; CHECK: normal: +; CHECK-NEXT: call void @function1.outlined.1(i32 [[B]]) +; CHECK-NEXT: ret i32 0 +; CHECK: fail1: +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[B]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[B]], 1 +; CHECK-NEXT: ret i32 0 +; +bb0: + %0 = add i32 %a, 4 + %1 = add i32 %b, 1 + %2 = add i32 %b, 1 + callbr void asm "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@function1, %fail1)) to label %normal [label %fail1] +normal: + %3 = add i32 %b, 1 + %4 = add i32 %b, 1 + ret i32 0 +fail1: + %5 = add i32 %b, 1 + %6 = add i32 %b, 1 + ret i32 0 +} + +define i32 @function2(i32 %a, i32 %b) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: bb0: +; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[A:%.*]], 4 +; CHECK-NEXT: call void @function2.outlined(i32 [[B:%.*]]) +; CHECK-NEXT: callbr void asm "xorl $0, $0 +; CHECK-NEXT: to label [[NORMAL:%.*]] [label %fail1] +; CHECK: normal: +; CHECK-NEXT: call void @function2.outlined.2(i32 [[B]]) +; CHECK-NEXT: ret i32 0 +; CHECK: fail1: +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[B]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[B]], 1 +; CHECK-NEXT: ret i32 0 +; +bb0: + %0 = add i32 %a, 4 + %1 = add i32 %b, 1 + %2 = add i32 %b, 1 + callbr void asm "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@function2, %fail1)) to label %normal [label %fail1] +normal: + %3 = add i32 %b, 1 + %4 = add i32 %b, 1 + ret i32 0 +fail1: + %5 = add i32 %b, 1 + %6 = add i32 %b, 1 + ret i32 0 +} diff --git a/llvm/test/Transforms/IROutliner/illegal-calls.ll b/llvm/test/Transforms/IROutliner/illegal-calls.ll new file mode 100644 index 000000000000..d073d2d9b66e --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-calls.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that we do not outline calls. Special calls, such as +; indirect or nameless calls require extra handling to ensure that there +; are no inconsistencies when outlining and consolidating regions. + +declare void @f1(i32*, i32*); + +define void @outline_constants1() { +; CHECK-LABEL: @outline_constants1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @outline_constants1.outlined.1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: call void @outline_constants1.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @f1(i32* %a, i32* %b) + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} + +define void @outline_constants2() { +; CHECK-LABEL: @outline_constants2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @outline_constants2.outlined.2(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: call void @outline_constants2.outlined(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + call void @f1(i32* %a, i32* %b) + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-catchpad.ll b/llvm/test/Transforms/IROutliner/illegal-catchpad.ll new file mode 100644 index 000000000000..a8c91bf38644 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-catchpad.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that catchpad instructions are not outlined even if they +; in a similar section. Dealing with exception handling inside of an outlined +; function would require a lot of handling that is not implemented yet. + +declare void @llvm.donothing() nounwind readnone + +define void @function1() personality i8 3 { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catchpad1] unwind to caller +; CHECK: catchpad1: +; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [] +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cs1 = catchswitch within none [label %catchpad1] unwind to caller +catchpad1: + catchpad within %cs1 [] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} + +define void @function2() personality i8 3 { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catchpad1] unwind to caller +; CHECK: catchpad1: +; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [] +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cs1 = catchswitch within none [label %catchpad1] unwind to caller +catchpad1: + catchpad within %cs1 [] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-cleanup.ll b/llvm/test/Transforms/IROutliner/illegal-cleanup.ll new file mode 100644 index 000000000000..6b2fd12324e4 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-cleanup.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that cleanuppad instructions are not outlined even if they +; in a similar section. Dealing with exception handling inside of an outlined +; function would require a lot of handling that is not implemented yet. + +declare void @llvm.donothing() nounwind readnone + +define void @function1() personality i8 3 { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEAN:%.*]] = cleanuppad within none [] +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %clean = cleanuppad within none [] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} + +define void @function2() personality i8 3 { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEAN:%.*]] = cleanuppad within none [] +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %clean = cleanuppad within none [] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-frozen.ll b/llvm/test/Transforms/IROutliner/illegal-frozen.ll new file mode 100644 index 000000000000..796653b81e92 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-frozen.ll @@ -0,0 +1,49 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; Show that we do not extract freeze instructions, since extra handling is +; required to mark any outputs used with freeze. + +define void @function1(i32* %a, i32* %b) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FIRST:%.*]] +; CHECK: first: +; CHECK-NEXT: [[C:%.*]] = freeze i32* [[A:%.*]] +; CHECK-NEXT: call void @function1.outlined(i32* [[C]], i32* [[B:%.*]]) +; CHECK-NEXT: ret void +; CHECK: next: +; CHECK-NEXT: br label [[FIRST]] +; +entry: + br label %first +first: + %c = freeze i32* %a + store i32 2, i32* %c, align 4 + store i32 3, i32* %b, align 4 + ret void +next: + br label %first +} + +define void @function2(i32* %a, i32* %b) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FIRST:%.*]] +; CHECK: first: +; CHECK-NEXT: [[C:%.*]] = freeze i32* [[A:%.*]] +; CHECK-NEXT: call void @function2.outlined(i32* [[C]], i32* [[B:%.*]]) +; CHECK-NEXT: ret void +; CHECK: next: +; CHECK-NEXT: br label [[FIRST]] +; +entry: + br label %first +first: + %c = freeze i32* %a + store i32 2, i32* %c, align 4 + store i32 3, i32* %b, align 4 + ret void +next: + br label %first +} diff --git a/llvm/test/Transforms/IROutliner/illegal-gep.ll b/llvm/test/Transforms/IROutliner/illegal-gep.ll new file mode 100644 index 000000000000..0aa2d9a22f57 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-gep.ll @@ -0,0 +1,48 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks to make sure that we do not outline getelementptr +; instructions since we must make extra checks on the final operands. + +%struct.RT = type { i8, [10 x [20 x i32]], i8 } +%struct.ST = type { i32, double, %struct.RT } + +define void @function1(%struct.ST* %s, i64 %t) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S:%.*]], i64 1 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_ST]], %struct.ST* [[S]], i64 [[T:%.*]] +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1 + %1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %t + ret void +} + +define void @function2(%struct.ST* %s, i64 %t) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S:%.*]], i64 1 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_ST]], %struct.ST* [[S]], i64 [[T:%.*]] +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1 + %1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %t + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-invoke.ll b/llvm/test/Transforms/IROutliner/illegal-invoke.ll new file mode 100644 index 000000000000..b38f4898cbdf --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-invoke.ll @@ -0,0 +1,64 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that invoke instructions are not outlined even if they +; in a similar section. Outlining does not currently handle control flow +; changes. + +declare void @llvm.donothing() nounwind readnone + +define void @function1() personality i8 3 { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEANUP:%.*]] = landingpad i8 +; CHECK-NEXT: cleanup +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @function2() personality i8 3 { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEANUP:%.*]] = landingpad i8 +; CHECK-NEXT: cleanup +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-landingpad.ll b/llvm/test/Transforms/IROutliner/illegal-landingpad.ll new file mode 100644 index 000000000000..20f31168b64d --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-landingpad.ll @@ -0,0 +1,64 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that landingpad instructions are not outlined even if they +; in a similar section. Dealing with exception handling inside of an outlined +; function would require a lot of handling that is not implemented yet. + +declare void @llvm.donothing() nounwind readnone + +define void @function1() personality i8 3 { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEANUP:%.*]] = landingpad i8 +; CHECK-NEXT: cleanup +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cleanup = landingpad i8 cleanup + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} + +define void @function2() personality i8 3 { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: invoke void @llvm.donothing() +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: exception: +; CHECK-NEXT: [[CLEANUP:%.*]] = landingpad i8 +; CHECK-NEXT: cleanup +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: br label [[NORMAL]] +; CHECK: normal: +; CHECK-NEXT: ret void +; +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + invoke void @llvm.donothing() to label %normal unwind label %exception +exception: + %cleanup = landingpad i8 cleanup + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + br label %normal +normal: + ret void +} diff --git a/llvm/test/Transforms/IROutliner/illegal-memcpy.ll b/llvm/test/Transforms/IROutliner/illegal-memcpy.ll new file mode 100644 index 000000000000..68241e08aeed --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-memcpy.ll @@ -0,0 +1,65 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that we do not outline memcpy intrinsics since it may require +; extra address space checks. + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) + +define i8 @function1(i8* noalias %s, i8* noalias %d, i64 %len) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[B_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[A_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[RET_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @function1.outlined.1(i8* [[S:%.*]], i8* [[D:%.*]], i8* [[A_LOC]], i8* [[B_LOC]]) +; CHECK-NEXT: [[A_RELOAD:%.*]] = load i8, i8* [[A_LOC]], align 1 +; CHECK-NEXT: [[B_RELOAD:%.*]] = load i8, i8* [[B_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[D]], i8* [[S]], i64 [[LEN:%.*]], i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: call void @function1.outlined(i8 [[A_RELOAD]], i8 [[B_RELOAD]], i8* [[S]], i8* [[RET_LOC]]) +; CHECK-NEXT: [[RET_RELOAD:%.*]] = load i8, i8* [[RET_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: ret i8 [[RET_RELOAD]] +; +entry: + %a = load i8, i8* %s + %b = load i8, i8* %d + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %d, i8* %s, i64 %len, i1 false) + %c = add i8 %a, %b + %ret = load i8, i8* %s + ret i8 %ret +} + +define i8 @function2(i8* noalias %s, i8* noalias %d, i64 %len) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[B_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[A_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[RET_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @function2.outlined.2(i8* [[S:%.*]], i8* [[D:%.*]], i8* [[A_LOC]], i8* [[B_LOC]]) +; CHECK-NEXT: [[A_RELOAD:%.*]] = load i8, i8* [[A_LOC]], align 1 +; CHECK-NEXT: [[B_RELOAD:%.*]] = load i8, i8* [[B_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[D]], i8* [[S]], i64 [[LEN:%.*]], i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: call void @function2.outlined(i8 [[A_RELOAD]], i8 [[B_RELOAD]], i8* [[S]], i8* [[RET_LOC]]) +; CHECK-NEXT: [[RET_RELOAD:%.*]] = load i8, i8* [[RET_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: ret i8 [[RET_RELOAD]] +; +entry: + %a = load i8, i8* %s + %b = load i8, i8* %d + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %d, i8* %s, i64 %len, i1 false) + %c = add i8 %a, %b + %ret = load i8, i8* %s + ret i8 %ret +} diff --git a/llvm/test/Transforms/IROutliner/illegal-memmove.ll b/llvm/test/Transforms/IROutliner/illegal-memmove.ll new file mode 100644 index 000000000000..98782cb6ed24 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-memmove.ll @@ -0,0 +1,65 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that we do not outline memcpy intrinsics since it may require +; extra address space checks. + +declare void @llvm.memmove.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) + +define i8 @function1(i8* noalias %s, i8* noalias %d, i64 %len) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[B_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[A_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[RET_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @function1.outlined.1(i8* [[S:%.*]], i8* [[D:%.*]], i8* [[A_LOC]], i8* [[B_LOC]]) +; CHECK-NEXT: [[A_RELOAD:%.*]] = load i8, i8* [[A_LOC]], align 1 +; CHECK-NEXT: [[B_RELOAD:%.*]] = load i8, i8* [[B_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @llvm.memmove.p0i8.p0i8.i64(i8* [[D]], i8* [[S]], i64 [[LEN:%.*]], i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: call void @function1.outlined(i8 [[A_RELOAD]], i8 [[B_RELOAD]], i8* [[S]], i8* [[RET_LOC]]) +; CHECK-NEXT: [[RET_RELOAD:%.*]] = load i8, i8* [[RET_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: ret i8 [[RET_RELOAD]] +; +entry: + %a = load i8, i8* %s + %b = load i8, i8* %d + call void @llvm.memmove.p0i8.p0i8.i64(i8* %d, i8* %s, i64 %len, i1 false) + %c = add i8 %a, %b + %ret = load i8, i8* %s + ret i8 %ret +} + +define i8 @function2(i8* noalias %s, i8* noalias %d, i64 %len) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[B_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[A_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: [[RET_LOC:%.*]] = alloca i8, align 1 +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @function2.outlined.2(i8* [[S:%.*]], i8* [[D:%.*]], i8* [[A_LOC]], i8* [[B_LOC]]) +; CHECK-NEXT: [[A_RELOAD:%.*]] = load i8, i8* [[A_LOC]], align 1 +; CHECK-NEXT: [[B_RELOAD:%.*]] = load i8, i8* [[B_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[A_LOC]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[B_LOC]]) +; CHECK-NEXT: call void @llvm.memmove.p0i8.p0i8.i64(i8* [[D]], i8* [[S]], i64 [[LEN:%.*]], i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: call void @function2.outlined(i8 [[A_RELOAD]], i8 [[B_RELOAD]], i8* [[S]], i8* [[RET_LOC]]) +; CHECK-NEXT: [[RET_RELOAD:%.*]] = load i8, i8* [[RET_LOC]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[RET_LOC]]) +; CHECK-NEXT: ret i8 [[RET_RELOAD]] +; +entry: + %a = load i8, i8* %s + %b = load i8, i8* %d + call void @llvm.memmove.p0i8.p0i8.i64(i8* %d, i8* %s, i64 %len, i1 false) + %c = add i8 %a, %b + %ret = load i8, i8* %s + ret i8 %ret +} diff --git a/llvm/test/Transforms/IROutliner/illegal-memset.ll b/llvm/test/Transforms/IROutliner/illegal-memset.ll new file mode 100644 index 000000000000..7dd294eb9f34 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-memset.ll @@ -0,0 +1,45 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test checks that we do not outline memset intrinsics since it requires +; extra address space checks. + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) + +define i64 @function1(i64 %x, i64 %z, i64 %n) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[POOL:%.*]] = alloca [59 x i64], align 4 +; CHECK-NEXT: [[TMP:%.*]] = bitcast [59 x i64]* [[POOL]] to i8* +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[TMP]], i8 0, i64 236, i1 false) +; CHECK-NEXT: call void @function1.outlined(i64 [[N:%.*]], i64 [[X:%.*]], i64 [[Z:%.*]]) +; CHECK-NEXT: ret i64 0 +; +entry: + %pool = alloca [59 x i64], align 4 + %tmp = bitcast [59 x i64]* %pool to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false) + %cmp3 = icmp eq i64 %n, 0 + %a = add i64 %x, %z + %c = add i64 %x, %z + ret i64 0 +} + +define i64 @function2(i64 %x, i64 %z, i64 %n) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[POOL:%.*]] = alloca [59 x i64], align 4 +; CHECK-NEXT: [[TMP:%.*]] = bitcast [59 x i64]* [[POOL]] to i8* +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[TMP]], i8 0, i64 236, i1 false) +; CHECK-NEXT: call void @function2.outlined(i64 [[N:%.*]], i64 [[X:%.*]], i64 [[Z:%.*]]) +; CHECK-NEXT: ret i64 0 +; +entry: + %pool = alloca [59 x i64], align 4 + %tmp = bitcast [59 x i64]* %pool to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false) + %cmp3 = icmp eq i64 %n, 0 + %a = add i64 %x, %z + %c = add i64 %x, %z + ret i64 0 +} diff --git a/llvm/test/Transforms/IROutliner/illegal-phi-nodes.ll b/llvm/test/Transforms/IROutliner/illegal-phi-nodes.ll new file mode 100644 index 000000000000..5d9ef53931c5 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-phi-nodes.ll @@ -0,0 +1,49 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; Show that we do not extract phi nodes as it would require extra label and +; control flow checking. + +define void @function1(i32* %a, i32* %b) { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FIRST:%.*]] +; CHECK: first: +; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 3, [[NEXT:%.*]] ] +; CHECK-NEXT: call void @function1.outlined(i32* [[A:%.*]], i32* [[B:%.*]]) +; CHECK-NEXT: ret void +; CHECK: next: +; CHECK-NEXT: br label [[FIRST]] +; +entry: + br label %first +first: + %0 = phi i32 [ 0, %entry ], [ 3, %next ] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + ret void +next: + br label %first +} + +define void @function2(i32* %a, i32* %b) { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FIRST:%.*]] +; CHECK: first: +; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 3, [[NEXT:%.*]] ] +; CHECK-NEXT: call void @function2.outlined(i32* [[A:%.*]], i32* [[B:%.*]]) +; CHECK-NEXT: ret void +; CHECK: next: +; CHECK-NEXT: br label [[FIRST]] +; +entry: + br label %first +first: + %0 = phi i32 [ 0, %entry ], [ 3, %next ] + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + ret void +next: + br label %first +} diff --git a/llvm/test/Transforms/IROutliner/illegal-vaarg.ll b/llvm/test/Transforms/IROutliner/illegal-vaarg.ll new file mode 100644 index 000000000000..7e194fe21621 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/illegal-vaarg.ll @@ -0,0 +1,87 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test ensures that we do not outline vararg instructions or intrinsics, as +; they may cause inconsistencies when outlining. + +declare void @llvm.va_start(i8*) +declare void @llvm.va_copy(i8*, i8*) +declare void @llvm.va_end(i8*) + +define i32 @func1(i32 %a, double %b, i8* %v, ...) nounwind { +; CHECK-LABEL: @func1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AP1_LOC:%.*]] = alloca i8*, align 8 +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B_ADDR:%.*]] = alloca double, align 8 +; CHECK-NEXT: [[AP:%.*]] = alloca i8*, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[LT_CAST:%.*]] = bitcast i8** [[AP1_LOC]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @func1.outlined(i32 [[A:%.*]], i32* [[A_ADDR]], double [[B:%.*]], double* [[B_ADDR]], i8** [[AP]], i8** [[AP1_LOC]]) +; CHECK-NEXT: [[AP1_RELOAD:%.*]] = load i8*, i8** [[AP1_LOC]], align 8 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @llvm.va_start(i8* [[AP1_RELOAD]]) +; CHECK-NEXT: [[TMP0:%.*]] = va_arg i8** [[AP]], i32 +; CHECK-NEXT: call void @llvm.va_copy(i8* [[V:%.*]], i8* [[AP1_RELOAD]]) +; CHECK-NEXT: call void @llvm.va_end(i8* [[AP1_RELOAD]]) +; CHECK-NEXT: store i32 [[TMP0]], i32* [[C]], align 4 +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[C]], align 4 +; CHECK-NEXT: ret i32 [[TMP]] +; +entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca double, align 8 + %ap = alloca i8*, align 4 + %c = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store double %b, double* %b.addr, align 8 + %ap1 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap1) + %0 = va_arg i8** %ap, i32 + call void @llvm.va_copy(i8* %v, i8* %ap1) + call void @llvm.va_end(i8* %ap1) + store i32 %0, i32* %c, align 4 + %tmp = load i32, i32* %c, align 4 + ret i32 %tmp +} + +define i32 @func2(i32 %a, double %b, i8* %v, ...) nounwind { +; CHECK-LABEL: @func2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[AP1_LOC:%.*]] = alloca i8*, align 8 +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B_ADDR:%.*]] = alloca double, align 8 +; CHECK-NEXT: [[AP:%.*]] = alloca i8*, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[LT_CAST:%.*]] = bitcast i8** [[AP1_LOC]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @func2.outlined(i32 [[A:%.*]], i32* [[A_ADDR]], double [[B:%.*]], double* [[B_ADDR]], i8** [[AP]], i8** [[AP1_LOC]]) +; CHECK-NEXT: [[AP1_RELOAD:%.*]] = load i8*, i8** [[AP1_LOC]], align 8 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[LT_CAST]]) +; CHECK-NEXT: call void @llvm.va_start(i8* [[AP1_RELOAD]]) +; CHECK-NEXT: [[TMP0:%.*]] = va_arg i8** [[AP]], i32 +; CHECK-NEXT: call void @llvm.va_copy(i8* [[V:%.*]], i8* [[AP1_RELOAD]]) +; CHECK-NEXT: call void @llvm.va_end(i8* [[AP1_RELOAD]]) +; CHECK-NEXT: store i32 [[TMP0]], i32* [[C]], align 4 +; CHECK-NEXT: [[AP2:%.*]] = bitcast i8** [[AP]] to i8* +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[C]], align 4 +; CHECK-NEXT: ret i32 [[TMP]] +; +entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca double, align 8 + %ap = alloca i8*, align 4 + %c = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store double %b, double* %b.addr, align 8 + %ap1 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap1) + %0 = va_arg i8** %ap, i32 + call void @llvm.va_copy(i8* %v, i8* %ap1) + call void @llvm.va_end(i8* %ap1) + store i32 %0, i32* %c, align 4 + %ap2 = bitcast i8** %ap to i8* + %tmp = load i32, i32* %c, align 4 + ret i32 %tmp +} diff --git a/llvm/test/Transforms/IROutliner/legal-debug.ll b/llvm/test/Transforms/IROutliner/legal-debug.ll new file mode 100644 index 000000000000..63ddd5042f10 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/legal-debug.ll @@ -0,0 +1,122 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner < %s | FileCheck %s + +; This test looks ahecks that debug info is extracted along with the other +; instructions. + +define void @function1() !dbg !6 { +; CHECK-LABEL: @function1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4, [[DBG17:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[A]], [[META9:metadata !.*]], metadata !DIExpression()), [[DBG17]] +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4, [[DBG18:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[B]], [[META11:metadata !.*]], metadata !DIExpression()), [[DBG18]] +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4, [[DBG19:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[C]], [[META12:metadata !.*]], metadata !DIExpression()), [[DBG19]] +; CHECK-NEXT: call void @function1.outlined(i32* [[A]], i32* [[B]], i32* [[C]]), [[DBG20:!dbg !.*]] +; CHECK-NEXT: ret void, [[DBG21:!dbg !.*]] +; +entry: + %a = alloca i32, align 4, !dbg !17 + call void @llvm.dbg.value(metadata i32* %a, metadata !9, metadata !DIExpression()), !dbg !17 + %b = alloca i32, align 4, !dbg !18 + call void @llvm.dbg.value(metadata i32* %b, metadata !11, metadata !DIExpression()), !dbg !18 + %c = alloca i32, align 4, !dbg !19 + call void @llvm.dbg.value(metadata i32* %c, metadata !12, metadata !DIExpression()), !dbg !19 + store i32 2, i32* %a, align 4, !dbg !20 + store i32 3, i32* %b, align 4, !dbg !21 + store i32 4, i32* %c, align 4, !dbg !22 + %al = load i32, i32* %a, align 4, !dbg !23 + call void @llvm.dbg.value(metadata i32 %al, metadata !13, metadata !DIExpression()), !dbg !23 + %bl = load i32, i32* %b, align 4, !dbg !24 + call void @llvm.dbg.value(metadata i32 %bl, metadata !15, metadata !DIExpression()), !dbg !24 + %cl = load i32, i32* %c, align 4, !dbg !25 + call void @llvm.dbg.value(metadata i32 %cl, metadata !16, metadata !DIExpression()), !dbg !25 + ret void, !dbg !26 +} + +define void @function2() !dbg !27 { +; CHECK-LABEL: @function2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4, [[DBG30:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[A]], [[META24:metadata !.*]], metadata !DIExpression()), [[DBG30]] +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4, [[DBG31:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[B]], [[META25:metadata !.*]], metadata !DIExpression()), [[DBG31]] +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4, [[DBG32:!dbg !.*]] +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32* [[C]], [[META26:metadata !.*]], metadata !DIExpression()), [[DBG32]] +; CHECK-NEXT: call void @function2.outlined(i32* [[A]], i32* [[B]], i32* [[C]]), [[DBG33:!dbg !.*]] +; CHECK-NEXT: ret void, [[DBG34:!dbg !.*]] +; +entry: + %a = alloca i32, align 4, !dbg !35 + call void @llvm.dbg.value(metadata i32* %a, metadata !29, metadata !DIExpression()), !dbg !35 + %b = alloca i32, align 4, !dbg !36 + call void @llvm.dbg.value(metadata i32* %b, metadata !30, metadata !DIExpression()), !dbg !36 + %c = alloca i32, align 4, !dbg !37 + call void @llvm.dbg.value(metadata i32* %c, metadata !31, metadata !DIExpression()), !dbg !37 + store i32 2, i32* %a, align 4, !dbg !38 + store i32 3, i32* %b, align 4, !dbg !39 + store i32 4, i32* %c, align 4, !dbg !40 + %al = load i32, i32* %a, align 4, !dbg !41 + call void @llvm.dbg.value(metadata i32 %al, metadata !32, metadata !DIExpression()), !dbg !41 + %bl = load i32, i32* %b, align 4, !dbg !42 + call void @llvm.dbg.value(metadata i32 %bl, metadata !33, metadata !DIExpression()), !dbg !42 + %cl = load i32, i32* %c, align 4, !dbg !43 + call void @llvm.dbg.value(metadata i32 %cl, metadata !34, metadata !DIExpression()), !dbg !43 + ret void, !dbg !44 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + +attributes #0 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.debugify = !{!3, !4} +!llvm.module.flags = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "legal-debug.ll", directory: "/") +!2 = !{} +!3 = !{i32 20} +!4 = !{i32 12} +!5 = !{i32 2, !"Debug Info Version", i32 3} +!6 = distinct !DISubprogram(name: "function1", linkageName: "function1", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8) +!7 = !DISubroutineType(types: !2) +!8 = !{!9, !11, !12, !13, !15, !16} +!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10) +!10 = !DIBasicType(name: "ty64", size: 64, encoding: DW_ATE_unsigned) +!11 = !DILocalVariable(name: "2", scope: !6, file: !1, line: 2, type: !10) +!12 = !DILocalVariable(name: "3", scope: !6, file: !1, line: 3, type: !10) +!13 = !DILocalVariable(name: "4", scope: !6, file: !1, line: 7, type: !14) +!14 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned) +!15 = !DILocalVariable(name: "5", scope: !6, file: !1, line: 8, type: !14) +!16 = !DILocalVariable(name: "6", scope: !6, file: !1, line: 9, type: !14) +!17 = !DILocation(line: 1, column: 1, scope: !6) +!18 = !DILocation(line: 2, column: 1, scope: !6) +!19 = !DILocation(line: 3, column: 1, scope: !6) +!20 = !DILocation(line: 4, column: 1, scope: !6) +!21 = !DILocation(line: 5, column: 1, scope: !6) +!22 = !DILocation(line: 6, column: 1, scope: !6) +!23 = !DILocation(line: 7, column: 1, scope: !6) +!24 = !DILocation(line: 8, column: 1, scope: !6) +!25 = !DILocation(line: 9, column: 1, scope: !6) +!26 = !DILocation(line: 10, column: 1, scope: !6) +!27 = distinct !DISubprogram(name: "function2", linkageName: "function2", scope: null, file: !1, line: 11, type: !7, scopeLine: 11, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !28) +!28 = !{!29, !30, !31, !32, !33, !34} +!29 = !DILocalVariable(name: "7", scope: !27, file: !1, line: 11, type: !10) +!30 = !DILocalVariable(name: "8", scope: !27, file: !1, line: 12, type: !10) +!31 = !DILocalVariable(name: "9", scope: !27, file: !1, line: 13, type: !10) +!32 = !DILocalVariable(name: "10", scope: !27, file: !1, line: 17, type: !14) +!33 = !DILocalVariable(name: "11", scope: !27, file: !1, line: 18, type: !14) +!34 = !DILocalVariable(name: "12", scope: !27, file: !1, line: 19, type: !14) +!35 = !DILocation(line: 11, column: 1, scope: !27) +!36 = !DILocation(line: 12, column: 1, scope: !27) +!37 = !DILocation(line: 13, column: 1, scope: !27) +!38 = !DILocation(line: 14, column: 1, scope: !27) +!39 = !DILocation(line: 15, column: 1, scope: !27) +!40 = !DILocation(line: 16, column: 1, scope: !27) +!41 = !DILocation(line: 17, column: 1, scope: !27) +!42 = !DILocation(line: 18, column: 1, scope: !27) +!43 = !DILocation(line: 19, column: 1, scope: !27) +!44 = !DILocation(line: 20, column: 1, scope: !27) _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits