173 lines
5.7 KiB
C++
173 lines
5.7 KiB
C++
/*
|
|
* 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 <vector>
|
|
|
|
#include "wabt/common.h"
|
|
#include "wabt/leb128.h"
|
|
#include "wabt/opcode-code-table.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 "wabt/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 "wabt/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; }
|
|
|
|
// Get the byte sequence for this opcode, including prefix.
|
|
std::vector<uint8_t> 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 constexpr uint32_t kMathPrefix = 0xfc;
|
|
static constexpr uint32_t kThreadsPrefix = 0xfe;
|
|
static constexpr 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) {
|
|
if (code >= (1 << MAX_OPCODE_BITS)) {
|
|
// Clamp to (2^bits - 1), since we know that it is an invalid code.
|
|
code = (1 << MAX_OPCODE_BITS) - 1;
|
|
}
|
|
return (prefix << MAX_OPCODE_BITS) | 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<Enum>(~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<uint32_t>(e) + 1;
|
|
*out_prefix = prefix_code >> MAX_OPCODE_BITS;
|
|
*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<Enum>(value));
|
|
}
|
|
}
|
|
|
|
return Opcode(EncodeInvalidOpcode(prefix_code));
|
|
}
|
|
|
|
} // namespace wabt
|
|
|
|
#endif // WABT_OPCODE_H_
|