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