// limitations under the License.
#include "fold.h"
+
#include "def_use_manager.h"
#include "ir_context.h"
#include <cassert>
+#include <cstdint>
#include <vector>
namespace spvtools {
namespace {
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483648)
+#endif
+
+#ifndef INT32_MAX
+#define INT32_MAX 2147483647
+#endif
+
+#ifndef UINT32_MAX
+#define UINT32_MAX 0xffffffff /* 4294967295U */
+#endif
+
// Returns the single-word result from performing the given unary operation on
// the operand value which is passed in as a 32-bit word.
uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) {
return OperateWords(opcode, operand_values_in_raw_words);
}
+// Returns true if |inst| is a binary operation that takes two integers as
+// parameters and folds to a constant that can be represented as an unsigned
+// 32-bit value. If |inst| can be folded, the resulting value is returned
+// in |*result|. Valid result types for the instruction are any integer (signed
+// or unsigned) with 32-bits or less, or a boolean value.
+bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
+ SpvOp opcode = inst->opcode();
+ ir::IRContext* context = inst->context();
+ analysis::ConstantManager* const_manger = context->get_constant_mgr();
+
+ uint32_t ids[2];
+ const analysis::IntConstant* constants[2];
+ for (uint32_t i = 0; i < 2; i++) {
+ const ir::Operand* operand = &inst->GetInOperand(i);
+ if (operand->type != SPV_OPERAND_TYPE_ID) {
+ return false;
+ }
+ ids[i] = operand->words[0];
+ const analysis::Constant* constant =
+ const_manger->FindDeclaredConstant(ids[i]);
+ constants[i] = (constant != nullptr ? constant->AsIntConstant() : nullptr);
+ }
+
+ switch (opcode) {
+ // Arthimetics
+ case SpvOp::SpvOpIMul:
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr && constants[i]->IsZero()) {
+ *result = 0;
+ return true;
+ }
+ }
+ break;
+ case SpvOp::SpvOpUDiv:
+ case SpvOp::SpvOpSDiv:
+ case SpvOp::SpvOpSRem:
+ case SpvOp::SpvOpSMod:
+ case SpvOp::SpvOpUMod:
+ // This changes undefined behaviour (ie divide by 0) into a 0.
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr && constants[i]->IsZero()) {
+ *result = 0;
+ return true;
+ }
+ }
+ break;
+
+ // Shifting
+ case SpvOp::SpvOpShiftRightLogical:
+ case SpvOp::SpvOpShiftLeftLogical:
+ if (constants[1] != nullptr) {
+ // When shifting by a value larger than the size of the result, the
+ // result is undefined. We are setting the undefined behaviour to a
+ // result of 0.
+ uint32_t shift_amount = constants[1]->GetU32BitValue();
+ if (shift_amount >= 32) {
+ *result = 0;
+ return true;
+ }
+ }
+ break;
+
+ // Bitwise operations
+ case SpvOp::SpvOpBitwiseOr:
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr) {
+ // TODO: Change the mask against a value based on the bit width of the
+ // instruction result type. This way we can handle say 16-bit values
+ // as well.
+ uint32_t mask = constants[i]->GetU32BitValue();
+ if (mask == 0xFFFFFFFF) {
+ *result = 0xFFFFFFFF;
+ return true;
+ }
+ }
+ }
+ break;
+ case SpvOp::SpvOpBitwiseAnd:
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr) {
+ if (constants[i]->IsZero()) {
+ *result = 0;
+ return true;
+ }
+ }
+ }
+ break;
+
+ // Comparison
+ case SpvOp::SpvOpULessThan:
+ if (constants[0] != nullptr &&
+ constants[0]->GetU32BitValue() == UINT32_MAX) {
+ *result = false;
+ return true;
+ }
+ if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) {
+ *result = false;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpSLessThan:
+ if (constants[0] != nullptr &&
+ constants[0]->GetS32BitValue() == INT32_MAX) {
+ *result = false;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetS32BitValue() == INT32_MIN) {
+ *result = false;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpUGreaterThan:
+ if (constants[0] != nullptr && constants[0]->IsZero()) {
+ *result = false;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetU32BitValue() == UINT32_MAX) {
+ *result = false;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpSGreaterThan:
+ if (constants[0] != nullptr &&
+ constants[0]->GetS32BitValue() == INT32_MIN) {
+ *result = false;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetS32BitValue() == INT32_MAX) {
+ *result = false;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpULessThanEqual:
+ if (constants[0] != nullptr && constants[0]->IsZero()) {
+ *result = true;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetU32BitValue() == UINT32_MAX) {
+ *result = true;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpSLessThanEqual:
+ if (constants[0] != nullptr &&
+ constants[0]->GetS32BitValue() == INT32_MIN) {
+ *result = true;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetS32BitValue() == INT32_MAX) {
+ *result = true;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpUGreaterThanEqual:
+ if (constants[0] != nullptr &&
+ constants[0]->GetU32BitValue() == UINT32_MAX) {
+ *result = true;
+ return true;
+ }
+ if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) {
+ *result = true;
+ return true;
+ }
+ break;
+ case SpvOp::SpvOpSGreaterThanEqual:
+ if (constants[0] != nullptr &&
+ constants[0]->GetS32BitValue() == INT32_MAX) {
+ *result = true;
+ return true;
+ }
+ if (constants[1] != nullptr &&
+ constants[1]->GetS32BitValue() == INT32_MIN) {
+ *result = true;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+// Returns true if |inst| is a binary operation on two boolean values, and folds
+// to a constant boolean value. If |inst| can be folded, the result value is
+// returned in |*result|.
+bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
+ SpvOp opcode = inst->opcode();
+ ir::IRContext* context = inst->context();
+ analysis::ConstantManager* const_manger = context->get_constant_mgr();
+
+ uint32_t ids[2];
+ const analysis::BoolConstant* constants[2];
+ for (uint32_t i = 0; i < 2; i++) {
+ const ir::Operand* operand = &inst->GetInOperand(i);
+ if (operand->type != SPV_OPERAND_TYPE_ID) {
+ return false;
+ }
+ ids[i] = operand->words[0];
+ const analysis::Constant* constant =
+ const_manger->FindDeclaredConstant(ids[i]);
+ constants[i] = (constant != nullptr ? constant->AsBoolConstant() : nullptr);
+ }
+
+ switch (opcode) {
+ // Logical
+ case SpvOp::SpvOpLogicalOr:
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr) {
+ if (constants[i]->value()) {
+ *result = true;
+ return true;
+ }
+ }
+ }
+ break;
+ case SpvOp::SpvOpLogicalAnd:
+ for (uint32_t i = 0; i < 2; i++) {
+ if (constants[i] != nullptr) {
+ if (!constants[i]->value()) {
+ *result = false;
+ return true;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+// Returns true if |inst| can be folded to an constant. If it can, the value
+// is returned in |result|. If not, |result| is unchanged. It is assumed that
+// not all operands are constant. Those cases are handled by |FoldScalar|.
+bool FoldIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
+ assert(IsFoldableOpcode(inst->opcode()) &&
+ "Unhandled instruction opcode in FoldScalars");
+ switch (inst->NumInOperands()) {
+ case 2:
+ return FoldBinaryIntegerOpToConstant(inst, result) ||
+ FoldBinaryBooleanOpToConstant(inst, result);
+ default:
+ return false;
+ }
+}
+
std::vector<uint32_t> FoldVectors(
SpvOp opcode, uint32_t num_dims,
const std::vector<const analysis::Constant*>& operands) {
constants.push_back(const_op);
});
+ uint32_t result_val = 0;
+ bool successful = false;
// If all parameters are constant, fold the instruction to a constant.
if (!missing_constants) {
- uint32_t result_val = FoldScalars(inst->opcode(), constants);
+ result_val = FoldScalars(inst->opcode(), constants);
+ successful = true;
+ }
+
+ if (!successful) {
+ successful = FoldIntegerOpToConstant(inst, &result_val);
+ }
+
+ if (successful) {
const analysis::Constant* result_const =
const_mgr->GetConstant(const_mgr->GetType(inst), {result_val});
return const_mgr->GetDefiningInstruction(result_const);
}
- // TODO: Add other folding opportunities that will generate a constant.
return nullptr;
}
// Nothing else yet.
return false;
}
+
ir::Instruction* FoldInstruction(ir::Instruction* inst,
std::function<uint32_t(uint32_t)> id_map) {
ir::Instruction* folded_inst = FoldInstructionToConstant(inst, id_map);
--- /dev/null
+// 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 <memory>
+#include <unordered_set>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <opt/fold.h>
+
+#include "opt/build_module.h"
+#include "opt/def_use_manager.h"
+#include "opt/ir_context.h"
+#include "opt/module.h"
+#include "pass_utils.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace {
+
+using ::testing::Contains;
+
+using namespace spvtools;
+using spvtools::opt::analysis::DefUseManager;
+
+template <class ResultType>
+struct InstructionFoldingCase {
+ InstructionFoldingCase(const std::string& tb, uint32_t id, ResultType result)
+ : test_body(tb), id_to_fold(id), expected_result(result) {}
+
+ std::string test_body;
+ uint32_t id_to_fold;
+ ResultType expected_result;
+};
+
+using IntegerInstructionFoldingTest =
+ ::testing::TestWithParam<InstructionFoldingCase<uint32_t>>;
+
+TEST_P(IntegerInstructionFoldingTest, Case) {
+ const auto& tc = GetParam();
+
+ // Build module.
+ std::unique_ptr<ir::IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ // Fold the instruction to test.
+ opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+ inst = opt::FoldInstruction(inst);
+
+ // Make sure the instruction folded as expected.
+ EXPECT_NE(inst, nullptr);
+ if (inst != nullptr) {
+ EXPECT_EQ(inst->opcode(), SpvOpConstant);
+ opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
+ const opt::analysis::IntConstant* result =
+ const_mrg->GetConstantFromInst(inst)->AsIntConstant();
+ EXPECT_NE(result, nullptr);
+ if (result != nullptr) {
+ EXPECT_EQ(result->GetU32BitValue(), tc.expected_result);
+ }
+ }
+}
+
+// Returns a common SPIR-V header for all of the test that follow.
+const std::string& Header() {
+ static const std::string header = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+%void = OpTypeVoid
+%void_func = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%short = OpTypeInt 16 1
+%int = OpTypeInt 32 1
+%long = OpTypeInt 64 1
+%uint = OpTypeInt 32 1
+%_ptr_int = OpTypePointer Function %int
+%_ptr_uint = OpTypePointer Function %uint
+%_ptr_bool = OpTypePointer Function %bool
+%short_0 = OpConstant %short 0
+%short_3 = OpConstant %short 3
+%int_0 = OpConstant %int 0
+%int_3 = OpConstant %int 3
+%int_min = OpConstant %int -2147483648
+%int_max = OpConstant %int 2147483647
+%long_0 = OpConstant %long 0
+%long_3 = OpConstant %long 3
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_32 = OpConstant %uint 32
+%uint_max = OpConstant %uint -1
+)";
+
+ return header;
+}
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTest,
+ ::testing::Values(
+ // Test case 0: fold 0*n
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpIMul %int %int_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 1: fold n*0
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpIMul %int %load %int_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 2: fold 0/n (signed)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSDiv %int %int_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 3: fold n/0 (signed)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSDiv %int %load %int_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 4: fold 0/n (unsigned)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUDiv %uint %uint_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 5: fold n/0 (unsigned)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSDiv %int %load %int_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 6: fold 0 remainder n
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSRem %int %int_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 7: fold n remainder 0
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSRem %int %load %int_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 8: fold 0%n (signed)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSMod %int %int_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 9: fold n%0 (signed)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSMod %int %load %int_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 10: fold 0%n (unsigned)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUMod %uint %uint_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 11: fold n%0 (unsigned)
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUMod %uint %load %uint_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 12: fold n << 32
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpShiftLeftLogical %uint %load %uint_32\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 13: fold n >> 32
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpShiftRightLogical %uint %load %uint_32\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0),
+ // Test case 14: fold n | 0xFFFFFFFF
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpBitwiseOr %uint %load %uint_max\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0xFFFFFFFF),
+ // Test case 15: fold 0xFFFFFFFF | n
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpBitwiseOr %uint %uint_max %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0xFFFFFFFF),
+ // Test case 16: fold n & 0
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpBitwiseAnd %uint %load %uint_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, 0)
+));
+// clang-format on
+
+using BooleanInstructionFoldingTest =
+ ::testing::TestWithParam<InstructionFoldingCase<bool>>;
+
+TEST_P(BooleanInstructionFoldingTest, Case) {
+ const auto& tc = GetParam();
+
+ // Build module.
+ std::unique_ptr<ir::IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ // Fold the instruction to test.
+ opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+ inst = opt::FoldInstruction(inst);
+
+ // Make sure the instruction folded as expected.
+ EXPECT_NE(inst, nullptr);
+ if (inst != nullptr) {
+ std::vector<SpvOp> bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse};
+ EXPECT_THAT(bool_opcodes, Contains(inst->opcode()));
+ opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
+ const opt::analysis::BoolConstant* result =
+ const_mrg->GetConstantFromInst(inst)->AsBoolConstant();
+ EXPECT_NE(result, nullptr);
+ if (result != nullptr) {
+ EXPECT_EQ(result->value(), tc.expected_result);
+ }
+ }
+}
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(TestCase, BooleanInstructionFoldingTest,
+ ::testing::Values(
+ // Test case 0: fold true || n
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%load = OpLoad %bool %n\n" +
+ "%2 = OpLogicalOr %bool %true %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 1: fold n || true
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%load = OpLoad %bool %n\n" +
+ "%2 = OpLogicalOr %bool %load %true\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 2: fold false && n
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%load = OpLoad %bool %n\n" +
+ "%2 = OpLogicalAnd %bool %false %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 3: fold n && false
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%load = OpLoad %bool %n\n" +
+ "%2 = OpLogicalAnd %bool %load %false\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 4: fold n < 0 (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpULessThan %bool %load %uint_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 5: fold UINT_MAX < n (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpULessThan %bool %uint_max %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 6: fold INT_MAX < n (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSLessThan %bool %int_max %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 7: fold n < INT_MIN (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSLessThan %bool %load %int_min\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 8: fold 0 > n (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThan %bool %uint_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 9: fold n > UINT_MAX (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThan %bool %load %uint_max\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 10: fold n > INT_MAX (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSGreaterThan %bool %load %int_max\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 11: fold INT_MIN > n (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpSGreaterThan %bool %int_min %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, false),
+ // Test case 12: fold 0 <= n (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpULessThanEqual %bool %uint_0 %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 13: fold n <= UINT_MAX (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpULessThanEqual %bool %load %uint_max\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 14: fold INT_MIN <= n (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSLessThanEqual %bool %int_min %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 15: fold n <= INT_MAX (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSLessThanEqual %bool %load %int_max\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 16: fold n >= 0 (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThanEqual %bool %load %uint_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 17: fold UINT_MAX >= n (unsigned)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThanEqual %bool %uint_max %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 18: fold n >= INT_MIN (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSGreaterThanEqual %bool %load %int_min\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true),
+ // Test case 19: fold INT_MAX >= n (signed)
+ InstructionFoldingCase<bool>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpSGreaterThanEqual %bool %int_max %load\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, true)
+));
+// clang-format on
+
+using InstructionNotFoldedTest =
+ ::testing::TestWithParam<InstructionFoldingCase<void*>>;
+
+TEST_P(InstructionNotFoldedTest, Case) {
+ const auto& tc = GetParam();
+
+ // Build module.
+ std::unique_ptr<ir::IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ // Fold the instruction to test.
+ opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+ inst = opt::FoldInstruction(inst);
+
+ // Make sure the instruction folded as expected.
+ EXPECT_EQ(inst, nullptr);
+}
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(TestCase, InstructionNotFoldedTest,
+ ::testing::Values(
+ // Test case 0: Don't fold n * m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpIMul %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 1: Don't fold n / m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpUDiv %uint %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 2: Don't fold n / m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpSDiv %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 3: Don't fold n remainder m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpSRem %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 4: Don't fold n % m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpSMod %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 5: Don't fold n % m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpUMod %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 6: Don't fold n << m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpShiftRightLogical %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 7: Don't fold n >> m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpShiftLeftLogical %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 8: Don't fold n | m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpBitwiseOr %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 9: Don't fold n & m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpBitwiseAnd %int %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 10: Don't fold n < m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpULessThan %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 11: Don't fold n > m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpUGreaterThan %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 12: Don't fold n <= m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpULessThanEqual %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 13: Don't fold n >= m (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%m = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%load_m = OpLoad %uint %m\n" +
+ "%2 = OpUGreaterThanEqual %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 14: Don't fold n < m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpULessThan %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 15: Don't fold n > m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpUGreaterThan %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 16: Don't fold n <= m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpULessThanEqual %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 17: Don't fold n >= m (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%m = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%load_m = OpLoad %int %m\n" +
+ "%2 = OpUGreaterThanEqual %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 18: Don't fold n || m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%m = OpVariable %_ptr_bool Function\n" +
+ "%load_n = OpLoad %bool %n\n" +
+ "%load_m = OpLoad %bool %m\n" +
+ "%2 = OpLogicalOr %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 19: Don't fold n && m
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_bool Function\n" +
+ "%m = OpVariable %_ptr_bool Function\n" +
+ "%load_n = OpLoad %bool %n\n" +
+ "%load_m = OpLoad %bool %m\n" +
+ "%2 = OpLogicalAnd %bool %load_n %load_m\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 20: Don't fold n * 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpIMul %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 21: Don't fold n / 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpUDiv %uint %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 22: Don't fold n / 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpSDiv %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 23: Don't fold n remainder 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpSRem %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 24: Don't fold n % 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpSMod %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 25: Don't fold n % 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpUMod %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 26: Don't fold n << 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpShiftRightLogical %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 27: Don't fold n >> 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpShiftLeftLogical %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 28: Don't fold n | 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpBitwiseOr %int %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 29: Don't fold n & 3
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpBitwiseAnd %uint %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 30: Don't fold n < 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpULessThan %bool %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 31: Don't fold n > 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThan %bool %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 32: Don't fold n <= 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpULessThanEqual %bool %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 33: Don't fold n >= 3 (unsigned)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_uint Function\n" +
+ "%load_n = OpLoad %uint %n\n" +
+ "%2 = OpUGreaterThanEqual %bool %load_n %uint_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 34: Don't fold n < 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpULessThan %bool %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 35: Don't fold n > 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpUGreaterThan %bool %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 36: Don't fold n <= 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpULessThanEqual %bool %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 37: Don't fold n >= 3 (signed)
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load_n = OpLoad %int %n\n" +
+ "%2 = OpUGreaterThanEqual %bool %load_n %int_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 38: Don't fold 0 + 3 (long), bad length
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpIAdd %long %long_0 %long_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr),
+ // Test case 39: Don't fold 0 + 3 (short), bad length
+ InstructionFoldingCase<void*>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpIAdd %short %short_0 %short_3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, nullptr)
+ ));
+// clang-format on
+} // anonymous namespace