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
23namespace panda::ecmascript::kungfu {
24
25// Checks if value is in range [lowerLimit, higherLimit] using a single
26// branch.
27template <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
36class GateMatcher {
37public:
38    explicit GateMatcher(GateRef gate, Circuit *circuit) : acc_(circuit), gate_(gate)
39    {
40    }
41
42    GateRef Gate() const
43    {
44        return gate_;
45    }
46
47    GateRef ValueIn(uint32_t index)
48    {
49        return acc_.GetValueIn(gate_, index);
50    }
51
52    OpCode Opcode() const
53    {
54        return acc_.GetOpCode(gate_);
55    }
56
57    MachineType MachineType() const
58    {
59        return acc_.GetMachineType(gate_);
60    }
61    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
97    bool Equals(const GateRef gate)
98    {
99        return gate == Gate();
100    }
101
102    GateAccessor acc_;
103
104    BitField ConditionValue()
105    {
106        ASSERT(IsFcmp() || IsIcmp());
107        return acc_.TryGetValue(Gate());
108    }
109private:
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.
119template <typename T, OpCode kOpcode, MachineType kMachineType> struct ValueMatcher : public GateMatcher {
120    using ValueType = T;
121
122    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
142    bool HasResolvedValue() const
143    {
144        return hasResolvedValue_;
145    }
146    const T &ResolvedValue() const
147    {
148        ASSERT(HasResolvedValue());
149        return resolvedValue_;
150    }
151
152private:
153    T resolvedValue_;
154    bool hasResolvedValue_;
155};
156
157// A pattern matcher for integer constants.
158template <typename T, OpCode kOpcode, MachineType kMachineType>
159struct IntMatcher final : public ValueMatcher<T, kOpcode, kMachineType> {
160    explicit IntMatcher(GateRef gate, Circuit *circuit) : ValueMatcher<T, kOpcode, kMachineType>(gate, circuit)
161    {
162    }
163
164    bool Is(const T &value) const
165    {
166        return this->HasResolvedValue() && this->ResolvedValue() == value;
167    }
168
169    bool IsInRange(const T &low, const T &high) const
170    {
171        return this->HasResolvedValue() && IsValueInRange(this->ResolvedValue(), low, high);
172    }
173    bool IsMultipleOf(T n) const
174    {
175        if (!this->HasResolvedValue()) {
176            return false;
177        }
178        return (this->ResolvedValue() % n) == 0;
179    }
180
181    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
191    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
200    bool IsNegative() const
201    {
202        if (!this->HasResolvedValue()) {
203            return false;
204        }
205        return this->ResolvedValue() < 0;
206    }
207};
208
209using Int32Matcher = IntMatcher<int32_t, OpCode::CONSTANT, MachineType::I32>;
210using Uint32Matcher = IntMatcher<uint32_t, OpCode::CONSTANT, MachineType::I32>;
211using Int64Matcher = IntMatcher<int64_t, OpCode::CONSTANT, MachineType::I64>;
212using Uint64Matcher = IntMatcher<uint64_t, OpCode::CONSTANT, MachineType::I64>;
213
214
215// A pattern matcher for floating point constants.
216template <typename T, OpCode kOpcode, MachineType kMachineType>
217struct FloatMatcher final : public ValueMatcher<T, kOpcode, kMachineType> {
218    explicit FloatMatcher(GateRef gate, Circuit *circuit) : ValueMatcher<T, kOpcode, kMachineType>(gate, circuit)
219    {
220    }
221
222    bool Is(const T &value) const
223    {
224        return this->HasResolvedValue() && this->ResolvedValue() == value;
225    }
226    bool IsInRange(const T &low, const T &high) const
227    {
228        return this->HasResolvedValue() && low <= this->ResolvedValue() && this->ResolvedValue() <= high;
229    }
230    bool IsMinusZero() const
231    {
232        if (!this->HasResolvedValue()) {
233            return false;
234        }
235        return this->Is(0.0) && std::signbit(this->ResolvedValue());
236    }
237
238    bool IsNegative() const
239    {
240        if (!this->HasResolvedValue()) {
241            return false;
242        }
243        return this->ResolvedValue() < 0.0;
244    }
245
246    bool IsNaN() const
247    {
248        if (!this->HasResolvedValue()) {
249            return false;
250        }
251        return std::isnan(this->ResolvedValue());
252    }
253
254    bool IsZero() const
255    {
256        if (!this->HasResolvedValue()) {
257            return false;
258        }
259        return this->Is(0.0) && !std::signbit(this->ResolvedValue());
260    }
261
262    bool IsNormal() const
263    {
264        if (!this->HasResolvedValue()) {
265            return false;
266        }
267        return std::isnormal(this->ResolvedValue());
268    }
269
270    bool IsInteger() const
271    {
272        if (!this->HasResolvedValue()) {
273            return false;
274        }
275        return std::nearbyint(this->ResolvedValue()) == this->ResolvedValue();
276    }
277};
278
279using Float32Matcher = FloatMatcher<float, OpCode::CONSTANT, MachineType::F32>;
280using 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.
286template <typename LeftExpr, typename RightExpr, MachineType rep> struct BinopMatcher : public GateMatcher {
287    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
295    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
308    const LeftExpr &Left() const
309    {
310        return left_;
311    }
312
313    const RightExpr &Right() const
314    {
315        return right_;
316    }
317
318    void SetLeft(GateRef left, Circuit* circuit)
319    {
320        left_  = LeftExpr(left, circuit);
321    }
322
323    void SetRight(GateRef right, Circuit* circuit)
324    {
325        right_ = RightExpr(right, circuit);
326    }
327
328    bool IsFoldable() const
329    {
330        return Left().HasResolvedValue() && Right().HasResolvedValue();
331    }
332    bool LeftEqualsRight() const
333    {
334        return Left().Gate() == Right().Gate();
335    }
336
337    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
348protected:
349    void SwapInputs()
350    {
351        std::swap(left_, right_);
352        acc_.ReplaceValueIn(Gate(), Left().Gate(), 0);
353        acc_.ReplaceValueIn(Gate(), Right().Gate(), 1);
354    }
355
356private:
357    void PutConstantOnRight()
358    {
359        if (Left().HasResolvedValue() && !Right().HasResolvedValue()) {
360            SwapInputs();
361        }
362    }
363    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
383using Int32BinopMatcher = BinopMatcher<Int32Matcher, Int32Matcher, MachineType::I32>;
384using Uint32BinopMatcher = BinopMatcher<Uint32Matcher, Uint32Matcher, MachineType::I32>;
385using Int64BinopMatcher = BinopMatcher<Int64Matcher, Int64Matcher, MachineType::I64>;
386using Uint64BinopMatcher = BinopMatcher<Uint64Matcher, Uint64Matcher, MachineType::I64>;
387using Float32BinopMatcher = BinopMatcher<Float32Matcher, Float32Matcher, MachineType::F32>;
388using Float64BinopMatcher = BinopMatcher<Float64Matcher, Float64Matcher, MachineType::F64>;
389
390
391} // namespace panda::ecmascript::kungfu
392#endif // ECMASCRIPT_COMPILER_GATE_MATCHERS_H