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 (4 / x) / 2 = 2 / x (x / 2) / 2 = x / 4
| 976 | // (4 / x) / 2 = 2 / x |
| 977 | // (x / 2) / 2 = x / 4 |
| 978 | FoldingRule MergeDivDivArithmetic() { |
| 979 | return [](IRContext* context, Instruction* inst, |
| 980 | const std::vector<const analysis::Constant*>& constants) { |
| 981 | assert(inst->opcode() == spv::Op::OpFDiv); |
| 982 | analysis::ConstantManager* const_mgr = context->get_constant_mgr(); |
| 983 | const analysis::Type* type = |
| 984 | context->get_type_mgr()->GetType(inst->type_id()); |
| 985 | |
| 986 | if (type->IsCooperativeMatrix()) { |
| 987 | return false; |
| 988 | } |
| 989 | |
| 990 | if (!inst->IsFloatingPointFoldingAllowed()) return false; |
| 991 | |
| 992 | uint32_t width = ElementWidth(type); |
| 993 | if (width != 32 && width != 64) return false; |
| 994 | |
| 995 | const analysis::Constant* const_input1 = ConstInput(constants); |
| 996 | if (!const_input1 || HasZero(const_input1)) return false; |
| 997 | Instruction* other_inst = NonConstInput(context, constants[0], inst); |
| 998 | if (!other_inst->IsFloatingPointFoldingAllowed()) return false; |
| 999 | |
| 1000 | bool first_is_variable = constants[0] == nullptr; |
| 1001 | if (other_inst->opcode() == inst->opcode()) { |
| 1002 | std::vector<const analysis::Constant*> other_constants = |
| 1003 | const_mgr->GetOperandConstants(other_inst); |
| 1004 | const analysis::Constant* const_input2 = ConstInput(other_constants); |
| 1005 | if (!const_input2 || HasZero(const_input2)) return false; |
| 1006 | |
| 1007 | bool other_first_is_variable = other_constants[0] == nullptr; |
| 1008 | |
| 1009 | spv::Op merge_op = inst->opcode(); |
| 1010 | if (other_first_is_variable) { |
| 1011 | // Constants magnify. |
| 1012 | merge_op = spv::Op::OpFMul; |
| 1013 | } |
| 1014 | |
| 1015 | // This is an x / (*) case. Swap the inputs. Doesn't harm multiply |
| 1016 | // because it is commutative. |
| 1017 | if (first_is_variable) std::swap(const_input1, const_input2); |
| 1018 | uint32_t merged_id = |
| 1019 | PerformOperation(const_mgr, merge_op, const_input1, const_input2); |
| 1020 | if (merged_id == 0) return false; |
| 1021 | |
| 1022 | uint32_t non_const_id = other_first_is_variable |
| 1023 | ? other_inst->GetSingleWordInOperand(0u) |
| 1024 | : other_inst->GetSingleWordInOperand(1u); |
| 1025 | |
| 1026 | spv::Op op = inst->opcode(); |
| 1027 | if (!first_is_variable && !other_first_is_variable) { |
| 1028 | // Effectively div of 1/x, so change to multiply. |
| 1029 | op = spv::Op::OpFMul; |
| 1030 | } |
| 1031 | |
| 1032 | uint32_t op1 = merged_id; |
| 1033 | uint32_t op2 = non_const_id; |
| 1034 | if (first_is_variable && other_first_is_variable) std::swap(op1, op2); |
| 1035 | inst->SetOpcode(op); |
no test coverage detected