diff --git a/llvm/include/llvm/Transforms/IPO/IROutliner.h b/llvm/include/llvm/Transforms/IPO/IROutliner.h --- a/llvm/include/llvm/Transforms/IPO/IROutliner.h +++ b/llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -321,9 +321,14 @@ // 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; } + // We only handle CallInsts that are not indirect, since we cannot guarantee + // that they have a name in these cases. + bool visitCallInst(CallInst &CI) { + Function *F = CI.getCalledFunction(); + if (!F || CI.isIndirectCall() || !F->hasName()) + return false; + return true; + } // 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 differently. diff --git a/llvm/test/Transforms/IROutliner/illegal-calls.ll b/llvm/test/Transforms/IROutliner/illegal-indirect-calls.ll rename from llvm/test/Transforms/IROutliner/illegal-calls.ll rename to llvm/test/Transforms/IROutliner/illegal-indirect-calls.ll --- a/llvm/test/Transforms/IROutliner/illegal-calls.ll +++ b/llvm/test/Transforms/IROutliner/illegal-indirect-calls.ll @@ -1,22 +1,21 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -verify -iroutliner -ir-outlining-no-cost < %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. +; This test checks that we do not outline indirect calls. We cannot guarantee +; that we have the same name in these cases, so two indirect calls cannot +; be considered similar. declare void @f1(i32*, i32*); +declare void @f2(i32*, i32*); -define void @outline_constants1() { -; CHECK-LABEL: @outline_constants1( +define void @function1(void()* %func) { +; 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: store i32 2, i32* [[A]], align 4 -; CHECK-NEXT: store i32 3, i32* [[B]], align 4 -; CHECK-NEXT: store i32 4, i32* [[C]], align 4 -; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: call void @outlined_ir_func_1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void [[FUNC:%.*]]() ; CHECK-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) ; CHECK-NEXT: ret void ; @@ -27,23 +26,21 @@ 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) + call void %func() %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( +define void @function2(void()* %func) { +; 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: store i32 2, i32* [[A]], align 4 -; CHECK-NEXT: store i32 3, i32* [[B]], align 4 -; CHECK-NEXT: store i32 4, i32* [[C]], align 4 -; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: call void @outlined_ir_func_1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void [[FUNC:%.*]]() ; CHECK-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) ; CHECK-NEXT: ret void ; @@ -54,7 +51,7 @@ 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) + call void %func() %al = load i32, i32* %a %bl = load i32, i32* %b %cl = load i32, i32* %c diff --git a/llvm/test/Transforms/IROutliner/outlining-calls.ll b/llvm/test/Transforms/IROutliner/outlining-calls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/outlining-calls.ll @@ -0,0 +1,93 @@ +; 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 can outline calls, but only if they have the same +; function type and the same name. + +declare void @f1(i32*, i32*); +declare void @f2(i32*, i32*); + +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 @outlined_ir_func_0(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 @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 @outlined_ir_func_0(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 @function3() { +; CHECK-LABEL: @function3( +; 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: store i32 2, i32* [[A]], align 4 +; CHECK-NEXT: store i32 3, i32* [[B]], align 4 +; CHECK-NEXT: store i32 4, i32* [[C]], align 4 +; CHECK-NEXT: call void @f2(i32* [[A]], i32* [[B]]) +; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[A]], align 4 +; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[B]], align 4 +; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[C]], align 4 +; 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 @f2(i32* %a, i32* %b) + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} + +; CHECK: define internal void @outlined_ir_func_0(i32* [[ARG0:%.*]], i32* [[ARG1:%.*]], i32* [[ARG2:%.*]]) +; CHECK: entry_to_outline: +; CHECK-NEXT: store i32 2, i32* [[ARG0]], align 4 +; CHECK-NEXT: store i32 3, i32* [[ARG1]], align 4 +; CHECK-NEXT: store i32 4, i32* [[ARG2]], align 4 +; CHECK-NEXT: call void @f1(i32* [[ARG0]], i32* [[ARG1]]) +; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[ARG0]], align 4 +; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[ARG1]], align 4 +; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[ARG2]], align 4