提交 3111f0ea 编写于 作者: D Daniel Larimer

Adding wasm-jit code

上级 ceb37eb8
add_subdirectory( fc )
add_subdirectory( chainbase )
add_subdirectory( wasm-jit )
add_subdirectory( binaryen )
add_subdirectory( wasmos )
add_subdirectory( wren )
......
sudo: false
language: c++
compiler:
- clang
- gcc
install:
- if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi
- if [ "$CXX" = "clang++" ] && [ "$TRAVIS_OS_NAME" != "osx" ]; then export CXX="clang++-3.8" CC="clang-3.8"; fi
script: ./travis-build.sh
os:
- linux
- osx
matrix:
exclude:
- os: osx
compiler: gcc
addons:
apt:
sources:
- llvm-toolchain-precise-3.8
- ubuntu-toolchain-r-test
packages:
- gcc-5
- g++-5
- libedit-dev
- clang-3.8
\ No newline at end of file
#pragma once
#ifndef EMSCRIPTEN_API
#define EMSCRIPTEN_API DLL_IMPORT
#endif
#include <vector>
namespace IR { struct Module; }
namespace Runtime { struct ModuleInstance; }
namespace Emscripten
{
EMSCRIPTEN_API void initInstance(const IR::Module& module,Runtime::ModuleInstance* moduleInstance);
EMSCRIPTEN_API void injectCommandArgs(const std::vector<const char*>& argStrings,std::vector<Runtime::Value>& outInvokeArgs);
}
\ No newline at end of file
#pragma once
#include "Platform/Platform.h"
#ifndef IR_API
#define IR_API DLL_IMPORT
#endif
namespace IR
{
enum { maxMemoryPages = (Uptr)65536 };
enum { numBytesPerPage = (Uptr)65536 };
enum { numBytesPerPageLog2 = (Uptr)16 };
enum { requireSharedFlagForAtomicOperators = false };
}
#pragma once
#include "Inline/BasicTypes.h"
#include "IR.h"
#include "Types.h"
#include <vector>
namespace IR
{
// An initializer expression: serialized like any other code, but may only be a constant or immutable global
struct InitializerExpression
{
enum class Type : U8
{
i32_const = 0x41,
i64_const = 0x42,
f32_const = 0x43,
f64_const = 0x44,
get_global = 0x23,
error = 0xff
};
Type type;
union
{
I32 i32;
I64 i64;
F32 f32;
F64 f64;
Uptr globalIndex;
};
InitializerExpression(): type(Type::error) {}
InitializerExpression(I32 inI32): type(Type::i32_const), i32(inI32) {}
InitializerExpression(I64 inI64): type(Type::i64_const), i64(inI64) {}
InitializerExpression(F32 inF32): type(Type::f32_const), f32(inF32) {}
InitializerExpression(F64 inF64): type(Type::f64_const), f64(inF64) {}
InitializerExpression(Type inType,Uptr inGlobalIndex): type(inType), globalIndex(inGlobalIndex) { assert(inType == Type::get_global); }
};
// A function definition
struct FunctionDef
{
IndexedFunctionType type;
std::vector<ValueType> nonParameterLocalTypes;
std::vector<U8> code;
std::vector<std::vector<U32>> branchTables;
};
// A table definition
struct TableDef
{
TableType type;
};
// A memory definition
struct MemoryDef
{
MemoryType type;
};
// A global definition
struct GlobalDef
{
GlobalType type;
InitializerExpression initializer;
};
// Describes an object imported into a module or a specific type
template<typename Type>
struct Import
{
Type type;
std::string moduleName;
std::string exportName;
};
typedef Import<Uptr> FunctionImport;
typedef Import<TableType> TableImport;
typedef Import<MemoryType> MemoryImport;
typedef Import<GlobalType> GlobalImport;
// Describes an export from a module. The interpretation of index depends on kind
struct Export
{
std::string name;
ObjectKind kind;
Uptr index;
};
// A data segment: a literal sequence of bytes that is copied into a Runtime::Memory when instantiating a module
struct DataSegment
{
Uptr memoryIndex;
InitializerExpression baseOffset;
std::vector<U8> data;
};
// A table segment: a literal sequence of function indices that is copied into a Runtime::Table when instantiating a module
struct TableSegment
{
Uptr tableIndex;
InitializerExpression baseOffset;
std::vector<Uptr> indices;
};
// A user-defined module section as an array of bytes
struct UserSection
{
std::string name;
std::vector<U8> data;
};
// An index-space for imports and definitions of a specific kind.
template<typename Definition,typename Type>
struct IndexSpace
{
std::vector<Import<Type>> imports;
std::vector<Definition> defs;
Uptr size() const { return imports.size() + defs.size(); }
Type getType(Uptr index) const
{
if(index < imports.size()) { return imports[index].type; }
else { return defs[index - imports.size()].type; }
}
};
// A WebAssembly module definition
struct Module
{
std::vector<const FunctionType*> types;
IndexSpace<FunctionDef,IndexedFunctionType> functions;
IndexSpace<TableDef,TableType> tables;
IndexSpace<MemoryDef,MemoryType> memories;
IndexSpace<GlobalDef,GlobalType> globals;
std::vector<Export> exports;
std::vector<DataSegment> dataSegments;
std::vector<TableSegment> tableSegments;
std::vector<UserSection> userSections;
Uptr startFunctionIndex;
Module() : startFunctionIndex(UINTPTR_MAX) {}
};
// Finds a named user section in a module.
inline bool findUserSection(const Module& module,const char* userSectionName,Uptr& outUserSectionIndex)
{
for(Uptr sectionIndex = 0;sectionIndex < module.userSections.size();++sectionIndex)
{
if(module.userSections[sectionIndex].name == userSectionName)
{
outUserSectionIndex = sectionIndex;
return true;
}
}
return false;
}
// Maps declarations in a module to names to use in disassembly.
struct DisassemblyNames
{
struct Function
{
std::string name;
std::vector<std::string> locals;
};
std::vector<std::string> types;
std::vector<Function> functions;
std::vector<std::string> tables;
std::vector<std::string> memories;
std::vector<std::string> globals;
};
// Looks for a name section in a module. If it exists, deserialize it into outNames.
// If it doesn't exist, fill outNames with sensible defaults.
IR_API void getDisassemblyNames(const Module& module,DisassemblyNames& outNames);
// Serializes a DisassemblyNames structure and adds it to the module as a name section.
IR_API void setDisassemblyNames(Module& module,const DisassemblyNames& names);
}
#pragma once
#include "Inline/BasicTypes.h"
#include "Inline/Serialization.h"
#include "IR.h"
#include "Module.h"
#include "Operators.h"
namespace IR
{
struct OperatorPrinter
{
typedef std::string Result;
OperatorPrinter(const Module& inModule,const FunctionDef& inFunctionDef)
: module(inModule), functionDef(inFunctionDef) {}
#define VISIT_OPCODE(encoding,name,nameString,Imm,...) \
std::string name(Imm imm = {}) \
{ \
return std::string(nameString) + describeImm(imm); \
}
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
std::string unknown(Opcode opcode)
{
return "<unknown opcode " + std::to_string((Uptr)opcode) + ">";
}
private:
const Module& module;
const FunctionDef& functionDef;
std::string describeImm(NoImm) { return ""; }
std::string describeImm(ControlStructureImm imm) { return std::string(" : ") + asString(imm.resultType); }
std::string describeImm(BranchImm imm) { return " " + std::to_string(imm.targetDepth); }
std::string describeImm(BranchTableImm imm)
{
std::string result = " " + std::to_string(imm.defaultTargetDepth);
const char* prefix = " [";
assert(imm.branchTableIndex < functionDef.branchTables.size());
for(auto depth : functionDef.branchTables[imm.branchTableIndex]) { result += prefix + std::to_string(depth); prefix = ","; }
result += "]";
return result;
}
template<typename NativeValue>
std::string describeImm(LiteralImm<NativeValue> imm) { return " " + asString(imm.value); }
template<bool isGlobal>
std::string describeImm(GetOrSetVariableImm<isGlobal> imm) { return " " + std::to_string(imm.variableIndex); }
std::string describeImm(CallImm imm)
{
const std::string typeString = imm.functionIndex >= module.functions.size()
? "<invalid function index>"
: asString(module.types[module.functions.getType(imm.functionIndex).index]);
return " " + std::to_string(imm.functionIndex) + " " + typeString;
}
std::string describeImm(CallIndirectImm imm)
{
const std::string typeString = imm.type.index >= module.types.size()
? "<invalid type index>"
: asString(module.types[imm.type.index]);
return " " + typeString;
}
template<Uptr naturalAlignmentLog2>
std::string describeImm(LoadOrStoreImm<naturalAlignmentLog2> imm)
{
return " align=" + std::to_string(1<<imm.alignmentLog2) + " offset=" + std::to_string(imm.offset);
}
std::string describeImm(MemoryImm) { return ""; }
#if ENABLE_SIMD_PROTOTYPE
template<Uptr numLanes>
std::string describeImm(LaneIndexImm<numLanes> imm) { return " " + std::to_string(imm.laneIndex); }
template<Uptr numLanes>
std::string describeImm(ShuffleImm<numLanes> imm)
{
std::string result = " [";
const char* prefix = "";
for(Uptr laneIndex = 0;laneIndex < numLanes;++laneIndex)
{
result += prefix
+ (imm.laneIndices[laneIndex] < numLanes ? 'a' : 'b')
+ std::to_string(imm.laneIndices[laneIndex]);
prefix = ",";
}
return result;
}
#endif
#if ENABLE_THREADING_PROTOTYPE
std::string describeImm(LaunchThreadImm) { return ""; }
template<Uptr naturalAlignmentLog2>
std::string describeImm(AtomicLoadOrStoreImm<naturalAlignmentLog2> imm)
{
return " align=" + std::to_string(1<<imm.alignmentLog2) + " offset=" + std::to_string(imm.offset);
}
#endif
};
}
\ No newline at end of file
此差异已折叠。
#pragma once
#include "Inline/BasicTypes.h"
#include "Inline/Errors.h"
#include "IR.h"
#include <vector>
#include <string>
namespace IR
{
// The type of a WebAssembly operand
enum class ValueType : U8
{
any = 0,
i32 = 1,
i64 = 2,
f32 = 3,
f64 = 4,
#if ENABLE_SIMD_PROTOTYPE
v128 = 5,
#endif
num,
max = num-1
};
template<ValueType type> struct ValueTypeInfo;
template<> struct ValueTypeInfo<ValueType::i32> { typedef I32 Value; };
template<> struct ValueTypeInfo<ValueType::i64> { typedef I64 Value; };
template<> struct ValueTypeInfo<ValueType::f32> { typedef F32 Value; };
template<> struct ValueTypeInfo<ValueType::f64> { typedef F64 Value; };
inline std::string asString(I32 value) { return std::to_string(value); }
inline std::string asString(I64 value) { return std::to_string(value); }
inline std::string asString(F32 value) { return std::to_string(value); }
inline std::string asString(F64 value) { return std::to_string(value); }
#if ENABLE_SIMD_PROTOTYPE
union V128
{
U8 u8[16];
I8 i8[16];
U16 u16[8];
I16 i16[8];
U32 u32[4];
I32 i32[4];
U64 u64[2];
I64 i64[2];
};
inline std::string asString(const V128& v128)
{
std::string result;
for(Uptr laneIndex = 0;laneIndex < 16;++laneIndex)
{
if(laneIndex != 0) { result += ' '; }
result += std::to_string(v128.u8[laneIndex]);
}
return result;
}
template<Uptr numLanes>
struct BoolVector
{
bool b[numLanes];
};
template<Uptr numLanes>
std::string asString(const BoolVector<numLanes>& boolVector)
{
std::string result = "(";
for(Uptr laneIndex = 0;laneIndex < numLanes;++laneIndex)
{
if(laneIndex != 0) { result += ' '; }
result += boolVector.b[laneIndex] ? '1' : '0';
}
result += ")";
return result;
}
template<> struct ValueTypeInfo<ValueType::v128> { typedef V128 Value; };
#endif
inline U8 getTypeBitWidth(ValueType type)
{
switch(type)
{
case ValueType::i32: return 32;
case ValueType::i64: return 64;
case ValueType::f32: return 32;
case ValueType::f64: return 64;
#if ENABLE_SIMD_PROTOTYPE
case ValueType::v128: return 128;
#endif
default: Errors::unreachable();
};
}
inline const char* asString(ValueType type)
{
switch(type)
{
case ValueType::any: return "any";
case ValueType::i32: return "i32";
case ValueType::i64: return "i64";
case ValueType::f32: return "f32";
case ValueType::f64: return "f64";
#if ENABLE_SIMD_PROTOTYPE
case ValueType::v128: return "v128";
#endif
default: Errors::unreachable();
};
}
// The type of a WebAssembly operator result. Mostly the same as ValueType, but allows operators with no result (none).
enum class ResultType : U8
{
none = 0,
i32 = (U8)ValueType::i32,
i64 = (U8)ValueType::i64,
f32 = (U8)ValueType::f32,
f64 = (U8)ValueType::f64,
#if ENABLE_SIMD_PROTOTYPE
v128 = (U8)ValueType::v128,
#endif
num,
max = num-1,
};
inline Uptr getArity(ResultType returnType) { return returnType == ResultType::none ? 0 : 1; }
inline const char* asString(ResultType type)
{
switch(type)
{
case ResultType::i32: return "i32";
case ResultType::i64: return "i64";
case ResultType::f32: return "f32";
case ResultType::f64: return "f64";
#if ENABLE_SIMD_PROTOTYPE
case ResultType::v128: return "v128";
#endif
case ResultType::none: return "()";
default: Errors::unreachable();
};
}
// Conversion between ValueType and ResultType.
inline ValueType asValueType(ResultType type)
{
assert(type != ResultType::none);
return (ValueType)type;
}
inline ResultType asResultType(ValueType type)
{
assert(type != ValueType::any);
return (ResultType)type;
}
// The type of a WebAssembly function
struct FunctionType
{
ResultType ret;
std::vector<ValueType> parameters;
IR_API static const FunctionType* get(ResultType ret,const std::initializer_list<ValueType>& parameters);
IR_API static const FunctionType* get(ResultType ret,const std::vector<ValueType>& parameters);
IR_API static const FunctionType* get(ResultType ret = ResultType::none);
private:
FunctionType(ResultType inRet,const std::vector<ValueType>& inParameters)
: ret(inRet), parameters(inParameters) {}
};
struct IndexedFunctionType
{
U32 index;
};
inline std::string asString(const std::vector<ValueType>& typeTuple)
{
std::string result = "(";
for(Uptr parameterIndex = 0;parameterIndex < typeTuple.size();++parameterIndex)
{
if(parameterIndex != 0) { result += ','; }
result += asString(typeTuple[parameterIndex]);
}
result += ")";
return result;
}
inline std::string asString(const FunctionType* functionType)
{
return asString(functionType->parameters) + "->" + asString(functionType->ret);
}
// A size constraint: a range of expected sizes for some size-constrained type.
// If max==UINT64_MAX, the maximum size is unbounded.
struct SizeConstraints
{
U64 min;
U64 max;
friend bool operator==(const SizeConstraints& left,const SizeConstraints& right) { return left.min == right.min && left.max == right.max; }
friend bool operator!=(const SizeConstraints& left,const SizeConstraints& right) { return left.min != right.min || left.max != right.max; }
friend bool isSubset(const SizeConstraints& super,const SizeConstraints& sub)
{
return sub.min >= super.min && sub.max <= super.max;
}
};
// The type of element a table contains: for now can only be anyfunc, meaning any function type.
enum class TableElementType : U8
{
anyfunc = 0x70
};
// The type of a table
struct TableType
{
TableElementType elementType;
bool isShared;
SizeConstraints size;
TableType(): elementType(TableElementType::anyfunc), isShared(false), size({0,UINT64_MAX}) {}
TableType(TableElementType inElementType,bool inIsShared,SizeConstraints inSize)
: elementType(inElementType), isShared(inIsShared), size(inSize) {}
friend bool operator==(const TableType& left,const TableType& right)
{ return left.elementType == right.elementType && left.isShared == right.isShared && left.size == right.size; }
friend bool operator!=(const TableType& left,const TableType& right)
{ return left.elementType != right.elementType || left.isShared != right.isShared || left.size != right.size; }
friend bool isSubset(const TableType& super,const TableType& sub)
{ return super.elementType == sub.elementType && super.isShared == sub.isShared && isSubset(super.size,sub.size); }
};
// The type of a memory
struct MemoryType
{
bool isShared;
SizeConstraints size;
MemoryType(): isShared(false), size({0,UINT64_MAX}) {}
MemoryType(bool inIsShared,const SizeConstraints& inSize): isShared(inIsShared), size(inSize) {}
friend bool operator==(const MemoryType& left,const MemoryType& right) { return left.isShared == right.isShared && left.size == right.size; }
friend bool operator!=(const MemoryType& left,const MemoryType& right) { return left.isShared != right.isShared || left.size != right.size; }
friend bool isSubset(const MemoryType& super,const MemoryType& sub) { return super.isShared == sub.isShared && isSubset(super.size,sub.size); }
};
// The type of a global
struct GlobalType
{
ValueType valueType;
bool isMutable;
GlobalType(): valueType(ValueType::any), isMutable(false) {}
GlobalType(ValueType inValueType,bool inIsMutable): valueType(inValueType), isMutable(inIsMutable) {}
friend bool operator==(const GlobalType& left,const GlobalType& right) { return left.valueType == right.valueType && left.isMutable == right.isMutable; }
friend bool operator!=(const GlobalType& left,const GlobalType& right) { return left.valueType != right.valueType || left.isMutable != right.isMutable; }
friend bool operator<=(const GlobalType& left,const GlobalType& right) { return left.valueType == right.valueType && left.isMutable == right.isMutable; }
friend bool operator>(const GlobalType& left,const GlobalType& right) { return !(left <= right); }
};
inline std::string asString(const GlobalType& globalType)
{
if(globalType.isMutable) { return std::string("global ") + asString(globalType.valueType); }
else { return std::string("immutable ") + asString(globalType.valueType); }
}
// The type of an object
enum class ObjectKind : U8
{
function = 0,
table = 1,
memory = 2,
global = 3,
module = 4,
max = 4,
invalid = 0xff,
};
struct ObjectType
{
const ObjectKind kind;
ObjectType() : kind(ObjectKind::invalid) {}
ObjectType(const FunctionType* inFunction) : kind(ObjectKind::function), function(inFunction) {}
ObjectType(TableType inTable) : kind(ObjectKind::table), table(inTable) {}
ObjectType(MemoryType inMemory) : kind(ObjectKind::memory), memory(inMemory) {}
ObjectType(GlobalType inGlobal) : kind(ObjectKind::global), global(inGlobal) {}
friend const FunctionType* asFunctionType(const ObjectType& objectType)
{
assert(objectType.kind == ObjectKind::function);
return objectType.function;
}
friend TableType asTableType(const ObjectType& objectType)
{
assert(objectType.kind == ObjectKind::table);
return objectType.table;
}
friend MemoryType asMemoryType(const ObjectType& objectType)
{
assert(objectType.kind == ObjectKind::memory);
return objectType.memory;
}
friend GlobalType asGlobalType(const ObjectType& objectType)
{
assert(objectType.kind == ObjectKind::global);
return objectType.global;
}
private:
union
{
const FunctionType* function;
TableType table;
MemoryType memory;
GlobalType global;
};
};
inline std::string asString(const ObjectType& objectType)
{
switch(objectType.kind)
{
case ObjectKind::function: return "func " + asString(asFunctionType(objectType));
case ObjectKind::table: return "table";
case ObjectKind::memory: return "memory";
case ObjectKind::global: return asString(asGlobalType(objectType));
default: Errors::unreachable();
};
}
}
\ No newline at end of file
#pragma once
#include "IR.h"
#include "IR/Operators.h"
#include <string>
namespace IR
{
struct Module;
struct FunctionDef;
struct ValidationException
{
std::string message;
ValidationException(std::string&& inMessage): message(inMessage) {}
};
struct CodeValidationStreamImpl;
struct CodeValidationStream
{
IR_API CodeValidationStream(const Module& module,const FunctionDef& function);
IR_API ~CodeValidationStream();
IR_API void finish();
#define VISIT_OPCODE(_,name,nameString,Imm,...) IR_API void name(Imm imm = {});
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
private:
CodeValidationStreamImpl* impl;
};
template<typename InnerStream>
struct CodeValidationProxyStream
{
CodeValidationProxyStream(const Module& module,const FunctionDef& function,InnerStream& inInnerStream)
: codeValidationStream(module,function)
, innerStream(inInnerStream)
{}
void finishValidation() { codeValidationStream.finish(); }
#define VISIT_OPCODE(_,name,nameString,Imm,...) \
void name(Imm imm = {}) \
{ \
codeValidationStream.name(imm); \
innerStream.name(imm); \
}
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
private:
CodeValidationStream codeValidationStream;
InnerStream& innerStream;
};
IR_API void validateDefinitions(const IR::Module& module);
}
#pragma once
#include <cstdint>
#include <cstddef>
typedef uint8_t U8;
typedef int8_t I8;
typedef uint16_t U16;
typedef int16_t I16;
typedef uint32_t U32;
typedef int32_t I32;
typedef uint64_t U64;
typedef int64_t I64;
typedef float F32;
typedef double F64;
// The OSX libc defines uintptr_t to be a long where U32/U64 are int. This causes uintptr_t/uint64 to be treated as distinct types for e.g. overloading.
// Work around it by defining our own Uptr/Iptr that are always int type.
template<size_t pointerSize>
struct PointerIntHelper;
template<> struct PointerIntHelper<4> { typedef I32 IntType; typedef U32 UnsignedIntType; };
template<> struct PointerIntHelper<8> { typedef I64 IntType; typedef U64 UnsignedIntType; };
typedef PointerIntHelper<sizeof(size_t)>::UnsignedIntType Uptr;
typedef PointerIntHelper<sizeof(size_t)>::IntType Iptr;
#pragma once
#include "Inline/BasicTypes.h"
#include "Inline/Errors.h"
#include "Platform/Platform.h"
#include <string.h>
#include <assert.h>
// Encapsulates a set of integers that are in the range 0 to maxIndexPlusOne (excluding maxIndexPlusOne).
// It uses 1 bit of storage for each integer in the range, and many operations look at all bits, so it's best suited to small ranges.
// However, this avoids heap allocations, and so is pretty fast for sets of small integers (e.g. U8).
template<typename Index,Uptr maxIndexPlusOne>
struct DenseStaticIntSet
{
DenseStaticIntSet()
{
memset(elements,0,sizeof(elements));
}
DenseStaticIntSet(Index index)
{
memset(elements,0,sizeof(elements));
add(index);
}
// Queries
inline bool contains(Index index) const
{
assert((Uptr)index < maxIndexPlusOne);
return (elements[index / indicesPerElement] & (1ull << (index % indicesPerElement))) != 0;
}
bool isEmpty() const
{
Element combinedElements = 0;
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
combinedElements |= elements[elementIndex];
}
return combinedElements == 0;
}
inline Index getSmallestMember() const
{
// Find the first element that has any bits set.
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
if(elements[elementIndex])
{
// Find the index of the lowest set bit in the element using countTrailingZeroes.
const Index result = (Index)(elementIndex * indicesPerElement + Platform::countTrailingZeroes(elements[elementIndex]));
assert(contains(result));
return result;
}
}
return maxIndexPlusOne;
}
// Adding/removing indices
inline void add(Index index)
{
assert((Uptr)index < maxIndexPlusOne);
elements[index / indicesPerElement] |= 1ull << (index % indicesPerElement);
}
inline void addRange(Index rangeMin,Index rangeMax)
{
assert(rangeMin <= rangeMax);
assert((Uptr)rangeMax < maxIndexPlusOne);
for(Index index = rangeMin;index <= rangeMax;++index)
{
add(index);
}
}
inline bool remove(Index index)
{
const Element elementMask = 1ull << (index % indicesPerElement);
const bool hadIndex = (elements[index / indicesPerElement] & elementMask) != 0;
elements[index / indicesPerElement] &= ~elementMask;
return hadIndex;
}
// Logical operators
friend DenseStaticIntSet operator~(const DenseStaticIntSet& set)
{
DenseStaticIntSet result;
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
result.elements[elementIndex] = ~set.elements[elementIndex];
}
return result;
}
friend DenseStaticIntSet operator|(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
DenseStaticIntSet result;
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
result.elements[elementIndex] = left.elements[elementIndex] | right.elements[elementIndex];
}
return result;
}
friend DenseStaticIntSet operator&(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
DenseStaticIntSet result;
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
result.elements[elementIndex] = left.elements[elementIndex] & right.elements[elementIndex];
}
return result;
}
friend DenseStaticIntSet operator^(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
DenseStaticIntSet result;
for(Uptr elementIndex = 0;elementIndex < numElements;++elementIndex)
{
result.elements[elementIndex] = left.elements[elementIndex] ^ right.elements[elementIndex];
}
return result;
}
// Comparisons
friend bool operator==(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
return memcmp(left.elements,right.elements,sizeof(DenseStaticIntSet::elements)) == 0;
}
friend bool operator!=(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
return memcmp(left.elements,right.elements,sizeof(DenseStaticIntSet::elements)) != 0;
}
friend bool operator<(const DenseStaticIntSet& left,const DenseStaticIntSet& right)
{
return memcmp(left.elements,right.elements,sizeof(DenseStaticIntSet::elements)) < 0;
}
private:
typedef U64 Element;
enum { indicesPerElement = sizeof(Element) * 8 };
enum { numElements = (maxIndexPlusOne + indicesPerElement - 1) / indicesPerElement };
U64 elements[numElements];
};
#pragma once
#include <stdarg.h>
#include <assert.h>
#include <cstdio>
#include <cstdlib>
namespace Errors
{
// Fatal error handling.
[[noreturn]] inline void fatalf(const char* messageFormat,...)
{
va_list varArgs;
va_start(varArgs,messageFormat);
std::vfprintf(stderr,messageFormat,varArgs);
std::fflush(stderr);
va_end(varArgs);
std::abort();
}
[[noreturn]] inline void fatal(const char* message) { fatalf("%s\n",message); }
[[noreturn]] inline void unreachable() { fatalf("reached unreachable code\n"); }
[[noreturn]] inline void unimplemented(const char* context) { fatalf("unimplemented: %s\n",context); }
}
// Like assert, but is never removed in any build configuration.
#define errorUnless(condition) if(!(condition)) { Errors::fatalf("errorUnless(%s) failed\n",#condition); }
#pragma once
#include "Inline/BasicTypes.h"
#include "Inline/Errors.h"
#include <string>
#include <cstdio>
namespace Floats
{
template<typename Float>
struct FloatComponents;
// The components of a 64-bit float
template<>
struct FloatComponents<F64>
{
typedef U64 Bits;
typedef F64 Float;
enum Constants : I64
{
maxSignificand = 0xfffffffffffff,
numSignificandBits = 52,
numSignificandHexits = 13,
canonicalSignificand = 0x8000000000000ull,
denormalExponent = -1023,
minNormalExponent = -1022,
maxNormalExponent = 1023,
exponentBias = 1023,
maxExponentBits = 0x7ff,
};
union
{
struct
{
U64 significand : 52;
U64 exponent : 11;
U64 sign : 1;
} bits;
Float value;
Bits bitcastInt;
};
};
// The components of a 32-bit float.
template<>
struct FloatComponents<F32>
{
typedef U32 Bits;
typedef F32 Float;
enum Constants : I32
{
maxSignificand = 0x7fffff,
numSignificandBits = 23,
numSignificandHexits = 6,
canonicalSignificand = 0x400000,
denormalExponent = -127,
minNormalExponent = -126,
maxNormalExponent = 127,
exponentBias = 127,
maxExponentBits = 0xff,
};
union
{
struct
{
U32 significand : 23;
U32 exponent : 8;
U32 sign : 1;
} bits;
Float value;
Bits bitcastInt;
};
};
// Prints a floating point value to a string, using the WebAssembly syntax for text floats.
template<typename Float>
std::string asString(Float f)
{
FloatComponents<Float> components;
components.value = f;
auto sign = std::string(components.bits.sign ? "-" : "+");
if(components.bits.exponent == FloatComponents<Float>::maxExponentBits)
{
// Handle infinity.
if(components.bits.significand == 0) { return sign + "infinity"; }
else
{
// Handle NaN.
char significandString[FloatComponents<Float>::numSignificandHexits + 1];
for(Uptr hexitIndex = 0;hexitIndex < FloatComponents<Float>::numSignificandHexits;++hexitIndex)
{
auto hexitValue = char((components.bits.significand >> ((FloatComponents<Float>::numSignificandHexits - hexitIndex - 1) * 4)) & 0xf);
significandString[hexitIndex] = hexitValue >= 10 ? ('a' + hexitValue - 10) : ('0' + hexitValue);
}
significandString[FloatComponents<Float>::numSignificandHexits] = 0;
return sign + "nan:0x" + significandString + "";
}
}
else
{
// If it's not infinite or NaN, just use the STL hexadecimal float printing.
char buffer[32];
auto numChars = std::sprintf(buffer,FloatComponents<Float>::numSignificandHexits == 6 ? "%.6a" : "%.13a",f);
if(unsigned(numChars) + 1 > sizeof(buffer))
{
Errors::fatal("not enough space in Floats::asString buffer");
}
return buffer;
}
}
}
#pragma once
#include "Platform/Platform.h"
#include <string>
#include <vector>
#include <string.h>
#include <algorithm>
namespace Serialization
{
// An exception that is thrown for various errors during serialization.
// Any code using serialization should handle it!
struct FatalSerializationException
{
std::string message;
FatalSerializationException(std::string&& inMessage)
: message(std::move(inMessage)) {}
};
// An abstract output stream.
struct OutputStream
{
enum { isInput = false };
OutputStream(): next(nullptr), end(nullptr) {}
Uptr capacity() const { return SIZE_MAX; }
// Advances the stream cursor by numBytes, and returns a pointer to the previous stream cursor.
inline U8* advance(Uptr numBytes)
{
if(next + numBytes > end) { extendBuffer(numBytes); }
assert(next + numBytes <= end);
U8* data = next;
next += numBytes;
return data;
}
protected:
U8* next;
U8* end;
// Called when there isn't enough space in the buffer to hold a write to the stream.
// Should update next and end to point to a new buffer, and ensure that the new
// buffer has at least numBytes. May throw FatalSerializationException.
virtual void extendBuffer(Uptr numBytes) = 0;
};
// An output stream that writes to an array of bytes.
struct ArrayOutputStream : public OutputStream
{
// Moves the output array from the stream to the caller.
std::vector<U8>&& getBytes()
{
bytes.resize(next - bytes.data());
next = nullptr;
end = nullptr;
return std::move(bytes);
}
private:
std::vector<U8> bytes;
virtual void extendBuffer(Uptr numBytes)
{
const Uptr nextIndex = next - bytes.data();
// Grow the array by larger and larger increments, so the time spent growing
// the buffer is O(1).
bytes.resize(std::max((Uptr)nextIndex+numBytes,(Uptr)bytes.size() * 7 / 5 + 32));
next = bytes.data() + nextIndex;
end = bytes.data() + bytes.size();
}
virtual bool canExtendBuffer(Uptr numBytes) const { return true; }
};
// An abstract input stream.
struct InputStream
{
enum { isInput = true };
InputStream(const U8* inNext,const U8* inEnd): next(inNext), end(inEnd) {}
virtual Uptr capacity() const = 0;
// Advances the stream cursor by numBytes, and returns a pointer to the previous stream cursor.
inline const U8* advance(Uptr numBytes)
{
if(next + numBytes > end) { getMoreData(numBytes); }
const U8* data = next;
next += numBytes;
return data;
}
// Returns a pointer to the current stream cursor, ensuring that there are at least numBytes following it.
inline const U8* peek(Uptr numBytes)
{
if(next + numBytes > end) { getMoreData(numBytes); }
return next;
}
protected:
const U8* next;
const U8* end;
// Called when there isn't enough space in the buffer to satisfy a read from the stream.
// Should update next and end to point to a new buffer, and ensure that the new
// buffer has at least numBytes. May throw FatalSerializationException.
virtual void getMoreData(Uptr numBytes) = 0;
};
// An input stream that reads from a contiguous range of memory.
struct MemoryInputStream : InputStream
{
MemoryInputStream(const U8* begin,Uptr numBytes): InputStream(begin,begin+numBytes) {}
virtual Uptr capacity() const { return end - next; }
private:
virtual void getMoreData(Uptr numBytes) { throw FatalSerializationException("expected data but found end of stream"); }
};
// Serialize raw byte sequences.
FORCEINLINE void serializeBytes(OutputStream& stream,const U8* bytes,Uptr numBytes)
{ memcpy(stream.advance(numBytes),bytes,numBytes); }
FORCEINLINE void serializeBytes(InputStream& stream,U8* bytes,Uptr numBytes)
{ memcpy(bytes,stream.advance(numBytes),numBytes); }
// Serialize basic C++ types.
template<typename Stream,typename Value>
FORCEINLINE void serializeNativeValue(Stream& stream,Value& value) { serializeBytes(stream,(U8*)&value,sizeof(Value)); }
template<typename Stream> void serialize(Stream& stream,U8& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,U32& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,U64& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,I8& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,I32& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,I64& i) { serializeNativeValue(stream,i); }
template<typename Stream> void serialize(Stream& stream,F32& f) { serializeNativeValue(stream,f); }
template<typename Stream> void serialize(Stream& stream,F64& f) { serializeNativeValue(stream,f); }
// LEB128 variable-length integer serialization.
template<typename Value,Uptr maxBits>
FORCEINLINE void serializeVarInt(OutputStream& stream,Value& inValue,Value minValue,Value maxValue)
{
Value value = inValue;
if(value < minValue || value > maxValue)
{
throw FatalSerializationException(std::string("out-of-range value: ") + std::to_string(minValue) + "<=" + std::to_string(value) + "<=" + std::to_string(maxValue));
}
bool more = true;
while(more)
{
U8 outputByte = (U8)(value&127);
value >>= 7;
more = std::is_signed<Value>::value
? (value != 0 && value != Value(-1)) || (value >= 0 && (outputByte & 0x40)) || (value < 0 && !(outputByte & 0x40))
: (value != 0);
if(more) { outputByte |= 0x80; }
*stream.advance(1) = outputByte;
};
}
template<typename Value,Uptr maxBits>
FORCEINLINE void serializeVarInt(InputStream& stream,Value& value,Value minValue,Value maxValue)
{
// First, read the variable number of input bytes into a fixed size buffer.
enum { maxBytes = (maxBits + 6) / 7 };
U8 bytes[maxBytes] = {0};
Uptr numBytes = 0;
I8 signExtendShift = (I8)sizeof(Value) * 8;
while(numBytes < maxBytes)
{
U8 byte = *stream.advance(1);
bytes[numBytes] = byte;
++numBytes;
signExtendShift -= 7;
if(!(byte & 0x80)) { break; }
};
// Ensure that the input does not encode more than maxBits of data.
enum { numUsedBitsInHighestByte = maxBits - (maxBytes-1) * 7 };
enum { highestByteUsedBitmask = U8(1<<numUsedBitsInHighestByte)-U8(1) };
enum { highestByteSignedBitmask = U8(~U8(highestByteUsedBitmask) & ~U8(0x80)) };
if((bytes[maxBytes-1] & ~highestByteUsedBitmask) != 0
&& ((bytes[maxBytes-1] & ~highestByteUsedBitmask) != U8(highestByteSignedBitmask) || !std::is_signed<Value>::value))
{ throw FatalSerializationException("Invalid LEB encoding: invalid final byte"); }
// Decode the buffer's bytes into the output integer.
value = 0;
for(Uptr byteIndex = 0;byteIndex < maxBytes;++byteIndex)
{ value |= Value(bytes[byteIndex] & ~0x80) << (byteIndex * 7); }
// Sign extend the output integer to the full size of Value.
if(std::is_signed<Value>::value && signExtendShift > 0)
{ value = Value(value << signExtendShift) >> signExtendShift; }
// Check that the output integer is in the expected range.
if(value < minValue || value > maxValue)
{ throw FatalSerializationException(std::string("out-of-range value: ") + std::to_string(minValue) + "<=" + std::to_string(value) + "<=" + std::to_string(maxValue)); }
}
// Helpers for various common LEB128 parameters.
template<typename Stream,typename Value> void serializeVarUInt1(Stream& stream,Value& value) { serializeVarInt<Value,1>(stream,value,0,1); }
template<typename Stream,typename Value> void serializeVarUInt7(Stream& stream,Value& value) { serializeVarInt<Value,7>(stream,value,0,127); }
template<typename Stream,typename Value> void serializeVarUInt32(Stream& stream,Value& value) { serializeVarInt<Value,32>(stream,value,0,UINT32_MAX); }
template<typename Stream,typename Value> void serializeVarUInt64(Stream& stream,Value& value) { serializeVarInt<Value,64>(stream,value,0,UINT64_MAX); }
template<typename Stream,typename Value> void serializeVarInt7(Stream& stream,Value& value) { serializeVarInt<Value,7>(stream,value,-64,63); }
template<typename Stream,typename Value> void serializeVarInt32(Stream& stream,Value& value) { serializeVarInt<Value,32>(stream,value,INT32_MIN,INT32_MAX); }
template<typename Stream,typename Value> void serializeVarInt64(Stream& stream,Value& value) { serializeVarInt<Value,64>(stream,value,INT64_MIN,INT64_MAX); }
// Serializes a constant. If deserializing, throws a FatalSerializationException if the deserialized value doesn't match the constant.
template<typename Constant>
void serializeConstant(InputStream& stream,const char* constantMismatchMessage,Constant constant)
{
Constant savedConstant;
serialize(stream,savedConstant);
if(savedConstant != constant)
{
throw FatalSerializationException(std::string(constantMismatchMessage) + ": loaded " + std::to_string(savedConstant) + " but was expecting " + std::to_string(constant));
}
}
template<typename Constant>
void serializeConstant(OutputStream& stream,const char* constantMismatchMessage,Constant constant)
{
serialize(stream,constant);
}
// Serialize containers.
template<typename Stream>
void serialize(Stream& stream,std::string& string)
{
Uptr size = string.size();
serializeVarUInt32(stream,size);
if(Stream::isInput)
{
// Advance the stream before resizing the string:
// try to get a serialization exception before making a huge allocation for malformed input.
const U8* inputBytes = stream.advance(size);
string.resize(size);
memcpy(const_cast<char*>(string.data()),inputBytes,size);
string.shrink_to_fit();
}
else { serializeBytes(stream,(U8*)string.c_str(),size); }
}
template<typename Stream,typename Element,typename Allocator,typename SerializeElement>
void serializeArray(Stream& stream,std::vector<Element,Allocator>& vector,SerializeElement serializeElement)
{
Uptr size = vector.size();
serializeVarUInt32(stream,size);
if(Stream::isInput)
{
// Grow the vector one element at a time:
// try to get a serialization exception before making a huge allocation for malformed input.
vector.clear();
for(Uptr index = 0;index < size;++index)
{
vector.push_back(Element());
serializeElement(stream,vector.back());
}
vector.shrink_to_fit();
}
else
{
for(Uptr index = 0;index < vector.size();++index) { serializeElement(stream,vector[index]); }
}
}
template<typename Stream,typename Element,typename Allocator>
void serialize(Stream& stream,std::vector<Element,Allocator>& vector)
{
serializeArray(stream,vector,[](Stream& stream,Element& element){serialize(stream,element);});
}
}
\ No newline at end of file
#pragma once
#include <chrono>
#include "Logging/Logging.h"
namespace Timing
{
// Encapsulates a timer that starts when constructed and stops when read.
struct Timer
{
Timer(): startTime(std::chrono::high_resolution_clock::now()), isStopped(false) {}
void stop() { endTime = std::chrono::high_resolution_clock::now(); }
U64 getMicroseconds()
{
if(!isStopped) { stop(); }
return std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count();
}
F64 getMilliseconds() { return getMicroseconds() / 1000.0; }
F64 getSeconds() { return getMicroseconds() / 1000000.0; }
private:
std::chrono::high_resolution_clock::time_point startTime;
std::chrono::high_resolution_clock::time_point endTime;
bool isStopped;
};
// Helpers for printing timers.
inline void logTimer(const char* context,Timer& timer) { Log::printf(Log::Category::metrics,"%s in %.2fms\n",context,timer.getMilliseconds()); }
inline void logRatePerSecond(const char* context,Timer& timer,F64 numerator,const char* numeratorUnit)
{
Log::printf(Log::Category::metrics,"%s in %.2fms (%f %s/s)\n",
context,
timer.getMilliseconds(),
numerator / timer.getSeconds(),
numeratorUnit
);
}
}
\ No newline at end of file
#pragma once
#include "BasicTypes.h"
#include <assert.h>
namespace UTF8
{
inline const U8* validateString(const U8* nextChar,const U8* endChar)
{
// Check that the string is a valid UTF-8 encoding.
// The valid ranges are taken from table 3-7 in the Unicode Standard 9.0:
// "Well-Formed UTF-8 Byte Sequences"
while(nextChar != endChar)
{
if(*nextChar < 0x80) { ++nextChar; }
else if(*nextChar >= 0xc2 && *nextChar <= 0xdf)
{
if(nextChar + 1 >= endChar
|| nextChar[1] < 0x80 || nextChar[1] > 0xbf) { break; }
nextChar += 2;
}
else if(*nextChar == 0xe0)
{
if(nextChar + 2 >= endChar
|| nextChar[1] < 0xa0 || nextChar[1] > 0xbf
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf) { break; }
nextChar += 3;
}
else if(*nextChar == 0xed)
{
if(nextChar + 2 >= endChar
|| nextChar[1] < 0xa0 || nextChar[1] > 0x9f
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf) { break; }
nextChar += 3;
}
else if(*nextChar >= 0xe1 && *nextChar <= 0xef)
{
if(nextChar + 2 >= endChar
|| nextChar[1] < 0x80 || nextChar[1] > 0xbf
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf) { break; }
nextChar += 3;
}
else if(*nextChar == 0xf0)
{
if(nextChar + 3 >= endChar
|| nextChar[1] < 0x90 || nextChar[1] > 0xbf
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf
|| nextChar[3] < 0x80 || nextChar[3] > 0xbf) { break; }
nextChar += 4;
}
else if(*nextChar >= 0xf1 && *nextChar <= 0xf3)
{
if(nextChar + 3 >= endChar
|| nextChar[1] < 0x80 || nextChar[1] > 0xbf
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf
|| nextChar[3] < 0x80 || nextChar[3] > 0xbf) { break; }
nextChar += 4;
}
else if(*nextChar == 0xf4)
{
if(nextChar + 3 >= endChar
|| nextChar[1] < 0x80 || nextChar[1] > 0x8f
|| nextChar[2] < 0x80 || nextChar[2] > 0xbf
|| nextChar[3] < 0x80 || nextChar[3] > 0xbf) { break; }
nextChar += 4;
}
else { break; }
}
return nextChar;
}
template<typename String>
inline void encodeCodepoint(U32 codepoint,String& outString)
{
if(codepoint < 0x80)
{
outString += char(codepoint);
}
else if(codepoint < 0x800)
{
outString += char((codepoint >> 6) & 0x1F) | 0xC0;
outString += char((codepoint & 0x3F) | 0x80);
}
else if(codepoint < 0x10000)
{
outString += char((codepoint >> 12) & 0x0F) | 0xE0;
outString += char((codepoint >> 6) & 0x3F) | 0x80;
outString += char((codepoint & 0x3F) | 0x80);
}
else
{
assert(codepoint < 0x200000);
outString += char((codepoint >> 18) & 0x07) | 0xF0;
outString += char((codepoint >> 12) & 0x3F) | 0x80;
outString += char((codepoint >> 6) & 0x3F) | 0x80;
outString += char((codepoint & 0x3F) | 0x80);
}
}
}
\ No newline at end of file
#pragma once
#ifndef LOGGING_API
#define LOGGING_API DLL_IMPORT
#endif
#include "Inline/BasicTypes.h"
#include "Platform/Platform.h"
// Debug logging.
namespace Log
{
// Allow filtering the logging by category.
enum class Category
{
error,
debug,
metrics,
num
};
LOGGING_API void setCategoryEnabled(Category category,bool enable);
LOGGING_API bool isCategoryEnabled(Category category);
// Print some categorized, formatted string, and flush the output. Newline is not included.
LOGGING_API void printf(Category category,const char* format,...);
};
\ No newline at end of file
#pragma once
#include <assert.h>
#include <vector>
#include <functional>
#include "Inline/BasicTypes.h"
#ifdef _WIN32
#define THREAD_LOCAL thread_local
#define DLL_EXPORT __declspec(dllexport)
#define DLL_IMPORT __declspec(dllimport)
#define FORCEINLINE __forceinline
#define SUPPRESS_UNUSED(variable) (void)(variable);
#include <intrin.h>
#define PACKED_STRUCT(definition) __pragma(pack(push, 1)) definition; __pragma(pack(pop))
#else
// Use __thread instead of the C++11 thread_local because Apple's clang doesn't support thread_local yet.
#define THREAD_LOCAL __thread
#define DLL_EXPORT
#define DLL_IMPORT
#define FORCEINLINE inline __attribute__((always_inline))
#define SUPPRESS_UNUSED(variable) (void)(variable);
#define PACKED_STRUCT(definition) definition __attribute__((packed));
#endif
#ifndef PLATFORM_API
#define PLATFORM_API DLL_IMPORT
#endif
namespace Platform
{
// countLeadingZeroes/countTrailingZeroes returns the number of leading/trailing zeroes, or the bit width of the input if no bits are set.
#ifdef _WIN32
// BitScanReverse/BitScanForward return 0 if the input is 0.
inline U64 countLeadingZeroes(U64 value) { unsigned long result; return _BitScanReverse64(&result,value) ? (63 - result) : 64; }
inline U32 countLeadingZeroes(U32 value) { unsigned long result; return _BitScanReverse(&result,value) ? (31 - result) : 32; }
inline U64 countTrailingZeroes(U64 value) { unsigned long result; return _BitScanForward64(&result,value) ? result : 64; }
inline U32 countTrailingZeroes(U32 value) { unsigned long result; return _BitScanForward(&result,value) ? result : 32; }
#else
// __builtin_clz/__builtin_ctz are undefined if the input is 0.
inline U64 countLeadingZeroes(U64 value) { return value == 0 ? 64 : __builtin_clzll(value); }
inline U32 countLeadingZeroes(U32 value) { return value == 0 ? 32 : __builtin_clz(value); }
inline U64 countTrailingZeroes(U64 value) { return value == 0 ? 64 : __builtin_ctzll(value); }
inline U32 countTrailingZeroes(U32 value) { return value == 0 ? 32 : __builtin_ctz(value); }
#endif
inline U64 floorLogTwo(U64 value) { return value <= 1 ? 0 : 63 - countLeadingZeroes(value); }
inline U32 floorLogTwo(U32 value) { return value <= 1 ? 0 : 31 - countLeadingZeroes(value); }
inline U64 ceilLogTwo(U64 value) { return floorLogTwo(value * 2 - 1); }
inline U32 ceilLogTwo(U32 value) { return floorLogTwo(value * 2 - 1); }
//
// Memory
//
// Describes allowed memory accesses.
enum class MemoryAccess
{
None,
ReadOnly,
ReadWrite,
Execute,
ReadWriteExecute
};
// Returns the base 2 logarithm of the smallest virtual page size.
PLATFORM_API Uptr getPageSizeLog2();
// Allocates virtual addresses without commiting physical pages to them.
// Returns the base virtual address of the allocated addresses, or nullptr if the virtual address space has been exhausted.
PLATFORM_API U8* allocateVirtualPages(Uptr numPages);
// Commits physical memory to the specified virtual pages.
// baseVirtualAddress must be a multiple of the preferred page size.
// Return true if successful, or false if physical memory has been exhausted.
PLATFORM_API bool commitVirtualPages(U8* baseVirtualAddress,Uptr numPages,MemoryAccess access = MemoryAccess::ReadWrite);
// Changes the allowed access to the specified virtual pages.
// baseVirtualAddress must be a multiple of the preferred page size.
// Return true if successful, or false if the access-level could not be set.
PLATFORM_API bool setVirtualPageAccess(U8* baseVirtualAddress,Uptr numPages,MemoryAccess access);
// Decommits the physical memory that was committed to the specified virtual pages.
// baseVirtualAddress must be a multiple of the preferred page size.
PLATFORM_API void decommitVirtualPages(U8* baseVirtualAddress,Uptr numPages);
// Frees virtual addresses. Any physical memory committed to the addresses must have already been decommitted.
// baseVirtualAddress must be a multiple of the preferred page size.
PLATFORM_API void freeVirtualPages(U8* baseVirtualAddress,Uptr numPages);
//
// Call stack and exceptions
//
// Describes a call stack.
struct CallStack
{
struct Frame
{
Uptr ip;
};
std::vector<Frame> stackFrames;
};
// Captures the execution context of the caller.
PLATFORM_API CallStack captureCallStack(Uptr numOmittedFramesFromTop = 0);
// Describes an instruction pointer.
PLATFORM_API bool describeInstructionPointer(Uptr ip,std::string& outDescription);
#ifdef _WIN32
// Registers/deregisters the data used by Windows SEH to unwind stack frames.
PLATFORM_API void registerSEHUnwindInfo(Uptr imageLoadAddress,Uptr pdataAddress,Uptr pdataNumBytes);
PLATFORM_API void deregisterSEHUnwindInfo(Uptr pdataAddress);
#endif
// Calls a thunk, and if it causes any of some specific hardware traps, returns true.
// If a trap was caught, the outCause, outContext, and outOperand parameters are set to describe the trap.
enum HardwareTrapType
{
none,
accessViolation,
stackOverflow,
intDivideByZeroOrOverflow
};
PLATFORM_API HardwareTrapType catchHardwareTraps(
CallStack& outTrapCallStack,
Uptr& outTrapOperand,
const std::function<void()>& thunk
);
//
// Threading
//
// Returns the current value of a clock that may be used as an absolute time for wait timeouts.
// The resolution is microseconds, and the origin is arbitrary.
PLATFORM_API U64 getMonotonicClock();
// Platform-independent mutexes.
struct Mutex;
PLATFORM_API Mutex* createMutex();
PLATFORM_API void destroyMutex(Mutex* mutex);
PLATFORM_API void lockMutex(Mutex* mutex);
PLATFORM_API void unlockMutex(Mutex* mutex);
// RAII-style lock for Mutex.
struct Lock
{
Lock(Mutex* inMutex) : mutex(inMutex) { lockMutex(mutex); }
~Lock() { unlockMutex(mutex); }
void release()
{
if(mutex)
{
unlockMutex(mutex);
}
mutex = nullptr;
}
void detach()
{
assert(mutex);
mutex = nullptr;
}
private:
Mutex* mutex;
};
// Platform-independent events.
struct Event;
PLATFORM_API Event* createEvent();
PLATFORM_API void destroyEvent(Event* event);
PLATFORM_API bool waitForEvent(Event* event,U64 untilClock);
PLATFORM_API void signalEvent(Event* event);
}
#pragma once
#include "Inline/BasicTypes.h"
#include "IR/IR.h"
#include "Runtime.h"
namespace Intrinsics
{
// An intrinsic function.
struct Function
{
Runtime::FunctionInstance* function;
RUNTIME_API Function(const char* inName,const IR::FunctionType* type,void* nativeFunction);
RUNTIME_API ~Function();
private:
const char* name;
};
// The base class of Intrinsic globals.
struct Global
{
Runtime::GlobalInstance* global;
RUNTIME_API Global(const char* inName,IR::GlobalType inType);
RUNTIME_API ~Global();
RUNTIME_API void reset();
protected:
void* value;
private:
const char* name;
IR::GlobalType globalType;
};
// A partially specialized template for Intrinsic globals:
// Provides access via implicit coercion to a value, and for mutable globals an assignment operator.
template<IR::ValueType type,bool isMutable>
struct GenericGlobal : Global
{
typedef typename IR::ValueTypeInfo<type>::Value Value;
GenericGlobal(const char* inName,Value inValue)
: Global(inName,IR::GlobalType(type,isMutable)) { *(Value*)value = inValue; }
operator Value() const { return *(Value*)value; }
void operator=(Value newValue) { *(Value*)value = newValue; }
};
template<IR::ValueType type>
struct GenericGlobal<type,false> : Global
{
typedef typename IR::ValueTypeInfo<type>::Value Value;
GenericGlobal(const char* inName,Value inValue)
: Global(inName,IR::GlobalType(type,false)) { *(Value*)value = inValue; }
operator Value() const { return *(Value*)value; }
void reset(Value inValue)
{
Global::reset();
*(Value*)value = inValue;
}
};
// Intrinsic memories and tables
struct Memory
{
RUNTIME_API Memory(const char* inName,const IR::MemoryType& inType);
RUNTIME_API ~Memory();
operator Runtime::MemoryInstance*() const { return memory; }
private:
const char* name;
Runtime::MemoryInstance* const memory;
};
struct Table
{
RUNTIME_API Table(const char* inName,const IR::TableType& inType);
RUNTIME_API ~Table();
operator Runtime::TableInstance*() const { return table; }
private:
const char* name;
Runtime::TableInstance* const table;
};
// Finds an intrinsic object by name and type.
RUNTIME_API Runtime::ObjectInstance* find(const std::string& name,const IR::ObjectType& type);
// Returns an array of all intrinsic runtime Objects; used as roots for garbage collection.
RUNTIME_API std::vector<Runtime::ObjectInstance*> getAllIntrinsicObjects();
}
namespace NativeTypes
{
typedef I32 i32;
typedef I64 i64;
typedef F32 f32;
typedef F64 f64;
typedef void none;
#if ENABLE_SIMD_PROTOTYPE
typedef IR::V128 v128;
typedef IR::V128 b8x16;
typedef IR::V128 b16x8;
typedef IR::V128 b32x4;
typedef IR::V128 b64x2;
#endif
};
// Macros for defining intrinsic functions of various arities.
#define DEFINE_INTRINSIC_FUNCTION0(module,cName,name,returnType) \
NativeTypes::returnType cName##returnType(); \
static Intrinsics::Function cName##returnType##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType),(void*)&cName##returnType); \
NativeTypes::returnType cName##returnType()
#define DEFINE_INTRINSIC_FUNCTION1(module,cName,name,returnType,arg0Type,arg0Name) \
NativeTypes::returnType cName##returnType##arg0Type(NativeTypes::arg0Type); \
static Intrinsics::Function cName##returnType##arg0Type##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType,{IR::ValueType::arg0Type}),(void*)&cName##returnType##arg0Type); \
NativeTypes::returnType cName##returnType##arg0Type(NativeTypes::arg0Type arg0Name)
#define DEFINE_INTRINSIC_FUNCTION2(module,cName,name,returnType,arg0Type,arg0Name,arg1Type,arg1Name) \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type(NativeTypes::arg0Type,NativeTypes::arg1Type); \
static Intrinsics::Function cName##returnType##arg0Type##arg1Type##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType,{IR::ValueType::arg0Type,IR::ValueType::arg1Type}),(void*)&cName##returnType##arg0Type##arg1Type); \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type(NativeTypes::arg0Type arg0Name,NativeTypes::arg1Type arg1Name)
#define DEFINE_INTRINSIC_FUNCTION3(module,cName,name,returnType,arg0Type,arg0Name,arg1Type,arg1Name,arg2Type,arg2Name) \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type(NativeTypes::arg0Type,NativeTypes::arg1Type,NativeTypes::arg2Type); \
static Intrinsics::Function cName##returnType##arg0Type##arg1Type##arg2Type##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType,{IR::ValueType::arg0Type,IR::ValueType::arg1Type,IR::ValueType::arg2Type}),(void*)&cName##returnType##arg0Type##arg1Type##arg2Type); \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type(NativeTypes::arg0Type arg0Name,NativeTypes::arg1Type arg1Name,NativeTypes::arg2Type arg2Name)
#define DEFINE_INTRINSIC_FUNCTION4(module,cName,name,returnType,arg0Type,arg0Name,arg1Type,arg1Name,arg2Type,arg2Name,arg3Type,arg3Name) \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type(NativeTypes::arg0Type,NativeTypes::arg1Type,NativeTypes::arg2Type,NativeTypes::arg3Type); \
static Intrinsics::Function cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType,{IR::ValueType::arg0Type,IR::ValueType::arg1Type,IR::ValueType::arg2Type,IR::ValueType::arg3Type}),(void*)&cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type); \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type(NativeTypes::arg0Type arg0Name,NativeTypes::arg1Type arg1Name,NativeTypes::arg2Type arg2Name,NativeTypes::arg3Type arg3Name)
#define DEFINE_INTRINSIC_FUNCTION5(module,cName,name,returnType,arg0Type,arg0Name,arg1Type,arg1Name,arg2Type,arg2Name,arg3Type,arg3Name,arg4Type,arg4Name) \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type##arg4Type(NativeTypes::arg0Type,NativeTypes::arg1Type,NativeTypes::arg2Type,NativeTypes::arg3Type,NativeTypes::arg4Type); \
static Intrinsics::Function cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type##arg4Type##Function(#module "." #name,IR::FunctionType::get(IR::ResultType::returnType,{IR::ValueType::arg0Type,IR::ValueType::arg1Type,IR::ValueType::arg2Type,IR::ValueType::arg3Type,IR::ValueType::arg4Type}),(void*)&cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type##arg4Type); \
NativeTypes::returnType cName##returnType##arg0Type##arg1Type##arg2Type##arg3Type##arg4Type(NativeTypes::arg0Type arg0Name,NativeTypes::arg1Type arg1Name,NativeTypes::arg2Type arg2Name,NativeTypes::arg3Type arg3Name,NativeTypes::arg4Type arg4Name)
// Macros for defining intrinsic globals, memories, and tables.
#define DEFINE_INTRINSIC_GLOBAL(module,cName,name,valueType,isMutable,initializer) \
static Intrinsics::GenericGlobal<IR::ValueType::valueType,isMutable> \
cName(#module "." #name,initializer);
#define DEFINE_INTRINSIC_MEMORY(module,cName,name,type) static Intrinsics::Memory cName(#module "." #name,type);
#define DEFINE_INTRINSIC_TABLE(module,cName,name,type) static Intrinsics::Table cName(#module "." #name,type);
\ No newline at end of file
#pragma once
#include "Inline/BasicTypes.h"
#include "Runtime.h"
#include <functional>
namespace Runtime
{
// An abstract resolver: maps module+export name pairs to a Runtime::Object.
struct Resolver
{
virtual bool resolve(const std::string& moduleName,const std::string& exportName,IR::ObjectType type,ObjectInstance*& outObject) = 0;
};
// A resolver for intrinsics.
struct IntrinsicResolver : Resolver
{
static RUNTIME_API IntrinsicResolver singleton;
RUNTIME_API bool resolve(const std::string& moduleName,const std::string& exportName,IR::ObjectType type,ObjectInstance*& outObject) override;
};
// A resolver that ignores the moduleName, and looks for the exportName in a single module.
struct ModuleExportResolver : Resolver
{
ModuleExportResolver(const IR::Module& inModule,ModuleInstance* inModuleInstance): module(inModule), moduleInstance(inModuleInstance) {}
bool resolve(const std::string& moduleName,const std::string& exportName,IR::ObjectType type,ObjectInstance*& outObject) override;
private:
const IR::Module& module;
ModuleInstance* moduleInstance;
};
// A resolver that lazily creates an inner resolver when it's first used, then forwards all queries to the inner resolver.
struct LazyResolver : Resolver
{
LazyResolver(std::function<Resolver*()>& inInnerResolverThunk)
: innerResolverThunk(std::move(inInnerResolverThunk)), innerResolver(nullptr) {}
bool resolve(const std::string& moduleName,const std::string& exportName,IR::ObjectType type,Runtime::ObjectInstance*& outObject) override
{
if(!innerResolver) { innerResolver = innerResolverThunk(); }
return innerResolver->resolve(moduleName,exportName,type,outObject);
}
private:
std::function<Resolver*()> innerResolverThunk;
Resolver* innerResolver;
};
// A resolver that always returns failure.
struct NullResolver : Resolver
{
bool resolve(const std::string& moduleName,const std::string& exportName,IR::ObjectType type,Runtime::ObjectInstance*& outObject) override
{
return false;
}
};
// Links a module using the given resolver, returning an array mapping import indices to objects.
// If the resolver fails to resolve any imports, throws a LinkException.
struct LinkResult
{
struct MissingImport
{
std::string moduleName;
std::string exportName;
IR::ObjectType type;
};
std::vector<MissingImport> missingImports;
ImportBindings resolvedImports;
bool success;
};
RUNTIME_API LinkResult linkModule(const IR::Module& module,Resolver& resolver);
}
\ No newline at end of file
#pragma once
#include "Inline/BasicTypes.h"
#include "TaggedValue.h"
#include "IR/Types.h"
#ifndef RUNTIME_API
#define RUNTIME_API DLL_IMPORT
#endif
// Declare IR::Module to avoid including the definition.
namespace IR { struct Module; }
namespace Runtime
{
// Initializes the runtime. Should only be called once per process.
RUNTIME_API void init();
// Information about a runtime exception.
struct Exception
{
enum class Cause : U8
{
unknown,
accessViolation,
stackOverflow,
integerDivideByZeroOrIntegerOverflow,
invalidFloatOperation,
invokeSignatureMismatch,
reachedUnreachable,
indirectCallSignatureMismatch,
undefinedTableElement,
calledAbort,
calledUnimplementedIntrinsic,
outOfMemory,
invalidSegmentOffset,
misalignedAtomicMemoryAccess
};
Cause cause;
std::vector<std::string> callStack;
};
// Returns a string that describes the given exception cause.
inline const char* describeExceptionCause(Exception::Cause cause)
{
switch(cause)
{
case Exception::Cause::accessViolation: return "access violation";
case Exception::Cause::stackOverflow: return "stack overflow";
case Exception::Cause::integerDivideByZeroOrIntegerOverflow: return "integer divide by zero or signed integer overflow";
case Exception::Cause::invalidFloatOperation: return "invalid floating point operation";
case Exception::Cause::invokeSignatureMismatch: return "invoke signature mismatch";
case Exception::Cause::reachedUnreachable: return "reached unreachable code";
case Exception::Cause::indirectCallSignatureMismatch: return "call_indirect to function with wrong signature";
case Exception::Cause::undefinedTableElement: return "undefined function table element";
case Exception::Cause::calledAbort: return "called abort";
case Exception::Cause::calledUnimplementedIntrinsic: return "called unimplemented intrinsic";
case Exception::Cause::outOfMemory: return "out of memory";
case Exception::Cause::invalidSegmentOffset: return "invalid segment offset";
case Exception::Cause::misalignedAtomicMemoryAccess: return "misaligned atomic memory access";
default: return "unknown";
}
}
// Causes a runtime exception.
[[noreturn]] RUNTIME_API void causeException(Exception::Cause cause);
// These are subclasses of Object, but are only defined within Runtime, so other modules must
// use these forward declarations as opaque pointers.
struct FunctionInstance;
struct TableInstance;
struct MemoryInstance;
struct GlobalInstance;
struct ModuleInstance;
// A runtime object of any type.
struct ObjectInstance
{
const IR::ObjectKind kind;
ObjectInstance(IR::ObjectKind inKind): kind(inKind) {}
virtual ~ObjectInstance() {}
};
// Tests whether an object is of the given type.
RUNTIME_API bool isA(ObjectInstance* object,const IR::ObjectType& type);
// Casts from object to subclasses, and vice versa.
inline FunctionInstance* asFunction(ObjectInstance* object) { assert(object && object->kind == IR::ObjectKind::function); return (FunctionInstance*)object; }
inline TableInstance* asTable(ObjectInstance* object) { assert(object && object->kind == IR::ObjectKind::table); return (TableInstance*)object; }
inline MemoryInstance* asMemory(ObjectInstance* object) { assert(object && object->kind == IR::ObjectKind::memory); return (MemoryInstance*)object; }
inline GlobalInstance* asGlobal(ObjectInstance* object) { assert(object && object->kind == IR::ObjectKind::global); return (GlobalInstance*)object; }
inline ModuleInstance* asModule(ObjectInstance* object) { assert(object && object->kind == IR::ObjectKind::module); return (ModuleInstance*)object; }
template<typename Instance> Instance* as(ObjectInstance* object);
template<> inline FunctionInstance* as<FunctionInstance>(ObjectInstance* object) { return asFunction(object); }
template<> inline TableInstance* as<TableInstance>(ObjectInstance* object) { return asTable(object); }
template<> inline MemoryInstance* as<MemoryInstance>(ObjectInstance* object) { return asMemory(object); }
template<> inline GlobalInstance* as<GlobalInstance>(ObjectInstance* object) { return asGlobal(object); }
template<> inline ModuleInstance* as<ModuleInstance>(ObjectInstance* object) { return asModule(object); }
inline ObjectInstance* asObject(FunctionInstance* function) { return (ObjectInstance*)function; }
inline ObjectInstance* asObject(TableInstance* table) { return (ObjectInstance*)table; }
inline ObjectInstance* asObject(MemoryInstance* memory) { return (ObjectInstance*)memory; }
inline ObjectInstance* asObject(GlobalInstance* global) { return (ObjectInstance*)global; }
inline ObjectInstance* asObject(ModuleInstance* module) { return (ObjectInstance*)module; }
// Casts from object to subclass that checks that the object is the right kind and returns null if not.
inline FunctionInstance* asFunctionNullable(ObjectInstance* object) { return object && object->kind == IR::ObjectKind::function ? (FunctionInstance*)object : nullptr; }
inline TableInstance* asTableNullable(ObjectInstance* object) { return object && object->kind == IR::ObjectKind::table ? (TableInstance*)object : nullptr; }
inline MemoryInstance* asMemoryNullable(ObjectInstance* object) { return object && object->kind == IR::ObjectKind::memory ? (MemoryInstance*)object : nullptr; }
inline GlobalInstance* asGlobalNullable(ObjectInstance* object) { return object && object->kind == IR::ObjectKind::global ? (GlobalInstance*)object : nullptr; }
inline ModuleInstance* asModuleNullable(ObjectInstance* object) { return object && object->kind == IR::ObjectKind::module ? (ModuleInstance*)object : nullptr; }
// Frees unreferenced Objects, using the provided array of Objects as the root set.
RUNTIME_API void freeUnreferencedObjects(std::vector<ObjectInstance*>&& rootObjectReferences);
//
// Functions
//
// Invokes a FunctionInstance with the given parameters, and returns the result.
// Throws a Runtime::Exception if a trap occurs.
RUNTIME_API Result invokeFunction(FunctionInstance* function,const std::vector<Value>& parameters);
// Returns the type of a FunctionInstance.
RUNTIME_API const IR::FunctionType* getFunctionType(FunctionInstance* function);
//
// Tables
//
// Creates a Table. May return null if the memory allocation fails.
RUNTIME_API TableInstance* createTable(IR::TableType type);
// Reads an element from the table. Assumes that index is in bounds.
RUNTIME_API ObjectInstance* getTableElement(TableInstance* table,Uptr index);
// Writes an element to the table. Assumes that index is in bounds, and returns a pointer to the previous value of the element.
RUNTIME_API ObjectInstance* setTableElement(TableInstance* table,Uptr index,ObjectInstance* newValue);
// Gets the current or maximum size of the table.
RUNTIME_API Uptr getTableNumElements(TableInstance* table);
RUNTIME_API Uptr getTableMaxElements(TableInstance* table);
// Grows or shrinks the size of a table by numElements. Returns the previous size of the table.
RUNTIME_API Iptr growTable(TableInstance* table,Uptr numElements);
RUNTIME_API Iptr shrinkTable(TableInstance* table,Uptr numElements);
//
// Memories
//
// Creates a Memory. May return null if the memory allocation fails.
RUNTIME_API MemoryInstance* createMemory(IR::MemoryType type);
// Gets the base address of the memory's data.
RUNTIME_API U8* getMemoryBaseAddress(MemoryInstance* memory);
// Gets the current or maximum size of the memory in pages.
RUNTIME_API Uptr getMemoryNumPages(MemoryInstance* memory);
RUNTIME_API Uptr getMemoryMaxPages(MemoryInstance* memory);
// Grows or shrinks the size of a memory by numPages. Returns the previous size of the memory.
RUNTIME_API Iptr growMemory(MemoryInstance* memory,Uptr numPages);
RUNTIME_API Iptr shrinkMemory(MemoryInstance* memory,Uptr numPages);
// Validates that an offset range is wholly inside a Memory's virtual address range.
RUNTIME_API U8* getValidatedMemoryOffsetRange(MemoryInstance* memory,Uptr offset,Uptr numBytes);
// Validates an access to a single element of memory at the given offset, and returns a reference to it.
template<typename Value> Value& memoryRef(MemoryInstance* memory,U32 offset)
{ return *(Value*)getValidatedMemoryOffsetRange(memory,offset,sizeof(Value)); }
// Validates an access to multiple elements of memory at the given offset, and returns a pointer to it.
template<typename Value> Value* memoryArrayPtr(MemoryInstance* memory,U32 offset,U32 numElements)
{ return (Value*)getValidatedMemoryOffsetRange(memory,offset,numElements * sizeof(Value)); }
//
// Globals
//
// Creates a GlobalInstance with the specified type and initial value.
RUNTIME_API GlobalInstance* createGlobal(IR::GlobalType type,Value initialValue);
// Reads the current value of a global.
RUNTIME_API Value getGlobalValue(GlobalInstance* global);
// Writes a new value to a global, and returns the previous value.
RUNTIME_API Value setGlobalValue(GlobalInstance* global,Value newValue);
//
// Modules
//
struct ImportBindings
{
std::vector<FunctionInstance*> functions;
std::vector<TableInstance*> tables;
std::vector<MemoryInstance*> memories;
std::vector<GlobalInstance*> globals;
};
// Instantiates a module, bindings its imports to the specified objects. May throw InstantiationException.
RUNTIME_API ModuleInstance* instantiateModule(const IR::Module& module,ImportBindings&& imports);
// Gets the default table/memory for a ModuleInstance.
RUNTIME_API MemoryInstance* getDefaultMemory(ModuleInstance* moduleInstance);
RUNTIME_API TableInstance* getDefaultTable(ModuleInstance* moduleInstance);
// Gets an object exported by a ModuleInstance by name.
RUNTIME_API ObjectInstance* getInstanceExport(ModuleInstance* moduleInstance,const std::string& name);
}
#pragma once
#include "IR/Types.h"
#include "Inline/Floats.h"
#include <string.h>
namespace Runtime
{
// A runtime value of any type.
struct UntaggedValue
{
union
{
I32 i32;
U32 u32;
I64 i64;
U64 u64;
F32 f32;
F64 f64;
#if ENABLE_SIMD_PROTOTYPE
IR::V128 v128;
#endif
};
UntaggedValue(I32 inI32) { i32 = inI32; }
UntaggedValue(I64 inI64) { i64 = inI64; }
UntaggedValue(U32 inU32) { u32 = inU32; }
UntaggedValue(U64 inU64) { u64 = inU64; }
UntaggedValue(F32 inF32) { f32 = inF32; }
UntaggedValue(F64 inF64) { f64 = inF64; }
#if ENABLE_SIMD_PROTOTYPE
UntaggedValue(IR::V128 inV128) { v128 = inV128; }
#endif
UntaggedValue() {memset(this,0,sizeof(*this));}
};
// A boxed value: may hold any value that can be passed to a function invoked through the runtime.
struct Value : UntaggedValue
{
IR::ValueType type;
Value(I32 inI32): UntaggedValue(inI32), type(IR::ValueType::i32) {}
Value(I64 inI64): UntaggedValue(inI64), type(IR::ValueType::i64) {}
Value(U32 inU32): UntaggedValue(inU32), type(IR::ValueType::i32) {}
Value(U64 inU64): UntaggedValue(inU64), type(IR::ValueType::i64) {}
Value(F32 inF32): UntaggedValue(inF32), type(IR::ValueType::f32) {}
Value(F64 inF64): UntaggedValue(inF64), type(IR::ValueType::f64) {}
#if ENABLE_SIMD_PROTOTYPE
Value(const IR::V128& inV128): UntaggedValue(inV128), type(IR::ValueType::v128) {}
#endif
Value(IR::ValueType inType,UntaggedValue inValue): UntaggedValue(inValue), type(inType) {}
Value(): type(IR::ValueType::any) {}
friend std::string asString(const Value& value)
{
switch(value.type)
{
case IR::ValueType::i32: return "i32(" + std::to_string(value.i32) + ")";
case IR::ValueType::i64: return "i64(" + std::to_string(value.i64) + ")";
case IR::ValueType::f32: return "f32(" + Floats::asString(value.f32) + ")";
case IR::ValueType::f64: return "f64(" + Floats::asString(value.f64) + ")";
#if ENABLE_SIMD_PROTOTYPE
case IR::ValueType::v128: return "v128(" + std::to_string(value.v128.u64[0]) + "," + std::to_string(value.v128.u64[1]) + ")";
#endif
default: Errors::unreachable();
}
}
};
// A boxed value: may hold any value that can be returned from a function invoked through the runtime.
struct Result : UntaggedValue
{
IR::ResultType type;
Result(I32 inI32): UntaggedValue(inI32), type(IR::ResultType::i32) {}
Result(I64 inI64): UntaggedValue(inI64), type(IR::ResultType::i64) {}
Result(U32 inU32): UntaggedValue(inU32), type(IR::ResultType::i32) {}
Result(U64 inU64): UntaggedValue(inU64), type(IR::ResultType::i64) {}
Result(F32 inF32): UntaggedValue(inF32), type(IR::ResultType::f32) {}
Result(F64 inF64): UntaggedValue(inF64), type(IR::ResultType::f64) {}
#if ENABLE_SIMD_PROTOTYPE
Result(const IR::V128& inV128): UntaggedValue(inV128), type(IR::ResultType::v128) {}
#endif
Result(IR::ResultType inType,UntaggedValue inValue): UntaggedValue(inValue), type(inType) {}
Result(const Value& inValue): UntaggedValue(inValue), type(asResultType(inValue.type)) {}
Result(): type(IR::ResultType::none) {}
friend std::string asString(const Result& result)
{
switch(result.type)
{
case IR::ResultType::none: return "()";
case IR::ResultType::i32: return "i32(" + std::to_string(result.i32) + ")";
case IR::ResultType::i64: return "i64(" + std::to_string(result.i64) + ")";
case IR::ResultType::f32: return "f32(" + Floats::asString(result.f32) + ")";
case IR::ResultType::f64: return "f64(" + Floats::asString(result.f64) + ")";
#if ENABLE_SIMD_PROTOTYPE
case IR::ResultType::v128: return "v128(" + std::to_string(result.v128.u64[0]) + "," + std::to_string(result.v128.u64[1]) + ")";
#endif
default: Errors::unreachable();
}
}
};
// Compares whether two UntaggedValue of the same type have identical bits.
inline bool areBitsEqual(IR::ResultType type,UntaggedValue a,UntaggedValue b)
{
switch(type)
{
case IR::ResultType::i32:
case IR::ResultType::f32: return a.i32 == b.i32;
case IR::ResultType::i64:
case IR::ResultType::f64: return a.i64 == b.i64;
#if ENABLE_SIMD_PROTOTYPE
case IR::ResultType::v128: return a.v128.u64[0] == b.v128.u64[0] && a.v128.u64[1] == b.v128.u64[1];
#endif
case IR::ResultType::none: return true;
default: Errors::unreachable();
};
}
// Compares whether two Value/Result have the same type and bits.
inline bool areBitsEqual(const Value& a,const Value& b) { return a.type == b.type && areBitsEqual(asResultType(a.type),a,b); }
inline bool areBitsEqual(const Result& a,const Result& b) { return a.type == b.type && areBitsEqual(a.type,a,b); }
inline bool areBitsEqual(const Result& a,const Value& b) { return a.type == asResultType(b.type) && areBitsEqual(a.type,a,b); }
inline bool areBitsEqual(const Value& a,const Result& b) { return asResultType(a.type) == b.type && areBitsEqual(b.type,a,b); }
}
\ No newline at end of file
#pragma once
#ifndef WEBASSEMBLY_API
#define WEBASSEMBLY_API DLL_IMPORT
#endif
#include "Inline/BasicTypes.h"
namespace IR { struct Module; struct DisassemblyNames; }
namespace Serialization { struct InputStream; struct OutputStream; }
namespace WASM
{
WEBASSEMBLY_API void serialize(Serialization::InputStream& stream,IR::Module& module);
WEBASSEMBLY_API void serialize(Serialization::OutputStream& stream,const IR::Module& module);
}
#pragma once
#include "Inline/BasicTypes.h"
#include "WAST.h"
#include "Runtime/TaggedValue.h"
#include <vector>
#include <memory>
namespace WAST
{
struct Command
{
enum Type
{
_register,
action,
assert_return,
assert_return_canonical_nan,
assert_return_arithmetic_nan,
assert_trap,
assert_invalid,
assert_malformed,
assert_unlinkable
};
const Type type;
const TextFileLocus locus;
Command(Type inType,TextFileLocus&& inLocus): type(inType), locus(inLocus) {}
};
// Parse a test script from a string. Returns true if it succeeds, and writes the test commands to outTestCommands.
WAST_API void parseTestCommands(
const char* string,
Uptr stringLength,
std::vector<std::unique_ptr<Command>>& outTestCommands,
std::vector<Error>& outErrors);
// Actions
enum class ActionType
{
_module,
invoke,
get,
};
struct Action
{
const ActionType type;
const TextFileLocus locus;
Action(ActionType inType,TextFileLocus&& inLocus): type(inType), locus(inLocus) {}
};
struct ModuleAction : Action
{
std::string internalModuleName;
std::unique_ptr<IR::Module> module;
ModuleAction(TextFileLocus&& inLocus,std::string&& inInternalModuleName,IR::Module* inModule)
: Action(ActionType::_module,std::move(inLocus)), internalModuleName(inInternalModuleName), module(inModule) {}
};
struct InvokeAction : Action
{
std::string internalModuleName;
std::string exportName;
std::vector<Runtime::Value> arguments;
InvokeAction(TextFileLocus&& inLocus,std::string&& inInternalModuleName,std::string&& inExportName,std::vector<Runtime::Value>&& inArguments)
: Action(ActionType::invoke,std::move(inLocus)), internalModuleName(inInternalModuleName), exportName(inExportName), arguments(inArguments)
{}
};
struct GetAction : Action
{
std::string internalModuleName;
std::string exportName;
GetAction(TextFileLocus&& inLocus,std::string&& inInternalModuleName,std::string&& inExportName)
: Action(ActionType::get,std::move(inLocus)), internalModuleName(inInternalModuleName), exportName(inExportName)
{}
};
// Commands
struct RegisterCommand : Command
{
std::string moduleName;
std::string internalModuleName;
RegisterCommand(TextFileLocus&& inLocus,std::string&& inModuleName,std::string&& inInternalModuleName)
: Command(Command::_register,std::move(inLocus)), moduleName(inModuleName), internalModuleName(inInternalModuleName)
{}
};
struct ActionCommand : Command
{
std::unique_ptr<Action> action;
ActionCommand(TextFileLocus&& inLocus,Action* inAction)
: Command(Command::action,std::move(inLocus)), action(inAction) {}
};
struct AssertReturnCommand : Command
{
std::unique_ptr<Action> action;
Runtime::Result expectedReturn;
AssertReturnCommand(TextFileLocus&& inLocus,Action* inAction,Runtime::Result inExpectedReturn)
: Command(Command::assert_return,std::move(inLocus)), action(inAction), expectedReturn(inExpectedReturn) {}
};
struct AssertReturnNaNCommand : Command
{
std::unique_ptr<Action> action;
AssertReturnNaNCommand(Command::Type inType,TextFileLocus&& inLocus,Action* inAction)
: Command(inType,std::move(inLocus)), action(inAction) {}
};
struct AssertTrapCommand : Command
{
std::unique_ptr<Action> action;
Runtime::Exception::Cause expectedCause;
AssertTrapCommand(TextFileLocus&& inLocus,Action* inAction,Runtime::Exception::Cause inExpectedCause)
: Command(Command::assert_trap,std::move(inLocus)), action(inAction), expectedCause(inExpectedCause) {}
};
struct AssertInvalidOrMalformedCommand : Command
{
bool wasInvalidOrMalformed;
AssertInvalidOrMalformedCommand(Command::Type inType,TextFileLocus&& inLocus,bool inWasInvalidOrMalformed)
: Command(inType,std::move(inLocus)), wasInvalidOrMalformed(inWasInvalidOrMalformed) {}
};
struct AssertUnlinkableCommand : Command
{
std::unique_ptr<ModuleAction> moduleAction;
AssertUnlinkableCommand(TextFileLocus&& inLocus,ModuleAction* inModuleAction)
: Command(Command::assert_unlinkable,std::move(inLocus)), moduleAction(inModuleAction) {}
};
}
\ No newline at end of file
#pragma once
#ifndef WAST_API
#define WAST_API DLL_IMPORT
#endif
#include "Inline/BasicTypes.h"
#include "Runtime/Runtime.h"
#include "WASM/WASM.h"
namespace WAST
{
// A location in a text file.
struct TextFileLocus
{
std::string sourceLine;
U32 newlines;
U32 tabs;
U32 characters;
TextFileLocus(): newlines(0), tabs(0), characters(0) {}
U32 lineNumber() const { return newlines + 1; }
U32 column(U32 spacesPerTab = 4) const { return tabs * spacesPerTab + characters + 1; }
std::string describe(U32 spacesPerTab = 4) const
{
return std::to_string(lineNumber()) + ":" + std::to_string(column(spacesPerTab));
}
};
// A WAST parse error.
struct Error
{
TextFileLocus locus;
std::string message;
};
// Parse a module from a string. Returns true if it succeeds, and writes the module to outModule.
// If it fails, returns false and appends a list of errors to outErrors.
WAST_API bool parseModule(const char* string,Uptr stringLength,IR::Module& outModule,std::vector<Error>& outErrors);
// Prints a module in WAST format.
WAST_API std::string print(const IR::Module& module);
}
\ No newline at end of file
# License
Copyright (c) 2016, Andrew Scheidecker
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of WAVM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/LICENSE](Test/spec/LICENSE).
[Source/ThirdParty/dtoa.c](Source/ThirdParty/dtoa.c) is covered by the license in that file.
[Source/ThirdParty/xxhash](Source/ThirdParty/xxhash) is covered by the license in [Source/ThirdParty/xxhash/LICENSE](Source/ThirdParty/xxhash/LICENSE).
\ No newline at end of file
[![Linux/OSX Build Status](https://travis-ci.org/AndrewScheidecker/WAVM.svg?branch=master)](https://travis-ci.org/AndrewScheidecker/WAVM)
Primary repo: https://github.com/AndrewScheidecker/WAVM
# Overview
This is a standalone VM for WebAssembly. It can load both the standard binary format, and the text format defined by the [WebAssembly reference interpreter](https://github.com/WebAssembly/spec/tree/master/ml-proto). For the text format, it can load both the standard stack machine syntax and the old-fashioned AST syntax used by the reference interpreter, and all of the testing commands.
# Building and running it
To build it, you'll need CMake and [LLVM 4.0](http://llvm.org/releases/download.html#4.0.0). If CMake can't find your LLVM directory, you can manually give it the location in the LLVM_DIR CMake configuration variable. Note that on Windows, you must compile LLVM from source, and manually point the LLVM_DIR configuration variable at `<LLVM build directory>\lib\cmake\llvm`.
### Building WAVM on Windows
**1.) Install the [Visual Studio C++ Build Tools for Visual Studio 2015 or 2017](http://landinghub.visualstudio.com/visual-cpp-build-tools)**
Take note of which version you have installed:
- If using Visual Studio 2015, use `-G"Visual Studio 14 Win64"` for the `<VS Generator Directive>` placeholder below
- If using Visual Studio 2017, use `-G"Visual Studio 15 Win64"` for the `<VS Generator Directive>` placeholder below
**2.) Build LLVM x64 on Windows with Visual Studio**
Create an llvm_build directory, navigate to that directory and run:
cmake -Thost=x64 <VS Generator Directive> -DCMAKE_INSTALL_PREFIX=<desired install path for LLVM> <path to LLVM source>
Open the generated LLVM.sln locateed within the 'llvm_build' directory in Visual Studio and build the "INSTALL" Project
The output binaries should be located in `<desired install path for LLVM>`
**3.) Build WAVM x64 on Windows with Visual Studio against LLVM x64**
Create a wavm_build directory, navigate to that directory and run:
cmake -Thost=x64 <VS Generator Directive> -DLLVM_DIR=<LLVM build directory>\lib\cmake\llvm <path to WAVM source>
Open the generated WAVM.sln located within the 'wavm_build' directory in Visual Studio and build the "ALL_BUILD" Project
The output binaries should be located in `wavm_build\bin`
# Usage
I've tested it on Windows with Visual C++ 2015/2017, Linux with GCC and clang, and MacOS with Xcode/clang. Travis CI is testing Linux/GCC, Linux/clang, and OSX/clang.
The primary executable is `wavm`:
```
Usage: wavm [switches] [programfile] [--] [arguments]
in.wast|in.wasm Specify program file (.wast/.wasm)
-f|--function name Specify function name to run in module rather than main
-c|--check Exit after checking that the program is valid
-d|--debug Write additional debug information to stdout
-- Stop parsing arguments
```
`wavm` will load a WebAssembly file and call `main` (or a specified function). Example programs to try without changing any code include those found in the Test/wast and Test/spec directory such as the following:
```
wavm Test/wast/helloworld.wast
wavm Test/Benchmark/Benchmark.wast
wavm Test/zlib/zlib.wast
```
WebAssembly programs that export a main function with the standard parameters will be passed in the command line arguments. If the same main function returns a i32 type it will become the exit code. WAVM supports Emscripten's defined I/O functions so programs can read from stdin and write to stdout and stderr. See [echo.wast](Test/wast/echo.wast) for an example of a program that echos the command line arguments back out through stdout.
There are a few additional executables that can be used to assemble the WAST file into a binary:
```
Assemble in.wast out.wasm
```
Disassemble a binary into a WAST file:
```
Disassemble in.wasm out.wast
```
and to execute a test script defined by a WAST file (see the [Test/spec directory](Test/spec) for examples of the syntax):
```
Test in.wast
```
# Architecture
## IR
The [IR](Include/IR) (Intermediate Representation) is the glue that the WAST parser, the WASM serialization, and the Runtime communicate through. It closely mirrors the semantics of the WebAssembly binary format, but is easier to work with in memory.
## Parsing
Parsing the WebAssembly text format uses a table-driven deterministic finite automaton to scan the input string for tokens. The tables are generated from a set of tokens that match literal strings, and a set of tokens that match regular expressions. The parser is a standard recursive descent parser.
## Runtime
The [Runtime](Source/Runtime/) is the primary consumer of the byte code. It provides an [API](Include/Runtime/Runtime.h) for instantiating WebAssembly modules and calling functions exported from them. To instantiate a module, it [initializes the module's runtime environment](Source/Runtime/ModuleInstance.cpp) (globals, memory objects, and table objects), [translates the byte code into LLVM IR](Source/Runtime/LLVMEmitIR.cpp), and [uses LLVM to generate machine code](Source/Runtime/LLVMJIT.cpp) for the module's functions.
# License
WAVM is provided under the terms of [LICENSE](LICENSE).
\ No newline at end of file
此差异已折叠。
#include "Inline/BasicTypes.h"
#include "Inline/Serialization.h"
#include "Logging/Logging.h"
#include "IR.h"
#include "Module.h"
using namespace Serialization;
namespace IR
{
void getDisassemblyNames(const Module& module,DisassemblyNames& outNames)
{
// Fill in the output with the correct number of blank names.
for(const auto& functionImport : module.functions.imports)
{
DisassemblyNames::Function functionNames;
functionNames.locals.resize(module.types[functionImport.type.index]->parameters.size());
outNames.functions.push_back(std::move(functionNames));
}
for(Uptr functionDefIndex = 0;functionDefIndex < module.functions.defs.size();++functionDefIndex)
{
const FunctionDef& functionDef = module.functions.defs[functionDefIndex];
DisassemblyNames::Function functionNames;
functionNames.locals.insert(functionNames.locals.begin(),module.types[functionDef.type.index]->parameters.size() + functionDef.nonParameterLocalTypes.size(),"");
outNames.functions.push_back(std::move(functionNames));
}
outNames.types.insert(outNames.types.end(),module.types.size(),"");
outNames.tables.insert(outNames.tables.end(),module.tables.defs.size() + module.tables.imports.size(),"");
outNames.memories.insert(outNames.memories.end(),module.memories.defs.size() + module.memories.imports.size(),"");
outNames.globals.insert(outNames.globals.end(),module.globals.defs.size() + module.globals.imports.size(),"");
// Deserialize the name section, if it is present.
Uptr userSectionIndex = 0;
if(findUserSection(module,"name",userSectionIndex))
{
try
{
const UserSection& nameSection = module.userSections[userSectionIndex];
MemoryInputStream stream(nameSection.data.data(),nameSection.data.size());
#if 0
while(stream.capacity())
{
U8 substreamType = 0;
serializeVarUInt7(stream,substreamType);
U32 numSubstreamBytes = 0;
serializeVarUInt32(stream,numSubstreamBytes);
MemoryInputStream substream(stream.advance(numSubstreamBytes),numSubstreamBytes);
switch(substreamType)
{
case 0: // function names
{
U32 numFunctionNames = 0;
serializeVarUInt32(substream,numFunctionNames);
for(Uptr functionNameIndex = 0;functionNameIndex < numFunctionNames;++functionNameIndex)
{
U32 functionIndex = 0;
serializeVarUInt32(substream,functionIndex);
std::string functionName;
serialize(substream,functionName);
if(functionIndex < outNames.functions.size()) { outNames.functions[functionIndex].name = std::move(functionName); }
}
break;
}
case 1: // local names
{
U32 numFunctionLocalNameMaps = 0;
serializeVarUInt32(substream,numFunctionLocalNameMaps);
for(Uptr functionNameIndex = 0;functionNameIndex < numFunctionLocalNameMaps;++functionNameIndex)
{
U32 functionIndex = 0;
serializeVarUInt32(substream,functionIndex);
U32 numLocalNames = 0;
serializeVarUInt32(substream,numLocalNames);
for(Uptr localNameIndex = 0;localNameIndex < numLocalNames;++numLocalNames)
{
U32 localIndex = 0;
serializeVarUInt32(substream,localIndex);
std::string localName;
serialize(substream,localName);
if(functionIndex < outNames.functions.size() && localIndex < outNames.functions[functionIndex].locals.size())
{
outNames.functions[functionIndex].locals[localIndex] = std::move(localName);
}
}
}
break;
}
};
};
#endif
Uptr numFunctionNames = 0;
serializeVarUInt32(stream,numFunctionNames);
numFunctionNames = std::min(numFunctionNames,(Uptr)outNames.functions.size());
for(Uptr functionIndex = 0;functionIndex < numFunctionNames;++functionIndex)
{
DisassemblyNames::Function& functionNames = outNames.functions[functionIndex];
serialize(stream,outNames.functions[functionIndex].name);
Uptr numLocalNames = 0;
serializeVarUInt32(stream,numLocalNames);
for(Uptr localIndex = 0;localIndex < numLocalNames;++localIndex)
{
std::string localName;
serialize(stream,localName);
if(localIndex < functionNames.locals.size()) { functionNames.locals[localIndex] = std::move(localName); }
}
}
}
catch(FatalSerializationException exception)
{
Log::printf(Log::Category::debug,"FatalSerializationException while deserializing WASM user name section: %s\n",exception.message.c_str());
}
}
}
void setDisassemblyNames(Module& module,const DisassemblyNames& names)
{
// Replace an existing name section if one is present, or create a new section.
Uptr userSectionIndex = 0;
if(!findUserSection(module,"name",userSectionIndex))
{
userSectionIndex = module.userSections.size();
module.userSections.push_back({"name",{}});
}
ArrayOutputStream stream;
Uptr numFunctionNames = names.functions.size();
serializeVarUInt32(stream,numFunctionNames);
for(Uptr functionIndex = 0;functionIndex < names.functions.size();++functionIndex)
{
std::string functionName = names.functions[functionIndex].name;
serialize(stream,functionName);
Uptr numLocalNames = names.functions[functionIndex].locals.size();
serializeVarUInt32(stream,numLocalNames);
for(Uptr localIndex = 0;localIndex < numLocalNames;++localIndex)
{
std::string localName = names.functions[functionIndex].locals[localIndex];
serialize(stream,localName);
}
}
module.userSections[userSectionIndex].data = stream.getBytes();
}
}
\ No newline at end of file
#include "Operators.h"
namespace IR
{
const char* getOpcodeName(Opcode opcode)
{
switch(opcode)
{
#define VISIT_OPCODE(encoding,name,nameString,Imm,...) case Opcode::name: return nameString;
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
default: return "unknown";
};
}
}
\ No newline at end of file
#include "Types.h"
#include <map>
namespace IR
{
struct FunctionTypeMap
{
struct Key
{
ResultType ret;
std::vector<ValueType> parameters;
friend bool operator==(const Key& left,const Key& right) { return left.ret == right.ret && left.parameters == right.parameters; }
friend bool operator!=(const Key& left,const Key& right) { return left.ret != right.ret || left.parameters != right.parameters; }
friend bool operator<(const Key& left,const Key& right) { return left.ret < right.ret || (left.ret == right.ret && left.parameters < right.parameters); }
};
static std::map<Key,FunctionType*>& get()
{
static std::map<Key,FunctionType*> map;
return map;
}
};
template<typename Key,typename Value,typename CreateValueThunk>
Value findExistingOrCreateNew(std::map<Key,Value>& map,Key&& key,CreateValueThunk createValueThunk)
{
auto mapIt = map.find(key);
if(mapIt != map.end()) { return mapIt->second; }
else
{
Value value = createValueThunk();
map.insert({std::move(key),value});
return value;
}
}
const FunctionType* FunctionType::get(ResultType ret,const std::initializer_list<ValueType>& parameters)
{ return findExistingOrCreateNew(FunctionTypeMap::get(),FunctionTypeMap::Key {ret,parameters},[=]{return new FunctionType(ret,parameters);}); }
const FunctionType* FunctionType::get(ResultType ret,const std::vector<ValueType>& parameters)
{ return findExistingOrCreateNew(FunctionTypeMap::get(),FunctionTypeMap::Key {ret,parameters},[=]{return new FunctionType(ret,parameters);}); }
const FunctionType* FunctionType::get(ResultType ret)
{ return findExistingOrCreateNew(FunctionTypeMap::get(),FunctionTypeMap::Key {ret,{}},[=]{return new FunctionType(ret,{});}); }
}
\ No newline at end of file
此差异已折叠。
#include "Logging.h"
#include "Platform/Platform.h"
#include <cstdio>
#include <cstdarg>
#include <cstdlib>
namespace Log
{
static Platform::Mutex* categoryEnabledMutex = Platform::createMutex();
static bool categoryEnabled[(Uptr)Category::num] =
{
true, // error
#ifdef _DEBUG // debug
true,
#else
false,
#endif
WAVM_METRICS_OUTPUT != 0 // metrics
};
void setCategoryEnabled(Category category,bool enable)
{
Platform::Lock lock(categoryEnabledMutex);
assert(category < Category::num);
categoryEnabled[(Uptr)category] = enable;
}
bool isCategoryEnabled(Category category)
{
Platform::Lock lock(categoryEnabledMutex);
assert(category < Category::num);
return categoryEnabled[(Uptr)category];
}
void printf(Category category,const char* format,...)
{
Platform::Lock lock(categoryEnabledMutex);
if(categoryEnabled[(Uptr)category])
{
va_list varArgs;
va_start(varArgs,format);
vfprintf(stdout,format,varArgs);
fflush(stdout);
va_end(varArgs);
}
}
}
此差异已折叠。
此差异已折叠。
#include "Inline/BasicTypes.h"
#include "CLI.h"
#include "WAST/WAST.h"
#include "WASM/WASM.h"
int commandMain(int argc,char** argv)
{
if(argc < 3)
{
std::cerr << "Usage: Assemble in.wast out.wasm [switches]" << std::endl;
std::cerr << " -n|--omit-names\t\tOmits WAST function and local names from the output" << std::endl;
return EXIT_FAILURE;
}
const char* inputFilename = argv[1];
const char* outputFilename = argv[2];
bool omitNames = false;
if(argc > 3)
{
for(Iptr argumentIndex = 3;argumentIndex < argc;++argumentIndex)
{
if(!strcmp(argv[argumentIndex],"-n") || !strcmp(argv[argumentIndex],"--omit-names"))
{
omitNames = true;
}
else
{
std::cerr << "Unrecognized argument: " << argv[argumentIndex] << std::endl;
return EXIT_FAILURE;
}
}
}
// Load the WAST module.
IR::Module module;
if(!loadTextModule(inputFilename,module)) { return EXIT_FAILURE; }
// If the command-line switch to omit names was specified, strip the name section.
if(omitNames)
{
for(auto sectionIt = module.userSections.begin();sectionIt != module.userSections.end();++sectionIt)
{
if(sectionIt->name == "name") { module.userSections.erase(sectionIt); break; }
}
}
// Write the binary module.
if(!saveBinaryModule(outputFilename,module)) { return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
#pragma once
#include "Inline/BasicTypes.h"
#include "Inline/Floats.h"
#include "Inline/Timing.h"
#include "WAST/WAST.h"
#include "WASM/WASM.h"
#include "IR/Module.h"
#include "IR/Validate.h"
#include "Runtime/Runtime.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cstring>
inline std::string loadFile(const char* filename)
{
Timing::Timer timer;
std::ifstream stream(filename,std::ios::binary | std::ios::ate);
if(!stream.is_open())
{
std::cerr << "Failed to open " << filename << ": " << std::strerror(errno) << std::endl;
return std::string();
}
std::string data;
data.resize((unsigned int)stream.tellg());
stream.seekg(0);
stream.read(const_cast<char*>(data.data()),data.size());
stream.close();
Timing::logRatePerSecond("loaded file",timer,data.size() / 1024.0 / 1024.0,"MB");
return data;
}
inline bool loadTextModule(const char* filename,const std::string& wastString,IR::Module& outModule)
{
std::vector<WAST::Error> parseErrors;
WAST::parseModule(wastString.c_str(),wastString.size(),outModule,parseErrors);
if(!parseErrors.size()) { return true; }
else
{
// Print any parse errors;
std::cerr << "Error parsing WebAssembly text file:" << std::endl;
for(auto& error : parseErrors)
{
std::cerr << filename << ":" << error.locus.describe() << ": " << error.message.c_str() << std::endl;
std::cerr << error.locus.sourceLine << std::endl;
std::cerr << std::setw(error.locus.column(8)) << "^" << std::endl;
}
return false;
}
}
inline bool loadTextModule(const char* filename,IR::Module& outModule)
{
// Read the file into a string.
auto wastBytes = loadFile(filename);
if(!wastBytes.size()) { return false; }
const std::string wastString = std::move(wastBytes);
return loadTextModule(filename,wastString,outModule);
}
inline bool loadBinaryModule(const std::string& wasmBytes,IR::Module& outModule)
{
Timing::Timer loadTimer;
// Load the module from a binary WebAssembly file.
try
{
Serialization::MemoryInputStream stream((const U8*)wasmBytes.data(),wasmBytes.size());
WASM::serialize(stream,outModule);
}
catch(Serialization::FatalSerializationException exception)
{
std::cerr << "Error deserializing WebAssembly binary file:" << std::endl;
std::cerr << exception.message << std::endl;
return false;
}
catch(IR::ValidationException exception)
{
std::cerr << "Error validating WebAssembly binary file:" << std::endl;
std::cerr << exception.message << std::endl;
return false;
}
catch(std::bad_alloc)
{
std::cerr << "Memory allocation failed: input is likely malformed" << std::endl;
return false;
}
Timing::logRatePerSecond("Loaded WASM",loadTimer,wasmBytes.size()/1024.0/1024.0,"MB");
return true;
}
inline bool loadBinaryModule(const char* wasmFilename,IR::Module& outModule)
{
// Read in packed .wasm file bytes.
auto wasmBytes = loadFile(wasmFilename);
if(!wasmBytes.size()) { return false; }
return loadBinaryModule(wasmBytes,outModule);
}
inline bool loadModule(const char* filename,IR::Module& outModule)
{
// Read the specified file into an array.
auto fileBytes = loadFile(filename);
if(!fileBytes.size()) { return false; }
// If the file starts with the WASM binary magic number, load it as a binary module.
if(*(U32*)fileBytes.data() == 0x6d736100) { return loadBinaryModule(fileBytes,outModule); }
else
{
// Otherwise, load it as a text module.
auto wastString = std::move(fileBytes);
return loadTextModule(filename,wastString,outModule);
}
}
inline bool saveBinaryModule(const char* wasmFilename,const IR::Module& module)
{
Timing::Timer saveTimer;
std::vector<U8> wasmBytes;
try
{
// Serialize the WebAssembly module.
Serialization::ArrayOutputStream stream;
WASM::serialize(stream,module);
wasmBytes = stream.getBytes();
}
catch(Serialization::FatalSerializationException exception)
{
std::cerr << "Error serializing WebAssembly binary file:" << std::endl;
std::cerr << exception.message << std::endl;
return false;
}
Timing::logRatePerSecond("Saved WASM",saveTimer,wasmBytes.size()/1024.0/1024.0,"MB");
// Write the serialized data to the output file.
std::ofstream outputStream(wasmFilename,std::ios::binary);
outputStream.write((char*)wasmBytes.data(),wasmBytes.size());
outputStream.close();
return true;
}
inline bool endsWith(const char *str, const char *suffix)
{
if(!str || !suffix) { return false; }
Uptr lenstr = strlen(str);
Uptr lensuffix = strlen(suffix);
if(lenstr < lensuffix) { return false; }
return (strncmp(str+lenstr-lensuffix, suffix, lensuffix) == 0);
}
int commandMain(int argc,char** argv);
int main(int argc,char** argv)
{
try
{
return commandMain(argc,argv);
}
catch(IR::ValidationException exception)
{
std::cerr << "Failed to validate module: " << exception.message << std::endl;
return EXIT_FAILURE;
}
catch(Runtime::Exception exception)
{
std::cerr << "Runtime exception: " << describeExceptionCause(exception.cause) << std::endl;
for(auto calledFunction : exception.callStack) { std::cerr << " " << calledFunction << std::endl; }
return EXIT_FAILURE;
}
catch(Serialization::FatalSerializationException exception)
{
std::cerr << "Fatal serialization exception: " << exception.message << std::endl;
return EXIT_FAILURE;
}
}
\ No newline at end of file
#include "Inline/BasicTypes.h"
#include "CLI.h"
#include "WAST/WAST.h"
#include "WASM/WASM.h"
int commandMain(int argc,char** argv)
{
if(argc != 3)
{
std::cerr << "Usage: Disassemble in.wasm out.wast" << std::endl;
return EXIT_FAILURE;
}
const char* inputFilename = argv[1];
const char* outputFilename = argv[2];
// Load the WASM file.
IR::Module module;
if(!loadBinaryModule(inputFilename,module)) { return EXIT_FAILURE; }
// Print the module to WAST.
const std::string wastString = WAST::print(module);
// Write the serialized data to the output file.
std::ofstream outputStream(outputFilename);
outputStream.write(wastString.data(),wastString.size());
outputStream.close();
return EXIT_SUCCESS;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
xxHash Library
Copyright (c) 2012-2014, Yann Collet
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
#pragma once
#include "Inline/BasicTypes.h"
#include "NFA.h"
namespace Regexp
{
// Parses a regular expression from a string, and adds a recognizer for it to the given NFA
// The recognizer will start from initialState, and end in finalState when the regular expression has been completely matched.
void addToNFA(const char* regexpString,NFA::Builder* nfaBuilder,NFA::StateIndex initialState,NFA::StateIndex finalState);
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
(module
(func (export "br") (block (br 0)))
(func (export "br_if") (block (br_if 0 (i32.const 1))))
(func (export "br_table") (block (br_table 0 (i32.const 0))))
)
(assert_return (invoke "br"))
(assert_return (invoke "br_if"))
(assert_return (invoke "br_table"))
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册