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