Skip to content

Commit fb80331

Browse files
folding_rules: generalise RedundantIAdd() to |^>><<+- with 0 on RHS and |^+ with 0 on LHS (KhronosGroup#6013)
All of OpBitwiseOr OpBitwiseXor OpShiftRightLogical OpShiftRightArithmetic OpShiftLeftLogical OpIAdd OpISub are no-ops with 0 on RHS, and all but the subs and shifts are no-ops with 0 on LHS. RedundantIAdd() already did this for OpIAdd, and it can be trivially generalised. Thus, this whole program reduces to a no-op: struct params_t { uint u; int i; float f; uint3 u3; int3 i3; float3 f3; }; RWStructuredBuffer<params_t> params; [numthreads(1, 1, 1)] void main() { params[0].u = params[0].u | 0; params[0].i = params[0].i | 0; params[0].u = params[0].u ^ 0; params[0].i = params[0].i ^ 0; params[0].u = params[0].u >> 0; params[0].i = params[0].i >> 0; params[0].u = params[0].u >> 0; params[0].i = params[0].i >> 0; params[0].u = params[0].u << 0; params[0].i = params[0].i << 0; params[0].u = params[0].u << 0; params[0].u3 = params[0].u3 | 0; params[0].i3 = params[0].i3 | 0; params[0].u3 = params[0].u3 ^ 0; params[0].i3 = params[0].i3 ^ 0; params[0].u3 = params[0].u3 >> 0; params[0].i3 = params[0].i3 >> 0; params[0].u3 = params[0].u3 >> 0; params[0].i3 = params[0].i3 >> 0; params[0].u3 = params[0].u3 << 0; params[0].i3 = params[0].i3 << 0; params[0].u3 = params[0].u3 << 0; params[0].u = params[0].u + 0; params[0].i = params[0].i + 0; params[0].f = params[0].f + 0; params[0].u = params[0].u - 0; params[0].i = params[0].i - 0; params[0].f = params[0].f - 0; params[0].u3 = params[0].u3 + 0; params[0].i3 = params[0].i3 + 0; params[0].f3 = params[0].f3 + 0; params[0].u3 = params[0].u3 - 0; params[0].i3 = params[0].i3 - 0; params[0].f3 = params[0].f3 - 0; params[0].u = 0 | params[0].u; params[0].i = 0 | params[0].i; params[0].u = 0 ^ params[0].u; params[0].i = 0 ^ params[0].i; params[0].u3 = 0 | params[0].u3; params[0].i3 = 0 | params[0].i3; params[0].u3 = 0 ^ params[0].u3; params[0].i3 = 0 ^ params[0].i3; params[0].u = 0 + params[0].u; params[0].i = 0 + params[0].i; params[0].f = 0 + params[0].f; params[0].u3 = 0 + params[0].u3; params[0].i3 = 0 + params[0].i3; params[0].f3 = 0 + params[0].f3; } Closes: google/shaderc#1484
1 parent 62a930f commit fb80331

File tree

2 files changed

+154
-25
lines changed

2 files changed

+154
-25
lines changed

source/opt/folding_rules.cpp

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,24 +2507,17 @@ FoldingRule RedundantFMix() {
25072507
};
25082508
}
25092509

