SPIRV-Tools/test/opt/fold_spec_const_op_composite_test.cpp
Diego Novillo 4ba9dcc8a0 Implement SSA CCP (SSA Conditional Constant Propagation).
This implements the conditional constant propagation pass proposed in

Constant propagation with conditional branches,
Wegman and Zadeck, ACM TOPLAS 13(2):181-210.

The main logic resides in CCPPass::VisitInstruction.  Instruction that
may produce a constant value are evaluated with the constant folder. If
they produce a new constant, the instruction is considered interesting.
Otherwise, it's considered varying (for unfoldable instructions) or
just not interesting (when not enough operands have a constant value).

The other main piece of logic is in CCPPass::VisitBranch.  This
evaluates the selector of the branch.  When it's found to be a known
value, it computes the destination basic block and sets it.  This tells
the propagator which branches to follow.

The patch required extensions to the constant manager as well. Instead
of hashing the Constant pointers, this patch changes the constant pool
to hash the contents of the Constant.  This allows the lookups to be
done using the actual values of the Constant, preventing duplicate
definitions.
2017-12-21 14:29:45 -05:00

1390 lines
66 KiB
C++

// Copyright (c) 2016 Google 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 "assembly_builder.h"
#include <sstream>
#include "pass_fixture.h"
#include "pass_utils.h"
namespace {
using namespace spvtools;
using FoldSpecConstantOpAndCompositePassBasicTest = PassTest<::testing::Test>;
TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, Empty) {
SinglePassRunAndCheck<opt::FoldSpecConstantOpAndCompositePass>(
"", "", /* skip_nop = */ true);
}
// A test of the basic functionality of FoldSpecConstantOpAndCompositePass.
// A spec constant defined with an integer addition operation should be folded
// to a normal constant with fixed value.
TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, Basic) {
AssemblyBuilder builder;
builder.AppendTypesConstantsGlobals({
// clang-format off
"%int = OpTypeInt 32 1",
"%frozen_spec_const_int = OpConstant %int 1",
"%const_int = OpConstant %int 2",
// Folding target:
"%spec_add = OpSpecConstantOp %int IAdd %frozen_spec_const_int %const_int",
// clang-format on
});
std::vector<const char*> expected = {
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
"OpName %void \"void\"",
"OpName %main_func_type \"main_func_type\"",
"OpName %main \"main\"",
"OpName %main_func_entry_block \"main_func_entry_block\"",
"OpName %int \"int\"",
"OpName %frozen_spec_const_int \"frozen_spec_const_int\"",
"OpName %const_int \"const_int\"",
"OpName %spec_add \"spec_add\"",
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
"%int = OpTypeInt 32 1",
"%frozen_spec_const_int = OpConstant %int 1",
"%const_int = OpConstant %int 2",
// The SpecConstantOp IAdd instruction should be replace by OpConstant
// instruction:
"%spec_add = OpConstant %int 3",
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<opt::FoldSpecConstantOpAndCompositePass>(
builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true);
}
// A test of skipping folding an instruction when the instruction result type
// has decorations.
TEST_F(FoldSpecConstantOpAndCompositePassBasicTest,
SkipWhenTypeHasDecorations) {
AssemblyBuilder builder;
builder
.AppendAnnotations({
// clang-format off
"OpDecorate %int RelaxedPrecision",
// clang-format on
})
.AppendTypesConstantsGlobals({
// clang-format off
"%int = OpTypeInt 32 1",
"%frozen_spec_const_int = OpConstant %int 1",
"%const_int = OpConstant %int 2",
// The following spec constant should not be folded as the result type
// has relaxed precision decoration.
"%spec_add = OpSpecConstantOp %int IAdd %frozen_spec_const_int %const_int",
// clang-format on
});
SinglePassRunAndCheck<opt::FoldSpecConstantOpAndCompositePass>(
builder.GetCode(), builder.GetCode(), /* skip_nop = */ true);
}
// All types and some common constants that are potentially required in
// FoldSpecConstantOpAndCompositeTest.
std::vector<std::string> CommonTypesAndConstants() {
return std::vector<std::string>{
// clang-format off
// scalar types
"%bool = OpTypeBool",
"%uint = OpTypeInt 32 0",
"%int = OpTypeInt 32 1",
"%float = OpTypeFloat 32",
"%double = OpTypeFloat 64",
// vector types
"%v2bool = OpTypeVector %bool 2",
"%v2uint = OpTypeVector %uint 2",
"%v2int = OpTypeVector %int 2",
"%v3int = OpTypeVector %int 3",
"%v4int = OpTypeVector %int 4",
"%v2float = OpTypeVector %float 2",
"%v2double = OpTypeVector %double 2",
// variable pointer types
"%_pf_bool = OpTypePointer Function %bool",
"%_pf_uint = OpTypePointer Function %uint",
"%_pf_int = OpTypePointer Function %int",
"%_pf_float = OpTypePointer Function %float",
"%_pf_double = OpTypePointer Function %double",
"%_pf_v2int = OpTypePointer Function %v2int",
"%_pf_v2float = OpTypePointer Function %v2float",
"%_pf_v2double = OpTypePointer Function %v2double",
// struct types
"%inner_struct = OpTypeStruct %bool %int %float",
"%outer_struct = OpTypeStruct %inner_struct %int",
"%flat_struct = OpTypeStruct %bool %int %float",
// common constants
// scalar constants:
"%bool_true = OpConstantTrue %bool",
"%bool_false = OpConstantFalse %bool",
"%bool_null = OpConstantNull %bool",
"%signed_zero = OpConstant %int 0",
"%unsigned_zero = OpConstant %uint 0",
"%signed_one = OpConstant %int 1",
"%unsigned_one = OpConstant %uint 1",
"%signed_two = OpConstant %int 2",
"%unsigned_two = OpConstant %uint 2",
"%signed_three = OpConstant %int 3",
"%unsigned_three = OpConstant %uint 3",
"%signed_null = OpConstantNull %int",
"%unsigned_null = OpConstantNull %uint",
// vector constants:
"%bool_true_vec = OpConstantComposite %v2bool %bool_true %bool_true",
"%bool_false_vec = OpConstantComposite %v2bool %bool_false %bool_false",
"%bool_null_vec = OpConstantNull %v2bool",
"%signed_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero",
"%unsigned_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
"%signed_one_vec = OpConstantComposite %v2int %signed_one %signed_one",
"%unsigned_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
"%signed_two_vec = OpConstantComposite %v2int %signed_two %signed_two",
"%unsigned_two_vec = OpConstantComposite %v2uint %unsigned_two %unsigned_two",
"%signed_three_vec = OpConstantComposite %v2int %signed_three %signed_three",
"%unsigned_three_vec = OpConstantComposite %v2uint %unsigned_three %unsigned_three",
"%signed_null_vec = OpConstantNull %v2int",
"%unsigned_null_vec = OpConstantNull %v2uint",
"%v4int_0_1_2_3 = OpConstantComposite %v4int %signed_zero %signed_one %signed_two %signed_three",
// clang-format on
};
}
// A helper function to strip OpName instructions from the given string of
// disassembly code. Returns the string with all OpName instruction stripped.
std::string StripOpNameInstructions(const std::string& str) {
std::stringstream ss(str);
std::ostringstream oss;
std::string inst_str;
while (std::getline(ss, inst_str, '\n')) {
if (inst_str.find("OpName %") == std::string::npos) {
oss << inst_str << '\n';
}
}
return oss.str();
}
struct FoldSpecConstantOpAndCompositePassTestCase {
// Original constants with unfolded spec constants.
std::vector<std::string> original;
// Expected cosntants after folding.
std::vector<std::string> expected;
};
using FoldSpecConstantOpAndCompositePassTest = PassTest<
::testing::TestWithParam<FoldSpecConstantOpAndCompositePassTestCase>>;
TEST_P(FoldSpecConstantOpAndCompositePassTest, ParamTestCase) {
AssemblyBuilder test_code_builder, expected_code_builder;
const auto& tc = GetParam();
test_code_builder.AppendTypesConstantsGlobals(CommonTypesAndConstants());
test_code_builder.AppendTypesConstantsGlobals(tc.original);
expected_code_builder.AppendTypesConstantsGlobals(CommonTypesAndConstants());
expected_code_builder.AppendTypesConstantsGlobals(tc.expected);
const std::string original = test_code_builder.GetCode();
const std::string expected = expected_code_builder.GetCode();
// Run the optimization and get the result code in disassembly.
std::string optimized;
auto status = opt::Pass::Status::SuccessWithoutChange;
std::tie(optimized, status) =
SinglePassRunAndDisassemble<opt::FoldSpecConstantOpAndCompositePass>(
original, /* skip_nop = */ true, /* do_validation = */ false);
// Check the optimized code, but ignore the OpName instructions.
EXPECT_NE(opt::Pass::Status::Failure, status);
EXPECT_EQ(
StripOpNameInstructions(expected) == StripOpNameInstructions(original),
status == opt::Pass::Status::SuccessWithoutChange);
EXPECT_EQ(StripOpNameInstructions(expected),
StripOpNameInstructions(optimized));
}
// Tests that OpSpecConstantComposite opcodes are replace with
// OpConstantComposite correctly.
INSTANTIATE_TEST_CASE_P(
Composite, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// normal vector
{
// original
{
"%spec_v2bool = OpSpecConstantComposite %v2bool %bool_true %bool_false",
"%spec_v2uint = OpSpecConstantComposite %v2uint %unsigned_one %unsigned_one",
"%spec_v2int_a = OpSpecConstantComposite %v2int %signed_one %signed_two",
// Spec constants whose value can not be fully resolved should
// not be processed.
"%spec_int = OpSpecConstant %int 99",
"%spec_v2int_b = OpSpecConstantComposite %v2int %signed_one %spec_int",
},
// expected
{
"%spec_v2bool = OpConstantComposite %v2bool %bool_true %bool_false",
"%spec_v2uint = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
"%spec_v2int_a = OpConstantComposite %v2int %signed_one %signed_two",
"%spec_int = OpSpecConstant %int 99",
"%spec_v2int_b = OpSpecConstantComposite %v2int %signed_one %spec_int",
},
},
// vector with null constants
{
// original
{
"%null_bool = OpConstantNull %bool",
"%null_int = OpConstantNull %int",
"%spec_v2bool = OpSpecConstantComposite %v2bool %null_bool %null_bool",
"%spec_v3int = OpSpecConstantComposite %v3int %null_int %null_int %null_int",
"%spec_v4int = OpSpecConstantComposite %v4int %null_int %null_int %null_int %null_int",
},
// expected
{
"%null_bool = OpConstantNull %bool",
"%null_int = OpConstantNull %int",
"%spec_v2bool = OpConstantComposite %v2bool %null_bool %null_bool",
"%spec_v3int = OpConstantComposite %v3int %null_int %null_int %null_int",
"%spec_v4int = OpConstantComposite %v4int %null_int %null_int %null_int %null_int",
},
},
// flat struct
{
// original
{
"%float_1 = OpConstant %float 1",
"%flat_1 = OpSpecConstantComposite %flat_struct %bool_true %signed_null %float_1",
// following struct should not be folded as the value of
// %spec_float is not determined.
"%spec_float = OpSpecConstant %float 1",
"%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_one %spec_float",
},
// expected
{
"%float_1 = OpConstant %float 1",
"%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%spec_float = OpSpecConstant %float 1",
"%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_one %spec_float",
}
},
// nested struct
{
// original
{
"%float_1 = OpConstant %float 1",
"%inner_1 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %float_1",
"%outer_1 = OpSpecConstantComposite %outer_struct %inner_1 %signed_one",
// following structs should not be folded as the value of
// %spec_float is not determined.
"%spec_float = OpSpecConstant %float 1",
"%inner_2 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float",
"%outer_2 = OpSpecConstantComposite %outer_struct %inner_2 %signed_one",
},
// expected
{
"%float_1 = OpConstant %float 1",
"%inner_1 = OpConstantComposite %inner_struct %bool_true %signed_null %float_1",
"%outer_1 = OpConstantComposite %outer_struct %inner_1 %signed_one",
"%spec_float = OpSpecConstant %float 1",
"%inner_2 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float",
"%outer_2 = OpSpecConstantComposite %outer_struct %inner_2 %signed_one",
}
},
// composite constants touched by OpUndef should be skipped
{
// original
{
"%undef = OpUndef %float",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef",
"%outer = OpSpecConstantComposite %outer_struct %inner %signed_one",
},
// expected
{
"%undef = OpUndef %float",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef",
"%outer = OpSpecConstantComposite %outer_struct %inner %signed_one",
},
}
// clang-format on
})));
// Tests for operations that resulting in different types.
INSTANTIATE_TEST_CASE_P(
Cast, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(
std::vector<FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// int -> bool scalar
{
// original
{
"%spec_bool_t = OpSpecConstantOp %bool INotEqual %signed_three %signed_zero",
"%spec_bool_f = OpSpecConstantOp %bool INotEqual %signed_zero %signed_zero",
"%spec_bool_from_null = OpSpecConstantOp %bool INotEqual %signed_null %signed_zero",
},
// expected
{
"%spec_bool_t = OpConstantTrue %bool",
"%spec_bool_f = OpConstantFalse %bool",
"%spec_bool_from_null = OpConstantFalse %bool",
},
},
// uint -> bool scalar
{
// original
{
"%spec_bool_t = OpSpecConstantOp %bool INotEqual %unsigned_three %unsigned_zero",
"%spec_bool_f = OpSpecConstantOp %bool INotEqual %unsigned_zero %unsigned_zero",
"%spec_bool_from_null = OpSpecConstantOp %bool INotEqual %unsigned_null %unsigned_zero",
},
// expected
{
"%spec_bool_t = OpConstantTrue %bool",
"%spec_bool_f = OpConstantFalse %bool",
"%spec_bool_from_null = OpConstantFalse %bool",
},
},
// bool -> int scalar
{
// original
{
"%spec_int_one = OpSpecConstantOp %int Select %bool_true %signed_one %signed_zero",
"%spec_int_zero = OpSpecConstantOp %int Select %bool_false %signed_one %signed_zero",
"%spec_int_from_null = OpSpecConstantOp %int Select %bool_null %signed_one %signed_zero",
},
// expected
{
"%spec_int_one = OpConstant %int 1",
"%spec_int_zero = OpConstant %int 0",
"%spec_int_from_null = OpConstant %int 0",
},
},
// uint -> int scalar
{
// original
{
"%spec_int_one = OpSpecConstantOp %int IAdd %unsigned_one %signed_zero",
"%spec_int_zero = OpSpecConstantOp %int IAdd %unsigned_zero %signed_zero",
"%spec_int_from_null = OpSpecConstantOp %int IAdd %unsigned_null %unsigned_zero",
},
// expected
{
"%spec_int_one = OpConstant %int 1",
"%spec_int_zero = OpConstant %int 0",
"%spec_int_from_null = OpConstant %int 0",
},
},
// bool -> uint scalar
{
// original
{
"%spec_uint_one = OpSpecConstantOp %uint Select %bool_true %unsigned_one %unsigned_zero",
"%spec_uint_zero = OpSpecConstantOp %uint Select %bool_false %unsigned_one %unsigned_zero",
"%spec_uint_from_null = OpSpecConstantOp %uint Select %bool_null %unsigned_one %unsigned_zero",
},
// expected
{
"%spec_uint_one = OpConstant %uint 1",
"%spec_uint_zero = OpConstant %uint 0",
"%spec_uint_from_null = OpConstant %uint 0",
},
},
// int -> uint scalar
{
// original
{
"%spec_uint_one = OpSpecConstantOp %uint IAdd %signed_one %unsigned_zero",
"%spec_uint_zero = OpSpecConstantOp %uint IAdd %signed_zero %unsigned_zero",
"%spec_uint_from_null = OpSpecConstantOp %uint IAdd %signed_null %unsigned_zero",
},
// expected
{
"%spec_uint_one = OpConstant %uint 1",
"%spec_uint_zero = OpConstant %uint 0",
"%spec_uint_from_null = OpConstant %uint 0",
},
},
// int -> bool vector
{
// original
{
"%spec_bool_t_vec = OpSpecConstantOp %v2bool INotEqual %signed_three_vec %signed_zero_vec",
"%spec_bool_f_vec = OpSpecConstantOp %v2bool INotEqual %signed_zero_vec %signed_zero_vec",
"%spec_bool_from_null = OpSpecConstantOp %v2bool INotEqual %signed_null_vec %signed_zero_vec",
},
// expected
{
"%true = OpConstantTrue %bool",
"%true_0 = OpConstantTrue %bool",
"%spec_bool_t_vec = OpConstantComposite %v2bool %true_0 %true_0",
"%false = OpConstantFalse %bool",
"%false_0 = OpConstantFalse %bool",
"%spec_bool_f_vec = OpConstantComposite %v2bool %false_0 %false_0",
"%false_1 = OpConstantFalse %bool",
"%false_2 = OpConstantFalse %bool",
"%spec_bool_from_null = OpConstantComposite %v2bool %false_2 %false_2",
},
},
// uint -> bool vector
{
// original
{
"%spec_bool_t_vec = OpSpecConstantOp %v2bool INotEqual %unsigned_three_vec %unsigned_zero_vec",
"%spec_bool_f_vec = OpSpecConstantOp %v2bool INotEqual %unsigned_zero_vec %unsigned_zero_vec",
"%spec_bool_from_null = OpSpecConstantOp %v2bool INotEqual %unsigned_null_vec %unsigned_zero_vec",
},
// expected
{
"%true = OpConstantTrue %bool",
"%true_0 = OpConstantTrue %bool",
"%spec_bool_t_vec = OpConstantComposite %v2bool %true_0 %true_0",
"%false = OpConstantFalse %bool",
"%false_0 = OpConstantFalse %bool",
"%spec_bool_f_vec = OpConstantComposite %v2bool %false_0 %false_0",
"%false_1 = OpConstantFalse %bool",
"%false_2 = OpConstantFalse %bool",
"%spec_bool_from_null = OpConstantComposite %v2bool %false_2 %false_2",
},
},
// bool -> int vector
{
// original
{
"%spec_int_one_vec = OpSpecConstantOp %v2int Select %bool_true_vec %signed_one_vec %signed_zero_vec",
"%spec_int_zero_vec = OpSpecConstantOp %v2int Select %bool_false_vec %signed_one_vec %signed_zero_vec",
"%spec_int_from_null = OpSpecConstantOp %v2int Select %bool_null_vec %signed_one_vec %signed_zero_vec",
},
// expected
{
"%int_1 = OpConstant %int 1",
"%int_1_0 = OpConstant %int 1",
"%spec_int_one_vec = OpConstantComposite %v2int %int_1_0 %int_1_0",
"%int_0 = OpConstant %int 0",
"%int_0_0 = OpConstant %int 0",
"%spec_int_zero_vec = OpConstantComposite %v2int %int_0_0 %int_0_0",
"%int_0_1 = OpConstant %int 0",
"%int_0_2 = OpConstant %int 0",
"%spec_int_from_null = OpConstantComposite %v2int %int_0_2 %int_0_2",
},
},
// uint -> int vector
{
// original
{
"%spec_int_one_vec = OpSpecConstantOp %v2int IAdd %unsigned_one_vec %signed_zero_vec",
"%spec_int_zero_vec = OpSpecConstantOp %v2int IAdd %unsigned_zero_vec %signed_zero_vec",
"%spec_int_from_null = OpSpecConstantOp %v2int IAdd %unsigned_null_vec %signed_zero_vec",
},
// expected
{
"%int_1 = OpConstant %int 1",
"%int_1_0 = OpConstant %int 1",
"%spec_int_one_vec = OpConstantComposite %v2int %int_1_0 %int_1_0",
"%int_0 = OpConstant %int 0",
"%int_0_0 = OpConstant %int 0",
"%spec_int_zero_vec = OpConstantComposite %v2int %int_0_0 %int_0_0",
"%int_0_1 = OpConstant %int 0",
"%int_0_2 = OpConstant %int 0",
"%spec_int_from_null = OpConstantComposite %v2int %int_0_2 %int_0_2",
},
},
// bool -> uint vector
{
// original
{
"%spec_uint_one_vec = OpSpecConstantOp %v2uint Select %bool_true_vec %unsigned_one_vec %unsigned_zero_vec",
"%spec_uint_zero_vec = OpSpecConstantOp %v2uint Select %bool_false_vec %unsigned_one_vec %unsigned_zero_vec",
"%spec_uint_from_null = OpSpecConstantOp %v2uint Select %bool_null_vec %unsigned_one_vec %unsigned_zero_vec",
},
// expected
{
"%uint_1 = OpConstant %uint 1",
"%uint_1_0 = OpConstant %uint 1",
"%spec_uint_one_vec = OpConstantComposite %v2uint %uint_1_0 %uint_1_0",
"%uint_0 = OpConstant %uint 0",
"%uint_0_0 = OpConstant %uint 0",
"%spec_uint_zero_vec = OpConstantComposite %v2uint %uint_0_0 %uint_0_0",
"%uint_0_1 = OpConstant %uint 0",
"%uint_0_2 = OpConstant %uint 0",
"%spec_uint_from_null = OpConstantComposite %v2uint %uint_0_2 %uint_0_2",
},
},
// int -> uint vector
{
// original
{
"%spec_uint_one_vec = OpSpecConstantOp %v2uint IAdd %signed_one_vec %unsigned_zero_vec",
"%spec_uint_zero_vec = OpSpecConstantOp %v2uint IAdd %signed_zero_vec %unsigned_zero_vec",
"%spec_uint_from_null = OpSpecConstantOp %v2uint IAdd %signed_null_vec %unsigned_zero_vec",
},
// expected
{
"%uint_1 = OpConstant %uint 1",
"%uint_1_0 = OpConstant %uint 1",
"%spec_uint_one_vec = OpConstantComposite %v2uint %uint_1_0 %uint_1_0",
"%uint_0 = OpConstant %uint 0",
"%uint_0_0 = OpConstant %uint 0",
"%spec_uint_zero_vec = OpConstantComposite %v2uint %uint_0_0 %uint_0_0",
"%uint_0_1 = OpConstant %uint 0",
"%uint_0_2 = OpConstant %uint 0",
"%spec_uint_from_null = OpConstantComposite %v2uint %uint_0_2 %uint_0_2",
},
},
// clang-format on
})));
// Tests about boolean scalar logical operations and comparison operations with
// scalar int/uint type.
INSTANTIATE_TEST_CASE_P(
Logical, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// scalar integer comparison
{
// original
{
"%int_minus_1 = OpConstant %int -1",
"%slt_0_1 = OpSpecConstantOp %bool SLessThan %signed_zero %signed_one",
"%sgt_0_1 = OpSpecConstantOp %bool SGreaterThan %signed_zero %signed_one",
"%sle_2_2 = OpSpecConstantOp %bool SLessThanEqual %signed_two %signed_two",
"%sge_2_1 = OpSpecConstantOp %bool SGreaterThanEqual %signed_two %signed_one",
"%sge_2_null = OpSpecConstantOp %bool SGreaterThanEqual %signed_two %signed_null",
"%sge_minus_1_null = OpSpecConstantOp %bool SGreaterThanEqual %int_minus_1 %signed_null",
"%ult_0_1 = OpSpecConstantOp %bool ULessThan %unsigned_zero %unsigned_one",
"%ugt_0_1 = OpSpecConstantOp %bool UGreaterThan %unsigned_zero %unsigned_one",
"%ule_2_3 = OpSpecConstantOp %bool ULessThanEqual %unsigned_two %unsigned_three",
"%uge_1_1 = OpSpecConstantOp %bool UGreaterThanEqual %unsigned_one %unsigned_one",
"%uge_2_null = OpSpecConstantOp %bool UGreaterThanEqual %unsigned_two %unsigned_null",
"%uge_minus_1_null = OpSpecConstantOp %bool UGreaterThanEqual %int_minus_1 %unsigned_null",
},
// expected
{
"%int_minus_1 = OpConstant %int -1",
"%slt_0_1 = OpConstantTrue %bool",
"%sgt_0_1 = OpConstantFalse %bool",
"%sle_2_2 = OpConstantTrue %bool",
"%sge_2_1 = OpConstantTrue %bool",
"%sge_2_null = OpConstantTrue %bool",
"%sge_minus_1_null = OpConstantFalse %bool",
"%ult_0_1 = OpConstantTrue %bool",
"%ugt_0_1 = OpConstantFalse %bool",
"%ule_2_3 = OpConstantTrue %bool",
"%uge_1_1 = OpConstantTrue %bool",
"%uge_2_null = OpConstantTrue %bool",
"%uge_minus_1_null = OpConstantTrue %bool",
},
},
// Logical and, or, xor.
{
// original
{
"%logical_or = OpSpecConstantOp %bool LogicalOr %bool_true %bool_false",
"%logical_and = OpSpecConstantOp %bool LogicalAnd %bool_true %bool_false",
"%logical_not = OpSpecConstantOp %bool LogicalNot %bool_true",
"%logical_eq = OpSpecConstantOp %bool LogicalEqual %bool_true %bool_true",
"%logical_neq = OpSpecConstantOp %bool LogicalNotEqual %bool_true %bool_true",
"%logical_and_null = OpSpecConstantOp %bool LogicalAnd %bool_true %bool_null",
},
// expected
{
"%logical_or = OpConstantTrue %bool",
"%logical_and = OpConstantFalse %bool",
"%logical_not = OpConstantFalse %bool",
"%logical_eq = OpConstantTrue %bool",
"%logical_neq = OpConstantFalse %bool",
"%logical_and_null = OpConstantFalse %bool",
},
},
// clang-format on
})));
// Tests about arithmetic operations for scalar int and uint types.
INSTANTIATE_TEST_CASE_P(
ScalarArithmetic, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// scalar integer negate
{
// original
{
"%int_minus_1 = OpSpecConstantOp %int SNegate %signed_one",
"%int_minus_2 = OpSpecConstantOp %int SNegate %signed_two",
"%int_neg_null = OpSpecConstantOp %int SNegate %signed_null",
"%int_max = OpConstant %int 2147483647",
"%int_neg_max = OpSpecConstantOp %int SNegate %int_max",
},
// expected
{
"%int_minus_1 = OpConstant %int -1",
"%int_minus_2 = OpConstant %int -2",
"%int_neg_null = OpConstant %int 0",
"%int_max = OpConstant %int 2147483647",
"%int_neg_max = OpConstant %int -2147483647",
},
},
// scalar integer not
{
// original
{
"%uint_4294967294 = OpSpecConstantOp %uint Not %unsigned_one",
"%uint_4294967293 = OpSpecConstantOp %uint Not %unsigned_two",
"%uint_neg_null = OpSpecConstantOp %uint Not %unsigned_null",
},
// expected
{
"%uint_4294967294 = OpConstant %uint 4294967294",
"%uint_4294967293 = OpConstant %uint 4294967293",
"%uint_neg_null = OpConstant %uint 4294967295",
},
},
// scalar integer add, sub, mul, div
{
// original
{
"%signed_max = OpConstant %int 2147483647",
"%signed_min = OpConstant %int -2147483648",
"%spec_int_iadd = OpSpecConstantOp %int IAdd %signed_three %signed_two",
"%spec_int_isub = OpSpecConstantOp %int ISub %signed_one %spec_int_iadd",
"%spec_int_sdiv = OpSpecConstantOp %int SDiv %spec_int_isub %signed_two",
"%spec_int_imul = OpSpecConstantOp %int IMul %spec_int_sdiv %signed_three",
"%spec_int_iadd_null = OpSpecConstantOp %int IAdd %spec_int_imul %signed_null",
"%spec_int_imul_null = OpSpecConstantOp %int IMul %spec_int_iadd_null %signed_null",
"%spec_int_iadd_overflow = OpSpecConstantOp %int IAdd %signed_max %signed_three",
"%spec_int_isub_overflow = OpSpecConstantOp %int ISub %signed_min %signed_three",
"%spec_uint_iadd = OpSpecConstantOp %uint IAdd %unsigned_three %unsigned_two",
"%spec_uint_isub = OpSpecConstantOp %uint ISub %unsigned_one %spec_uint_iadd",
"%spec_uint_udiv = OpSpecConstantOp %uint UDiv %spec_uint_isub %unsigned_three",
"%spec_uint_imul = OpSpecConstantOp %uint IMul %spec_uint_udiv %unsigned_two",
"%spec_uint_isub_null = OpSpecConstantOp %uint ISub %spec_uint_imul %signed_null",
},
// expected
{
"%signed_max = OpConstant %int 2147483647",
"%signed_min = OpConstant %int -2147483648",
"%spec_int_iadd = OpConstant %int 5",
"%spec_int_isub = OpConstant %int -4",
"%spec_int_sdiv = OpConstant %int -2",
"%spec_int_imul = OpConstant %int -6",
"%spec_int_iadd_null = OpConstant %int -6",
"%spec_int_imul_null = OpConstant %int 0",
"%spec_int_iadd_overflow = OpConstant %int -2147483646",
"%spec_int_isub_overflow = OpConstant %int 2147483645",
"%spec_uint_iadd = OpConstant %uint 5",
"%spec_uint_isub = OpConstant %uint 4294967292",
"%spec_uint_udiv = OpConstant %uint 1431655764",
"%spec_uint_imul = OpConstant %uint 2863311528",
"%spec_uint_isub_null = OpConstant %uint 2863311528",
},
},
// scalar integer rem, mod
{
// original
{
// common constants
"%int_7 = OpConstant %int 7",
"%uint_7 = OpConstant %uint 7",
"%int_minus_7 = OpConstant %int -7",
"%int_minus_3 = OpConstant %int -3",
// srem
"%7_srem_3 = OpSpecConstantOp %int SRem %int_7 %signed_three",
"%minus_7_srem_3 = OpSpecConstantOp %int SRem %int_minus_7 %signed_three",
"%7_srem_minus_3 = OpSpecConstantOp %int SRem %int_7 %int_minus_3",
"%minus_7_srem_minus_3 = OpSpecConstantOp %int SRem %int_minus_7 %int_minus_3",
// smod
"%7_smod_3 = OpSpecConstantOp %int SMod %int_7 %signed_three",
"%minus_7_smod_3 = OpSpecConstantOp %int SMod %int_minus_7 %signed_three",
"%7_smod_minus_3 = OpSpecConstantOp %int SMod %int_7 %int_minus_3",
"%minus_7_smod_minus_3 = OpSpecConstantOp %int SMod %int_minus_7 %int_minus_3",
// umod
"%7_umod_3 = OpSpecConstantOp %uint UMod %uint_7 %unsigned_three",
// null constant
"%null_srem_3 = OpSpecConstantOp %int SRem %signed_null %signed_three",
"%null_smod_3 = OpSpecConstantOp %int SMod %signed_null %signed_three",
"%null_umod_3 = OpSpecConstantOp %uint UMod %unsigned_null %unsigned_three",
},
// expected
{
// common constants
"%int_7 = OpConstant %int 7",
"%uint_7 = OpConstant %uint 7",
"%int_minus_7 = OpConstant %int -7",
"%int_minus_3 = OpConstant %int -3",
// srem
"%7_srem_3 = OpConstant %int 1",
"%minus_7_srem_3 = OpConstant %int -1",
"%7_srem_minus_3 = OpConstant %int 1",
"%minus_7_srem_minus_3 = OpConstant %int -1",
// smod
"%7_smod_3 = OpConstant %int 1",
"%minus_7_smod_3 = OpConstant %int 2",
"%7_smod_minus_3 = OpConstant %int -2",
"%minus_7_smod_minus_3 = OpConstant %int -1",
// umod
"%7_umod_3 = OpConstant %uint 1",
// null constant
"%null_srem_3 = OpConstant %int 0",
"%null_smod_3 = OpConstant %int 0",
"%null_umod_3 = OpConstant %uint 0",
},
},
// scalar integer bitwise and shift
{
// original
{
// bitwise
"%xor_1_3 = OpSpecConstantOp %int BitwiseXor %signed_one %signed_three",
"%and_1_2 = OpSpecConstantOp %int BitwiseAnd %signed_one %xor_1_3",
"%or_1_2 = OpSpecConstantOp %int BitwiseOr %signed_one %xor_1_3",
"%xor_3_null = OpSpecConstantOp %int BitwiseXor %or_1_2 %signed_null",
// shift
"%unsigned_31 = OpConstant %uint 31",
"%unsigned_left_shift_max = OpSpecConstantOp %uint ShiftLeftLogical %unsigned_one %unsigned_31",
"%unsigned_right_shift_logical = OpSpecConstantOp %uint ShiftRightLogical %unsigned_left_shift_max %unsigned_31",
"%signed_right_shift_arithmetic = OpSpecConstantOp %int ShiftRightArithmetic %unsigned_left_shift_max %unsigned_31",
"%left_shift_null_31 = OpSpecConstantOp %uint ShiftLeftLogical %unsigned_null %unsigned_31",
"%right_shift_31_null = OpSpecConstantOp %uint ShiftRightLogical %unsigned_31 %unsigned_null",
},
// expected
{
"%xor_1_3 = OpConstant %int 2",
"%and_1_2 = OpConstant %int 0",
"%or_1_2 = OpConstant %int 3",
"%xor_3_null = OpConstant %int 3",
"%unsigned_31 = OpConstant %uint 31",
"%unsigned_left_shift_max = OpConstant %uint 2147483648",
"%unsigned_right_shift_logical = OpConstant %uint 1",
"%signed_right_shift_arithmetic = OpConstant %int -1",
"%left_shift_null_31 = OpConstant %uint 0",
"%right_shift_31_null = OpConstant %uint 31",
},
},
// Skip folding if any operands have undetermined value.
{
// original
{
"%spec_int = OpSpecConstant %int 1",
"%spec_iadd = OpSpecConstantOp %int IAdd %signed_three %spec_int",
},
// expected
{
"%spec_int = OpSpecConstant %int 1",
"%spec_iadd = OpSpecConstantOp %int IAdd %signed_three %spec_int",
},
},
// clang-format on
})));
// Tests about arithmetic operations for vector int and uint types.
INSTANTIATE_TEST_CASE_P(
VectorArithmetic, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// vector integer negate
{
// original
{
"%v2int_minus_1 = OpSpecConstantOp %v2int SNegate %signed_one_vec",
"%v2int_minus_2 = OpSpecConstantOp %v2int SNegate %signed_two_vec",
"%v2int_neg_null = OpSpecConstantOp %v2int SNegate %signed_null_vec",
},
// expected
{
"%int_n1 = OpConstant %int -1",
"%int_n1_0 = OpConstant %int -1",
"%v2int_minus_1 = OpConstantComposite %v2int %int_n1_0 %int_n1_0",
"%int_n2 = OpConstant %int -2",
"%int_n2_0 = OpConstant %int -2",
"%v2int_minus_2 = OpConstantComposite %v2int %int_n2_0 %int_n2_0",
"%int_0 = OpConstant %int 0",
"%int_0_0 = OpConstant %int 0",
"%v2int_neg_null = OpConstantComposite %v2int %int_0_0 %int_0_0",
},
},
// vector integer (including null vetors) add, sub, div, mul
{
// original
{
"%spec_v2int_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %signed_two_vec",
"%spec_v2int_isub = OpSpecConstantOp %v2int ISub %signed_one_vec %spec_v2int_iadd",
"%spec_v2int_sdiv = OpSpecConstantOp %v2int SDiv %spec_v2int_isub %signed_two_vec",
"%spec_v2int_imul = OpSpecConstantOp %v2int IMul %spec_v2int_sdiv %signed_three_vec",
"%spec_v2int_iadd_null = OpSpecConstantOp %v2int IAdd %spec_v2int_imul %signed_null_vec",
"%spec_v2uint_iadd = OpSpecConstantOp %v2uint IAdd %unsigned_three_vec %unsigned_two_vec",
"%spec_v2uint_isub = OpSpecConstantOp %v2uint ISub %unsigned_one_vec %spec_v2uint_iadd",
"%spec_v2uint_udiv = OpSpecConstantOp %v2uint UDiv %spec_v2uint_isub %unsigned_three_vec",
"%spec_v2uint_imul = OpSpecConstantOp %v2uint IMul %spec_v2uint_udiv %unsigned_two_vec",
"%spec_v2uint_isub_null = OpSpecConstantOp %v2uint ISub %spec_v2uint_imul %signed_null_vec",
},
// expected
{
"%int_5 = OpConstant %int 5",
"%int_5_0 = OpConstant %int 5",
"%spec_v2int_iadd = OpConstantComposite %v2int %int_5_0 %int_5_0",
"%int_n4 = OpConstant %int -4",
"%int_n4_0 = OpConstant %int -4",
"%spec_v2int_isub = OpConstantComposite %v2int %int_n4_0 %int_n4_0",
"%int_n2 = OpConstant %int -2",
"%int_n2_0 = OpConstant %int -2",
"%spec_v2int_sdiv = OpConstantComposite %v2int %int_n2_0 %int_n2_0",
"%int_n6 = OpConstant %int -6",
"%int_n6_0 = OpConstant %int -6",
"%spec_v2int_imul = OpConstantComposite %v2int %int_n6_0 %int_n6_0",
"%int_n6_1 = OpConstant %int -6",
"%int_n6_2 = OpConstant %int -6",
"%spec_v2int_iadd_null = OpConstantComposite %v2int %int_n6_2 %int_n6_2",
"%uint_5 = OpConstant %uint 5",
"%uint_5_0 = OpConstant %uint 5",
"%spec_v2uint_iadd = OpConstantComposite %v2uint %uint_5_0 %uint_5_0",
"%uint_4294967292 = OpConstant %uint 4294967292",
"%uint_4294967292_0 = OpConstant %uint 4294967292",
"%spec_v2uint_isub = OpConstantComposite %v2uint %uint_4294967292_0 %uint_4294967292_0",
"%uint_1431655764 = OpConstant %uint 1431655764",
"%uint_1431655764_0 = OpConstant %uint 1431655764",
"%spec_v2uint_udiv = OpConstantComposite %v2uint %uint_1431655764_0 %uint_1431655764_0",
"%uint_2863311528 = OpConstant %uint 2863311528",
"%uint_2863311528_0 = OpConstant %uint 2863311528",
"%spec_v2uint_imul = OpConstantComposite %v2uint %uint_2863311528_0 %uint_2863311528_0",
"%uint_2863311528_1 = OpConstant %uint 2863311528",
"%uint_2863311528_2 = OpConstant %uint 2863311528",
"%spec_v2uint_isub_null = OpConstantComposite %v2uint %uint_2863311528_2 %uint_2863311528_2",
},
},
// vector integer rem, mod
{
// original
{
// common constants
"%int_7 = OpConstant %int 7",
"%v2int_7 = OpConstantComposite %v2int %int_7 %int_7",
"%uint_7 = OpConstant %uint 7",
"%v2uint_7 = OpConstantComposite %v2uint %uint_7 %uint_7",
"%int_minus_7 = OpConstant %int -7",
"%v2int_minus_7 = OpConstantComposite %v2int %int_minus_7 %int_minus_7",
"%int_minus_3 = OpConstant %int -3",
"%v2int_minus_3 = OpConstantComposite %v2int %int_minus_3 %int_minus_3",
// srem
"%7_srem_3 = OpSpecConstantOp %v2int SRem %v2int_7 %signed_three_vec",
"%minus_7_srem_3 = OpSpecConstantOp %v2int SRem %v2int_minus_7 %signed_three_vec",
"%7_srem_minus_3 = OpSpecConstantOp %v2int SRem %v2int_7 %v2int_minus_3",
"%minus_7_srem_minus_3 = OpSpecConstantOp %v2int SRem %v2int_minus_7 %v2int_minus_3",
// smod
"%7_smod_3 = OpSpecConstantOp %v2int SMod %v2int_7 %signed_three_vec",
"%minus_7_smod_3 = OpSpecConstantOp %v2int SMod %v2int_minus_7 %signed_three_vec",
"%7_smod_minus_3 = OpSpecConstantOp %v2int SMod %v2int_7 %v2int_minus_3",
"%minus_7_smod_minus_3 = OpSpecConstantOp %v2int SMod %v2int_minus_7 %v2int_minus_3",
// umod
"%7_umod_3 = OpSpecConstantOp %v2uint UMod %v2uint_7 %unsigned_three_vec",
},
// expected
{
// common constants
"%int_7 = OpConstant %int 7",
"%v2int_7 = OpConstantComposite %v2int %int_7 %int_7",
"%uint_7 = OpConstant %uint 7",
"%v2uint_7 = OpConstantComposite %v2uint %uint_7 %uint_7",
"%int_minus_7 = OpConstant %int -7",
"%v2int_minus_7 = OpConstantComposite %v2int %int_minus_7 %int_minus_7",
"%int_minus_3 = OpConstant %int -3",
"%v2int_minus_3 = OpConstantComposite %v2int %int_minus_3 %int_minus_3",
// srem
"%int_1 = OpConstant %int 1",
"%int_1_0 = OpConstant %int 1",
"%7_srem_3 = OpConstantComposite %v2int %int_1_0 %int_1_0",
"%int_n1 = OpConstant %int -1",
"%int_n1_0 = OpConstant %int -1",
"%minus_7_srem_3 = OpConstantComposite %v2int %int_n1_0 %int_n1_0",
"%int_1_1 = OpConstant %int 1",
"%int_1_2 = OpConstant %int 1",
"%7_srem_minus_3 = OpConstantComposite %v2int %int_1_2 %int_1_2",
"%int_n1_1 = OpConstant %int -1",
"%int_n1_2 = OpConstant %int -1",
"%minus_7_srem_minus_3 = OpConstantComposite %v2int %int_n1_2 %int_n1_2",
// smod
"%int_1_3 = OpConstant %int 1",
"%int_1_4 = OpConstant %int 1",
"%7_smod_3 = OpConstantComposite %v2int %int_1_4 %int_1_4",
"%int_2 = OpConstant %int 2",
"%int_2_0 = OpConstant %int 2",
"%minus_7_smod_3 = OpConstantComposite %v2int %int_2_0 %int_2_0",
"%int_n2 = OpConstant %int -2",
"%int_n2_0 = OpConstant %int -2",
"%7_smod_minus_3 = OpConstantComposite %v2int %int_n2_0 %int_n2_0",
"%int_n1_3 = OpConstant %int -1",
"%int_n1_4 = OpConstant %int -1",
"%minus_7_smod_minus_3 = OpConstantComposite %v2int %int_n1_4 %int_n1_4",
// umod
"%uint_1 = OpConstant %uint 1",
"%uint_1_0 = OpConstant %uint 1",
"%7_umod_3 = OpConstantComposite %v2uint %uint_1_0 %uint_1_0",
},
},
// vector integer bitwise, shift
{
// original
{
"%xor_1_3 = OpSpecConstantOp %v2int BitwiseXor %signed_one_vec %signed_three_vec",
"%and_1_2 = OpSpecConstantOp %v2int BitwiseAnd %signed_one_vec %xor_1_3",
"%or_1_2 = OpSpecConstantOp %v2int BitwiseOr %signed_one_vec %xor_1_3",
"%unsigned_31 = OpConstant %uint 31",
"%v2unsigned_31 = OpConstantComposite %v2uint %unsigned_31 %unsigned_31",
"%unsigned_left_shift_max = OpSpecConstantOp %v2uint ShiftLeftLogical %unsigned_one_vec %v2unsigned_31",
"%unsigned_right_shift_logical = OpSpecConstantOp %v2uint ShiftRightLogical %unsigned_left_shift_max %v2unsigned_31",
"%signed_right_shift_arithmetic = OpSpecConstantOp %v2int ShiftRightArithmetic %unsigned_left_shift_max %v2unsigned_31",
},
// expected
{
"%int_2 = OpConstant %int 2",
"%int_2_0 = OpConstant %int 2",
"%xor_1_3 = OpConstantComposite %v2int %int_2_0 %int_2_0",
"%int_0 = OpConstant %int 0",
"%int_0_0 = OpConstant %int 0",
"%and_1_2 = OpConstantComposite %v2int %int_0_0 %int_0_0",
"%int_3 = OpConstant %int 3",
"%int_3_0 = OpConstant %int 3",
"%or_1_2 = OpConstantComposite %v2int %int_3_0 %int_3_0",
"%unsigned_31 = OpConstant %uint 31",
"%v2unsigned_31 = OpConstantComposite %v2uint %unsigned_31 %unsigned_31",
"%uint_2147483648 = OpConstant %uint 2147483648",
"%uint_2147483648_0 = OpConstant %uint 2147483648",
"%unsigned_left_shift_max = OpConstantComposite %v2uint %uint_2147483648_0 %uint_2147483648_0",
"%uint_1 = OpConstant %uint 1",
"%uint_1_0 = OpConstant %uint 1",
"%unsigned_right_shift_logical = OpConstantComposite %v2uint %uint_1_0 %uint_1_0",
"%int_n1 = OpConstant %int -1",
"%int_n1_0 = OpConstant %int -1",
"%signed_right_shift_arithmetic = OpConstantComposite %v2int %int_n1_0 %int_n1_0",
},
},
// Skip folding if any vector operands or components of the operands
// have undetermined value.
{
// original
{
"%spec_int = OpSpecConstant %int 1",
"%spec_vec = OpSpecConstantComposite %v2int %signed_zero %spec_int",
"%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %spec_vec",
},
// expected
{
"%spec_int = OpSpecConstant %int 1",
"%spec_vec = OpSpecConstantComposite %v2int %signed_zero %spec_int",
"%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %spec_vec",
},
},
// Skip folding if any vector operands are defined by OpUndef
{
// original
{
"%undef = OpUndef %int",
"%vec = OpConstantComposite %v2int %undef %signed_one",
"%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %vec",
},
// expected
{
"%undef = OpUndef %int",
"%vec = OpConstantComposite %v2int %undef %signed_one",
"%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %vec",
},
},
// clang-format on
})));
// Tests for SpecConstantOp CompositeExtract instruction
INSTANTIATE_TEST_CASE_P(
CompositeExtract, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// normal vector
{
// original
{
"%r = OpSpecConstantOp %int CompositeExtract %signed_three_vec 0",
"%x = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 0",
"%y = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 1",
"%z = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 2",
"%w = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 3",
},
// expected
{
"%r = OpConstant %int 3",
"%x = OpConstant %int 0",
"%y = OpConstant %int 1",
"%z = OpConstant %int 2",
"%w = OpConstant %int 3",
},
},
// null vector
{
// original
{
"%x = OpSpecConstantOp %int CompositeExtract %signed_null_vec 0",
"%y = OpSpecConstantOp %int CompositeExtract %signed_null_vec 1",
"%null_v4int = OpConstantNull %v4int",
"%z = OpSpecConstantOp %int CompositeExtract %signed_null_vec 2",
},
// expected
{
"%x = OpConstantNull %int",
"%y = OpConstantNull %int",
"%null_v4int = OpConstantNull %v4int",
"%z = OpConstantNull %int",
}
},
// normal flat struct
{
// original
{
"%float_1 = OpConstant %float 1",
"%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%extract_bool = OpSpecConstantOp %bool CompositeExtract %flat_1 0",
"%extract_int = OpSpecConstantOp %int CompositeExtract %flat_1 1",
"%extract_float_1 = OpSpecConstantOp %float CompositeExtract %flat_1 2",
// foldable composite constants built with OpSpecConstantComposite
// should also be processed.
"%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%extract_float_2 = OpSpecConstantOp %float CompositeExtract %flat_2 2",
},
// expected
{
"%float_1 = OpConstant %float 1",
"%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%extract_bool = OpConstantTrue %bool",
"%extract_int = OpConstantNull %int",
"%extract_float_1 = OpConstant %float 1",
"%flat_2 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%extract_float_2 = OpConstant %float 1",
},
},
// null flat struct
{
// original
{
"%flat = OpConstantNull %flat_struct",
"%extract_bool = OpSpecConstantOp %bool CompositeExtract %flat 0",
"%extract_int = OpSpecConstantOp %int CompositeExtract %flat 1",
"%extract_float = OpSpecConstantOp %float CompositeExtract %flat 2",
},
// expected
{
"%flat = OpConstantNull %flat_struct",
"%extract_bool = OpConstantNull %bool",
"%extract_int = OpConstantNull %int",
"%extract_float = OpConstantNull %float",
},
},
// normal nested struct
{
// original
{
"%float_1 = OpConstant %float 1",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_null %float_1",
"%outer = OpConstantComposite %outer_struct %inner %signed_one",
"%extract_inner = OpSpecConstantOp %inner_struct CompositeExtract %outer 0",
"%extract_int = OpSpecConstantOp %int CompositeExtract %outer 1",
"%extract_inner_float = OpSpecConstantOp %int CompositeExtract %outer 0 2",
},
// expected
{
"%float_1 = OpConstant %float 1",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_null %float_1",
"%outer = OpConstantComposite %outer_struct %inner %signed_one",
"%extract_inner = OpConstantComposite %flat_struct %bool_true %signed_null %float_1",
"%extract_int = OpConstant %int 1",
"%extract_inner_float = OpConstant %float 1",
},
},
// null nested struct
{
// original
{
"%outer = OpConstantNull %outer_struct",
"%extract_inner = OpSpecConstantOp %inner_struct CompositeExtract %outer 0",
"%extract_int = OpSpecConstantOp %int CompositeExtract %outer 1",
"%extract_inner_float = OpSpecConstantOp %float CompositeExtract %outer 0 2",
},
// expected
{
"%outer = OpConstantNull %outer_struct",
"%extract_inner = OpConstantNull %inner_struct",
"%extract_int = OpConstantNull %int",
"%extract_inner_float = OpConstantNull %float",
},
},
// skip folding if the any composite constant's value are not fully
// determined, even though the extracting target might have
// determined value.
{
// original
{
"%float_1 = OpConstant %float 1",
"%spec_float = OpSpecConstant %float 1",
"%spec_inner = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float",
"%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %signed_one",
"%spec_vec = OpSpecConstantComposite %v2float %spec_float %float_1",
"%extract_inner = OpSpecConstantOp %int CompositeExtract %spec_inner 1",
"%extract_outer = OpSpecConstantOp %int CompositeExtract %spec_outer 1",
"%extract_vec = OpSpecConstantOp %float CompositeExtract %spec_vec 1",
},
// expected
{
"%float_1 = OpConstant %float 1",
"%spec_float = OpSpecConstant %float 1",
"%spec_inner = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float",
"%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %signed_one",
"%spec_vec = OpSpecConstantComposite %v2float %spec_float %float_1",
"%extract_inner = OpSpecConstantOp %int CompositeExtract %spec_inner 1",
"%extract_outer = OpSpecConstantOp %int CompositeExtract %spec_outer 1",
"%extract_vec = OpSpecConstantOp %float CompositeExtract %spec_vec 1",
},
},
// skip if the composite constant depends on the result of OpUndef,
// even though the composite extract target element does not depends
// on the OpUndef.
{
// original
{
"%undef = OpUndef %float",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef",
"%outer = OpConstantComposite %outer_struct %inner %signed_one",
"%extract_inner = OpSpecConstantOp %int CompositeExtract %inner 1",
"%extract_outer = OpSpecConstantOp %int CompositeExtract %outer 1",
},
// expected
{
"%undef = OpUndef %float",
"%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef",
"%outer = OpConstantComposite %outer_struct %inner %signed_one",
"%extract_inner = OpSpecConstantOp %int CompositeExtract %inner 1",
"%extract_outer = OpSpecConstantOp %int CompositeExtract %outer 1",
},
},
// TODO(qining): Add tests for Array and other composite type constants.
// clang-format on
})));
// Tests the swizzle operations for spec const vectors.
INSTANTIATE_TEST_CASE_P(
VectorShuffle, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// normal vector
{
// original
{
"%xy = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 0 1",
"%yz = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 1 2",
"%zw = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 2 3",
"%wx = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 3 0",
"%xx = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 0 0",
"%yyy = OpSpecConstantOp %v3int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 1 1 1",
"%wwww = OpSpecConstantOp %v4int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 2 2 2 2",
},
// expected
{
"%xy = OpConstantComposite %v2int %signed_zero %signed_one",
"%yz = OpConstantComposite %v2int %signed_one %signed_two",
"%zw = OpConstantComposite %v2int %signed_two %signed_three",
"%wx = OpConstantComposite %v2int %signed_three %signed_zero",
"%xx = OpConstantComposite %v2int %signed_zero %signed_zero",
"%yyy = OpConstantComposite %v3int %signed_one %signed_one %signed_one",
"%wwww = OpConstantComposite %v4int %signed_two %signed_two %signed_two %signed_two",
},
},
// null vector
{
// original
{
"%a = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %v4int_0_1_2_3 0 1",
"%b = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %v4int_0_1_2_3 2 3",
"%c = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %signed_null_vec 3 4",
"%d = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %signed_null_vec 1 2",
},
// expected
{
"%60 = OpConstantNull %int",
"%a = OpConstantComposite %v2int %60 %60",
"%62 = OpConstantNull %int",
"%b = OpConstantComposite %v2int %signed_zero %signed_one",
"%64 = OpConstantNull %int",
"%c = OpConstantComposite %v2int %signed_three %64",
"%66 = OpConstantNull %int",
"%d = OpConstantComposite %v2int %66 %66",
}
},
// skip if any of the components of the vector operands do not have
// determined value, even though the result vector might not be
// built with those undermined values.
{
// original
{
"%spec_int = OpSpecConstant %int 1",
"%spec_ivec = OpSpecConstantComposite %v2int %signed_null %spec_int",
"%a = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 0 1",
"%b = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 3 4",
},
// expected
{
"%spec_int = OpSpecConstant %int 1",
"%spec_ivec = OpSpecConstantComposite %v2int %signed_null %spec_int",
"%a = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 0 1",
"%b = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 3 4",
},
},
// Skip if any components of the two vector operands depend on
// the result of OpUndef. Even though the selected components do
// not depend on the OpUndef result.
{
// original
{
"%undef = OpUndef %int",
"%vec_1 = OpConstantComposite %v2int %undef %signed_one",
"%dep = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 0 3",
"%not_dep_element = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 1 3",
"%no_dep_vector = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 2 3",
},
// expected
{
"%undef = OpUndef %int",
"%vec_1 = OpConstantComposite %v2int %undef %signed_one",
"%dep = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 0 3",
"%not_dep_element = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 1 3",
"%no_dep_vector = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 2 3",
},
},
// clang-format on
})));
// Test with long use-def chain.
INSTANTIATE_TEST_CASE_P(
LongDefUseChain, FoldSpecConstantOpAndCompositePassTest,
::testing::ValuesIn(std::vector<
FoldSpecConstantOpAndCompositePassTestCase>({
// clang-format off
// Long Def-Use chain with binary operations.
{
// original
{
"%array_size = OpConstant %int 4",
"%type_arr_int_4 = OpTypeArray %int %array_size",
"%spec_int_0 = OpConstant %int 100",
"%spec_int_1 = OpConstant %int 1",
"%spec_int_2 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_1",
"%spec_int_3 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_2",
"%spec_int_4 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_3",
"%spec_int_5 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_4",
"%spec_int_6 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_5",
"%spec_int_7 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_6",
"%spec_int_8 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_7",
"%spec_int_9 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_8",
"%spec_int_10 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_9",
"%spec_int_11 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_10",
"%spec_int_12 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_11",
"%spec_int_13 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_12",
"%spec_int_14 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_13",
"%spec_int_15 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_14",
"%spec_int_16 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_15",
"%spec_int_17 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_16",
"%spec_int_18 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_17",
"%spec_int_19 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_18",
"%spec_int_20 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_19",
"%used_vec_a = OpSpecConstantComposite %v2int %spec_int_18 %spec_int_19",
"%used_vec_b = OpSpecConstantOp %v2int IMul %used_vec_a %used_vec_a",
"%spec_int_21 = OpSpecConstantOp %int CompositeExtract %used_vec_b 0",
"%array = OpConstantComposite %type_arr_int_4 %spec_int_20 %spec_int_20 %spec_int_21 %spec_int_21",
// Spec constants whose values can not be fully resolved should
// not be processed.
"%spec_int_22 = OpSpecConstant %int 123",
"%spec_int_23 = OpSpecConstantOp %int IAdd %spec_int_22 %signed_one",
},
// expected
{
"%array_size = OpConstant %int 4",
"%type_arr_int_4 = OpTypeArray %int %array_size",
"%spec_int_0 = OpConstant %int 100",
"%spec_int_1 = OpConstant %int 1",
"%spec_int_2 = OpConstant %int 101",
"%spec_int_3 = OpConstant %int -1",
"%spec_int_4 = OpConstant %int 99",
"%spec_int_5 = OpConstant %int 1",
"%spec_int_6 = OpConstant %int 101",
"%spec_int_7 = OpConstant %int -1",
"%spec_int_8 = OpConstant %int 99",
"%spec_int_9 = OpConstant %int 1",
"%spec_int_10 = OpConstant %int 101",
"%spec_int_11 = OpConstant %int -1",
"%spec_int_12 = OpConstant %int 99",
"%spec_int_13 = OpConstant %int 1",
"%spec_int_14 = OpConstant %int 101",
"%spec_int_15 = OpConstant %int -1",
"%spec_int_16 = OpConstant %int 101",
"%spec_int_17 = OpConstant %int 201",
"%spec_int_18 = OpConstant %int -101",
"%spec_int_19 = OpConstant %int -1",
"%spec_int_20 = OpConstant %int 101",
"%used_vec_a = OpConstantComposite %v2int %spec_int_18 %spec_int_19",
"%int_10201 = OpConstant %int 10201",
"%int_1 = OpConstant %int 1",
"%used_vec_b = OpConstantComposite %v2int %int_10201 %int_1",
"%spec_int_21 = OpConstant %int 10201",
"%array = OpConstantComposite %type_arr_int_4 %spec_int_20 %spec_int_20 %spec_int_21 %spec_int_21",
"%spec_int_22 = OpSpecConstant %int 123",
"%spec_int_23 = OpSpecConstantOp %int IAdd %spec_int_22 %signed_one",
},
},
// Long Def-Use chain with swizzle
})));
} // anonymous namespace