blob: 45d196fa6dc47a04bb27f048d71cad403cdd47cf [file] [log] [blame]
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "instruction_simplifier_shared.h"
18
19namespace art {
20
21namespace {
22
23bool TrySimpleMultiplyAccumulatePatterns(HMul* mul,
24 HBinaryOperation* input_binop,
25 HInstruction* input_other) {
26 DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
27 DCHECK(input_binop->IsAdd() || input_binop->IsSub());
28 DCHECK_NE(input_binop, input_other);
29 if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
30 return false;
31 }
32
33 // Try to interpret patterns like
34 // a * (b <+/-> 1)
35 // as
36 // (a * b) <+/-> a
37 HInstruction* input_a = input_other;
38 HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
39 HInstruction::InstructionKind op_kind;
40
41 if (input_binop->IsAdd()) {
42 if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
43 // Interpret
44 // a * (b + 1)
45 // as
46 // (a * b) + a
47 input_b = input_binop->GetLeastConstantLeft();
48 op_kind = HInstruction::kAdd;
49 }
50 } else {
51 DCHECK(input_binop->IsSub());
52 if (input_binop->GetRight()->IsConstant() &&
53 input_binop->GetRight()->AsConstant()->IsMinusOne()) {
54 // Interpret
55 // a * (b - (-1))
56 // as
57 // a + (a * b)
58 input_b = input_binop->GetLeft();
59 op_kind = HInstruction::kAdd;
60 } else if (input_binop->GetLeft()->IsConstant() &&
61 input_binop->GetLeft()->AsConstant()->IsOne()) {
62 // Interpret
63 // a * (1 - b)
64 // as
65 // a - (a * b)
66 input_b = input_binop->GetRight();
67 op_kind = HInstruction::kSub;
68 }
69 }
70
71 if (input_b == nullptr) {
72 // We did not find a pattern we can optimize.
73 return false;
74 }
75
76 ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
77 HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate(
78 mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
79
80 mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
81 input_binop->GetBlock()->RemoveInstruction(input_binop);
82
83 return true;
84}
85
86} // namespace
87
88bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) {
89 Primitive::Type type = mul->GetType();
90 switch (isa) {
91 case kArm:
92 case kThumb2:
93 if (type != Primitive::kPrimInt) {
94 return false;
95 }
96 break;
97 case kArm64:
98 if (!Primitive::IsIntOrLongType(type)) {
99 return false;
100 }
101 break;
102 default:
103 return false;
104 }
105
106 HInstruction* use = mul->HasNonEnvironmentUses()
107 ? mul->GetUses().GetFirst()->GetUser()
108 : nullptr;
109
110 ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
111
112 if (mul->HasOnlyOneNonEnvironmentUse()) {
113 if (use->IsAdd() || use->IsSub()) {
114 // Replace code looking like
115 // MUL tmp, x, y
116 // SUB dst, acc, tmp
117 // with
118 // MULSUB dst, acc, x, y
119 // Note that we do not want to (unconditionally) perform the merge when the
120 // multiplication has multiple uses and it can be merged in all of them.
121 // Multiple uses could happen on the same control-flow path, and we would
122 // then increase the amount of work. In the future we could try to evaluate
123 // whether all uses are on different control-flow paths (using dominance and
124 // reverse-dominance information) and only perform the merge when they are.
125 HInstruction* accumulator = nullptr;
126 HBinaryOperation* binop = use->AsBinaryOperation();
127 HInstruction* binop_left = binop->GetLeft();
128 HInstruction* binop_right = binop->GetRight();
129 // Be careful after GVN. This should not happen since the `HMul` has only
130 // one use.
131 DCHECK_NE(binop_left, binop_right);
132 if (binop_right == mul) {
133 accumulator = binop_left;
134 } else if (use->IsAdd()) {
135 DCHECK_EQ(binop_left, mul);
136 accumulator = binop_right;
137 }
138
139 if (accumulator != nullptr) {
140 HMultiplyAccumulate* mulacc =
141 new (arena) HMultiplyAccumulate(type,
142 binop->GetKind(),
143 accumulator,
144 mul->GetLeft(),
145 mul->GetRight());
146
147 binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
148 DCHECK(!mul->HasUses());
149 mul->GetBlock()->RemoveInstruction(mul);
150 return true;
151 }
152 } else if (use->IsNeg() && isa != kArm) {
153 HMultiplyAccumulate* mulacc =
154 new (arena) HMultiplyAccumulate(type,
155 HInstruction::kSub,
156 mul->GetBlock()->GetGraph()->GetConstant(type, 0),
157 mul->GetLeft(),
158 mul->GetRight());
159
160 use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc);
161 DCHECK(!mul->HasUses());
162 mul->GetBlock()->RemoveInstruction(mul);
163 return true;
164 }
165 }
166
167 // Use multiply accumulate instruction for a few simple patterns.
168 // We prefer not applying the following transformations if the left and
169 // right inputs perform the same operation.
170 // We rely on GVN having squashed the inputs if appropriate. However the
171 // results are still correct even if that did not happen.
172 if (mul->GetLeft() == mul->GetRight()) {
173 return false;
174 }
175
176 HInstruction* left = mul->GetLeft();
177 HInstruction* right = mul->GetRight();
178 if ((right->IsAdd() || right->IsSub()) &&
179 TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) {
180 return true;
181 }
182 if ((left->IsAdd() || left->IsSub()) &&
183 TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) {
184 return true;
185 }
186 return false;
187}
188
189} // namespace art