SPIRV-Tools/test/opt/inline_test.cpp
Steven Perron 07ce16d1e7 Set the parent for basic blocks during inlining.
Inlining is not setting the parent (function) for each basic block.
This can cause problems for later optimizations.  The solution is to set
the parent for each new block just before it is linked into the
function.
2017-12-12 13:39:08 -05:00

2613 lines
84 KiB
C++

// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pass_fixture.h"
#include "pass_utils.h"
namespace {
using namespace spvtools;
using InlineTest = PassTest<::testing::Test>;
TEST_F(InlineTest, Simple) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// return bar.x + bar.y;
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 140",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %color \"color\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %gl_FragColor \"gl_FragColor\"",
"%void = OpTypeVoid",
"%10 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%14 = OpTypeFunction %float %_ptr_Function_v4float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint_1 = OpConstant %uint 1",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %float None %14",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%26 = OpLabel",
"%27 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%28 = OpLoad %float %27",
"%29 = OpAccessChain %_ptr_Function_float %bar %uint_1",
"%30 = OpLoad %float %29",
"%31 = OpFAdd %float %28 %30",
"OpReturnValue %31",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %10",
"%21 = OpLabel",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%22 = OpLoad %v4float %BaseColor",
"OpStore %param %22",
"%23 = OpFunctionCall %float %foo_vf4_ %param",
"%24 = OpCompositeConstruct %v4float %23 %23 %23 %23",
"OpStore %color %24",
"%25 = OpLoad %v4float %color",
"OpStore %gl_FragColor %25",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %10",
"%21 = OpLabel",
"%32 = OpVariable %_ptr_Function_float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%22 = OpLoad %v4float %BaseColor",
"OpStore %param %22",
"%33 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%34 = OpLoad %float %33",
"%35 = OpAccessChain %_ptr_Function_float %param %uint_1",
"%36 = OpLoad %float %35",
"%37 = OpFAdd %float %34 %36",
"OpStore %32 %37",
"%23 = OpLoad %float %32",
"%24 = OpCompositeConstruct %v4float %23 %23 %23 %23",
"OpStore %color %24",
"%25 = OpLoad %v4float %color",
"OpStore %gl_FragColor %25",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, Nested) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo2(float f, float f2)
// {
// return f * f2;
// }
//
// float foo(vec4 bar)
// {
// return foo2(bar.x + bar.y, bar.z);
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 140",
"OpName %main \"main\"",
"OpName %foo2_f1_f1_ \"foo2(f1;f1;\"",
"OpName %f \"f\"",
"OpName %f2 \"f2\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %param \"param\"",
"OpName %param_0 \"param\"",
"OpName %color \"color\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param_1 \"param\"",
"OpName %gl_FragColor \"gl_FragColor\"",
"%void = OpTypeVoid",
"%15 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%_ptr_Function_float = OpTypePointer Function %float",
"%18 = OpTypeFunction %float %_ptr_Function_float %_ptr_Function_float",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%21 = OpTypeFunction %float %_ptr_Function_v4float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%uint_1 = OpConstant %uint 1",
"%uint_2 = OpConstant %uint 2",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo2_f1_f1_ = OpFunction %float None %18",
"%f = OpFunctionParameter %_ptr_Function_float",
"%f2 = OpFunctionParameter %_ptr_Function_float",
"%33 = OpLabel",
"%34 = OpLoad %float %f",
"%35 = OpLoad %float %f2",
"%36 = OpFMul %float %34 %35",
"OpReturnValue %36",
"OpFunctionEnd",
"%foo_vf4_ = OpFunction %float None %21",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%37 = OpLabel",
"%param = OpVariable %_ptr_Function_float Function",
"%param_0 = OpVariable %_ptr_Function_float Function",
"%38 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%39 = OpLoad %float %38",
"%40 = OpAccessChain %_ptr_Function_float %bar %uint_1",
"%41 = OpLoad %float %40",
"%42 = OpFAdd %float %39 %41",
"OpStore %param %42",
"%43 = OpAccessChain %_ptr_Function_float %bar %uint_2",
"%44 = OpLoad %float %43",
"OpStore %param_0 %44",
"%45 = OpFunctionCall %float %foo2_f1_f1_ %param %param_0",
"OpReturnValue %45",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %15",
"%28 = OpLabel",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param_1 = OpVariable %_ptr_Function_v4float Function",
"%29 = OpLoad %v4float %BaseColor",
"OpStore %param_1 %29",
"%30 = OpFunctionCall %float %foo_vf4_ %param_1",
"%31 = OpCompositeConstruct %v4float %30 %30 %30 %30",
"OpStore %color %31",
"%32 = OpLoad %v4float %color",
"OpStore %gl_FragColor %32",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %15",
"%28 = OpLabel",
"%57 = OpVariable %_ptr_Function_float Function",
"%46 = OpVariable %_ptr_Function_float Function",
"%47 = OpVariable %_ptr_Function_float Function",
"%48 = OpVariable %_ptr_Function_float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param_1 = OpVariable %_ptr_Function_v4float Function",
"%29 = OpLoad %v4float %BaseColor",
"OpStore %param_1 %29",
"%49 = OpAccessChain %_ptr_Function_float %param_1 %uint_0",
"%50 = OpLoad %float %49",
"%51 = OpAccessChain %_ptr_Function_float %param_1 %uint_1",
"%52 = OpLoad %float %51",
"%53 = OpFAdd %float %50 %52",
"OpStore %46 %53",
"%54 = OpAccessChain %_ptr_Function_float %param_1 %uint_2",
"%55 = OpLoad %float %54",
"OpStore %47 %55",
"%58 = OpLoad %float %46",
"%59 = OpLoad %float %47",
"%60 = OpFMul %float %58 %59",
"OpStore %57 %60",
"%56 = OpLoad %float %57",
"OpStore %48 %56",
"%30 = OpLoad %float %48",
"%31 = OpCompositeConstruct %v4float %30 %30 %30 %30",
"OpStore %color %31",
"%32 = OpLoad %v4float %color",
"OpStore %gl_FragColor %32",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, InOutParameter) {
// #version 400
//
// in vec4 Basecolor;
//
// void foo(inout vec4 bar)
// {
// bar.z = bar.x + bar.y;
// }
//
// void main()
// {
// vec4 b = Basecolor;
// foo(b);
// vec4 color = vec4(b.z);
// gl_FragColor = color;
// }
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %Basecolor %gl_FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 400",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %b \"b\"",
"OpName %Basecolor \"Basecolor\"",
"OpName %param \"param\"",
"OpName %color \"color\"",
"OpName %gl_FragColor \"gl_FragColor\"",
"%void = OpTypeVoid",
"%11 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%15 = OpTypeFunction %void %_ptr_Function_v4float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint_1 = OpConstant %uint 1",
"%uint_2 = OpConstant %uint 2",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%Basecolor = OpVariable %_ptr_Input_v4float Input",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %void None %15",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%32 = OpLabel",
"%33 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%34 = OpLoad %float %33",
"%35 = OpAccessChain %_ptr_Function_float %bar %uint_1",
"%36 = OpLoad %float %35",
"%37 = OpFAdd %float %34 %36",
"%38 = OpAccessChain %_ptr_Function_float %bar %uint_2",
"OpStore %38 %37",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %11",
"%23 = OpLabel",
"%b = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%24 = OpLoad %v4float %Basecolor",
"OpStore %b %24",
"%25 = OpLoad %v4float %b",
"OpStore %param %25",
"%26 = OpFunctionCall %void %foo_vf4_ %param",
"%27 = OpLoad %v4float %param",
"OpStore %b %27",
"%28 = OpAccessChain %_ptr_Function_float %b %uint_2",
"%29 = OpLoad %float %28",
"%30 = OpCompositeConstruct %v4float %29 %29 %29 %29",
"OpStore %color %30",
"%31 = OpLoad %v4float %color",
"OpStore %gl_FragColor %31",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %11",
"%23 = OpLabel",
"%b = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%24 = OpLoad %v4float %Basecolor",
"OpStore %b %24",
"%25 = OpLoad %v4float %b",
"OpStore %param %25",
"%39 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%40 = OpLoad %float %39",
"%41 = OpAccessChain %_ptr_Function_float %param %uint_1",
"%42 = OpLoad %float %41",
"%43 = OpFAdd %float %40 %42",
"%44 = OpAccessChain %_ptr_Function_float %param %uint_2",
"OpStore %44 %43",
"%27 = OpLoad %v4float %param",
"OpStore %b %27",
"%28 = OpAccessChain %_ptr_Function_float %b %uint_2",
"%29 = OpLoad %float %28",
"%30 = OpCompositeConstruct %v4float %29 %29 %29 %29",
"OpStore %color %30",
"%31 = OpLoad %v4float %color",
"OpStore %gl_FragColor %31",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, BranchInCallee) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// float r = bar.x;
// if (r < 0.0)
// r = -r;
// return r;
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
//
// gl_FragColor = color;
// }
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 140",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %r \"r\"",
"OpName %color \"color\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %gl_FragColor \"gl_FragColor\"",
"%void = OpTypeVoid",
"%11 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%15 = OpTypeFunction %float %_ptr_Function_v4float",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%float_0 = OpConstant %float 0",
"%bool = OpTypeBool",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %float None %15",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%28 = OpLabel",
"%r = OpVariable %_ptr_Function_float Function",
"%29 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%30 = OpLoad %float %29",
"OpStore %r %30",
"%31 = OpLoad %float %r",
"%32 = OpFOrdLessThan %bool %31 %float_0",
"OpSelectionMerge %33 None",
"OpBranchConditional %32 %34 %33",
"%34 = OpLabel",
"%35 = OpLoad %float %r",
"%36 = OpFNegate %float %35",
"OpStore %r %36",
"OpBranch %33",
"%33 = OpLabel",
"%37 = OpLoad %float %r",
"OpReturnValue %37",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %11",
"%23 = OpLabel",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%24 = OpLoad %v4float %BaseColor",
"OpStore %param %24",
"%25 = OpFunctionCall %float %foo_vf4_ %param",
"%26 = OpCompositeConstruct %v4float %25 %25 %25 %25",
"OpStore %color %26",
"%27 = OpLoad %v4float %color",
"OpStore %gl_FragColor %27",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %11",
"%23 = OpLabel",
"%38 = OpVariable %_ptr_Function_float Function",
"%39 = OpVariable %_ptr_Function_float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%24 = OpLoad %v4float %BaseColor",
"OpStore %param %24",
"%40 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%41 = OpLoad %float %40",
"OpStore %38 %41",
"%42 = OpLoad %float %38",
"%43 = OpFOrdLessThan %bool %42 %float_0",
"OpSelectionMerge %44 None",
"OpBranchConditional %43 %45 %44",
"%45 = OpLabel",
"%46 = OpLoad %float %38",
"%47 = OpFNegate %float %46",
"OpStore %38 %47",
"OpBranch %44",
"%44 = OpLabel",
"%48 = OpLoad %float %38",
"OpStore %39 %48",
"%25 = OpLoad %float %39",
"%26 = OpCompositeConstruct %v4float %25 %25 %25 %25",
"OpStore %color %26",
"%27 = OpLoad %v4float %color",
"OpStore %gl_FragColor %27",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, PhiAfterCall) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo(float bar)
// {
// float r = bar;
// if (r < 0.0)
// r = -r;
// return r;
// }
//
// void main()
// {
// vec4 color = BaseColor;
// if (foo(color.x) > 2.0 && foo(color.y) > 2.0)
// color = vec4(0.0);
// gl_FragColor = color;
// }
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 140",
"OpName %main \"main\"",
"OpName %foo_f1_ \"foo(f1;\"",
"OpName %bar \"bar\"",
"OpName %r \"r\"",
"OpName %color \"color\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %param_0 \"param\"",
"OpName %gl_FragColor \"gl_FragColor\"",
"%void = OpTypeVoid",
"%12 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%_ptr_Function_float = OpTypePointer Function %float",
"%15 = OpTypeFunction %float %_ptr_Function_float",
"%float_0 = OpConstant %float 0",
"%bool = OpTypeBool",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%float_2 = OpConstant %float 2",
"%uint_1 = OpConstant %uint 1",
"%25 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_f1_ = OpFunction %float None %15",
"%bar = OpFunctionParameter %_ptr_Function_float",
"%43 = OpLabel",
"%r = OpVariable %_ptr_Function_float Function",
"%44 = OpLoad %float %bar",
"OpStore %r %44",
"%45 = OpLoad %float %r",
"%46 = OpFOrdLessThan %bool %45 %float_0",
"OpSelectionMerge %47 None",
"OpBranchConditional %46 %48 %47",
"%48 = OpLabel",
"%49 = OpLoad %float %r",
"%50 = OpFNegate %float %49",
"OpStore %r %50",
"OpBranch %47",
"%47 = OpLabel",
"%51 = OpLoad %float %r",
"OpReturnValue %51",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %12",
"%27 = OpLabel",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_float Function",
"%param_0 = OpVariable %_ptr_Function_float Function",
"%28 = OpLoad %v4float %BaseColor",
"OpStore %color %28",
"%29 = OpAccessChain %_ptr_Function_float %color %uint_0",
"%30 = OpLoad %float %29",
"OpStore %param %30",
"%31 = OpFunctionCall %float %foo_f1_ %param",
"%32 = OpFOrdGreaterThan %bool %31 %float_2",
"OpSelectionMerge %33 None",
"OpBranchConditional %32 %34 %33",
"%34 = OpLabel",
"%35 = OpAccessChain %_ptr_Function_float %color %uint_1",
"%36 = OpLoad %float %35",
"OpStore %param_0 %36",
"%37 = OpFunctionCall %float %foo_f1_ %param_0",
"%38 = OpFOrdGreaterThan %bool %37 %float_2",
"OpBranch %33",
"%33 = OpLabel",
"%39 = OpPhi %bool %32 %27 %38 %34",
"OpSelectionMerge %40 None",
"OpBranchConditional %39 %41 %40",
"%41 = OpLabel",
"OpStore %color %25",
"OpBranch %40",
"%40 = OpLabel",
"%42 = OpLoad %v4float %color",
"OpStore %gl_FragColor %42",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %12",
"%27 = OpLabel",
"%62 = OpVariable %_ptr_Function_float Function",
"%63 = OpVariable %_ptr_Function_float Function",
"%52 = OpVariable %_ptr_Function_float Function",
"%53 = OpVariable %_ptr_Function_float Function",
"%color = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_float Function",
"%param_0 = OpVariable %_ptr_Function_float Function",
"%28 = OpLoad %v4float %BaseColor",
"OpStore %color %28",
"%29 = OpAccessChain %_ptr_Function_float %color %uint_0",
"%30 = OpLoad %float %29",
"OpStore %param %30",
"%54 = OpLoad %float %param",
"OpStore %52 %54",
"%55 = OpLoad %float %52",
"%56 = OpFOrdLessThan %bool %55 %float_0",
"OpSelectionMerge %57 None",
"OpBranchConditional %56 %58 %57",
"%58 = OpLabel",
"%59 = OpLoad %float %52",
"%60 = OpFNegate %float %59",
"OpStore %52 %60",
"OpBranch %57",
"%57 = OpLabel",
"%61 = OpLoad %float %52",
"OpStore %53 %61",
"%31 = OpLoad %float %53",
"%32 = OpFOrdGreaterThan %bool %31 %float_2",
"OpSelectionMerge %33 None",
"OpBranchConditional %32 %34 %33",
"%34 = OpLabel",
"%35 = OpAccessChain %_ptr_Function_float %color %uint_1",
"%36 = OpLoad %float %35",
"OpStore %param_0 %36",
"%64 = OpLoad %float %param_0",
"OpStore %62 %64",
"%65 = OpLoad %float %62",
"%66 = OpFOrdLessThan %bool %65 %float_0",
"OpSelectionMerge %67 None",
"OpBranchConditional %66 %68 %67",
"%68 = OpLabel",
"%69 = OpLoad %float %62",
"%70 = OpFNegate %float %69",
"OpStore %62 %70",
"OpBranch %67",
"%67 = OpLabel",
"%71 = OpLoad %float %62",
"OpStore %63 %71",
"%37 = OpLoad %float %63",
"%38 = OpFOrdGreaterThan %bool %37 %float_2",
"OpBranch %33",
"%33 = OpLabel",
"%39 = OpPhi %bool %32 %57 %38 %67",
"OpSelectionMerge %40 None",
"OpBranchConditional %39 %41 %40",
"%41 = OpLabel",
"OpStore %color %25",
"OpBranch %40",
"%40 = OpLabel",
"%42 = OpLoad %v4float %color",
"OpStore %gl_FragColor %42",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, OpSampledImageOutOfBlock) {
// #version 450
//
// uniform texture2D t2D;
// uniform sampler samp;
// out vec4 FragColor;
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// float r = bar.x;
// if (r < 0.0)
// r = -r;
// return r;
// }
//
// void main()
// {
// vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
// vec4 color2 = vec4(foo(BaseColor));
// vec4 color3 = texture(sampler2D(t2D, samp), vec2(0.5));
// FragColor = (color1 + color2 + color3)/3;
// }
//
// Note: the before SPIR-V will need to be edited to create a use of
// the OpSampledImage across the function call.
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 450",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %r \"r\"",
"OpName %color1 \"color1\"",
"OpName %t2D \"t2D\"",
"OpName %samp \"samp\"",
"OpName %color2 \"color2\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %color3 \"color3\"",
"OpName %FragColor \"FragColor\"",
"OpDecorate %t2D DescriptorSet 0",
"OpDecorate %samp DescriptorSet 0",
"%void = OpTypeVoid",
"%15 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%19 = OpTypeFunction %float %_ptr_Function_v4float",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%float_0 = OpConstant %float 0",
"%bool = OpTypeBool",
"%25 = OpTypeImage %float 2D 0 0 0 1 Unknown",
"%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25",
"%t2D = OpVariable %_ptr_UniformConstant_25 UniformConstant",
"%27 = OpTypeSampler",
"%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27",
"%samp = OpVariable %_ptr_UniformConstant_27 UniformConstant",
"%29 = OpTypeSampledImage %25",
"%v2float = OpTypeVector %float 2",
"%float_1 = OpConstant %float 1",
"%32 = OpConstantComposite %v2float %float_1 %float_1",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%float_0_5 = OpConstant %float 0.5",
"%35 = OpConstantComposite %v2float %float_0_5 %float_0_5",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%FragColor = OpVariable %_ptr_Output_v4float Output",
"%float_3 = OpConstant %float 3",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %float None %19",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%56 = OpLabel",
"%r = OpVariable %_ptr_Function_float Function",
"%57 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%58 = OpLoad %float %57",
"OpStore %r %58",
"%59 = OpLoad %float %r",
"%60 = OpFOrdLessThan %bool %59 %float_0",
"OpSelectionMerge %61 None",
"OpBranchConditional %60 %62 %61",
"%62 = OpLabel",
"%63 = OpLoad %float %r",
"%64 = OpFNegate %float %63",
"OpStore %r %64",
"OpBranch %61",
"%61 = OpLabel",
"%65 = OpLoad %float %r",
"OpReturnValue %65",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %15",
"%38 = OpLabel",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%39 = OpLoad %25 %t2D",
"%40 = OpLoad %27 %samp",
"%41 = OpSampledImage %29 %39 %40",
"%42 = OpImageSampleImplicitLod %v4float %41 %32",
"OpStore %color1 %42",
"%43 = OpLoad %v4float %BaseColor",
"OpStore %param %43",
"%44 = OpFunctionCall %float %foo_vf4_ %param",
"%45 = OpCompositeConstruct %v4float %44 %44 %44 %44",
"OpStore %color2 %45",
"%46 = OpLoad %25 %t2D",
"%47 = OpLoad %27 %samp",
"%48 = OpImageSampleImplicitLod %v4float %41 %35",
"OpStore %color3 %48",
"%49 = OpLoad %v4float %color1",
"%50 = OpLoad %v4float %color2",
"%51 = OpFAdd %v4float %49 %50",
"%52 = OpLoad %v4float %color3",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%55 = OpFDiv %v4float %53 %54",
"OpStore %FragColor %55",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %15",
"%38 = OpLabel",
"%66 = OpVariable %_ptr_Function_float Function",
"%67 = OpVariable %_ptr_Function_float Function",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%39 = OpLoad %25 %t2D",
"%40 = OpLoad %27 %samp",
"%41 = OpSampledImage %29 %39 %40",
"%42 = OpImageSampleImplicitLod %v4float %41 %32",
"OpStore %color1 %42",
"%43 = OpLoad %v4float %BaseColor",
"OpStore %param %43",
"%68 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%69 = OpLoad %float %68",
"OpStore %66 %69",
"%70 = OpLoad %float %66",
"%71 = OpFOrdLessThan %bool %70 %float_0",
"OpSelectionMerge %72 None",
"OpBranchConditional %71 %73 %72",
"%73 = OpLabel",
"%74 = OpLoad %float %66",
"%75 = OpFNegate %float %74",
"OpStore %66 %75",
"OpBranch %72",
"%72 = OpLabel",
"%76 = OpLoad %float %66",
"OpStore %67 %76",
"%44 = OpLoad %float %67",
"%45 = OpCompositeConstruct %v4float %44 %44 %44 %44",
"OpStore %color2 %45",
"%46 = OpLoad %25 %t2D",
"%47 = OpLoad %27 %samp",
"%77 = OpSampledImage %29 %39 %40",
"%48 = OpImageSampleImplicitLod %v4float %77 %35",
"OpStore %color3 %48",
"%49 = OpLoad %v4float %color1",
"%50 = OpLoad %v4float %color2",
"%51 = OpFAdd %v4float %49 %50",
"%52 = OpLoad %v4float %color3",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%55 = OpFDiv %v4float %53 %54",
"OpStore %FragColor %55",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, OpImageOutOfBlock) {
// #version 450
//
// uniform texture2D t2D;
// uniform sampler samp;
// uniform sampler samp2;
//
// out vec4 FragColor;
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// float r = bar.x;
// if (r < 0.0)
// r = -r;
// return r;
// }
//
// void main()
// {
// vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
// vec4 color2 = vec4(foo(BaseColor));
// vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5));
// FragColor = (color1 + color2 + color3)/3;
// }
// Note: the before SPIR-V will need to be edited to create an OpImage
// from the first OpSampledImage, place it before the call and use it
// in the second OpSampledImage following the call.
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 450",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %r \"r\"",
"OpName %color1 \"color1\"",
"OpName %t2D \"t2D\"",
"OpName %samp \"samp\"",
"OpName %color2 \"color2\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %color3 \"color3\"",
"OpName %samp2 \"samp2\"",
"OpName %FragColor \"FragColor\"",
"OpDecorate %t2D DescriptorSet 0",
"OpDecorate %samp DescriptorSet 0",
"OpDecorate %samp2 DescriptorSet 0",
"%void = OpTypeVoid",
"%16 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%20 = OpTypeFunction %float %_ptr_Function_v4float",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%float_0 = OpConstant %float 0",
"%bool = OpTypeBool",
"%26 = OpTypeImage %float 2D 0 0 0 1 Unknown",
"%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26",
"%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant",
"%28 = OpTypeSampler",
"%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28",
"%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant",
"%30 = OpTypeSampledImage %26",
"%v2float = OpTypeVector %float 2",
"%float_1 = OpConstant %float 1",
"%33 = OpConstantComposite %v2float %float_1 %float_1",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant",
"%float_0_5 = OpConstant %float 0.5",
"%36 = OpConstantComposite %v2float %float_0_5 %float_0_5",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%FragColor = OpVariable %_ptr_Output_v4float Output",
"%float_3 = OpConstant %float 3",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %float None %20",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%58 = OpLabel",
"%r = OpVariable %_ptr_Function_float Function",
"%59 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%60 = OpLoad %float %59",
"OpStore %r %60",
"%61 = OpLoad %float %r",
"%62 = OpFOrdLessThan %bool %61 %float_0",
"OpSelectionMerge %63 None",
"OpBranchConditional %62 %64 %63",
"%64 = OpLabel",
"%65 = OpLoad %float %r",
"%66 = OpFNegate %float %65",
"OpStore %r %66",
"OpBranch %63",
"%63 = OpLabel",
"%67 = OpLoad %float %r",
"OpReturnValue %67",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %16",
"%39 = OpLabel",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%40 = OpLoad %26 %t2D",
"%41 = OpLoad %28 %samp",
"%42 = OpSampledImage %30 %40 %41",
"%43 = OpImageSampleImplicitLod %v4float %42 %33",
"%44 = OpImage %26 %42",
"%45 = OpLoad %28 %samp2",
"OpStore %color1 %43",
"%46 = OpLoad %v4float %BaseColor",
"OpStore %param %46",
"%47 = OpFunctionCall %float %foo_vf4_ %param",
"%48 = OpCompositeConstruct %v4float %47 %47 %47 %47",
"OpStore %color2 %48",
"%49 = OpSampledImage %30 %44 %45",
"%50 = OpImageSampleImplicitLod %v4float %49 %36",
"OpStore %color3 %50",
"%51 = OpLoad %v4float %color1",
"%52 = OpLoad %v4float %color2",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpLoad %v4float %color3",
"%55 = OpFAdd %v4float %53 %54",
"%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%57 = OpFDiv %v4float %55 %56",
"OpStore %FragColor %57",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %16",
"%39 = OpLabel",
"%68 = OpVariable %_ptr_Function_float Function",
"%69 = OpVariable %_ptr_Function_float Function",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%40 = OpLoad %26 %t2D",
"%41 = OpLoad %28 %samp",
"%42 = OpSampledImage %30 %40 %41",
"%43 = OpImageSampleImplicitLod %v4float %42 %33",
"%44 = OpImage %26 %42",
"%45 = OpLoad %28 %samp2",
"OpStore %color1 %43",
"%46 = OpLoad %v4float %BaseColor",
"OpStore %param %46",
"%70 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%71 = OpLoad %float %70",
"OpStore %68 %71",
"%72 = OpLoad %float %68",
"%73 = OpFOrdLessThan %bool %72 %float_0",
"OpSelectionMerge %74 None",
"OpBranchConditional %73 %75 %74",
"%75 = OpLabel",
"%76 = OpLoad %float %68",
"%77 = OpFNegate %float %76",
"OpStore %68 %77",
"OpBranch %74",
"%74 = OpLabel",
"%78 = OpLoad %float %68",
"OpStore %69 %78",
"%47 = OpLoad %float %69",
"%48 = OpCompositeConstruct %v4float %47 %47 %47 %47",
"OpStore %color2 %48",
"%79 = OpSampledImage %30 %40 %41",
"%80 = OpImage %26 %79",
"%49 = OpSampledImage %30 %80 %45",
"%50 = OpImageSampleImplicitLod %v4float %49 %36",
"OpStore %color3 %50",
"%51 = OpLoad %v4float %color1",
"%52 = OpLoad %v4float %color2",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpLoad %v4float %color3",
"%55 = OpFAdd %v4float %53 %54",
"%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%57 = OpFDiv %v4float %55 %56",
"OpStore %FragColor %57",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, OpImageAndOpSampledImageOutOfBlock) {
// #version 450
//
// uniform texture2D t2D;
// uniform sampler samp;
// uniform sampler samp2;
//
// out vec4 FragColor;
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// float r = bar.x;
// if (r < 0.0)
// r = -r;
// return r;
// }
//
// void main()
// {
// vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
// vec4 color2 = vec4(foo(BaseColor));
// vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5));
// FragColor = (color1 + color2 + color3)/3;
// }
// Note: the before SPIR-V will need to be edited to create an OpImage
// and subsequent OpSampledImage that is used across the function call.
const std::vector<const char*> predefs = {
// clang-format off
"OpCapability Shader",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
"OpExecutionMode %main OriginUpperLeft",
"OpSource GLSL 450",
"OpName %main \"main\"",
"OpName %foo_vf4_ \"foo(vf4;\"",
"OpName %bar \"bar\"",
"OpName %r \"r\"",
"OpName %color1 \"color1\"",
"OpName %t2D \"t2D\"",
"OpName %samp \"samp\"",
"OpName %color2 \"color2\"",
"OpName %BaseColor \"BaseColor\"",
"OpName %param \"param\"",
"OpName %color3 \"color3\"",
"OpName %samp2 \"samp2\"",
"OpName %FragColor \"FragColor\"",
"OpDecorate %t2D DescriptorSet 0",
"OpDecorate %samp DescriptorSet 0",
"OpDecorate %samp2 DescriptorSet 0",
"%void = OpTypeVoid",
"%16 = OpTypeFunction %void",
"%float = OpTypeFloat 32",
"%v4float = OpTypeVector %float 4",
"%_ptr_Function_v4float = OpTypePointer Function %v4float",
"%20 = OpTypeFunction %float %_ptr_Function_v4float",
"%_ptr_Function_float = OpTypePointer Function %float",
"%uint = OpTypeInt 32 0",
"%uint_0 = OpConstant %uint 0",
"%float_0 = OpConstant %float 0",
"%bool = OpTypeBool",
"%26 = OpTypeImage %float 2D 0 0 0 1 Unknown",
"%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26",
"%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant",
"%28 = OpTypeSampler",
"%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28",
"%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant",
"%30 = OpTypeSampledImage %26",
"%v2float = OpTypeVector %float 2",
"%float_1 = OpConstant %float 1",
"%33 = OpConstantComposite %v2float %float_1 %float_1",
"%_ptr_Input_v4float = OpTypePointer Input %v4float",
"%BaseColor = OpVariable %_ptr_Input_v4float Input",
"%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant",
"%float_0_5 = OpConstant %float 0.5",
"%36 = OpConstantComposite %v2float %float_0_5 %float_0_5",
"%_ptr_Output_v4float = OpTypePointer Output %v4float",
"%FragColor = OpVariable %_ptr_Output_v4float Output",
"%float_3 = OpConstant %float 3",
// clang-format on
};
const std::vector<const char*> nonEntryFuncs = {
// clang-format off
"%foo_vf4_ = OpFunction %float None %20",
"%bar = OpFunctionParameter %_ptr_Function_v4float",
"%58 = OpLabel",
"%r = OpVariable %_ptr_Function_float Function",
"%59 = OpAccessChain %_ptr_Function_float %bar %uint_0",
"%60 = OpLoad %float %59",
"OpStore %r %60",
"%61 = OpLoad %float %r",
"%62 = OpFOrdLessThan %bool %61 %float_0",
"OpSelectionMerge %63 None",
"OpBranchConditional %62 %64 %63",
"%64 = OpLabel",
"%65 = OpLoad %float %r",
"%66 = OpFNegate %float %65",
"OpStore %r %66",
"OpBranch %63",
"%63 = OpLabel",
"%67 = OpLoad %float %r",
"OpReturnValue %67",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> before = {
// clang-format off
"%main = OpFunction %void None %16",
"%39 = OpLabel",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%40 = OpLoad %26 %t2D",
"%41 = OpLoad %28 %samp",
"%42 = OpSampledImage %30 %40 %41",
"%43 = OpImageSampleImplicitLod %v4float %42 %33",
"%44 = OpImage %26 %42",
"%45 = OpLoad %28 %samp2",
"%46 = OpSampledImage %30 %44 %45",
"OpStore %color1 %43",
"%47 = OpLoad %v4float %BaseColor",
"OpStore %param %47",
"%48 = OpFunctionCall %float %foo_vf4_ %param",
"%49 = OpCompositeConstruct %v4float %48 %48 %48 %48",
"OpStore %color2 %49",
"%50 = OpImageSampleImplicitLod %v4float %46 %36",
"OpStore %color3 %50",
"%51 = OpLoad %v4float %color1",
"%52 = OpLoad %v4float %color2",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpLoad %v4float %color3",
"%55 = OpFAdd %v4float %53 %54",
"%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%57 = OpFDiv %v4float %55 %56",
"OpStore %FragColor %57",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
const std::vector<const char*> after = {
// clang-format off
"%main = OpFunction %void None %16",
"%39 = OpLabel",
"%68 = OpVariable %_ptr_Function_float Function",
"%69 = OpVariable %_ptr_Function_float Function",
"%color1 = OpVariable %_ptr_Function_v4float Function",
"%color2 = OpVariable %_ptr_Function_v4float Function",
"%param = OpVariable %_ptr_Function_v4float Function",
"%color3 = OpVariable %_ptr_Function_v4float Function",
"%40 = OpLoad %26 %t2D",
"%41 = OpLoad %28 %samp",
"%42 = OpSampledImage %30 %40 %41",
"%43 = OpImageSampleImplicitLod %v4float %42 %33",
"%44 = OpImage %26 %42",
"%45 = OpLoad %28 %samp2",
"%46 = OpSampledImage %30 %44 %45",
"OpStore %color1 %43",
"%47 = OpLoad %v4float %BaseColor",
"OpStore %param %47",
"%70 = OpAccessChain %_ptr_Function_float %param %uint_0",
"%71 = OpLoad %float %70",
"OpStore %68 %71",
"%72 = OpLoad %float %68",
"%73 = OpFOrdLessThan %bool %72 %float_0",
"OpSelectionMerge %74 None",
"OpBranchConditional %73 %75 %74",
"%75 = OpLabel",
"%76 = OpLoad %float %68",
"%77 = OpFNegate %float %76",
"OpStore %68 %77",
"OpBranch %74",
"%74 = OpLabel",
"%78 = OpLoad %float %68",
"OpStore %69 %78",
"%48 = OpLoad %float %69",
"%49 = OpCompositeConstruct %v4float %48 %48 %48 %48",
"OpStore %color2 %49",
"%79 = OpSampledImage %30 %40 %41",
"%80 = OpImage %26 %79",
"%81 = OpSampledImage %30 %80 %45",
"%50 = OpImageSampleImplicitLod %v4float %81 %36",
"OpStore %color3 %50",
"%51 = OpLoad %v4float %color1",
"%52 = OpLoad %v4float %color2",
"%53 = OpFAdd %v4float %51 %52",
"%54 = OpLoad %v4float %color3",
"%55 = OpFAdd %v4float %53 %54",
"%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
"%57 = OpFDiv %v4float %55 %56",
"OpStore %FragColor %57",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
/* skip_nop = */ false, /* do_validate = */ true);
}
TEST_F(InlineTest, EarlyReturnFunctionInlined) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// if (bar.x < 0.0)
// return 0.0;
// return bar.x;
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %foo_vf4_ "foo(vf4;"
OpName %bar "bar"
OpName %color "color"
OpName %BaseColor "BaseColor"
OpName %param "param"
OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%14 = OpTypeFunction %float %_ptr_Function_v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string nonEntryFuncs =
R"(%foo_vf4_ = OpFunction %float None %14
%bar = OpFunctionParameter %_ptr_Function_v4float
%27 = OpLabel
%28 = OpAccessChain %_ptr_Function_float %bar %uint_0
%29 = OpLoad %float %28
%30 = OpFOrdLessThan %bool %29 %float_0
OpSelectionMerge %31 None
OpBranchConditional %30 %32 %31
%32 = OpLabel
OpReturnValue %float_0
%31 = OpLabel
%33 = OpAccessChain %_ptr_Function_float %bar %uint_0
%34 = OpLoad %float %33
OpReturnValue %34
OpFunctionEnd
)";
const std::string before =
R"(%main = OpFunction %void None %10
%22 = OpLabel
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%23 = OpLoad %v4float %BaseColor
OpStore %param %23
%24 = OpFunctionCall %float %foo_vf4_ %param
%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
OpStore %color %25
%26 = OpLoad %v4float %color
OpStore %gl_FragColor %26
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%false = OpConstantFalse %bool
%main = OpFunction %void None %10
%22 = OpLabel
%35 = OpVariable %_ptr_Function_float Function
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%23 = OpLoad %v4float %BaseColor
OpStore %param %23
OpBranch %36
%36 = OpLabel
OpLoopMerge %37 %38 None
OpBranch %39
%39 = OpLabel
%40 = OpAccessChain %_ptr_Function_float %param %uint_0
%41 = OpLoad %float %40
%42 = OpFOrdLessThan %bool %41 %float_0
OpSelectionMerge %43 None
OpBranchConditional %42 %44 %43
%44 = OpLabel
OpStore %35 %float_0
OpBranch %37
%43 = OpLabel
%45 = OpAccessChain %_ptr_Function_float %param %uint_0
%46 = OpLoad %float %45
OpStore %35 %46
OpBranch %37
%38 = OpLabel
OpBranchConditional %false %36 %37
%37 = OpLabel
%24 = OpLoad %float %35
%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
OpStore %color %25
%26 = OpLoad %v4float %color
OpStore %gl_FragColor %26
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + before + nonEntryFuncs, predefs + after + nonEntryFuncs, false,
true);
}
TEST_F(InlineTest, EarlyReturnNotAppearingLastInFunctionInlined) {
// Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/755
//
// Original example is derived from:
//
// #version 450
//
// float foo() {
// if (true) {
// }
// }
//
// void main() { foo(); }
//
// But the order of basic blocks in foo is changed so that the return
// block is listed second-last. There is only one return in the callee
// but it does not appear last.
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpName %main "main"
OpName %foo_ "foo("
%void = OpTypeVoid
%4 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
)";
const std::string nonEntryFuncs =
R"(%foo_ = OpFunction %void None %4
%7 = OpLabel
OpSelectionMerge %8 None
OpBranchConditional %true %9 %8
%8 = OpLabel
OpReturn
%9 = OpLabel
OpBranch %8
OpFunctionEnd
)";
const std::string before =
R"(%main = OpFunction %void None %4
%10 = OpLabel
%11 = OpFunctionCall %void %foo_
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %4
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %true %13 %12
%12 = OpLabel
OpBranch %14
%13 = OpLabel
OpBranch %12
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, ForwardReferencesInPhiInlined) {
// The basic structure of the test case is like this:
//
// int foo() {
// int result = 1;
// if (true) {
// result = 1;
// }
// return result;
// }
//
// void main() {
// int x = foo();
// }
//
// but with modifications: Using Phi instead of load/store, and the
// return block in foo appears before the "then" block.
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpName %main "main"
OpName %foo_ "foo("
OpName %x "x"
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%8 = OpTypeFunction %int
%bool = OpTypeBool
%true = OpConstantTrue %bool
%int_0 = OpConstant %int 0
%_ptr_Function_int = OpTypePointer Function %int
)";
const std::string nonEntryFuncs =
R"(%foo_ = OpFunction %int None %8
%13 = OpLabel
%14 = OpCopyObject %int %int_0
OpSelectionMerge %15 None
OpBranchConditional %true %16 %15
%15 = OpLabel
%17 = OpPhi %int %14 %13 %18 %16
OpReturnValue %17
%16 = OpLabel
%18 = OpCopyObject %int %int_0
OpBranch %15
OpFunctionEnd
)";
const std::string before =
R"(%main = OpFunction %void None %6
%19 = OpLabel
%x = OpVariable %_ptr_Function_int Function
%20 = OpFunctionCall %int %foo_
OpStore %x %20
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %6
%19 = OpLabel
%21 = OpVariable %_ptr_Function_int Function
%x = OpVariable %_ptr_Function_int Function
%22 = OpCopyObject %int %int_0
OpSelectionMerge %23 None
OpBranchConditional %true %24 %23
%23 = OpLabel
%26 = OpPhi %int %22 %19 %25 %24
OpStore %21 %26
OpBranch %27
%24 = OpLabel
%25 = OpCopyObject %int %int_0
OpBranch %23
%27 = OpLabel
%20 = OpLoad %int %21
OpStore %x %20
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, EarlyReturnInLoopIsNotInlined) {
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// while (true) {
// if (bar.x < 0.0)
// return 0.0;
// return bar.x;
// }
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::string assembly =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %foo_vf4_ "foo(vf4;"
OpName %bar "bar"
OpName %color "color"
OpName %BaseColor "BaseColor"
OpName %param "param"
OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%14 = OpTypeFunction %float %_ptr_Function_v4float
%bool = OpTypeBool
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %10
%23 = OpLabel
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%24 = OpLoad %v4float %BaseColor
OpStore %param %24
%25 = OpFunctionCall %float %foo_vf4_ %param
%26 = OpCompositeConstruct %v4float %25 %25 %25 %25
OpStore %color %26
%27 = OpLoad %v4float %color
OpStore %gl_FragColor %27
OpReturn
OpFunctionEnd
%foo_vf4_ = OpFunction %float None %14
%bar = OpFunctionParameter %_ptr_Function_v4float
%28 = OpLabel
OpBranch %29
%29 = OpLabel
OpLoopMerge %30 %31 None
OpBranch %32
%32 = OpLabel
OpBranchConditional %true %33 %30
%33 = OpLabel
%34 = OpAccessChain %_ptr_Function_float %bar %uint_0
%35 = OpLoad %float %34
%36 = OpFOrdLessThan %bool %35 %float_0
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %37
%38 = OpLabel
OpReturnValue %float_0
%37 = OpLabel
%39 = OpAccessChain %_ptr_Function_float %bar %uint_0
%40 = OpLoad %float %39
OpReturnValue %40
%31 = OpLabel
OpBranch %29
%30 = OpLabel
%41 = OpUndef %float
OpReturnValue %41
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(assembly, assembly, false,
true);
}
TEST_F(InlineTest, ExternalFunctionIsNotInlined) {
// In particular, don't crash.
// See report https://github.com/KhronosGroup/SPIRV-Tools/issues/605
const std::string assembly =
R"(OpCapability Addresses
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Physical32 OpenCL
OpEntryPoint Kernel %1 "entry_pt"
OpDecorate %2 LinkageAttributes "external" Import
%void = OpTypeVoid
%4 = OpTypeFunction %void
%2 = OpFunction %void None %4
OpFunctionEnd
%1 = OpFunction %void None %4
%5 = OpLabel
%6 = OpFunctionCall %void %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(assembly, assembly, false,
true);
}
TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCallee) {
// Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/787
//
// CFG structure is:
// foo:
// fooentry -> fooexit
//
// main:
// entry -> loop
// loop -> loop, merge
// loop calls foo()
// merge
//
// Since the callee has multiple blocks, it will split the calling block
// into at least two, resulting in a new "back-half" block that contains
// the instructions after the inlined function call. If the calling block
// has an OpLoopMerge that points back to the calling block itself, then
// the OpLoopMerge can't remain in the back-half block, but must be
// moved to the end of the original calling block, and it continue target
// operand updated to point to the back-half block.
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%bool = OpTypeBool
%true = OpConstantTrue %bool
%void = OpTypeVoid
)";
const std::string nonEntryFuncs =
R"(%5 = OpTypeFunction %void
%6 = OpFunction %void None %5
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %5
%9 = OpLabel
OpBranch %10
%10 = OpLabel
%11 = OpFunctionCall %void %6
OpLoopMerge %12 %10 None
OpBranchConditional %true %10 %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%1 = OpFunction %void None %5
%9 = OpLabel
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %13
%13 = OpLabel
OpBranchConditional %true %10 %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, MultiBlockLoopHeaderCallsMultiBlockCallee) {
// Like SingleBlockLoopCallsMultiBlockCallee but the loop has several
// blocks, but the function call still occurs in the loop header.
// Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/800
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%bool = OpTypeBool
%true = OpConstantTrue %bool
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%int_5 = OpConstant %int 5
%void = OpTypeVoid
%11 = OpTypeFunction %void
)";
const std::string nonEntryFuncs =
R"(%12 = OpFunction %void None %11
%13 = OpLabel
%14 = OpCopyObject %int %int_1
OpBranch %15
%15 = OpLabel
%16 = OpCopyObject %int %int_2
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %11
%17 = OpLabel
OpBranch %18
%18 = OpLabel
%19 = OpCopyObject %int %int_3
%20 = OpFunctionCall %void %12
%21 = OpCopyObject %int %int_4
OpLoopMerge %22 %23 None
OpBranchConditional %true %23 %22
%23 = OpLabel
%24 = OpCopyObject %int %int_5
OpBranchConditional %true %18 %22
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%1 = OpFunction %void None %11
%17 = OpLabel
OpBranch %18
%18 = OpLabel
%19 = OpCopyObject %int %int_3
%25 = OpCopyObject %int %int_1
OpLoopMerge %22 %23 None
OpBranch %26
%26 = OpLabel
%27 = OpCopyObject %int %int_2
%21 = OpCopyObject %int %int_4
OpBranchConditional %true %23 %22
%23 = OpLabel
%24 = OpCopyObject %int %int_5
OpBranchConditional %true %18 %22
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge) {
// This is similar to SingleBlockLoopCallsMultiBlockCallee except
// that calleee block also has a merge instruction in its first block.
// That merge instruction must be an OpSelectionMerge (because the entry
// block of a function can't be the header of a loop since the entry
// block can't be the target of a branch).
//
// In this case the OpLoopMerge can't be placed in the same block as
// the OpSelectionMerge, so inlining must create a new block to contain
// the callee contents.
//
// Additionally, we have two dummy OpCopyObject instructions to prove that
// the OpLoopMerge is moved to the right location.
//
// Also ensure that OpPhis within the cloned callee code are valid.
// We need to test that the predecessor blocks are remapped correctly so that
// dominance rules are satisfied
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%void = OpTypeVoid
%6 = OpTypeFunction %void
)";
// This callee has multiple blocks, and an OpPhi in the last block
// that references a value from the first block. This tests that
// cloned block IDs are remapped appropriately. The OpPhi dominance
// requires that the remapped %9 must be in a block that dominates
// the remapped %8.
const std::string nonEntryFuncs =
R"(%7 = OpFunction %void None %6
%8 = OpLabel
%9 = OpCopyObject %bool %true
OpSelectionMerge %10 None
OpBranchConditional %true %10 %10
%10 = OpLabel
%11 = OpPhi %bool %9 %8
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %6
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpCopyObject %bool %false
%15 = OpFunctionCall %void %7
OpLoopMerge %16 %13 None
OpBranchConditional %true %13 %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
// Note the remapped Phi uses %17 as the parent instead
// of %13, demonstrating that the parent block has been remapped
// correctly.
const std::string after =
R"(%1 = OpFunction %void None %6
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpCopyObject %bool %false
OpLoopMerge %16 %19 None
OpBranch %17
%17 = OpLabel
%18 = OpCopyObject %bool %true
OpSelectionMerge %19 None
OpBranchConditional %true %19 %19
%19 = OpLabel
%20 = OpPhi %bool %18 %17
OpBranchConditional %true %13 %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest,
MultiBlockLoopHeaderCallsFromToMultiBlockCalleeHavingSelectionMerge) {
// This is similar to SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge
// but the call is in the header block of a multi block loop.
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%bool = OpTypeBool
%true = OpConstantTrue %bool
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%int_5 = OpConstant %int 5
%void = OpTypeVoid
%11 = OpTypeFunction %void
)";
const std::string nonEntryFuncs =
R"(%12 = OpFunction %void None %11
%13 = OpLabel
%14 = OpCopyObject %int %int_1
OpSelectionMerge %15 None
OpBranchConditional %true %15 %15
%15 = OpLabel
%16 = OpCopyObject %int %int_2
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %11
%17 = OpLabel
OpBranch %18
%18 = OpLabel
%19 = OpCopyObject %int %int_3
%20 = OpFunctionCall %void %12
%21 = OpCopyObject %int %int_4
OpLoopMerge %22 %23 None
OpBranchConditional %true %23 %22
%23 = OpLabel
%24 = OpCopyObject %int %int_5
OpBranchConditional %true %18 %22
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%1 = OpFunction %void None %11
%17 = OpLabel
OpBranch %18
%18 = OpLabel
%19 = OpCopyObject %int %int_3
OpLoopMerge %22 %23 None
OpBranch %25
%25 = OpLabel
%26 = OpCopyObject %int %int_1
OpSelectionMerge %27 None
OpBranchConditional %true %27 %27
%27 = OpLabel
%28 = OpCopyObject %int %int_2
%21 = OpCopyObject %int %int_4
OpBranchConditional %true %23 %22
%23 = OpLabel
%24 = OpCopyObject %int %int_5
OpBranchConditional %true %18 %22
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(
InlineTest,
SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMergeAndMultiReturns) {
// This is similar to SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge
// except that in addition to starting with a selection header, the
// callee also has multi returns.
//
// So now we have to accommodate:
// - The caller's OpLoopMerge (which must move to the first block)
// - The single-trip loop to wrap the multi returns, and
// - The callee's selection merge in its first block.
// Each of these must go into their own blocks.
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%bool = OpTypeBool
%int = OpTypeInt 32 1
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%void = OpTypeVoid
%12 = OpTypeFunction %void
)";
const std::string nonEntryFuncs =
R"(%13 = OpFunction %void None %12
%14 = OpLabel
%15 = OpCopyObject %int %int_0
OpReturn
%16 = OpLabel
%17 = OpCopyObject %int %int_1
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %12
%18 = OpLabel
OpBranch %19
%19 = OpLabel
%20 = OpCopyObject %int %int_2
%21 = OpFunctionCall %void %13
%22 = OpCopyObject %int %int_3
OpLoopMerge %23 %19 None
OpBranchConditional %true %19 %23
%23 = OpLabel
%24 = OpCopyObject %int %int_4
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%1 = OpFunction %void None %12
%18 = OpLabel
OpBranch %19
%19 = OpLabel
%20 = OpCopyObject %int %int_2
OpLoopMerge %23 %26 None
OpBranch %25
%25 = OpLabel
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%29 = OpCopyObject %int %int_0
OpBranch %26
%30 = OpLabel
%31 = OpCopyObject %int %int_1
OpBranch %26
%27 = OpLabel
OpBranchConditional %false %25 %26
%26 = OpLabel
%22 = OpCopyObject %int %int_3
OpBranchConditional %true %19 %23
%23 = OpLabel
%24 = OpCopyObject %int %int_4
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, CalleeWithMultiReturnAndPhiRequiresEntryBlockRemapping) {
// The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/790
//
// The callee has multiple returns, and so must be wrapped with a single-trip
// loop. That code must remap the callee entry block ID to the introduced
// loop body's ID. Otherwise you can get a dominance error in a cloned OpPhi.
const std::string predefs =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
OpSource OpenCL_C 120
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%void = OpTypeVoid
%9 = OpTypeFunction %void
%bool = OpTypeBool
%false = OpConstantFalse %bool
)";
// This callee has multiple returns, and a Phi in the second block referencing
// a value generated in the entry block.
const std::string nonEntryFuncs =
R"(%12 = OpFunction %void None %9
%13 = OpLabel
%14 = OpCopyObject %int %int_0
OpBranch %15
%15 = OpLabel
%16 = OpPhi %int %14 %13
%17 = OpCopyObject %int %int_1
OpReturn
%18 = OpLabel
%19 = OpCopyObject %int %int_2
OpReturn
OpFunctionEnd
)";
const std::string before =
R"(%1 = OpFunction %void None %9
%20 = OpLabel
%21 = OpCopyObject %int %int_3
%22 = OpFunctionCall %void %12
%23 = OpCopyObject %int %int_4
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%1 = OpFunction %void None %9
%20 = OpLabel
%21 = OpCopyObject %int %int_3
OpBranch %24
%24 = OpLabel
OpLoopMerge %25 %26 None
OpBranch %27
%27 = OpLabel
%28 = OpCopyObject %int %int_0
OpBranch %29
%29 = OpLabel
%30 = OpPhi %int %28 %27
%31 = OpCopyObject %int %int_1
OpBranch %25
%32 = OpLabel
%33 = OpCopyObject %int %int_2
OpBranch %25
%26 = OpLabel
OpBranchConditional %false %24 %25
%25 = OpLabel
%23 = OpCopyObject %int %int_4
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + nonEntryFuncs + before, predefs + nonEntryFuncs + after, false,
true);
}
TEST_F(InlineTest, Decorated1) {
// Same test as Simple with the difference
// that OpFAdd in the outlined function is
// decorated with RelaxedPrecision
// Expected result is an equal decoration
// of the corresponding inlined instruction
//
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// return bar.x + bar.y;
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %foo_vf4_ "foo(vf4;"
OpName %bar "bar"
OpName %color "color"
OpName %BaseColor "BaseColor"
OpName %param "param"
OpName %gl_FragColor "gl_FragColor"
OpDecorate %9 RelaxedPrecision
)";
const std::string before =
R"(%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%15 = OpTypeFunction %float %_ptr_Function_v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%uint_1 = OpConstant %uint 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %11
%22 = OpLabel
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%23 = OpLoad %v4float %BaseColor
OpStore %param %23
%24 = OpFunctionCall %float %foo_vf4_ %param
%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
OpStore %color %25
%26 = OpLoad %v4float %color
OpStore %gl_FragColor %26
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpDecorate %37 RelaxedPrecision
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%15 = OpTypeFunction %float %_ptr_Function_v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%uint_1 = OpConstant %uint 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %11
%22 = OpLabel
%32 = OpVariable %_ptr_Function_float Function
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%23 = OpLoad %v4float %BaseColor
OpStore %param %23
%33 = OpAccessChain %_ptr_Function_float %param %uint_0
%34 = OpLoad %float %33
%35 = OpAccessChain %_ptr_Function_float %param %uint_1
%36 = OpLoad %float %35
%37 = OpFAdd %float %34 %36
OpStore %32 %37
%24 = OpLoad %float %32
%25 = OpCompositeConstruct %v4float %24 %24 %24 %24
OpStore %color %25
%26 = OpLoad %v4float %color
OpStore %gl_FragColor %26
OpReturn
OpFunctionEnd
)";
const std::string nonEntryFuncs =
R"(%foo_vf4_ = OpFunction %float None %15
%bar = OpFunctionParameter %_ptr_Function_v4float
%27 = OpLabel
%28 = OpAccessChain %_ptr_Function_float %bar %uint_0
%29 = OpLoad %float %28
%30 = OpAccessChain %_ptr_Function_float %bar %uint_1
%31 = OpLoad %float %30
%9 = OpFAdd %float %29 %31
OpReturnValue %9
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + before + nonEntryFuncs, predefs + after + nonEntryFuncs, false,
true);
}
TEST_F(InlineTest, Decorated2) {
// Same test as Simple with the difference
// that the Result <id> of the outlined OpFunction
// is decorated with RelaxedPrecision
// Expected result is an equal decoration
// of the created return variable
//
// #version 140
//
// in vec4 BaseColor;
//
// float foo(vec4 bar)
// {
// return bar.x + bar.y;
// }
//
// void main()
// {
// vec4 color = vec4(foo(BaseColor));
// gl_FragColor = color;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %foo_vf4_ "foo(vf4;"
OpName %bar "bar"
OpName %color "color"
OpName %BaseColor "BaseColor"
OpName %param "param"
OpName %gl_FragColor "gl_FragColor"
OpDecorate %foo_vf4_ RelaxedPrecision
)";
const std::string before =
R"(%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%14 = OpTypeFunction %float %_ptr_Function_v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%uint_1 = OpConstant %uint 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %10
%21 = OpLabel
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%22 = OpLoad %v4float %BaseColor
OpStore %param %22
%23 = OpFunctionCall %float %foo_vf4_ %param
%24 = OpCompositeConstruct %v4float %23 %23 %23 %23
OpStore %color %24
%25 = OpLoad %v4float %color
OpStore %gl_FragColor %25
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpDecorate %32 RelaxedPrecision
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%14 = OpTypeFunction %float %_ptr_Function_v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%uint_1 = OpConstant %uint 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %10
%21 = OpLabel
%32 = OpVariable %_ptr_Function_float Function
%color = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%22 = OpLoad %v4float %BaseColor
OpStore %param %22
%33 = OpAccessChain %_ptr_Function_float %param %uint_0
%34 = OpLoad %float %33
%35 = OpAccessChain %_ptr_Function_float %param %uint_1
%36 = OpLoad %float %35
%37 = OpFAdd %float %34 %36
OpStore %32 %37
%23 = OpLoad %float %32
%24 = OpCompositeConstruct %v4float %23 %23 %23 %23
OpStore %color %24
%25 = OpLoad %v4float %color
OpStore %gl_FragColor %25
OpReturn
OpFunctionEnd
)";
const std::string nonEntryFuncs =
R"(%foo_vf4_ = OpFunction %float None %14
%bar = OpFunctionParameter %_ptr_Function_v4float
%26 = OpLabel
%27 = OpAccessChain %_ptr_Function_float %bar %uint_0
%28 = OpLoad %float %27
%29 = OpAccessChain %_ptr_Function_float %bar %uint_1
%30 = OpLoad %float %29
%31 = OpFAdd %float %28 %30
OpReturnValue %31
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(
predefs + before + nonEntryFuncs, predefs + after + nonEntryFuncs, false,
true);
}
TEST_F(InlineTest, DeleteName) {
// Test that the name of the result id of the call is deleted.
const std::string before =
R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
OpName %main_entry "main_entry"
OpName %foo_result "foo_result"
OpName %void_fn "void_fn"
OpName %foo "foo"
OpName %foo_entry "foo_entry"
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%foo = OpFunction %void None %void_fn
%foo_entry = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %void_fn
%main_entry = OpLabel
%foo_result = OpFunctionCall %void %foo
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
OpName %main_entry "main_entry"
OpName %void_fn "void_fn"
OpName %foo "foo"
OpName %foo_entry "foo_entry"
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%foo = OpFunction %void None %void_fn
%foo_entry = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %void_fn
%main_entry = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InlineExhaustivePass>(before, after, false, true);
}
TEST_F(InlineTest, SetParent) {
// Test that after inlining all basic blocks have the correct parent.
const std::string text =
R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
OpName %main_entry "main_entry"
OpName %foo_result "foo_result"
OpName %void_fn "void_fn"
OpName %foo "foo"
OpName %foo_entry "foo_entry"
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%foo = OpFunction %void None %void_fn
%foo_entry = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %void_fn
%main_entry = OpLabel
%foo_result = OpFunctionCall %void %foo
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
opt::InlineExhaustivePass pass;
pass.Run(context.get());
for (ir::Function& func : *context->module()) {
for (ir::BasicBlock& bb : func) {
EXPECT_TRUE(bb.GetParent() == &func);
}
}
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Empty modules
// Modules without function definitions
// Modules in which all functions do not call other functions
// Recursive functions (calling self & calling each other)
// Caller and callee both accessing the same global variable
// Functions with OpLine & OpNoLine
// Others?
// TODO(dneto): Test suggestions from code review
// https://github.com/KhronosGroup/SPIRV-Tools/pull/534
//
// Callee function returns a value generated outside the callee,
// e.g. a constant value. This might exercise some logic not yet
// exercised by the current tests: the false branch in the "if"
// inside the SpvOpReturnValue case in InlinePass::GenInlineCode?
// SampledImage before function call, but callee is only single block.
// Then the SampledImage instruction is not cloned. Documents existing
// behaviour.
// SampledImage after function call. It is not cloned or changed.
} // anonymous namespace