text: /*
   * Copyright 2018 WebAssembly Community Group participants
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  // Abstracts out operations from specific opcodes.
  
  #ifndef wasm_ir_abstract_h
  #define wasm_ir_abstract_h
  
  #include <wasm.h>
  
  namespace wasm {
  
  namespace Abstract {
  
  enum Op {
    // Unary
    Abs,
    Neg,
    // Binary
    Add,
    Sub,
    Mul,
    DivU,
    DivS,
    Rem,
    RemU,
    RemS,
    Shl,
    ShrU,
    ShrS,
    RotL,
    RotR,
    And,
    Or,
    Xor,
    // Relational
    EqZ,
    Eq,
    Ne,
    LtS,
    LtU,
    LeS,
    LeU,
    GtS,
    GtU,
    GeS,
    GeU
  };
  
  inline bool hasAnyShift(BinaryOp op) {
    return op == ShlInt32 || op == ShrSInt32 || op == ShrUInt32 ||
           op == RotLInt32 || op == RotRInt32 || op == ShlInt64 ||
           op == ShrSInt64 || op == ShrUInt64 || op == RotLInt64 ||
           op == RotRInt64;
  }
  
  // Provide a wasm type and an abstract op and get the concrete one. For example,
  // you can provide i32 and Add and receive the specific opcode for a 32-bit
  // addition, AddInt32. If the op does not exist, it returns Invalid.
  inline UnaryOp getUnary(Type type, Op op) {
    switch (type.getBasic()) {
      case Type::i32: {
        switch (op) {
          case EqZ:
            return EqZInt32;
          default:
            return InvalidUnary;
        }
        break;
      }
      case Type::i64: {
        switch (op) {
          case EqZ:
            return EqZInt64;
          default:
            return InvalidUnary;
        }
        break;
      }
      case Type::f32: {
        switch (op) {
          case Abs:
            return AbsFloat32;
          case Neg:
            return NegFloat32;
          default:
            return InvalidUnary;
        }
        break;
      }
      case Type::f64: {
        switch (op) {
          case Abs:
            return AbsFloat64;
          case Neg:
            return NegFloat64;
          default:
            return InvalidUnary;
        }
        break;
      }
      case Type::v128:
      case Type::funcref:
      case Type::externref:
      case Type::exnref:
      case Type::anyref:
      case Type::eqref:
      case Type::i31ref:
      case Type::none:
      case Type::unreachable: {
        return InvalidUnary;
      }
    }
    WASM_UNREACHABLE("invalid type");
  }
  
  inline BinaryOp getBinary(Type type, Op op) {
    switch (type.getBasic()) {
      case Type::i32: {
        switch (op) {
          case Add:
            return AddInt32;
          case Sub:
            return SubInt32;
          case Mul:
            return MulInt32;
          case DivU:
            return DivUInt32;
          case DivS:
            return DivSInt32;
          case RemU:
            return RemUInt32;
          case RemS:
            return RemSInt32;
          case Shl:
            return ShlInt32;
          case ShrU:
            return ShrUInt32;
          case ShrS:
            return ShrSInt32;
          case RotL:
            return RotLInt32;
          case RotR:
            return RotRInt32;
          case And:
            return AndInt32;
          case Or:
            return OrInt32;
          case Xor:
            return XorInt32;
          case Eq:
            return EqInt32;
          case Ne:
            return NeInt32;
          case LtS:
            return LtSInt32;
          case LtU:
            return LtUInt32;
          case LeS:
            return LeSInt32;
          case LeU:
            return LeUInt32;
          case GtS:
            return GtSInt32;
          case GtU:
            return GtUInt32;
          case GeS:
            return GeSInt32;
          case GeU:
            return GeUInt32;
          default:
            return InvalidBinary;
        }
        break;
      }
      case Type::i64: {
        switch (op) {
          case Add:
            return AddInt64;
          case Sub:
            return SubInt64;
          case Mul:
            return MulInt64;
          case DivU:
            return DivUInt64;
          case DivS:
            return DivSInt64;
          case RemU:
            return RemUInt64;
          case RemS:
            return RemSInt64;
          case Shl:
            return ShlInt64;
          case ShrU:
            return ShrUInt64;
          case ShrS:
            return ShrSInt64;
          case RotL:
            return RotLInt64;
          case RotR:
            return RotRInt64;
          case And:
            return AndInt64;
          case Or:
            return OrInt64;
          case Xor:
            return XorInt64;
          case Eq:
            return EqInt64;
          case Ne:
            return NeInt64;
          case LtS:
            return LtSInt64;
          case LtU:
            return LtUInt64;
          case LeS:
            return LeSInt64;
          case LeU:
            return LeUInt64;
          case GtS:
            return GtSInt64;
          case GtU:
            return GtUInt64;
          case GeS:
            return GeSInt64;
          case GeU:
            return GeUInt64;
          default:
            return InvalidBinary;
        }
        break;
      }
      case Type::f32: {
        switch (op) {
          case Add:
            return AddFloat32;
          case Sub:
            return SubFloat32;
          case Mul:
            return MulFloat32;
          case DivU:
            return DivFloat32;
          case DivS:
            return DivFloat32;
          case Eq:
            return EqFloat32;
          case Ne:
            return NeFloat32;
          default:
            return InvalidBinary;
        }
        break;
      }
      case Type::f64: {
        switch (op) {
          case Add:
            return AddFloat64;
          case Sub:
            return SubFloat64;
          case Mul:
            return MulFloat64;
          case DivU:
            return DivFloat64;
          case DivS:
            return DivFloat64;
          case Eq:
            return EqFloat64;
          case Ne:
            return NeFloat64;
          default:
            return InvalidBinary;
        }
        break;
      }
      case Type::v128:
      case Type::funcref:
      case Type::externref:
      case Type::exnref:
      case Type::anyref:
      case Type::eqref:
      case Type::i31ref:
      case Type::none:
      case Type::unreachable: {
        return InvalidBinary;
      }
    }
    WASM_UNREACHABLE("invalid type");
  }
  
  } // namespace Abstract
  
  } // namespace wasm
  
  #endif // wasm_ir_abstract_h
  