2510-
// This rule handles addition of zero for integers.
2511-
FoldingRule RedundantIAdd() {
2512-
return [](IRContext* context, Instruction* inst,
2513-
const std::vector<const analysis::Constant*>& constants) {
2514-
assert(inst->opcode() == spv::Op::OpIAdd &&
2515-
"Wrong opcode. Should be OpIAdd.");
2510+
// Returns a folding rule that folds the instruction to the operand not being
2511+
// checked if the operand that is checked is zero.
2512+
FoldingRule RedundantBinaryOpWithZeroOperand(uint32_t arg) {
2513+
return [arg](IRContext* context, Instruction* inst,
2514+
const std::vector<const analysis::Constant*>& constants) {
2515+
assert(constants.size() == 2);
25162516

2517-
uint32_t operand = std::numeric_limits<uint32_t>::max();
2518-
const analysis::Type* operand_type = nullptr;
2519-
if (constants[0] && constants[0]->IsZero()) {
2520-
operand = inst->GetSingleWordInOperand(1);
2521-
operand_type = constants[0]->type();
2522-
} else if (constants[1] && constants[1]->IsZero()) {
2523-
operand = inst->GetSingleWordInOperand(0);
2524-
operand_type = constants[1]->type();
2525-
}
2517+
if (constants[arg] && constants[arg]->IsZero()) {
2518+
auto operand = inst->GetSingleWordInOperand(1 - arg);
2519+
auto operand_type = constants[arg]->type();
25262520

2527-
if (operand != std::numeric_limits<uint32_t>::max()) {
25282521
const analysis::Type* inst_type =
25292522
context->get_type_mgr()->GetType(inst->type_id());
25302523
if (inst_type->IsSame(operand_type)) {
@@ -2539,6 +2532,38 @@ FoldingRule RedundantIAdd() {
25392532
};
25402533
}
25412534

2535+
// This rule handles any of RedundantBinaryRhs0Ops with a 0 or vector 0 on the
2536+
// right-hand side.
2537+
static const constexpr spv::Op RedundantBinaryRhs0Ops[] = {
2538+
spv::Op::OpBitwiseOr,
2539+
spv::Op::OpBitwiseXor,
2540+
spv::Op::OpShiftRightLogical,
2541+
spv::Op::OpShiftRightArithmetic,
2542+
spv::Op::OpShiftLeftLogical,
2543+
spv::Op::OpIAdd,
2544+
spv::Op::OpISub};
2545+
FoldingRule RedundantBinaryRhs0(spv::Op op) {
2546+
assert(std::find(std::begin(RedundantBinaryRhs0Ops),
2547+
std::end(RedundantBinaryRhs0Ops),
2548+
op) != std::end(RedundantBinaryRhs0Ops) &&
2549+
"Wrong opcode.");
2550+
(void)op;
2551+
return RedundantBinaryOpWithZeroOperand(1);
2552+
}
2553+
2554+
// This rule handles any of RedundantBinaryLhs0Ops with a 0 or vector 0 on the
2555+
// left-hand side.
2556+
static const constexpr spv::Op RedundantBinaryLhs0Ops[] = {
2557+
spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpIAdd};
2558+
FoldingRule RedundantBinaryLhs0(spv::Op op) {
2559+
assert(std::find(std::begin(RedundantBinaryLhs0Ops),
2560+
std::end(RedundantBinaryLhs0Ops),
2561+
op) != std::end(RedundantBinaryLhs0Ops) &&
2562+
"Wrong opcode.");
2563+
(void)op;
2564+
return RedundantBinaryOpWithZeroOperand(0);
2565+
}
2566+
25422567
// This rule look for a dot with a constant vector containing a single 1 and
25432568
// the rest 0s. This is the same as doing an extract.
25442569
FoldingRule DotProductDoingExtract() {
@@ -2876,6 +2901,11 @@ void FoldingRules::AddFoldingRules() {
28762901
// Note that the order in which rules are added to the list matters. If a rule
28772902
// applies to the instruction, the rest of the rules will not be attempted.
28782903
// Take that into consideration.
2904+
for (auto op : RedundantBinaryRhs0Ops)
2905+
rules_[op].push_back(RedundantBinaryRhs0(op));
2906+
for (auto op : RedundantBinaryLhs0Ops)
2907+
rules_[op].push_back(RedundantBinaryLhs0(op));
2908+
28792909
rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector());
28802910

28812911
rules_[spv::Op::OpCompositeConstruct].push_back(
@@ -2921,7 +2951,6 @@ void FoldingRules::AddFoldingRules() {
29212951
rules_[spv::Op::OpFSub].push_back(MergeSubAddArithmetic());
29222952
rules_[spv::Op::OpFSub].push_back(MergeSubSubArithmetic());
29232953

2924-
rules_[spv::Op::OpIAdd].push_back(RedundantIAdd());
29252954
rules_[spv::Op::OpIAdd].push_back(MergeAddNegateArithmetic());
29262955
rules_[spv::Op::OpIAdd].push_back(MergeAddAddArithmetic());
29272956
rules_[spv::Op::OpIAdd].push_back(MergeAddSubArithmetic());

test/opt/fold_test.cpp

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5054,7 +5054,57 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
50545054
"OpReturn\n" +
50555055
"OpFunctionEnd",
50565056
2, 0),
5057-
// Test case 2: Fold n + 0
5057+
// Test case 2: Fold n | 0
5058+
InstructionFoldingCase<uint32_t>(
5059+
Header() + "%main = OpFunction %void None %void_func\n" +
5060+
"%main_lab = OpLabel\n" +
5061+
"%n = OpVariable %_ptr_uint Function\n" +
5062+
"%3 = OpLoad %uint %n\n" +
5063+
"%2 = OpBitwiseOr %uint %3 %uint_0\n" +
5064+
"OpReturn\n" +
5065+
"OpFunctionEnd",
5066+
2, 3),
5067+
// Test case 3: Fold n ^ 0
5068+
InstructionFoldingCase<uint32_t>(
5069+
Header() + "%main = OpFunction %void None %void_func\n" +
5070+
"%main_lab = OpLabel\n" +
5071+
"%n = OpVariable %_ptr_uint Function\n" +
5072+
"%3 = OpLoad %uint %n\n" +
5073+
"%2 = OpBitwiseXor %uint %3 %uint_0\n" +
5074+
"OpReturn\n" +
5075+
"OpFunctionEnd",
5076+
2, 3),
5077+
// Test case 4: Fold n >> 0
5078+
InstructionFoldingCase<uint32_t>(
5079+
Header() + "%main = OpFunction %void None %void_func\n" +
5080+
"%main_lab = OpLabel\n" +
5081+
"%n = OpVariable %_ptr_uint Function\n" +
5082+
"%3 = OpLoad %uint %n\n" +
5083+
"%2 = OpShiftRightLogical %uint %3 %uint_0\n" +
5084+
"OpReturn\n" +
5085+
"OpFunctionEnd",
5086+
2, 3),
5087+
// Test case 5: Fold n >> 0
5088+
InstructionFoldingCase<uint32_t>(
5089+
Header() + "%main = OpFunction %void None %void_func\n" +
5090+
"%main_lab = OpLabel\n" +
5091+
"%n = OpVariable %_ptr_uint Function\n" +
5092+
"%3 = OpLoad %uint %n\n" +
5093+
"%2 = OpShiftRightArithmetic %uint %3 %uint_0\n" +
5094+
"OpReturn\n" +
5095+
"OpFunctionEnd",
5096+
2, 3),
5097+
// Test case 6: Fold n << 0
5098+
InstructionFoldingCase<uint32_t>(
5099+
Header() + "%main = OpFunction %void None %void_func\n" +
5100+
"%main_lab = OpLabel\n" +
5101+
"%n = OpVariable %_ptr_uint Function\n" +
5102+
"%3 = OpLoad %uint %n\n" +
5103+
"%2 = OpShiftLeftLogical %uint %3 %uint_0\n" +
5104+
"OpReturn\n" +
5105+
"OpFunctionEnd",
5106+
2, 3),
5107+
// Test case 7: Fold n + 0
50585108
InstructionFoldingCase<uint32_t>(
50595109
Header() + "%main = OpFunction %void None %void_func\n" +
50605110
"%main_lab = OpLabel\n" +
@@ -5064,7 +5114,37 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
50645114
"OpReturn\n" +
50655115
"OpFunctionEnd",
50665116
2, 3),
5067-
// Test case 3: Fold 0 + n
5117+
// Test case 8: Fold n - 0
5118+
InstructionFoldingCase<uint32_t>(
5119+
Header() + "%main = OpFunction %void None %void_func\n" +
5120+
"%main_lab = OpLabel\n" +
5121+
"%n = OpVariable %_ptr_uint Function\n" +
5122+
"%3 = OpLoad %uint %n\n" +
5123+
"%2 = OpISub %uint %3 %uint_0\n" +
5124+
"OpReturn\n" +
5125+
"OpFunctionEnd",
5126+
2, 3),
5127+
// Test case 9: Fold 0 | n
5128+
InstructionFoldingCase<uint32_t>(
5129+
Header() + "%main = OpFunction %void None %void_func\n" +
5130+
"%main_lab = OpLabel\n" +
5131+
"%n = OpVariable %_ptr_uint Function\n" +
5132+
"%3 = OpLoad %uint %n\n" +
5133+
"%2 = OpBitwiseOr %uint %uint_0 %3\n" +
5134+
"OpReturn\n" +
5135+
"OpFunctionEnd",
5136+
2, 3),
5137+
// Test case 10: Fold 0 ^ n
5138+
InstructionFoldingCase<uint32_t>(
5139+
Header() + "%main = OpFunction %void None %void_func\n" +
5140+
"%main_lab = OpLabel\n" +
5141+
"%n = OpVariable %_ptr_uint Function\n" +
5142+
"%3 = OpLoad %uint %n\n" +
5143+
"%2 = OpBitwiseXor %uint %uint_0 %3\n" +
5144+
"OpReturn\n" +
5145+
"OpFunctionEnd",
5146+
2, 3),
5147+
// Test case 11: Fold 0 + n
50685148
InstructionFoldingCase<uint32_t>(
50695149
Header() + "%main = OpFunction %void None %void_func\n" +
50705150
"%main_lab = OpLabel\n" +
@@ -5074,7 +5154,7 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
50745154
"OpReturn\n" +
50755155
"OpFunctionEnd",
50765156
2, 3),
5077-
// Test case 4: Don't fold n + (1,0)
5157+
// Test case 12: Don't fold n + (1,0)
50785158
InstructionFoldingCase<uint32_t>(
50795159
Header() + "%main = OpFunction %void None %void_func\n" +
50805160
"%main_lab = OpLabel\n" +
@@ -5084,7 +5164,7 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
50845164
"OpReturn\n" +
50855165
"OpFunctionEnd",
50865166
2, 0),
5087-
// Test case 5: Don't fold (1,0) + n
5167+
// Test case 13: Don't fold (1,0) + n
50885168
InstructionFoldingCase<uint32_t>(
50895169
Header() + "%main = OpFunction %void None %void_func\n" +
50905170
"%main_lab = OpLabel\n" +
@@ -5094,7 +5174,7 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
50945174
"OpReturn\n" +
50955175
"OpFunctionEnd",
50965176
2, 0),
5097-
// Test case 6: Fold n + (0,0)
5177+
// Test case 14: Fold n + (0,0)
50985178
InstructionFoldingCase<uint32_t>(
50995179
Header() + "%main = OpFunction %void None %void_func\n" +
51005180
"%main_lab = OpLabel\n" +
@@ -5104,7 +5184,7 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
51045184
"OpReturn\n" +
51055185
"OpFunctionEnd",
51065186
2, 3),
5107-
// Test case 7: Fold (0,0) + n
5187+
// Test case 15: Fold (0,0) + n
51085188
InstructionFoldingCase<uint32_t>(
51095189
Header() + "%main = OpFunction %void None %void_func\n" +
51105190
"%main_lab = OpLabel\n" +
@@ -5114,7 +5194,27 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
51145194
"OpReturn\n" +
51155195
"OpFunctionEnd",
51165196
2, 3),
5117-
// Test case 8: Don't fold because of undefined value. Using 4294967295
5197+
// Test case 16: Fold n | (0,0)
5198+
InstructionFoldingCase<uint32_t>(
5199+
Header() + "%main = OpFunction %void None %void_func\n" +
5200+
"%main_lab = OpLabel\n" +
5201+
"%n = OpVariable %_ptr_v2int Function\n" +
5202+
"%3 = OpLoad %v2int %n\n" +
5203+
"%2 = OpBitwiseOr %v2int %3 %v2int_0_0\n" +
5204+
"OpReturn\n" +
5205+
"OpFunctionEnd",
5206+
2, 3),
5207+
// Test case 17: Fold (0,0) | n
5208+
InstructionFoldingCase<uint32_t>(
5209+
Header() + "%main = OpFunction %void None %void_func\n" +
5210+
"%main_lab = OpLabel\n" +
5211+
"%n = OpVariable %_ptr_v2int Function\n" +
5212+
"%3 = OpLoad %v2int %n\n" +
5213+
"%2 = OpBitwiseOr %v2int %v2int_0_0 %3\n" +
5214+
"OpReturn\n" +
5215+
"OpFunctionEnd",
5216+
2, 3),
5217+
// Test case 18: Don't fold because of undefined value. Using 4294967295
51185218
// means that entry is undefined. We do not expect it to ever happen, so
51195219
// not worth folding.
51205220
InstructionFoldingCase<uint32_t>(
@@ -5126,7 +5226,7 @@ INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingT
51265226
"OpReturn\n" +
51275227
"OpFunctionEnd",
51285228
2, 0),
5129-
// Test case 9: Don't fold because of undefined value. Using 4294967295
5229+
// Test case 19: Don't fold because of undefined value. Using 4294967295
51305230
// means that entry is undefined. We do not expect it to ever happen, so
51315231
// not worth folding.
51325232
InstructionFoldingCase<uint32_t>(

0 commit comments

Comments
 (0)