/* * Copyright 2017 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. */ #ifndef WABT_OPCODE_H_ #define WABT_OPCODE_H_ #include #include "src/common.h" #include "src/opcode-code-table.h" #include "src/leb128.h" namespace wabt { class Features; struct Opcode { // Opcode enumerations. // // NOTE: this enum does not match the binary encoding. // enum Enum : uint32_t { #define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ text, decomp) \ Name, #include "src/opcode.def" #undef WABT_OPCODE Invalid, }; // Static opcode objects. #define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ text, decomp) \ static Opcode Name##_Opcode; #include "src/opcode.def" #undef WABT_OPCODE Opcode() = default; // Provided so Opcode can be member of a union. Opcode(Enum e) : enum_(e) {} operator Enum() const { return enum_; } static Opcode FromCode(uint32_t); static Opcode FromCode(uint8_t prefix, uint32_t code); bool HasPrefix() const { return GetInfo().prefix != 0; } uint8_t GetPrefix() const { return GetInfo().prefix; } uint32_t GetCode() const { return GetInfo().code; } size_t GetLength() const { return GetBytes().size(); } const char* GetName() const { return GetInfo().name; } const char* GetDecomp() const { return *GetInfo().decomp ? GetInfo().decomp : GetInfo().name; } Type GetResultType() const { return GetInfo().result_type; } Type GetParamType1() const { return GetInfo().param_types[0]; } Type GetParamType2() const { return GetInfo().param_types[1]; } Type GetParamType3() const { return GetInfo().param_types[2]; } Type GetParamType(int n) const { return GetInfo().param_types[n - 1]; } Address GetMemorySize() const { return GetInfo().memory_size; } // If this is a load/store op, the type depends on the memory used. Type GetMemoryParam(Type param, const Limits* limits, bool has_address_operands) { return limits && limits->is_64 && has_address_operands ? Type(Type::I64) : param; } // Get the byte sequence for this opcode, including prefix. std::vector GetBytes() const; // Get the lane count of an extract/replace simd op. uint32_t GetSimdLaneCount() const; // Return 1 if |alignment| matches the alignment of |opcode|, or if // |alignment| is WABT_USE_NATURAL_ALIGNMENT. bool IsNaturallyAligned(Address alignment) const; // If |alignment| is WABT_USE_NATURAL_ALIGNMENT, return the alignment of // |opcode|, else return |alignment|. Address GetAlignment(Address alignment) const; static bool IsPrefixByte(uint8_t byte) { return byte == kMathPrefix || byte == kThreadsPrefix || byte == kSimdPrefix; } bool IsEnabled(const Features& features) const; bool IsInvalid() const { return enum_ >= Invalid; } private: static const uint32_t kMathPrefix = 0xfc; static const uint32_t kThreadsPrefix = 0xfe; static const uint32_t kSimdPrefix = 0xfd; struct Info { const char* name; const char* decomp; Type result_type; Type param_types[3]; Address memory_size; uint8_t prefix; uint32_t code; uint32_t prefix_code; // See PrefixCode below. Used for fast lookup. }; static uint32_t PrefixCode(uint8_t prefix, uint32_t code) { // For now, 8 bits is enough for all codes. if (code >= 0x100) { // Clamp to 0xff, since we know that it is an invalid code. code = 0xff; } return (prefix << 8) | code; } // The Opcode struct only stores an enumeration (Opcode::Enum) of all valid // opcodes, densely packed. We want to be able to store invalid opcodes as // well, for display to the user. To encode these, we use PrefixCode() to // generate a uint32_t of the prefix/code pair, then negate the value so it // doesn't overlap with the valid enum values. The negation is done using // `~code + 1` since prefix_code is unsigned, and MSVC warns if you use - on // an unsigned value. // // | 0 | Opcode::Invalid | INT32_MAX+1 UINT32_MAX | // |---------------|-------------------------|---------------------------| // | valid opcodes | unused space | invalid opcodes | // static Enum EncodeInvalidOpcode(uint32_t prefix_code) { Enum result = static_cast(~prefix_code + 1); assert(result >= Invalid); return result; } static void DecodeInvalidOpcode(Enum e, uint8_t* out_prefix, uint32_t* out_code) { uint32_t prefix_code = ~static_cast(e) + 1; *out_prefix = prefix_code >> 8; *out_code = prefix_code & 0xff; } Info GetInfo() const; static Info infos_[]; Enum enum_; }; // static inline Opcode Opcode::FromCode(uint32_t code) { return FromCode(0, code); } // static inline Opcode Opcode::FromCode(uint8_t prefix, uint32_t code) { uint32_t prefix_code = PrefixCode(prefix, code); if (WABT_LIKELY(prefix_code < WABT_ARRAY_SIZE(WabtOpcodeCodeTable))) { uint32_t value = WabtOpcodeCodeTable[prefix_code]; // The default value in the table is 0. That's a valid value, but only if // the code is 0 (for nop). if (WABT_LIKELY(value != 0 || code == 0)) { return Opcode(static_cast(value)); } } return Opcode(EncodeInvalidOpcode(prefix_code)); } } // namespace wabt #endif // WABT_OPCODE_H_