blob: f34bd1516ecb41143eb5f66cb05afcfdb61121e7 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef asmjs_wasm_ir_h
#define asmjs_wasm_ir_h
#include "asmjs/Wasm.h"
namespace js {
class PropertyName;
namespace wasm {
enum class Stmt : uint8_t
{
Ret,
Block,
IfThen,
IfElse,
Switch,
While,
DoWhile,
ForInitInc,
ForInitNoInc,
ForNoInitNoInc,
ForNoInitInc,
Label,
Continue,
ContinueLabel,
Break,
BreakLabel,
CallInternal,
CallIndirect,
CallImport,
AtomicsFence,
// asm.js specific
// Expression statements (to be removed in the future)
I32Expr,
F32Expr,
F64Expr,
I32X4Expr,
F32X4Expr,
Id,
Noop,
InterruptCheckHead,
InterruptCheckLoop,
DebugCheckPoint,
Bad
};
enum class I32 : uint8_t
{
// Common opcodes
GetLocal,
SetLocal,
GetGlobal,
SetGlobal,
CallInternal,
CallIndirect,
CallImport,
Conditional,
Comma,
Literal,
// Binary arith opcodes
Add,
Sub,
Mul,
SDiv,
SMod,
UDiv,
UMod,
Min,
Max,
// Unary arith opcodes
Not,
Neg,
// Bitwise opcodes
BitOr,
BitAnd,
BitXor,
BitNot,
Lsh,
ArithRsh,
LogicRsh,
// Conversion opcodes
FromF32,
FromF64,
// Math builtin opcodes
Clz,
Abs,
// Comparison opcodes
// Ordering matters (EmitComparison expects signed opcodes to be placed
// before unsigned opcodes)
EqI32,
NeI32,
SLtI32,
SLeI32,
SGtI32,
SGeI32,
ULtI32,
ULeI32,
UGtI32,
UGeI32,
EqF32,
NeF32,
LtF32,
LeF32,
GtF32,
GeF32,
EqF64,
NeF64,
LtF64,
LeF64,
GtF64,
GeF64,
// Heap accesses opcodes
SLoad8,
SLoad16,
SLoad32,
ULoad8,
ULoad16,
ULoad32,
Store8,
Store16,
Store32,
// Atomics opcodes
AtomicsCompareExchange,
AtomicsExchange,
AtomicsLoad,
AtomicsStore,
AtomicsBinOp,
// SIMD opcodes
I32X4SignMask,
F32X4SignMask,
I32X4ExtractLane,
// Specific to AsmJS
Id,
Bad
};
enum class F32 : uint8_t
{
// Common opcodes
GetLocal,
SetLocal,
GetGlobal,
SetGlobal,
CallInternal,
CallIndirect,
CallImport,
Conditional,
Comma,
Literal,
// Binary arith opcodes
Add,
Sub,
Mul,
Div,
Min,
Max,
Neg,
// Math builtin opcodes
Abs,
Sqrt,
Ceil,
Floor,
// Conversion opcodes
FromF64,
FromS32,
FromU32,
// Heap accesses opcodes
Load,
StoreF32,
StoreF64,
// SIMD opcodes
F32X4ExtractLane,
// asm.js specific
Id,
Bad
};
enum class F64 : uint8_t
{
// Common opcodes
GetLocal,
SetLocal,
GetGlobal,
SetGlobal,
CallInternal,
CallIndirect,
CallImport,
Conditional,
Comma,
Literal,
// Binary arith opcodes
Add,
Sub,
Mul,
Div,
Min,
Max,
Mod,
Neg,
// Math builtin opcodes
Abs,
Sqrt,
Ceil,
Floor,
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Exp,
Log,
Pow,
Atan2,
// Conversions opcodes
FromF32,
FromS32,
FromU32,
// Heap accesses opcodes
Load,
StoreF32,
StoreF64,
// asm.js specific
Id,
Bad
};
enum class I32X4 : uint8_t
{
// Common opcodes
GetLocal,
SetLocal,
GetGlobal,
SetGlobal,
CallInternal,
CallIndirect,
CallImport,
Conditional,
Comma,
Literal,
// Specific opcodes
Ctor,
Unary,
Binary,
BinaryCompI32X4,
BinaryCompF32X4,
BinaryBitwise,
BinaryShift,
ReplaceLane,
FromF32X4,
FromF32X4Bits,
Swizzle,
Shuffle,
Select,
BitSelect,
Splat,
Load,
Store,
// asm.js specific
Id,
Bad
};
enum class F32X4 : uint8_t
{
// Common opcodes
GetLocal,
SetLocal,
GetGlobal,
SetGlobal,
CallInternal,
CallIndirect,
CallImport,
Conditional,
Comma,
Literal,
// Specific opcodes
Ctor,
Unary,
Binary,
BinaryBitwise,
ReplaceLane,
FromI32X4,
FromI32X4Bits,
Swizzle,
Shuffle,
Select,
BitSelect,
Splat,
Load,
Store,
// asm.js specific
Id,
Bad
};
enum NeedsBoundsCheck : uint8_t
{
NO_BOUNDS_CHECK,
NEEDS_BOUNDS_CHECK
};
// The FuncIR class contains the intermediate representation of a parsed/decoded
// and validated asm.js/WebAssembly function. The FuncIR lives only until it
// is fully compiled. Its contents are assumed to be well-formed; all validation
// of untrusted content must happen before FuncIR generation. A FuncIR object is
// associated with a LifoAlloc allocation which contains all the memory
// referenced by the FuncIR.
class FuncIR
{
typedef Vector<wasm::Val, 4, LifoAllocPolicy<Fallible>> VarInitVector;
typedef Vector<uint8_t, 4096, LifoAllocPolicy<Fallible>> Bytecode;
// Note: this unrooted field assumes AutoKeepAtoms via TokenStream via
// asm.js compilation. Wasm compilation will require an alternative way to
// name CodeRanges (index).
PropertyName* name_;
unsigned line_;
unsigned column_;
uint32_t index_;
const wasm::LifoSig* sig_;
VarInitVector varInits_;
Bytecode bytecode_;
unsigned generateTime_;
public:
FuncIR(LifoAlloc& alloc, PropertyName* name, unsigned line, unsigned column)
: name_(name),
line_(line),
column_(column),
index_(UINT_MAX),
sig_(nullptr),
varInits_(alloc),
bytecode_(alloc),
generateTime_(UINT_MAX)
{}
bool addVariable(wasm::Val v) {
return varInits_.append(v);
}
void finish(uint32_t funcIndex, const wasm::LifoSig& sig, unsigned generateTime) {
MOZ_ASSERT(index_ == UINT_MAX);
MOZ_ASSERT(!sig_);
MOZ_ASSERT(generateTime_ == UINT_MAX);
index_ = funcIndex;
sig_ = &sig;
generateTime_ = generateTime;
}
private:
template<class T> size_t writePrimitive(T v) {
size_t writeAt = bytecode_.length();
if (!bytecode_.append(reinterpret_cast<uint8_t*>(&v), sizeof(T)))
return -1;
return writeAt;
}
template<class T> T readPrimitive(size_t* pc) const {
MOZ_ASSERT(*pc + sizeof(T) <= bytecode_.length());
T ret;
memcpy(&ret, &bytecode_[*pc], sizeof(T));
*pc += sizeof(T);
return ret;
}
public:
size_t writeU8(uint8_t i) { return writePrimitive<uint8_t>(i); }
size_t writeI32(int32_t i) { return writePrimitive<int32_t>(i); }
size_t writeU32(uint32_t i) { return writePrimitive<uint32_t>(i); }
size_t writeF32(float f) { return writePrimitive<float>(f); }
size_t writeF64(double d) { return writePrimitive<double>(d); }
size_t writeI32X4(const int32_t* i4) {
size_t pos = bytecode_.length();
for (size_t i = 0; i < 4; i++)
writePrimitive<int32_t>(i4[i]);
return pos;
}
size_t writeF32X4(const float* f4) {
size_t pos = bytecode_.length();
for (size_t i = 0; i < 4; i++)
writePrimitive<float>(f4[i]);
return pos;
}
uint8_t readU8 (size_t* pc) const { return readPrimitive<uint8_t>(pc); }
int32_t readI32(size_t* pc) const { return readPrimitive<int32_t>(pc); }
float readF32(size_t* pc) const { return readPrimitive<float>(pc); }
uint32_t readU32(size_t* pc) const { return readPrimitive<uint32_t>(pc); }
double readF64(size_t* pc) const { return readPrimitive<double>(pc); }
const wasm::LifoSig* readSig(size_t* pc) const { return readPrimitive<wasm::LifoSig*>(pc); }
jit::SimdConstant readI32X4(size_t* pc) const {
int32_t x = readI32(pc);
int32_t y = readI32(pc);
int32_t z = readI32(pc);
int32_t w = readI32(pc);
return jit::SimdConstant::CreateX4(x, y, z, w);
}
jit::SimdConstant readF32X4(size_t* pc) const {
float x = readF32(pc);
float y = readF32(pc);
float z = readF32(pc);
float w = readF32(pc);
return jit::SimdConstant::CreateX4(x, y, z, w);
}
#ifdef DEBUG
bool pcIsPatchable(size_t pc, unsigned size) const {
bool patchable = true;
for (unsigned i = 0; patchable && i < size; i++)
patchable &= wasm::Stmt(bytecode_[pc]) == wasm::Stmt::Bad;
return patchable;
}
#endif
void patchU8(size_t pc, uint8_t i) {
MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
bytecode_[pc] = i;
}
template<class T>
void patch32(size_t pc, T i) {
static_assert(sizeof(T) == sizeof(uint32_t),
"patch32 must be used with 32-bits wide types");
MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t)));
memcpy(&bytecode_[pc], &i, sizeof(uint32_t));
}
void patchSig(size_t pc, const wasm::LifoSig* ptr) {
MOZ_ASSERT(pcIsPatchable(pc, sizeof(wasm::LifoSig*)));
memcpy(&bytecode_[pc], &ptr, sizeof(wasm::LifoSig*));
}
// Read-only interface
PropertyName* name() const { return name_; }
unsigned line() const { return line_; }
unsigned column() const { return column_; }
uint32_t index() const { MOZ_ASSERT(index_ != UINT32_MAX); return index_; }
size_t size() const { return bytecode_.length(); }
const wasm::LifoSig& sig() const { MOZ_ASSERT(sig_); return *sig_; }
size_t numVarInits() const { return varInits_.length(); }
wasm::Val varInit(size_t i) const { return varInits_[i]; }
size_t numLocals() const { return sig_->args().length() + varInits_.length(); }
unsigned generateTime() const { MOZ_ASSERT(generateTime_ != UINT_MAX); return generateTime_; }
};
} // namespace wasm
} // namespace js
#endif // asmjs_wasm_ir_h