Skip to content

Instantly share code, notes, and snippets.

@yurydelendik
Last active May 3, 2016 16:13
Show Gist options
  • Save yurydelendik/6619394d4a47d8448170d429dd74ca99 to your computer and use it in GitHub Desktop.
Save yurydelendik/6619394d4a47d8448170d429dd74ca99 to your computer and use it in GitHub Desktop.
SM WASM with debug information
# HG changeset patch
# User Yury Delendik <[email protected]>
# Date 1462291965 18000
# Tue May 03 11:12:45 2016 -0500
# Node ID 88863f30dd23ebd15831f55b73e0e6df0ec614ae
# Parent 77cead2cd20300623eea2416bc9bce4d5021df09
Parse code annotations
diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -30,16 +30,17 @@ static const uint32_t EncodingVersion
static const char TypeSectionId[] = "type";
static const char ImportSectionId[] = "import";
static const char FunctionSectionId[] = "function";
static const char TableSectionId[] = "table";
static const char MemorySectionId[] = "memory";
static const char ExportSectionId[] = "export";
static const char CodeSectionId[] = "code";
static const char DataSectionId[] = "data";
+static const char DbgTablesId[] = "_experiment_dbg_tables";
enum class ValType
{
I32 = 0x01,
I64 = 0x02,
F32 = 0x03,
F64 = 0x04,
@@ -399,24 +400,26 @@ class Encoder
offset++;
return offset - start + 1;
}
static const size_t ExprLimit = 2 * UINT8_MAX - 1;
public:
explicit Encoder(Bytes& bytes)
- : bytes_(bytes)
+ : bytes_(bytes), data(nullptr)
{
MOZ_ASSERT(empty());
}
size_t currentOffset() const { return bytes_.length(); }
bool empty() const { return currentOffset() == 0; }
+ void *data;
+
// Fixed-size encoding operations simply copy the literal bytes (without
// attempting to align).
MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
return write<uint8_t>(i);
}
MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
return write<uint32_t>(i);
diff --git a/js/src/asmjs/WasmBinaryToText.cpp b/js/src/asmjs/WasmBinaryToText.cpp
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -44,19 +44,20 @@ struct WasmRenderContext
uint32_t indent;
DeclaredSigVector signatures;
Uint32Vector funcSigs;
Uint32Vector funcLocals;
Uint32Vector importSigs;
uint32_t currentFuncIndex;
+ bool saveLocs;
WasmRenderContext(JSContext* cx, Decoder& d, StringBuffer& buffer)
- : cx(cx), d(d), buffer(buffer), indent(0), currentFuncIndex(0)
+ : cx(cx), d(d), buffer(buffer), indent(0), currentFuncIndex(0), saveLocs(true)
{}
};
/*****************************************************************************/
// utilities
static bool
RenderFail(WasmRenderContext& c, const char* str)
@@ -968,18 +969,37 @@ RenderSelect(WasmRenderContext& c)
if (!RenderExpr(c))
return false;
return c.buffer.append(")");
}
static bool
+RenderLoc(WasmRenderContext& c)
+{
+ if (!c.buffer.append("(;!loc 1 1 "))
+ return false;
+
+ if (!RenderInt32(c, (uint32_t)c.d.currentOffset()))
+ return false;
+
+ if (!c.buffer.append(";)"))
+ return false;
+ return true;
+}
+
+static bool
RenderExpr(WasmRenderContext& c)
{
+ if (c.saveLocs) {
+ if (!RenderLoc(c))
+ return RenderFail(c, "unable to save annotation");
+ }
+
Expr expr;
if (!c.d.readExpr(&expr))
return RenderFail(c, "unable to read expression");
switch (expr) {
case Expr::Nop:
return RenderNop(c);
case Expr::Unreachable:
@@ -1626,16 +1646,25 @@ RenderFunctionBody(WasmRenderContext& c,
while (c.d.currentPosition() < bodyEnd) {
if (!RenderFullLine(c))
return false;
}
if (c.d.currentPosition() != bodyEnd)
return RenderFail(c, "function body length mismatch");
+ if (c.saveLocs) {
+ if (!RenderIndent(c))
+ return false;
+ if (!RenderLoc(c))
+ return RenderFail(c, "unable to save annotation");
+ if (!c.buffer.append("\n"))
+ return false;
+ }
+
c.indent--;
return true;
}
static bool
RenderCodeSection(WasmRenderContext& c)
{
@@ -1643,16 +1672,23 @@ RenderCodeSection(WasmRenderContext& c)
if (!c.d.startSection(CodeSectionId, &sectionStart, &sectionSize))
return RenderFail(c, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numFuncSigs = c.funcSigs.length();
+ if (c.saveLocs) {
+ if (!RenderIndent(c))
+ return false;
+ if (!c.buffer.append("(;!file 1 \"file.wasm\";)\n"))
+ return false;
+ }
+
uint32_t numFuncBodies;
if (!c.d.readVarU32(&numFuncBodies))
return RenderFail(c, "expected function body count");
if (numFuncBodies != numFuncSigs)
return RenderFail(c, "function body count does not match function signature count");
for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
uint32_t sigIndex = c.funcSigs[funcIndex];
const DeclaredSig& sig = c.signatures[sigIndex];
diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -185,17 +185,17 @@ class WasmAstSig : public WasmAstBase
return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
}
static bool match(const WasmAstSig* lhs, Lookup rhs) {
return *lhs == rhs;
}
};
class WasmAstNode : public WasmAstBase
-{};
+{ };
enum class WasmAstExprKind
{
BinaryOperator,
Block,
Branch,
BranchTable,
Call,
@@ -210,33 +210,55 @@ enum class WasmAstExprKind
Return,
SetLocal,
Store,
TernaryOperator,
UnaryOperator,
Unreachable
};
+struct WasmExprAnnotation {
+ uint32_t locAnnotationId;
+ uint32_t bookmarkAnnotationId;
+
+ WasmExprAnnotation() : locAnnotationId(0), bookmarkAnnotationId(0) {}
+ WasmExprAnnotation(uint32_t locAnnotationId, uint32_t bookmarkAnnotationId) : locAnnotationId(locAnnotationId), bookmarkAnnotationId(bookmarkAnnotationId) {}
+
+ bool operator==(WasmExprAnnotation rhs) const {
+ if (locAnnotationId != rhs.locAnnotationId)
+ return false;
+ if (bookmarkAnnotationId != rhs.bookmarkAnnotationId)
+ return false;
+ return true;
+ }
+ bool operator!=(WasmExprAnnotation rhs) const {
+ return !(*this == rhs);
+ }
+};
+
class WasmAstExpr : public WasmAstNode
{
const WasmAstExprKind kind_;
protected:
explicit WasmAstExpr(WasmAstExprKind kind)
- : kind_(kind)
+ : kind_(kind),
+ annotation()
{}
public:
WasmAstExprKind kind() const { return kind_; }
template <class T>
T& as() {
MOZ_ASSERT(kind() == T::Kind);
return static_cast<T&>(*this);
}
+
+ WasmExprAnnotation annotation;
};
struct WasmAstNop : WasmAstExpr
{
WasmAstNop()
: WasmAstExpr(WasmAstExprKind::Nop)
{}
};
@@ -499,23 +521,28 @@ class WasmAstFunc : public WasmAstNode
public:
WasmAstFunc(WasmName name, WasmRef sig, WasmAstValTypeVector&& vars,
WasmNameVector&& locals, WasmAstExprVector&& body)
: name_(name),
sig_(sig),
vars_(Move(vars)),
localNames_(Move(locals)),
- body_(Move(body))
+ body_(Move(body)),
+ annotationBegin(),
+ annotationEnd()
{}
WasmRef& sig() { return sig_; }
const WasmAstValTypeVector& vars() const { return vars_; }
const WasmNameVector& locals() const { return localNames_; }
const WasmAstExprVector& body() const { return body_; }
WasmName name() const { return name_; }
+
+ WasmExprAnnotation annotationBegin;
+ WasmExprAnnotation annotationEnd;
};
class WasmAstImport : public WasmAstNode
{
WasmName name_;
WasmName module_;
WasmName func_;
uint32_t sigIndex_;
@@ -589,43 +616,57 @@ class WasmAstMemory : public WasmAstNode
maxSize_(maxSize),
segments_(Move(segments))
{}
uint32_t initialSize() const { return initialSize_; }
const Maybe<uint32_t>& maxSize() const { return maxSize_; }
const WasmAstSegmentVector& segments() const { return segments_; }
};
+struct WasmAstModuleDbgInfo {
+ WasmNameVector dbgTables;
+ WasmNameVector annotations;
+ WasmNameVector files;
+
+ WasmAstModuleDbgInfo(LifoAlloc& lifo)
+ : dbgTables(lifo),
+ annotations(lifo),
+ files(lifo)
+ {}
+};
+
class WasmAstModule : public WasmAstNode
{
typedef WasmAstVector<WasmAstFunc*> FuncVector;
typedef WasmAstVector<WasmAstImport*> ImportVector;
typedef WasmAstVector<WasmAstExport*> ExportVector;
typedef WasmAstVector<WasmAstSig*> SigVector;
typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
LifoAlloc& lifo_;
WasmAstMemory* memory_;
SigVector sigs_;
SigMap sigMap_;
ImportVector imports_;
ExportVector exports_;
WasmAstTable* table_;
FuncVector funcs_;
+ WasmAstModuleDbgInfo dbgInfo_;
public:
explicit WasmAstModule(LifoAlloc& lifo)
: lifo_(lifo),
memory_(nullptr),
sigs_(lifo),
sigMap_(lifo),
imports_(lifo),
exports_(lifo),
table_(nullptr),
- funcs_(lifo)
+ funcs_(lifo),
+ dbgInfo_(lifo)
{}
bool init() {
return sigMap_.init();
}
bool setMemory(WasmAstMemory* memory) {
if (memory_)
return false;
memory_ = memory;
@@ -669,16 +710,19 @@ class WasmAstModule : public WasmAstNode
return imports_.append(imp);
}
bool append(WasmAstExport* exp) {
return exports_.append(exp);
}
const ExportVector& exports() const {
return exports_;
}
+ WasmAstModuleDbgInfo& dbgInfo() {
+ return dbgInfo_;
+ }
bool initTable(WasmAstTable* table) {
if (table_)
return false;
table_ = table;
return true;
}
WasmAstTable* maybeTable() const {
return table_;
@@ -835,29 +879,34 @@ class WasmToken
SetLocal,
Store,
Table,
TernaryOpcode,
Text,
Type,
UnaryOpcode,
Unreachable,
- ValueType
+ ValueType,
+ Annotation
};
private:
Kind kind_;
const char16_t* begin_;
const char16_t* end_;
union {
uint32_t index_;
uint64_t uint_;
int64_t sint_;
FloatLiteralKind floatLiteralKind_;
ValType valueType_;
Expr expr_;
+ struct {
+ const char16_t* begin;
+ const char16_t* end;
+ } annotationType_;
} u;
public:
explicit WasmToken() = default;
WasmToken(Kind kind, const char16_t* begin, const char16_t* end)
: kind_(kind),
begin_(begin),
end_(end)
{
@@ -892,16 +941,27 @@ class WasmToken
const char16_t* begin, const char16_t* end)
: kind_(Float),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
u.floatLiteralKind_ = floatLiteralKind;
}
+ explicit WasmToken(const char16_t* typeBegin, const char16_t* typeEnd,
+ const char16_t* begin, const char16_t* end)
+ : kind_(Annotation),
+ begin_(begin),
+ end_(end)
+ {
+ MOZ_ASSERT(typeBegin != typeEnd);
+ MOZ_ASSERT(begin != end);
+ u.annotationType_.begin = typeBegin;
+ u.annotationType_.end = typeEnd;
+ }
explicit WasmToken(Kind kind, ValType valueType, const char16_t* begin, const char16_t* end)
: kind_(kind),
begin_(begin),
end_(end)
{
MOZ_ASSERT(begin != end);
MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
u.valueType_ = valueType;
@@ -962,16 +1022,20 @@ class WasmToken
return u.valueType_;
}
Expr expr() const {
MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == TernaryOpcode ||
kind_ == ComparisonOpcode || kind_ == ConversionOpcode ||
kind_ == Load || kind_ == Store);
return u.expr_;
}
+ WasmName annotationName() const {
+ MOZ_ASSERT(kind_ == Annotation);
+ return WasmName(u.annotationType_.begin, u.annotationType_.end - u.annotationType_.begin);
+ }
};
} // end anonymous namespace
static bool
IsWasmNewLine(char16_t c)
{
return c == '\n';
@@ -1141,16 +1205,38 @@ ConsumeTextByte(const char16_t** curp, c
}
if (byte)
*byte = u8;
cur++;
return true;
}
+static bool
+ConsumeAnnotation(const char16_t** curp, const char16_t* end)
+{
+ // Skipping similar to multi-line comment.
+ const char16_t* cur = *curp;
+ size_t level = 1;
+ while (cur != end) {
+ char16_t ch = *(cur++);
+ if (ch == '(' && cur != end && *cur == ';') {
+ cur++;
+ level++;
+ } else if (ch == ';' && cur != end && *cur == ')') {
+ cur++;
+ if (--level == 0) {
+ *curp = cur;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
namespace {
class WasmTokenStream
{
static const uint32_t LookaheadSize = 2;
const char16_t* cur_;
const char16_t* const end_;
@@ -1170,16 +1256,17 @@ class WasmTokenStream
return true;
}
WasmToken fail(const char16_t* begin) const {
return WasmToken(begin);
}
WasmToken nan(const char16_t* begin);
WasmToken literal(const char16_t* begin);
+ WasmToken annotation(const char16_t* begin, const char16_t* end);
WasmToken next();
void skipSpaces();
public:
WasmTokenStream(const char16_t* text, UniqueChars* error)
: cur_(text),
end_(text + js_strlen(text)),
lineStart_(text),
@@ -1350,26 +1437,47 @@ WasmTokenStream::literal(const char16_t*
CheckedInt<uint32_t> index = u.value();
if (index.isValid())
return WasmToken(index.value(), begin, cur_);
return WasmToken(u.value(), begin, cur_);
}
+WasmToken
+WasmTokenStream::annotation(const char16_t* begin, const char16_t* end)
+{
+ MOZ_ASSERT(begin[0] == '(' && begin[1] == ';' && begin[2] == '!');
+ begin += 3;
+ end -= 2;
+ MOZ_ASSERT(end[0] == ';' && end[1] == ')');
+ const char16_t* cur = begin;
+ const char16_t* typeBegin = cur;
+ while (cur != end && !IsWasmSpace(*cur))
+ cur++;
+ const char16_t* typeEnd = cur;
+ while (cur != end && IsWasmSpace(*cur))
+ cur++;
+ return WasmToken(typeBegin, typeEnd, cur, end);
+}
+
void
WasmTokenStream::skipSpaces()
{
while (cur_ != end_) {
char16_t ch = *cur_;
if (ch == ';' && consume(MOZ_UTF16(";;"))) {
// Skipping single line comment.
while (cur_ != end_ && !IsWasmNewLine(*cur_))
cur_++;
} else if (ch == '(' && consume(MOZ_UTF16("(;"))) {
+ if (*cur_ == '!') {
+ cur_ -= 2; // set position before "(;"
+ return; // and return
+ }
// Skipping multi-line and possibly nested comments.
size_t level = 1;
while (cur_ != end_) {
char16_t ch = *cur_;
if (ch == '(' && consume(MOZ_UTF16("(;"))) {
level++;
} else if (ch == ';' && consume(MOZ_UTF16(";)"))) {
if (--level == 0)
@@ -1418,16 +1526,21 @@ WasmTokenStream::next()
case '$':
cur_++;
while (cur_ != end_ && IsNameAfterDollar(*cur_))
cur_++;
return WasmToken(WasmToken::Name, begin, cur_);
case '(':
+ if (consume(MOZ_UTF16("(;!"))) {
+ if (!ConsumeAnnotation(&cur_, end_))
+ fail(begin);
+ return annotation(begin, cur_);
+ }
cur_++;
return WasmToken(WasmToken::OpenParen, begin, cur_);
case ')':
cur_++;
return WasmToken(WasmToken::CloseParen, begin, cur_);
case '=':
@@ -2050,53 +2163,97 @@ WasmTokenStream::next()
return fail(begin);
}
/*****************************************************************************/
// wasm text format parser
namespace {
+struct WasmAnnotationState {
+ WasmExprAnnotation currentAnnotation;
+ WasmNameVector dbgTables;
+ WasmNameVector annotations;
+ WasmNameVector files;
+
+ WasmAnnotationState(LifoAlloc& lifo)
+ : currentAnnotation(),
+ dbgTables(lifo),
+ annotations(lifo),
+ files(lifo)
+ {}
+};
+
struct WasmParseContext
{
WasmTokenStream ts;
LifoAlloc& lifo;
UniqueChars* error;
DtoaState* dtoaState;
+ WasmAnnotationState annotationState;
WasmParseContext(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
: ts(text, error),
lifo(lifo),
error(error),
- dtoaState(NewDtoaState())
+ dtoaState(NewDtoaState()),
+ annotationState(lifo)
{}
bool fail(const char* message) {
error->reset(JS_smprintf(message));
return false;
}
~WasmParseContext() {
DestroyDtoaState(dtoaState);
}
};
} // end anonymous namespace
static WasmAstExpr*
ParseExprInsideParens(WasmParseContext& c);
+static void
+ParseAnnotations(WasmParseContext& c)
+{
+ WasmToken token;
+ while (c.ts.getIf(WasmToken::Annotation, &token)) {
+ const WasmName &name = token.annotationName();
+ bool result;
+ if (name == WasmName(MOZ_UTF16("dbg_section"), 11)) {
+ result = c.annotationState.dbgTables.append(token.name());
+ } else if (name == WasmName(MOZ_UTF16("loc"), 3)) {
+ result = c.annotationState.annotations.append(token.name());
+ c.annotationState.currentAnnotation.locAnnotationId =
+ c.annotationState.annotations.length();
+ } else if (name == WasmName(MOZ_UTF16("bookmark"), 8)) {
+ result = c.annotationState.annotations.append(token.name());
+ c.annotationState.currentAnnotation.bookmarkAnnotationId =
+ c.annotationState.annotations.length();
+ } else if (name == WasmName(MOZ_UTF16("file"), 4)) {
+ result = c.annotationState.files.append(token.name());
+ }
+ }
+}
+
static WasmAstExpr*
ParseExpr(WasmParseContext& c)
{
+ ParseAnnotations(c);
+
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr)
return nullptr;
+ expr->annotation = annotation;
+ c.annotationState.currentAnnotation = annotation;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return expr;
}
static WasmAstBlock*
@@ -2105,71 +2262,91 @@ ParseBlock(WasmParseContext& c, Expr exp
WasmAstExprVector exprs(c.lifo);
WasmName breakName = c.ts.getIfName();
WasmName continueName;
if (expr == Expr::Loop)
continueName = c.ts.getIfName();
+ ParseAnnotations(c);
while (c.ts.getIf(WasmToken::OpenParen)) {
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr || !exprs.append(expr))
return nullptr;
+ expr->annotation = annotation;
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
}
return new(c.lifo) WasmAstBlock(expr, breakName, continueName, Move(exprs));
}
static WasmAstBranch*
ParseBranch(WasmParseContext& c, Expr expr)
{
MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
WasmRef target;
if (!c.ts.matchRef(&target, c.error))
return nullptr;
+ ParseAnnotations(c);
+
WasmAstExpr* value = nullptr;
if (c.ts.getIf(WasmToken::OpenParen)) {
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
value = ParseExprInsideParens(c);
if (!value)
return nullptr;
+ value->annotation = annotation;
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
}
WasmAstExpr* cond = nullptr;
if (expr == Expr::BrIf) {
if (c.ts.getIf(WasmToken::OpenParen)) {
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
cond = ParseExprInsideParens(c);
if (!cond)
return nullptr;
+ cond->annotation = annotation;
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
} else {
cond = value;
value = nullptr;
}
}
return new(c.lifo) WasmAstBranch(expr, cond, target, value);
}
static bool
ParseArgs(WasmParseContext& c, WasmAstExprVector* args)
{
+ ParseAnnotations(c);
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstExpr* arg = ParseExprInsideParens(c);
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
if (!arg || !args->append(arg))
return false;
+ arg->annotation = annotation;
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
+ ParseAnnotations(c);
}
return true;
}
static WasmAstCall*
ParseCall(WasmParseContext& c, Expr expr)
{
@@ -2636,23 +2813,29 @@ ParseIf(WasmParseContext& c)
WasmAstExpr* cond = ParseExpr(c);
if (!cond)
return nullptr;
WasmAstExpr* thenBranch = ParseExpr(c);
if (!thenBranch)
return nullptr;
+ ParseAnnotations(c);
+
WasmAstExpr* elseBranch = nullptr;
if (c.ts.getIf(WasmToken::OpenParen)) {
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
elseBranch = ParseExprInsideParens(c);
if (!elseBranch)
return nullptr;
+ elseBranch->annotation = annotation;
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
}
return new(c.lifo) WasmAstIf(cond, thenBranch, elseBranch);
}
static bool
ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, uint32_t* alignLog2, WasmAstExpr** base)
{
@@ -2932,23 +3115,28 @@ ParseFunc(WasmParseContext& c, WasmAstMo
WasmAstValTypeVector vars(c.lifo);
WasmAstValTypeVector args(c.lifo);
WasmNameVector locals(c.lifo);
WasmName funcName = c.ts.getIfName();
WasmRef sig;
+ c.annotationState.currentAnnotation = WasmExprAnnotation();
+ ParseAnnotations(c);
+ WasmExprAnnotation annotationBegin = c.annotationState.currentAnnotation;
+
WasmToken openParen;
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
if (c.ts.getIf(WasmToken::Type)) {
if (!c.ts.matchRef(&sig, c.error))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
} else {
c.ts.unget(openParen);
}
}
WasmAstExprVector body(c.lifo);
ExprType result = ExprType::Void;
@@ -2968,33 +3156,42 @@ ParseFunc(WasmParseContext& c, WasmAstMo
return nullptr;
break;
case WasmToken::Result:
if (!ParseResult(c, &result))
return nullptr;
break;
default:
c.ts.unget(token);
+ WasmExprAnnotation annotation = c.annotationState.currentAnnotation;
WasmAstExpr* expr = ParseExprInsideParens(c);
if (!expr || !body.append(expr))
return nullptr;
+ expr->annotation = annotation;
+ c.annotationState.currentAnnotation = annotation;
break;
}
+ ParseAnnotations(c);
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
}
if (sig.isInvalid()) {
uint32_t sigIndex;
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
sig.setIndex(sigIndex);
}
- return new(c.lifo) WasmAstFunc(funcName, sig, Move(vars), Move(locals), Move(body));
+ WasmAstFunc* f =
+ new(c.lifo) WasmAstFunc(funcName, sig, Move(vars), Move(locals), Move(body));
+ f->annotationBegin = annotationBegin;
+ f->annotationEnd = c.annotationState.currentAnnotation;
+ return f;
}
static bool
ParseFuncType(WasmParseContext& c, WasmAstSig* sig)
{
WasmAstValTypeVector args(c.lifo);
ExprType result = ExprType::Void;
@@ -3157,16 +3354,18 @@ ParseModule(const char16_t* text, LifoAl
return nullptr;
if (!c.ts.match(WasmToken::Module, c.error))
return nullptr;
auto module = new(c.lifo) WasmAstModule(c.lifo);
if (!module || !module->init())
return nullptr;
+ ParseAnnotations(c);
+
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken section = c.ts.get();
switch (section.kind()) {
case WasmToken::Type: {
WasmAstSig* sig = ParseTypeDef(c);
if (!sig || !module->append(sig))
return nullptr;
@@ -3212,23 +3411,29 @@ ParseModule(const char16_t* text, LifoAl
}
default:
c.ts.generateError(section, c.error);
return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
+ ParseAnnotations(c);
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::EndOfFile, c.error))
return nullptr;
+ WasmAstModuleDbgInfo& dbgInfo = module->dbgInfo();
+ dbgInfo.dbgTables.swap(c.annotationState.dbgTables);
+ dbgInfo.annotations.swap(c.annotationState.annotations);
+ dbgInfo.files.swap(c.annotationState.files);
+
return module;
}
/*****************************************************************************/
// wasm name resolution
namespace {
@@ -3662,268 +3867,401 @@ ResolveModule(LifoAlloc& lifo, WasmAstMo
}
return true;
}
/*****************************************************************************/
// wasm function body serialization
+typedef Vector<char16_t, 0, SystemAllocPolicy> WasmCharsVector;
+
+class CodeAnnotationWriter
+{
+ WasmCharsVector chars;
+ const WasmNameVector& annotations_;
+ const WasmNameVector& files_;
+ WasmExprAnnotation currentAnnotation_;
+ uint32_t baseOffset_;
+
+public:
+ CodeAnnotationWriter(const WasmNameVector& annotations, const WasmNameVector& files)
+ : chars(),
+ annotations_(annotations),
+ files_(files),
+ currentAnnotation_(),
+ baseOffset_(0)
+ { }
+
+ bool initialize()
+ {
+ if (!writeName(WasmName(MOZ_UTF16(".code_annotations\n"), 18)))
+ return false;
+ size_t numFiles = files_.length();
+ for (size_t i = 0; i < numFiles; i++) {
+ bool result = writeName(WasmName(MOZ_UTF16(" .file "), 10)) &&
+ writeName(files_[i]) &&
+ writeName(WasmName(MOZ_UTF16("\n"), 1));
+ if (!result)
+ return false;
+ }
+ return true;
+ }
+
+ bool writeFunctionBegin(const WasmExprAnnotation& annotationBegin, uint32_t offset)
+ {
+ currentAnnotation_ = WasmExprAnnotation();
+ baseOffset_ = offset;
+ writeOffset(annotationBegin, offset);
+ return writeName(WasmName(MOZ_UTF16(" .function"), 13));
+ }
+
+ bool writeFunctionEnd(const WasmExprAnnotation& annotationEnd, uint32_t offset)
+ {
+ writeOffset(annotationEnd, offset);
+ return writeName(WasmName(MOZ_UTF16("\n"), 1));
+ }
+
+ bool writeOffset(const WasmExprAnnotation &annotation, uint32_t offset)
+ {
+ if (annotation == currentAnnotation_)
+ return true; // no need
+
+ if (!writeAnnotation(annotation))
+ return false;
+
+ MOZ_ASSERT(baseOffset_ <= offset);
+ uint32_t n = offset - baseOffset_;
+ return chars.append(MOZ_UTF16(' ')) && writeNumber(n);
+ }
+
+ bool writeNumber(uint32_t value)
+ {
+ uint32_t tmp = value;
+ do {
+ if (!chars.append(MOZ_UTF16('0')))
+ return false;
+ tmp /= 10;
+ } while (tmp);
+
+ char16_t *p = chars.end();
+ tmp = value;
+ while (tmp) {
+ *(--p) = MOZ_UTF16('0') + (tmp % 10);
+ tmp /= 10;
+ }
+ return true;
+ }
+
+ bool writeAnnotation(const WasmExprAnnotation &annotation)
+ {
+ if (annotation.locAnnotationId != currentAnnotation_.locAnnotationId &&
+ (annotation.locAnnotationId == 0 || currentAnnotation_.locAnnotationId == 0 ||
+ annotations_[annotation.locAnnotationId - 1] != annotations_[currentAnnotation_.locAnnotationId - 1])) {
+ bool result = chars.append(MOZ_UTF16(" loc("), 5) &&
+ (annotation.locAnnotationId > 0 ? writeName(annotations_[annotation.locAnnotationId - 1]) : true) &&
+ chars.append(MOZ_UTF16(')'));
+ if (!result)
+ return false;
+ }
+ if (annotation.bookmarkAnnotationId != currentAnnotation_.bookmarkAnnotationId &&
+ (annotation.bookmarkAnnotationId == 0 || currentAnnotation_.bookmarkAnnotationId == 0 ||
+ annotations_[annotation.bookmarkAnnotationId - 1] != annotations_[currentAnnotation_.bookmarkAnnotationId - 1])) {
+ bool result = chars.append(MOZ_UTF16(" b("), 3) &&
+ (annotation.bookmarkAnnotationId > 0 ? writeName(annotations_[annotation.bookmarkAnnotationId - 1]) : true) &&
+ chars.append(MOZ_UTF16(')'));
+ if (!result)
+ return false;
+ }
+ currentAnnotation_ = annotation;
+ return true;
+ }
+
+ bool writeName(const WasmName &wasmName)
+ {
+ return chars.append(wasmName.begin(), wasmName.end());
+ }
+
+ WasmName finalize()
+ {
+ return WasmName(chars.begin(), chars.length());
+ }
+};
+
+
+class EncoderWithAnnotation : public Encoder
+{
+ public:
+ CodeAnnotationWriter& cw;
+
+ EncoderWithAnnotation(Bytes& bytes, CodeAnnotationWriter& cw)
+ : Encoder(bytes), cw(cw)
+ { }
+
+ MOZ_MUST_USE bool writeExprAndAnnotation(Expr expr, const WasmExprAnnotation& annotation)
+ {
+ if (!cw.writeOffset(annotation, currentOffset()))
+ return false;
+ return Encoder::writeExpr(expr);
+ }
+};
+
+
static bool
-EncodeExpr(Encoder& e, WasmAstExpr& expr);
+EncodeExpr(EncoderWithAnnotation& e, WasmAstExpr& expr);
static bool
-EncodeBlock(Encoder& e, WasmAstBlock& b)
+EncodeBlock(EncoderWithAnnotation& e, WasmAstBlock& b)
{
- if (!e.writeExpr(b.expr()))
+ if (!e.writeExprAndAnnotation(b.expr(), b.annotation))
return false;
size_t numExprs = b.exprs().length();
for (size_t i = 0; i < numExprs; i++) {
if (!EncodeExpr(e, *b.exprs()[i]))
return false;
}
if (!e.writeExpr(Expr::End))
return false;
return true;
}
static bool
-EncodeBranch(Encoder& e, WasmAstBranch& br)
+EncodeBranch(EncoderWithAnnotation& e, WasmAstBranch& br)
{
MOZ_ASSERT(br.expr() == Expr::Br || br.expr() == Expr::BrIf);
uint32_t arity = 0;
if (br.maybeValue()) {
arity = 1;
if (!EncodeExpr(e, *br.maybeValue()))
return false;
}
if (br.expr() == Expr::BrIf) {
if (!EncodeExpr(e, br.cond()))
return false;
}
- if (!e.writeExpr(br.expr()))
+ if (!e.writeExprAndAnnotation(br.expr(), br.annotation))
return false;
if (!e.writeVarU32(arity))
return false;
if (!e.writeVarU32(br.target().index()))
return false;
return true;
}
static bool
-EncodeArgs(Encoder& e, const WasmAstExprVector& args)
+EncodeArgs(EncoderWithAnnotation& e, const WasmAstExprVector& args)
{
for (WasmAstExpr* arg : args) {
if (!EncodeExpr(e, *arg))
return false;
}
return true;
}
static bool
-EncodeCall(Encoder& e, WasmAstCall& c)
+EncodeCall(EncoderWithAnnotation& e, WasmAstCall& c)
{
if (!EncodeArgs(e, c.args()))
return false;
- if (!e.writeExpr(c.expr()))
+ if (!e.writeExprAndAnnotation(c.expr(), c.annotation))
return false;
if (!e.writeVarU32(c.args().length()))
return false;
if (!e.writeVarU32(c.func().index()))
return false;
return true;
}
static bool
-EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c)
+EncodeCallIndirect(EncoderWithAnnotation& e, WasmAstCallIndirect& c)
{
if (!EncodeExpr(e, *c.index()))
return false;
if (!EncodeArgs(e, c.args()))
return false;
- if (!e.writeExpr(Expr::CallIndirect))
+ if (!e.writeExprAndAnnotation(Expr::CallIndirect, c.annotation))
return false;
if (!e.writeVarU32(c.args().length()))
return false;
if (!e.writeVarU32(c.sig().index()))
return false;
return true;
}
static bool
-EncodeConst(Encoder& e, WasmAstConst& c)
+EncodeConst(EncoderWithAnnotation& e, WasmAstConst& c)
{
switch (c.val().type()) {
case ValType::I32:
- return e.writeExpr(Expr::I32Const) &&
+ return e.writeExprAndAnnotation(Expr::I32Const, c.annotation) &&
e.writeVarS32(c.val().i32());
case ValType::I64:
- return e.writeExpr(Expr::I64Const) &&
+ return e.writeExprAndAnnotation(Expr::I64Const, c.annotation) &&
e.writeVarS64(c.val().i64());
case ValType::F32:
- return e.writeExpr(Expr::F32Const) &&
+ return e.writeExprAndAnnotation(Expr::F32Const, c.annotation) &&
e.writeFixedF32(c.val().f32());
case ValType::F64:
- return e.writeExpr(Expr::F64Const) &&
+ return e.writeExprAndAnnotation(Expr::F64Const, c.annotation) &&
e.writeFixedF64(c.val().f64());
default:
break;
}
MOZ_CRASH("Bad value type");
}
static bool
-EncodeGetLocal(Encoder& e, WasmAstGetLocal& gl)
+EncodeGetLocal(EncoderWithAnnotation& e, WasmAstGetLocal& gl)
{
- return e.writeExpr(Expr::GetLocal) &&
+ return e.writeExprAndAnnotation(Expr::GetLocal, gl.annotation) &&
e.writeVarU32(gl.local().index());
}
static bool
-EncodeSetLocal(Encoder& e, WasmAstSetLocal& sl)
+EncodeSetLocal(EncoderWithAnnotation& e, WasmAstSetLocal& sl)
{
return EncodeExpr(e, sl.value()) &&
- e.writeExpr(Expr::SetLocal) &&
+ e.writeExprAndAnnotation(Expr::SetLocal, sl.annotation) &&
e.writeVarU32(sl.local().index());
}
static bool
-EncodeUnaryOperator(Encoder& e, WasmAstUnaryOperator& b)
+EncodeUnaryOperator(EncoderWithAnnotation& e, WasmAstUnaryOperator& b)
{
return EncodeExpr(e, *b.op()) &&
- e.writeExpr(b.expr());
+ e.writeExprAndAnnotation(b.expr(), b.annotation);
}
static bool
-EncodeBinaryOperator(Encoder& e, WasmAstBinaryOperator& b)
+EncodeBinaryOperator(EncoderWithAnnotation& e, WasmAstBinaryOperator& b)
{
return EncodeExpr(e, *b.lhs()) &&
EncodeExpr(e, *b.rhs()) &&
- e.writeExpr(b.expr());
+ e.writeExprAndAnnotation(b.expr(), b.annotation);
}
static bool
-EncodeTernaryOperator(Encoder& e, WasmAstTernaryOperator& b)
+EncodeTernaryOperator(EncoderWithAnnotation& e, WasmAstTernaryOperator& b)
{
return EncodeExpr(e, *b.op0()) &&
EncodeExpr(e, *b.op1()) &&
EncodeExpr(e, *b.op2()) &&
- e.writeExpr(b.expr());
+ e.writeExprAndAnnotation(b.expr(), b.annotation);
}
static bool
-EncodeComparisonOperator(Encoder& e, WasmAstComparisonOperator& b)
+EncodeComparisonOperator(EncoderWithAnnotation& e, WasmAstComparisonOperator& b)
{
return EncodeExpr(e, *b.lhs()) &&
EncodeExpr(e, *b.rhs()) &&
- e.writeExpr(b.expr());
+ e.writeExprAndAnnotation(b.expr(), b.annotation);
}
static bool
-EncodeConversionOperator(Encoder& e, WasmAstConversionOperator& b)
+EncodeConversionOperator(EncoderWithAnnotation& e, WasmAstConversionOperator& b)
{
return EncodeExpr(e, *b.op()) &&
- e.writeExpr(b.expr());
+ e.writeExprAndAnnotation(b.expr(), b.annotation);
}
static bool
-EmitIf(Encoder& e, WasmAstIf& i)
+EmitIf(EncoderWithAnnotation& e, WasmAstIf& i)
{
return EncodeExpr(e, i.cond()) &&
- e.writeExpr(Expr::If) &&
+ e.writeExprAndAnnotation(Expr::If, i.annotation) &&
EncodeExpr(e, i.thenBranch()) &&
(!i.hasElse() ||
(e.writeExpr(Expr::Else) &&
EncodeExpr(e, i.elseBranch()))) &&
e.writeExpr(Expr::End);
}
static bool
-EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address)
+EncodeLoadStoreAddress(EncoderWithAnnotation &e, const WasmAstLoadStoreAddress &address)
{
return EncodeExpr(e, address.base());
}
static bool
-EncodeLoadStoreFlags(Encoder &e, const WasmAstLoadStoreAddress &address)
+EncodeLoadStoreFlags(EncoderWithAnnotation &e, const WasmAstLoadStoreAddress &address)
{
return e.writeVarU32(address.flags()) &&
e.writeVarU32(address.offset());
}
static bool
-EncodeLoad(Encoder& e, WasmAstLoad& l)
+EncodeLoad(EncoderWithAnnotation& e, WasmAstLoad& l)
{
return EncodeLoadStoreAddress(e, l.address()) &&
- e.writeExpr(l.expr()) &&
+ e.writeExprAndAnnotation(l.expr(), l.annotation) &&
EncodeLoadStoreFlags(e, l.address());
}
static bool
-EncodeStore(Encoder& e, WasmAstStore& s)
+EncodeStore(EncoderWithAnnotation& e, WasmAstStore& s)
{
return EncodeLoadStoreAddress(e, s.address()) &&
EncodeExpr(e, s.value()) &&
- e.writeExpr(s.expr()) &&
+ e.writeExprAndAnnotation(s.expr(), s.annotation) &&
EncodeLoadStoreFlags(e, s.address());
}
static bool
-EncodeReturn(Encoder& e, WasmAstReturn& r)
+EncodeReturn(EncoderWithAnnotation& e, WasmAstReturn& r)
{
uint32_t arity = 0;
if (r.maybeExpr()) {
arity = 1;
if (!EncodeExpr(e, *r.maybeExpr()))
return false;
}
- if (!e.writeExpr(Expr::Return))
+ if (!e.writeExprAndAnnotation(Expr::Return, r.annotation))
return false;
if (!e.writeVarU32(arity))
return false;
return true;
}
static bool
-EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt)
+EncodeBranchTable(EncoderWithAnnotation& e, WasmAstBranchTable& bt)
{
uint32_t arity = 0;
if (bt.maybeValue()) {
arity = 1;
if (!EncodeExpr(e, *bt.maybeValue()))
return false;
}
if (!EncodeExpr(e, bt.index()))
return false;
- if (!e.writeExpr(Expr::BrTable))
+ if (!e.writeExprAndAnnotation(Expr::BrTable, bt.annotation))
return false;
if (!e.writeVarU32(arity))
return false;
if (!e.writeVarU32(bt.table().length()))
return false;
@@ -3934,23 +4272,23 @@ EncodeBranchTable(Encoder& e, WasmAstBra
if (!e.writeFixedU32(bt.def().index()))
return false;
return true;
}
static bool
-EncodeExpr(Encoder& e, WasmAstExpr& expr)
+EncodeExpr(EncoderWithAnnotation& e, WasmAstExpr& expr)
{
switch (expr.kind()) {
case WasmAstExprKind::Nop:
- return e.writeExpr(Expr::Nop);
+ return e.writeExprAndAnnotation(Expr::Nop, expr.annotation);
case WasmAstExprKind::Unreachable:
- return e.writeExpr(Expr::Unreachable);
+ return e.writeExprAndAnnotation(Expr::Unreachable, expr.annotation);
case WasmAstExprKind::BinaryOperator:
return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
case WasmAstExprKind::Block:
return EncodeBlock(e, expr.as<WasmAstBlock>());
case WasmAstExprKind::Branch:
return EncodeBranch(e, expr.as<WasmAstBranch>());
case WasmAstExprKind::Call:
return EncodeCall(e, expr.as<WasmAstCall>());
@@ -4190,41 +4528,47 @@ EncodeTableSection(Encoder& e, WasmAstMo
return false;
}
e.finishSection(offset);
return true;
}
static bool
-EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
+EncodeFunctionBody(EncoderWithAnnotation& e, WasmAstFunc& func)
{
size_t bodySizeAt;
if (!e.writePatchableVarU32(&bodySizeAt))
return false;
size_t beforeBody = e.currentOffset();
+ if (!e.cw.writeFunctionBegin(func.annotationBegin, e.currentOffset()))
+ return false;
+
ValTypeVector varTypes;
if (!varTypes.appendAll(func.vars()))
return false;
if (!EncodeLocalEntries(e, varTypes))
return false;
for (WasmAstExpr* expr : func.body()) {
if (!EncodeExpr(e, *expr))
return false;
}
+ if (!e.cw.writeFunctionEnd(func.annotationEnd, e.currentOffset()))
+ return false;
+
e.patchVarU32(bodySizeAt, e.currentOffset() - beforeBody);
return true;
}
static bool
-EncodeCodeSection(Encoder& e, WasmAstModule& module)
+EncodeCodeSection(EncoderWithAnnotation& e, WasmAstModule& module)
{
if (module.funcs().empty())
return true;
size_t offset;
if (!e.startSection(CodeSectionId, &offset))
return false;
@@ -4286,19 +4630,49 @@ EncodeDataSection(Encoder& e, WasmAstMod
return false;
}
e.finishSection(offset);
return true;
}
static bool
+EncodeDbgTables(Encoder& e, WasmAstModule& module, CodeAnnotationWriter &cw)
+{
+ if (module.dbgInfo().dbgTables.empty() && module.dbgInfo().files.empty())
+ return true;
+
+ size_t offset;
+ if (!e.startSection(DbgTablesId, &offset))
+ return false;
+
+ if (!e.writeVarU32(module.dbgInfo().dbgTables.length() + 1))
+ return false;
+
+ WasmName annotationsTable = cw.finalize();
+ if (!EncodeBytes(e, annotationsTable))
+ return false;
+
+ for (WasmName dbgTable : module.dbgInfo().dbgTables) {
+ if (!EncodeBytes(e, dbgTable))
+ return false;
+ }
+
+ e.finishSection(offset);
+ return true;
+}
+
+static bool
EncodeModule(WasmAstModule& module, Bytes* bytes)
{
- Encoder e(*bytes);
+ CodeAnnotationWriter cw(module.dbgInfo().annotations, module.dbgInfo().files);
+ if (!cw.initialize())
+ return false;
+
+ EncoderWithAnnotation e(*bytes, cw);
if (!e.writeFixedU32(MagicNumber))
return false;
if (!e.writeFixedU32(EncodingVersion))
return false;
if (!EncodeTypeSection(e, module))
@@ -4320,16 +4694,19 @@ EncodeModule(WasmAstModule& module, Byte
return false;
if (!EncodeCodeSection(e, module))
return false;
if (!EncodeDataSection(e, module))
return false;
+ if (!EncodeDbgTables(e, module, cw))
+ return false;
+
return true;
}
/*****************************************************************************/
bool
wasm::TextToBinary(const char16_t* text, Bytes* bytes, UniqueChars* error)
{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment