1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef ECMASCRIPT_COMPILER_GATE_MATCHERS_H
16 #define ECMASCRIPT_COMPILER_GATE_MATCHERS_H
17
18 #include "ecmascript/compiler/circuit.h"
19 #include "ecmascript/compiler/circuit_builder.h"
20 #include "utils/bit_utils.h"
21
22
23 namespace panda::ecmascript::kungfu {
24
25 // Checks if value is in range [lowerLimit, higherLimit] using a single
26 // branch.
IsValueInRange(T value, U lowerLimit, U higherLimit)27 template <typename T, typename U> inline constexpr bool IsValueInRange(T value, U lowerLimit, U higherLimit)
28 {
29 static_assert(sizeof(U) <= sizeof(T));
30 using unsigned_T = typename std::make_unsigned<T>::type;
31 // Use static_cast to support enum classes.
32 return static_cast<unsigned_T>(static_cast<unsigned_T>(value) - static_cast<unsigned_T>(lowerLimit)) <=
33 static_cast<unsigned_T>(static_cast<unsigned_T>(higherLimit) - static_cast<unsigned_T>(lowerLimit));
34 }
35
36 class GateMatcher {
37 public:
GateMatcher(GateRef gate, Circuit *circuit)38 explicit GateMatcher(GateRef gate, Circuit *circuit) : acc_(circuit), gate_(gate)
39 {
40 }
41
Gate() const42 GateRef Gate() const
43 {
44 return gate_;
45 }
46
ValueIn(uint32_t index)47 GateRef ValueIn(uint32_t index)
48 {
49 return acc_.GetValueIn(gate_, index);
50 }
51
Opcode() const52 OpCode Opcode() const
53 {
54 return acc_.GetOpCode(gate_);
55 }
56
MachineType() const57 MachineType MachineType() const
58 {
59 return acc_.GetMachineType(gate_);
60 }
InputAt(int index) const61 GateRef InputAt(int index) const
62 {
63 return acc_.GetValueIn(gate_, index);
64 }
65
66 #define DECLARE_IS_GATE(NAME, OP, R, S, D, V) \
67 bool Is##NAME() const \
68 { \
69 return (Opcode() == (OpCode::OP)); \
70 }
71 IMMUTABLE_META_DATA_CACHE_LIST(DECLARE_IS_GATE)
72 GATE_META_DATA_LIST_WITH_BOOL(DECLARE_IS_GATE)
73 GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(DECLARE_IS_GATE)
74 GATE_META_DATA_LIST_WITH_SIZE(DECLARE_IS_GATE)
75 GATE_META_DATA_LIST_WITH_ONE_PARAMETER(DECLARE_IS_GATE) GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_IS_GATE)
76 GATE_META_DATA_LIST_FOR_CALL(DECLARE_IS_GATE) GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_IS_GATE)
77 GATE_META_DATA_LIST_FOR_NEW(DECLARE_IS_GATE)
78 #undef DECLARE_IS_GATE
79
80 #define DECLARE_IS_INST(NAME, OPCODEID, MACHINETYPEID) \
81 bool Ism##NAME() const \
82 { \
83 return Is##OPCODEID() && MachineType() == (MACHINETYPEID); \
84 }
85 BINARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(DECLARE_IS_INST)
86 UNARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(DECLARE_IS_INST)
87 #undef DECLARE_IS_INST
88
89 #define DECLARE_IS_INST(NAME, OPCODEID, CONDITION) \
90 bool Ism##NAME() const \
91 { \
92 return Is##OPCODEID() && static_cast<uint64_t>(CONDITION) == ConditionValue(); \
93 }; \
94 BINARY_CMP_METHOD_LIST_WITHOUT_BITWIDTH(DECLARE_IS_INST)
95 #undef DECLARE_IS_INST
96
Equals(const GateRef gate)97 bool Equals(const GateRef gate)
98 {
99 return gate == Gate();
100 }
101
102 GateAccessor acc_;
103
ConditionValue()104 BitField ConditionValue()
105 {
106 ASSERT(IsFcmp() || IsIcmp());
107 return acc_.TryGetValue(Gate());
108 }
109 private:
110 GateRef gate_;
111 };
112
113 // A pattern matcher for abitrary value constants.
114 //
115 // Note that value identities on the input gate are skipped when matching. The
116 // resolved value may not be a parameter of the input gate. The Gate() method
117 // returns the unmodified input gate. This is by design, as reducers may wish to
118 // match value constants but delay reducing the gate until a later phase.
119 template <typename T, OpCode kOpcode, MachineType kMachineType> struct ValueMatcher : public GateMatcher {
120 using ValueType = T;
121
ValueMatcherpanda::ecmascript::kungfu::ValueMatcher122 explicit ValueMatcher(GateRef gate, Circuit *circuit)
123 : GateMatcher(gate, circuit), resolvedValue_(), hasResolvedValue_(false)
124 {
125 if (acc_.GetOpCode(gate) == kOpcode && acc_.GetMachineType(gate) == kMachineType) {
126 hasResolvedValue_ = true;
127 }
128
129 if (hasResolvedValue_) {
130 if (kMachineType == F64) {
131 resolvedValue_ = acc_.GetFloat64FromConstant(gate);
132 } else if (kMachineType == I32) {
133 resolvedValue_ = acc_.GetInt32FromConstant(gate);
134 } else if (kMachineType == I64) {
135 resolvedValue_ = acc_.GetConstantValue(gate);
136 } else {
137 hasResolvedValue_ = false;
138 }
139 }
140 }
141
HasResolvedValuepanda::ecmascript::kungfu::ValueMatcher142 bool HasResolvedValue() const
143 {
144 return hasResolvedValue_;
145 }
ResolvedValuepanda::ecmascript::kungfu::ValueMatcher146 const T &ResolvedValue() const
147 {
148 ASSERT(HasResolvedValue());
149 return resolvedValue_;
150 }
151
152 private:
153 T resolvedValue_;
154 bool hasResolvedValue_;
155 };
156
157 // A pattern matcher for integer constants.
158 template <typename T, OpCode kOpcode, MachineType kMachineType>
159 struct IntMatcher final : public ValueMatcher<T, kOpcode, kMachineType> {
IntMatcherpanda::ecmascript::kungfu::final160 explicit IntMatcher(GateRef gate, Circuit *circuit) : ValueMatcher<T, kOpcode, kMachineType>(gate, circuit)
161 {
162 }
163
Ispanda::ecmascript::kungfu::final164 bool Is(const T &value) const
165 {
166 return this->HasResolvedValue() && this->ResolvedValue() == value;
167 }
168
IsInRangepanda::ecmascript::kungfu::final169 bool IsInRange(const T &low, const T &high) const
170 {
171 return this->HasResolvedValue() && IsValueInRange(this->ResolvedValue(), low, high);
172 }
IsMultipleOfpanda::ecmascript::kungfu::final173 bool IsMultipleOf(T n) const
174 {
175 if (!this->HasResolvedValue()) {
176 return false;
177 }
178 return (this->ResolvedValue() % n) == 0;
179 }
180
IsPowerOf2panda::ecmascript::kungfu::final181 bool IsPowerOf2() const
182 {
183 if (!this->HasResolvedValue() || this->ResolvedValue() <= 0) {
184 return false;
185 }
186 using unsigned_type = typename std::make_unsigned<T>::type;
187 const unsigned_type resolvedValue = static_cast<unsigned_type>(this->ResolvedValue());
188 return (resolvedValue & (resolvedValue - 1)) == 0;
189 }
190
IsNegativePowerOf2panda::ecmascript::kungfu::final191 bool IsNegativePowerOf2() const
192 {
193 if (!this->HasResolvedValue() || this->ResolvedValue() >= 0) {
194 return false;
195 }
196 return ((this->ResolvedValue() == std::numeric_limits<T>::min()) ||
197 (-this->ResolvedValue() & (-this->ResolvedValue() - 1)) == 0);
198 }
199
IsNegativepanda::ecmascript::kungfu::final200 bool IsNegative() const
201 {
202 if (!this->HasResolvedValue()) {
203 return false;
204 }
205 return this->ResolvedValue() < 0;
206 }
207 };
208
209 using Int32Matcher = IntMatcher<int32_t, OpCode::CONSTANT, MachineType::I32>;
210 using Uint32Matcher = IntMatcher<uint32_t, OpCode::CONSTANT, MachineType::I32>;
211 using Int64Matcher = IntMatcher<int64_t, OpCode::CONSTANT, MachineType::I64>;
212 using Uint64Matcher = IntMatcher<uint64_t, OpCode::CONSTANT, MachineType::I64>;
213
214
215 // A pattern matcher for floating point constants.
216 template <typename T, OpCode kOpcode, MachineType kMachineType>
217 struct FloatMatcher final : public ValueMatcher<T, kOpcode, kMachineType> {
FloatMatcherpanda::ecmascript::kungfu::final218 explicit FloatMatcher(GateRef gate, Circuit *circuit) : ValueMatcher<T, kOpcode, kMachineType>(gate, circuit)
219 {
220 }
221
Ispanda::ecmascript::kungfu::final222 bool Is(const T &value) const
223 {
224 return this->HasResolvedValue() && this->ResolvedValue() == value;
225 }
IsInRangepanda::ecmascript::kungfu::final226 bool IsInRange(const T &low, const T &high) const
227 {
228 return this->HasResolvedValue() && low <= this->ResolvedValue() && this->ResolvedValue() <= high;
229 }
IsMinusZeropanda::ecmascript::kungfu::final230 bool IsMinusZero() const
231 {
232 if (!this->HasResolvedValue()) {
233 return false;
234 }
235 return this->Is(0.0) && std::signbit(this->ResolvedValue());
236 }
237
IsNegativepanda::ecmascript::kungfu::final238 bool IsNegative() const
239 {
240 if (!this->HasResolvedValue()) {
241 return false;
242 }
243 return this->ResolvedValue() < 0.0;
244 }
245
IsNaNpanda::ecmascript::kungfu::final246 bool IsNaN() const
247 {
248 if (!this->HasResolvedValue()) {
249 return false;
250 }
251 return std::isnan(this->ResolvedValue());
252 }
253
IsZeropanda::ecmascript::kungfu::final254 bool IsZero() const
255 {
256 if (!this->HasResolvedValue()) {
257 return false;
258 }
259 return this->Is(0.0) && !std::signbit(this->ResolvedValue());
260 }
261
IsNormalpanda::ecmascript::kungfu::final262 bool IsNormal() const
263 {
264 if (!this->HasResolvedValue()) {
265 return false;
266 }
267 return std::isnormal(this->ResolvedValue());
268 }
269
IsIntegerpanda::ecmascript::kungfu::final270 bool IsInteger() const
271 {
272 if (!this->HasResolvedValue()) {
273 return false;
274 }
275 return std::nearbyint(this->ResolvedValue()) == this->ResolvedValue();
276 }
277 };
278
279 using Float32Matcher = FloatMatcher<float, OpCode::CONSTANT, MachineType::F32>;
280 using Float64Matcher = FloatMatcher<double, OpCode::CONSTANT, MachineType::F64>;
281
282
283 // For shorter pattern matching code, this struct matches both the left and
284 // right hand sides of a binary operation and can put constants on the right
285 // if they appear on the left hand side of a commutative operation.
286 template <typename LeftExpr, typename RightExpr, MachineType rep> struct BinopMatcher : public GateMatcher {
BinopMatcherpanda::ecmascript::kungfu::BinopMatcher287 explicit BinopMatcher(GateRef gate, Circuit *circuit)
288 : GateMatcher(gate, circuit), left_(InputAt(0), circuit), right_(InputAt(1), circuit)
289 {
290 if (IsCommutative(Opcode())) {
291 PutConstantOnRight();
292 }
293 }
294
BinopMatcherpanda::ecmascript::kungfu::BinopMatcher295 BinopMatcher(GateRef gate, bool allowInputSwap, Circuit *circuit)
296 : GateMatcher(gate, circuit), left_(InputAt(0), circuit), right_(InputAt(1), circuit)
297 {
298 if (allowInputSwap) {
299 PutConstantOnRight();
300 }
301 }
302
303 using LeftMatcher = LeftExpr;
304 using RightMatcher = RightExpr;
305
306 // static constexpr MachineType representation = rep;
307
Leftpanda::ecmascript::kungfu::BinopMatcher308 const LeftExpr &Left() const
309 {
310 return left_;
311 }
312
Rightpanda::ecmascript::kungfu::BinopMatcher313 const RightExpr &Right() const
314 {
315 return right_;
316 }
317
SetLeftpanda::ecmascript::kungfu::BinopMatcher318 void SetLeft(GateRef left, Circuit* circuit)
319 {
320 left_ = LeftExpr(left, circuit);
321 }
322
SetRightpanda::ecmascript::kungfu::BinopMatcher323 void SetRight(GateRef right, Circuit* circuit)
324 {
325 right_ = RightExpr(right, circuit);
326 }
327
IsFoldablepanda::ecmascript::kungfu::BinopMatcher328 bool IsFoldable() const
329 {
330 return Left().HasResolvedValue() && Right().HasResolvedValue();
331 }
LeftEqualsRightpanda::ecmascript::kungfu::BinopMatcher332 bool LeftEqualsRight() const
333 {
334 return Left().Gate() == Right().Gate();
335 }
336
OwnsInputpanda::ecmascript::kungfu::BinopMatcher337 bool OwnsInput(GateRef input)
338 {
339 auto use = acc_.Uses(input);
340 for (auto it = use.begin(); it != use.end(); it++) {
341 if (*it != Gate()) {
342 return false;
343 }
344 }
345 return true;
346 }
347
348 protected:
SwapInputspanda::ecmascript::kungfu::BinopMatcher349 void SwapInputs()
350 {
351 std::swap(left_, right_);
352 acc_.ReplaceValueIn(Gate(), Left().Gate(), 0);
353 acc_.ReplaceValueIn(Gate(), Right().Gate(), 1);
354 }
355
356 private:
PutConstantOnRightpanda::ecmascript::kungfu::BinopMatcher357 void PutConstantOnRight()
358 {
359 if (Left().HasResolvedValue() && !Right().HasResolvedValue()) {
360 SwapInputs();
361 }
362 }
IsCommutativepanda::ecmascript::kungfu::BinopMatcher363 static bool IsCommutative(OpCode opcode)
364 {
365 switch (opcode) {
366 case OpCode::ADD:
367 case OpCode::AND:
368 case OpCode::OR:
369 case OpCode::XOR:
370 case OpCode::MUL:
371 case OpCode::ADD_WITH_OVERFLOW:
372 case OpCode::MUL_WITH_OVERFLOW:
373 return true;
374 default:
375 return false;
376 }
377 return false;
378 }
379 LeftExpr left_;
380 RightExpr right_;
381 };
382
383 using Int32BinopMatcher = BinopMatcher<Int32Matcher, Int32Matcher, MachineType::I32>;
384 using Uint32BinopMatcher = BinopMatcher<Uint32Matcher, Uint32Matcher, MachineType::I32>;
385 using Int64BinopMatcher = BinopMatcher<Int64Matcher, Int64Matcher, MachineType::I64>;
386 using Uint64BinopMatcher = BinopMatcher<Uint64Matcher, Uint64Matcher, MachineType::I64>;
387 using Float32BinopMatcher = BinopMatcher<Float32Matcher, Float32Matcher, MachineType::F32>;
388 using Float64BinopMatcher = BinopMatcher<Float64Matcher, Float64Matcher, MachineType::F64>;
389
390
391 } // namespace panda::ecmascript::kungfu
392 #endif // ECMASCRIPT_COMPILER_GATE_MATCHERS_H