break;
case SpvOpSDiv:
case SpvOpUDiv:
- // To avoid losing precision we won't perform division that would result
- // in a remainder. Unfortunate code duplication results.
- if (input2->AsIntConstant()->IsZero()) return 0;
- if (width == 64) {
- if (type->IsSigned()) {
- if (input1->GetS64() % input2->GetS64() != 0) return 0;
- int64_t val = input1->GetS64() / input2->GetS64();
- words = ExtractInts(static_cast<uint64_t>(val));
- } else {
- if (input1->GetU64() % input2->GetU64() != 0) return 0;
- uint64_t val = input1->GetU64() / input2->GetU64();
- words = ExtractInts(val);
- }
- } else {
- if (type->IsSigned()) {
- if (input1->GetS32() % input2->GetS32() != 0) return 0;
- int32_t val = input1->GetS32() / input2->GetS32();
- words.push_back(static_cast<uint32_t>(val));
- } else {
- if (input1->GetU32() % input2->GetU32() != 0) return 0;
- uint32_t val = input1->GetU32() / input2->GetU32();
- words.push_back(val);
- }
- }
+ assert(false && "Should not merge integer division");
break;
case SpvOpIAdd:
FOLD_OP(+);
}
// Merges divides into subsequent multiplies if each instruction contains one
-// constant operand.
+// constant operand. Does not support integer operations.
// Cases:
-// 2 * (x / 2) = 4 / x
-// 2 * (2 / x) = x * 1
+// 2 * (x / 2) = x * 1
+// 2 * (2 / x) = 4 / x
// (x / 2) * 2 = x * 1
// (2 / x) * 2 = 4 / x
FoldingRule MergeMulDivArithmetic() {
return [](ir::Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
- assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul);
+ assert(inst->opcode() == SpvOpFMul);
ir::IRContext* context = inst->context();
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
const analysis::Type* type =
context->get_type_mgr()->GetType(inst->type_id());
- if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed())
- return false;
+ if (!inst->IsFloatingPointFoldingAllowed()) return false;
uint32_t width = ElementWidth(type);
if (width != 32 && width != 64) return false;
const analysis::Constant* const_input1 = ConstInput(constants);
if (!const_input1) return false;
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
- if (HasFloatingPoint(type) && !other_inst->IsFloatingPointFoldingAllowed())
- return false;
+ if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
- if (other_inst->opcode() == SpvOpFDiv ||
- other_inst->opcode() == SpvOpSDiv ||
- other_inst->opcode() == SpvOpUDiv) {
+ if (other_inst->opcode() == SpvOpFDiv) {
std::vector<const analysis::Constant*> other_constants =
const_mgr->GetOperandConstants(other_inst);
const analysis::Constant* const_input2 = ConstInput(other_constants);
}
// Merges consecutive divides if each instruction contains one constant operand.
+// Does not support integer division.
// Cases:
// 2 / (x / 2) = 4 / x
// 4 / (2 / x) = 2 * x
FoldingRule MergeDivDivArithmetic() {
return [](ir::Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
- assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv ||
- inst->opcode() == SpvOpUDiv);
+ assert(inst->opcode() == SpvOpFDiv);
ir::IRContext* context = inst->context();
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
const analysis::Type* type =
context->get_type_mgr()->GetType(inst->type_id());
- bool uses_float = HasFloatingPoint(type);
- if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+ if (!inst->IsFloatingPointFoldingAllowed()) return false;
uint32_t width = ElementWidth(type);
if (width != 32 && width != 64) return false;
const analysis::Constant* const_input1 = ConstInput(constants);
if (!const_input1) return false;
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
- if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
- return false;
+ if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
bool first_is_variable = constants[0] == nullptr;
if (other_inst->opcode() == inst->opcode()) {
SpvOp merge_op = inst->opcode();
if (other_first_is_variable) {
// Constants magnify.
- merge_op = uses_float ? SpvOpFMul : SpvOpIMul;
+ merge_op = SpvOpFMul;
}
// This is an x / (*) case. Swap the inputs. Doesn't harm multiply
SpvOp op = inst->opcode();
if (!first_is_variable && !other_first_is_variable) {
// Effectively div of 1/x, so change to multiply.
- op = uses_float ? SpvOpFMul : SpvOpIMul;
+ op = SpvOpFMul;
}
uint32_t op1 = merged_id;
}
// Fold multiplies succeeded by divides where each instruction contains a
-// constant operand.
+// constant operand. Does not support integer divide.
// Cases:
// 4 / (x * 2) = 2 / x
// 4 / (2 * x) = 2 / x
FoldingRule MergeDivMulArithmetic() {
return [](ir::Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
- assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv ||
- inst->opcode() == SpvOpUDiv);
+ assert(inst->opcode() == SpvOpFDiv);
ir::IRContext* context = inst->context();
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
const analysis::Type* type =
context->get_type_mgr()->GetType(inst->type_id());
- bool uses_float = HasFloatingPoint(type);
- if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+ if (!inst->IsFloatingPointFoldingAllowed()) return false;
uint32_t width = ElementWidth(type);
if (width != 32 && width != 64) return false;
const analysis::Constant* const_input1 = ConstInput(constants);
if (!const_input1) return false;
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
- if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
- return false;
+ if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
bool first_is_variable = constants[0] == nullptr;
- if (other_inst->opcode() == SpvOpFMul ||
- other_inst->opcode() == SpvOpIMul) {
+ if (other_inst->opcode() == SpvOpFMul) {
std::vector<const analysis::Constant*> other_constants =
const_mgr->GetOperandConstants(other_inst);
const analysis::Constant* const_input2 = ConstInput(other_constants);
rules_[SpvOpIMul].push_back(IntMultipleBy1());
rules_[SpvOpIMul].push_back(MergeMulMulArithmetic());
- rules_[SpvOpIMul].push_back(MergeMulDivArithmetic());
rules_[SpvOpIMul].push_back(MergeMulNegateArithmetic());
rules_[SpvOpISub].push_back(MergeSubNegateArithmetic());
rules_[SpvOpPhi].push_back(RedundantPhi());
- rules_[SpvOpSDiv].push_back(MergeDivDivArithmetic());
- rules_[SpvOpSDiv].push_back(MergeDivMulArithmetic());
rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic());
rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
rules_[SpvOpSelect].push_back(RedundantSelect());
- rules_[SpvOpUDiv].push_back(MergeDivDivArithmetic());
- rules_[SpvOpUDiv].push_back(MergeDivMulArithmetic());
rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic());
}
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%float_0p5 = OpConstant %float 0.5
+%v2float_2_2 = OpConstantComposite %v2float %float_2 %float_2
%v2float_2_3 = OpConstantComposite %v2float %float_2 %float_3
%v2float_3_2 = OpConstantComposite %v2float %float_3 %float_2
+%v2float_4_4 = OpConstantComposite %v2float %float_4 %float_4
%v2float_2_0p5 = OpConstantComposite %v2float %float_2 %float_0p5
%double_n1 = OpConstant %double -1
%105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps.
"OpReturn\n" +
"OpFunctionEnd\n",
4, true),
- // Test case 10: merge imul of sdiv
- // 4 * (x / 2) = 2 * x
+ // Test case 10: Do not merge imul of sdiv
+ // 4 * (x / 2)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpIMul %int %int_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 11: merge imul of sdiv
- // (x / 2) * 4 = 2 * x
+ 4, false),
+ // Test case 11: Do not merge imul of sdiv
+ // (x / 2) * 4
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpIMul %int %3 %int_4\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 12: merge imul of udiv
- // 4 * (x / 2) = 2 * x
+ 4, false),
+ // Test case 12: Do not merge imul of udiv
+ // 4 * (x / 2)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
- "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" +
- "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_uint Function\n" +
"%4 = OpIMul %uint %uint_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 13: merge imul of udiv
- // (x / 2) * 4 = 2 * x
+ 4, false),
+ // Test case 13: Do not merge imul of udiv
+ // (x / 2) * 4
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
- "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" +
- "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_uint Function\n" +
"%4 = OpIMul %uint %3 %uint_4\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 14: Don't fold if would have remainder
- // (x / 3) * 4
+ 4, false),
+ // Test case 14: Don't fold
+ // (x / 3) * 4
InstructionFoldingCase<bool>(
Header() +
"%main = OpFunction %void None %void_func\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
4, false),
- // Test case 15: merge vector imul of sdiv
+ // Test case 15: merge vector fmul of fdiv
// (x / {2,2}) * {4,4} = x * {2,2}
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[v2int_2_2:%\\w+]] = OpConstantComposite [[v2int]] [[int_2]] [[int_2]]\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[v2int]]\n" +
- "; CHECK: %4 = OpIMul [[v2int]] [[ld]] [[v2int_2_2]]\n" +
+ "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+ "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" +
+ "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+ "; CHECK: [[v2float_2_2:%\\w+]] = OpConstantComposite [[v2float]] [[float_2]] [[float_2]]\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[v2float]]\n" +
+ "; CHECK: %4 = OpFMul [[v2float]] [[ld]] [[v2float_2_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
- "%var = OpVariable %_ptr_v2int Function\n" +
- "%2 = OpLoad %v2int %var\n" +
- "%3 = OpSDiv %v2int %2 %v2int_2_2\n" +
- "%4 = OpIMul %v2int %3 %v2int_4_4\n" +
+ "%var = OpVariable %_ptr_v2float Function\n" +
+ "%2 = OpLoad %v2float %var\n" +
+ "%3 = OpFDiv %v2float %2 %v2float_2_2\n" +
+ "%4 = OpFMul %v2float %3 %v2float_4_4\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
4, true),
- // Test case 15: merge vector imul of snegate
+ // Test case 16: merge vector imul of snegate
// (-x) * {2,2} = x * {-2,-2}
InstructionFoldingCase<bool>(
Header() +
"OpReturn\n" +
"OpFunctionEnd\n",
4, true),
- // Test case 15: merge vector imul of snegate
+ // Test case 17: merge vector imul of snegate
// {2,2} * (-x) = x * {-2,-2}
InstructionFoldingCase<bool>(
Header() +
"OpReturn\n" +
"OpFunctionEnd\n",
4, true),
- // Test case 3: merge consecutive sdiv
- // 4 / (2 / x) = 2 * x
+ // Test case 3: Do not merge consecutive sdiv
+ // 4 / (2 / x)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpIMul [[int]] [[int_2]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %int_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 4: merge consecutive sdiv
- // 4 / (x / 2) = 8 / x
+ 4, false),
+ // Test case 4: Do not merge consecutive sdiv
+ // 4 / (x / 2)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_8:%\\w+]] = OpConstant [[int]] 8\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[int_8]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %int_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 5: merge consecutive sdiv
- // (4 / x) / 2 = 2 / x
+ 4, false),
+ // Test case 5: Do not merge consecutive sdiv
+ // (4 / x) / 2
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[int_2]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %3 %int_2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 6: merge consecutive sdiv
- // (x / 4) / 2 = x / 8
+ 4, false),
+ // Test case 6: Do not merge consecutive sdiv
+ // (x / 4) / 2
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_8:%\\w+]] = OpConstant [[int]] 8\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_8]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %3 %int_2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 7: merge sdiv of imul
- // 4 / (2 * x) = 2 / x
+ 4, false),
+ // Test case 7: Do not merge sdiv of imul
+ // 4 / (2 * x)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[int_2]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %int_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 8: merge sdiv of imul
- // 4 / (x * 2) = 2 / x
+ 4, false),
+ // Test case 8: Do not merge sdiv of imul
+ // 4 / (x * 2)
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[int_2]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %int_4 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 9: merge sdiv of imul
- // (4 * x) / 2 = x * 2
+ 4, false),
+ // Test case 9: Do not merge sdiv of imul
+ // (4 * x) / 2
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %3 %int_2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 10: merge sdiv of imul
- // (x * 4) / 2 = x * 2
+ 4, false),
+ // Test case 10: Do not merge sdiv of imul
+ // (x * 4) / 2
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
"%4 = OpSDiv %int %3 %int_2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
+ 4, false),
// Test case 11: merge sdiv of snegate
// (-x) / 2 = x / -2
InstructionFoldingCase<bool>(