Bug 854602 - improve asm.js validation errors (r=terrence)
authorLuke Wagner <luke@mozilla.com>
Mon, 06 May 2013 18:27:51 -0700
changeset 131032 b7c57f0f04272bc9a64546b18b85b498cb8f056f
parent 131031 4ca9a6bd8f64d8d8892362ef2e1cebde209907eb
child 131033 65463f2f7ba9f4a1bc45631ea875081a7547593d
push id27653
push userlwagner@mozilla.com
push dateTue, 07 May 2013 01:32:02 +0000
treeherdermozilla-inbound@b7c57f0f0427 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs854602
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 854602 - improve asm.js validation errors (r=terrence)
js/src/ion/AsmJS.cpp
js/src/jit-test/lib/asm.js
js/src/js.msg
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -404,16 +404,31 @@ class Type
           case Intish:
             return MIRType_Int32;
           case Void:
             return MIRType_None;
         }
         JS_NOT_REACHED("Invalid Type");
         return MIRType_None;
     }
+
+    const char *toChars() const {
+        switch (which_) {
+          case Double:    return "double";
+          case Doublish:  return "doublish";
+          case Fixnum:    return "fixnum";
+          case Int:       return "int";
+          case Signed:    return "signed";
+          case Unsigned:  return "unsigned";
+          case Intish:    return "intish";
+          case Void:      return "void";
+        }
+        JS_NOT_REACHED("Invalid Type");
+        return "";
+    }
 };
 
 // Represents the subset of Type that can be used as the return type of a
 // function.
 class RetType
 {
   public:
     enum Which {
@@ -1069,17 +1084,17 @@ class ModuleCompiler
     FuncVector                     functions_;
     FuncPtrTableVector             funcPtrTables_;
     ExitMap                        exits_;
     MathNameMap                    standardLibraryMathNames_;
     GlobalAccessVector             globalAccesses_;
     Label                          stackOverflowLabel_;
     Label                          operationCallbackLabel_;
 
-    const char *                   errorString_;
+    char *                         errorString_;
     ParseNode *                    errorNode_;
     TokenStream &                  tokenStream_;
 
     DebugOnly<int>                 currentPass_;
 
     bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) {
         JSAtom *atom = Atomize(cx_, name, strlen(name));
         if (!atom)
@@ -1100,19 +1115,22 @@ class ModuleCompiler
         globalAccesses_(cx),
         errorString_(NULL),
         errorNode_(NULL),
         tokenStream_(ts),
         currentPass_(1)
     {}
 
     ~ModuleCompiler() {
-        if (errorString_)
-            tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin, JSMSG_USE_ASM_TYPE_FAIL,
+        if (errorString_) {
+            tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin,
+                                          JSMSG_USE_ASM_TYPE_FAIL,
                                           errorString_);
+            JS_smprintf_free(errorString_);
+        }
 
         // Avoid spurious Label assertions on compilation failure.
         if (!stackOverflowLabel_.bound())
             stackOverflowLabel_.bind(0);
         if (!operationCallbackLabel_.bound())
             operationCallbackLabel_.bind(0);
     }
 
@@ -1145,23 +1163,48 @@ class ModuleCompiler
 
         module_ = cx_->new_<AsmJSModule>(cx_);
         if (!module_)
             return false;
 
         return true;
     }
 
-    bool fail(const char *str, ParseNode *pn) {
+    bool fail(ParseNode *pn, const char *str) {
         JS_ASSERT(!errorString_);
         JS_ASSERT(!errorNode_);
         JS_ASSERT(str);
         JS_ASSERT(pn);
-        errorString_ = str;
+        errorNode_ = pn;
+        errorString_ = strdup(str);
+        return false;
+    }
+
+    bool failfVA(ParseNode *pn, const char *fmt, va_list ap) {
+        JS_ASSERT(!errorString_);
+        JS_ASSERT(!errorNode_);
+        JS_ASSERT(fmt);
+        JS_ASSERT(pn);
         errorNode_ = pn;
+        errorString_ = JS_vsmprintf(fmt, ap);
+        return false;
+    }
+
+    bool failf(ParseNode *pn, const char *fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        failfVA(pn, fmt, ap);
+        va_end(ap);
+        return false;
+    }
+
+    bool failName(ParseNode *pn, const char *fmt, PropertyName *name) {
+        JSAutoByteString bytes(cx_, name);
+        if (bytes.ptr())
+            failf(pn, fmt, bytes.ptr());
         return false;
     }
 
     /*************************************************** Read-only interface */
 
     JSContext *cx() const { return cx_; }
     MacroAssembler &masm() { return masm_; }
     Label &stackOverflowLabel() { return stackOverflowLabel_; }
@@ -1547,22 +1590,35 @@ class FunctionCompiler
                 curBlock_->add(ins);
                 curBlock_->initSlot(info().localSlot(local.slot), ins);
             }
         }
 
         return true;
     }
 
-    bool fail(const char *str, ParseNode *pn)
+    bool fail(ParseNode *pn, const char *str)
+    {
+        return m_.fail(pn, str);
+    }
+
+    bool failf(ParseNode *pn, const char *fmt, ...)
     {
-        m_.fail(str, pn);
+        va_list ap;
+        va_start(ap, fmt);
+        m_.failfVA(pn, fmt, ap);
+        va_end(ap);
         return false;
     }
 
+    bool failName(ParseNode *pn, const char *fmt, PropertyName *name)
+    {
+        return m_.failName(pn, fmt, name);
+    }
+
     ~FunctionCompiler()
     {
         if (!m().hasError() && !cx()->isExceptionPending()) {
             JS_ASSERT(loopStack_.empty());
             JS_ASSERT(unlabeledBreaks_.empty());
             JS_ASSERT(unlabeledContinues_.empty());
             JS_ASSERT(labeledBreaks_.empty());
             JS_ASSERT(labeledContinues_.empty());
@@ -2377,58 +2433,58 @@ NewAsmJSModuleObject(JSContext *cx, Scop
 
 /*****************************************************************************/
 // asm.js type-checking and code-generation algorithm
 
 static bool
 CheckIdentifier(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode)
 {
     if (name == m.cx()->names().arguments || name == m.cx()->names().eval)
-        return m.fail("disallowed asm.js parameter name", nameNode);
+        return m.failName(nameNode, "'%s' is not an allowed identifier", name);
     return true;
 }
 
 static bool
 CheckModuleLevelName(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode)
 {
     if (!CheckIdentifier(m, name, nameNode))
         return false;
 
     if (name == m.moduleFunctionName() ||
         name == m.module().globalArgumentName() ||
         name == m.module().importArgumentName() ||
         name == m.module().bufferArgumentName() ||
         m.lookupGlobal(name))
     {
-        return m.fail("Duplicate names not allowed", nameNode);
+        return m.failName(nameNode, "duplicate name '%s' not allowed", name);
     }
 
     return true;
 }
 
 static bool
 CheckFunctionHead(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter)
 {
     if (FunctionObject(fn)->hasRest())
-        return m.fail("rest args not allowed in asm.js", fn);
+        return m.fail(fn, "rest args not allowed");
     if (!FunctionHasStatementList(fn))
-        return m.fail("expression closures not allowed in asm.js", fn);
+        return m.fail(fn, "expression closures not allowed");
 
     *stmtIter = ListHead(FunctionStatementList(fn));
     return true;
 }
 
 static bool
 CheckArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name)
 {
     if (!IsDefinition(arg))
-        return m.fail("overlapping argument names not allowed", arg);
+        return m.failName(arg, "duplicate argument name '%s' not allowed", arg->name());
 
     if (MaybeDefinitionInitializer(arg))
-        return m.fail("default arguments not allowed", arg);
+        return m.fail(arg, "default arguments not allowed");
 
     if (!CheckIdentifier(m, arg->name(), arg))
         return false;
 
     *name = arg->name();
     return true;
 }
 
@@ -2448,17 +2504,17 @@ static bool
 CheckModuleArguments(ModuleCompiler &m, ParseNode *fn)
 {
     unsigned numFormals;
     ParseNode *arg1 = FunctionArgsList(fn, &numFormals);
     ParseNode *arg2 = arg1 ? NextNode(arg1) : NULL;
     ParseNode *arg3 = arg2 ? NextNode(arg2) : NULL;
 
     if (numFormals > 3)
-        return m.fail("asm.js modules takes at most 3 argument.", fn);
+        return m.fail(fn, "asm.js modules takes at most 3 argument");
 
     PropertyName *arg1Name = NULL;
     if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name))
         return false;
     m.initGlobalArgumentName(arg1Name);
 
     PropertyName *arg2Name = NULL;
     if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name))
@@ -2474,24 +2530,24 @@ CheckModuleArguments(ModuleCompiler &m, 
 }
 
 static bool
 SkipUseAsmDirective(ModuleCompiler &m, ParseNode **stmtIter)
 {
     ParseNode *firstStatement = *stmtIter;
 
     if (!IsExpressionStatement(firstStatement))
-        return m.fail("No funny stuff before the 'use asm' directive", firstStatement);
+        return m.fail(firstStatement, "unsupported statement before 'use asm' directive");
 
     ParseNode *expr = ExpressionStatementExpr(firstStatement);
     if (!expr || !expr->isKind(PNK_STRING))
-        return m.fail("No funny stuff before the 'use asm' directive", firstStatement);
+        return m.fail(firstStatement, "unsupported statement before 'use asm' directive");
 
     if (StringAtom(expr) != m.cx()->names().useAsm)
-        return m.fail("asm.js precludes other directives", firstStatement);
+        return m.fail(firstStatement, "\"use asm\" precludes other directives");
 
     *stmtIter = NextNode(firstStatement);
     return true;
 }
 
 static bool
 CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
 {
@@ -2502,95 +2558,101 @@ CheckGlobalVariableInitConstant(ModuleCo
       case NumLit::NegativeInt:
       case NumLit::BigUnsigned:
         type = VarType::Int;
         break;
       case NumLit::Double:
         type = VarType::Double;
         break;
       case NumLit::OutOfRangeInt:
-        return m.fail("Global initializer is out of representable integer range", initNode);
+        return m.fail(initNode, "global initializer is out of representable integer range");
     }
     return m.addGlobalVarInitConstant(varName, type, literal.value());
 }
 
 static bool
 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
                     ParseNode **coercedExpr = NULL)
 {
     switch (coercionNode->getKind()) {
       case PNK_BITOR: {
         ParseNode *rhs = BinaryRight(coercionNode);
 
         if (!IsNumericLiteral(rhs))
-            return m.fail("Must use |0 for argument/return coercion.", rhs);
+            return m.fail(rhs, "must use |0 for argument/return coercion");
 
         NumLit rhsLiteral = ExtractNumericLiteral(rhs);
         if (rhsLiteral.which() != NumLit::Fixnum || rhsLiteral.toInt32() != 0)
-            return m.fail("Must use |0 for argument/return coercion.", rhs);
+            return m.fail(rhs, "must use |0 for argument/return coercion");
 
         *coercion = AsmJS_ToInt32;
         if (coercedExpr)
             *coercedExpr = BinaryLeft(coercionNode);
         return true;
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
       default:;
     }
 
-    return m.fail("in coercion expression, the expression must be of the form +x or x|0", coercionNode);
+    return m.fail(coercionNode, "in coercion expression, the expression must be of the form +x or x|0");
 }
 
 static bool
 CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
 {
     AsmJSCoercion coercion;
     ParseNode *coercedExpr;
     if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
         return false;
 
     if (!coercedExpr->isKind(PNK_DOT))
-        return m.fail("Bad global variable import expression", coercedExpr);
+        return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
 
     ParseNode *base = DotBase(coercedExpr);
     PropertyName *field = DotMember(coercedExpr);
 
-    if (!IsUseOfName(base, m.module().importArgumentName()))
-        return m.fail("Expecting c.y where c is the import parameter", coercedExpr);
+    PropertyName *importName = m.module().importArgumentName();
+    if (!importName)
+        return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
+    if (!IsUseOfName(base, importName))
+        return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
 
     return m.addGlobalVarImport(varName, field, coercion);
 }
 
 static bool
 CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr, bool first)
 {
     ParseNode *ctorExpr = ListHead(newExpr);
     if (!ctorExpr->isKind(PNK_DOT))
-        return m.fail("Only valid 'new' import is 'new global.XYZArray(buf)'", ctorExpr);
+        return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
 
     ParseNode *base = DotBase(ctorExpr);
     PropertyName *field = DotMember(ctorExpr);
 
-    if (!IsUseOfName(base, m.module().globalArgumentName()))
-        return m.fail("Expecting global.y", base);
+    PropertyName *globalName = m.module().globalArgumentName();
+    if (!globalName)
+        return m.fail(base, "cannot create array view without an asm.js global parameter");
+    if (!IsUseOfName(base, globalName))
+        return m.failName(base, "expecting '%s.*Array", globalName);
 
     ParseNode *bufArg = NextNode(ctorExpr);
-    if (!bufArg)
-        return m.fail("Constructor needs an argument", ctorExpr);
-
-    if (NextNode(bufArg) != NULL)
-        return m.fail("Only one argument may be passed to a typed array constructor", bufArg);
-
-    if (!IsUseOfName(bufArg, m.module().bufferArgumentName()))
-        return m.fail("Argument to typed array constructor must be ArrayBuffer name", bufArg);
+    if (!bufArg || NextNode(bufArg) != NULL)
+        return m.fail(ctorExpr, "array view constructor takes exactly one argument");
+
+    PropertyName *bufferName = m.module().bufferArgumentName();
+    if (!bufferName)
+        return m.fail(bufArg, "cannot create array view without an asm.js heap parameter");
+    if (!IsUseOfName(bufArg, bufferName))
+        return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
     JSAtomState &names = m.cx()->names();
     ArrayBufferView::ViewType type;
     if (field == names.Int8Array)
         type = ArrayBufferView::TYPE_INT8;
     else if (field == names.Uint8Array)
         type = ArrayBufferView::TYPE_UINT8;
     else if (field == names.Int16Array)
@@ -2601,81 +2663,80 @@ CheckNewArrayView(ModuleCompiler &m, Pro
         type = ArrayBufferView::TYPE_INT32;
     else if (field == names.Uint32Array)
         type = ArrayBufferView::TYPE_UINT32;
     else if (field == names.Float32Array)
         type = ArrayBufferView::TYPE_FLOAT32;
     else if (field == names.Float64Array)
         type = ArrayBufferView::TYPE_FLOAT64;
     else
-        return m.fail("could not match typed array name", ctorExpr);
+        return m.fail(ctorExpr, "could not match typed array name");
 
     return m.addArrayView(varName, type, field);
 }
 
 static bool
 CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
 {
     ParseNode *base = DotBase(initNode);
     PropertyName *field = DotMember(initNode);
 
     if (base->isKind(PNK_DOT)) {
         ParseNode *global = DotBase(base);
         PropertyName *math = DotMember(base);
         if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math)
-            return m.fail("Expecting global.Math", base);
+            return m.fail(base, "expecting global.Math");
 
         AsmJSMathBuiltin mathBuiltin;
         if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
-            return m.fail("Does not match a standard Math builtin", initNode);
+            return m.failName(initNode, "'%s' is not a standard Math builtin", field);
 
         return m.addMathBuiltin(varName, mathBuiltin, field);
     }
 
     if (IsUseOfName(base, m.module().globalArgumentName())) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, js_NaN, field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, js_PositiveInfinity, field);
-        return m.fail("Does not match a standard global constant", initNode);
+        return m.failName(initNode, "'%s' is not a standard global constant", field);
     }
 
     if (IsUseOfName(base, m.module().importArgumentName()))
         return m.addFFI(varName, field);
 
-    return m.fail("Expecting c.y where c is either the global or import parameter", initNode);
+    return m.fail(initNode, "expecting c.y where c is either the global or foreign parameter");
 }
 
 static bool
 CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool first)
 {
     if (!IsDefinition(var))
-        return m.fail("Import variable names must be unique", var);
+        return m.fail(var, "import variable names must be unique");
 
     if (!CheckModuleLevelName(m, var->name(), var))
         return false;
 
     ParseNode *initNode = MaybeDefinitionInitializer(var);
     if (!initNode)
-        return m.fail("Module import needs initializer", var);
+        return m.fail(var, "module import needs initializer");
 
     if (IsNumericLiteral(initNode))
         return CheckGlobalVariableInitConstant(m, var->name(), initNode);
 
     if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS))
         return CheckGlobalVariableInitImport(m, var->name(), initNode);
 
     if (initNode->isKind(PNK_NEW))
         return CheckNewArrayView(m, var->name(), initNode, first);
 
     if (initNode->isKind(PNK_DOT))
         return CheckGlobalDotImport(m, var->name(), initNode);
 
-    return m.fail("Unsupported import expression", initNode);
-
+    return m.fail(initNode, "unsupported import expression");
 }
 
 static bool
 CheckModuleGlobals(ModuleCompiler &m, ParseNode **stmtIter)
 {
     ParseNode *stmt = SkipEmptyStatements(*stmtIter);
 
     bool first = true;
@@ -2688,42 +2749,46 @@ CheckModuleGlobals(ModuleCompiler &m, Pa
         }
     }
 
     *stmtIter = stmt;
     return true;
 }
 
 static bool
+ArgFail(ModuleCompiler &m, PropertyName *argName, ParseNode *stmt)
+{
+    return m.failName(stmt, "expecting argument type declaration for '%s' of the "
+                      "form 'arg = arg|0' or 'arg = +arg'", argName);
+}
+
+static bool
 CheckArgumentType(ModuleCompiler &m, ParseNode *fn, PropertyName *argName, ParseNode *stmt,
                   VarType *type)
 {
-    if (!stmt)
-        return m.fail("Missing parameter type declaration statements", fn);
-
-    if (!IsExpressionStatement(stmt))
-        return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt);
+    if (!stmt || !IsExpressionStatement(stmt))
+        return ArgFail(m, argName, stmt ? stmt : fn);
 
     ParseNode *initNode = ExpressionStatementExpr(stmt);
     if (!initNode || !initNode->isKind(PNK_ASSIGN))
-        return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt);
+        return ArgFail(m, argName, stmt);
 
     ParseNode *argNode = BinaryLeft(initNode);
     ParseNode *coercionNode = BinaryRight(initNode);
 
     if (!IsUseOfName(argNode, argName))
-        return m.fail("left-hand side of 'arg = expr;' must be the name of an argument.", argNode);
+        return ArgFail(m, argName, stmt);
 
     ParseNode *coercedExpr;
     AsmJSCoercion coercion;
     if (!CheckTypeAnnotation(m, coercionNode, &coercion, &coercedExpr))
         return false;
 
     if (!IsUseOfName(coercedExpr, argName))
-        return m.fail("For argument type declaration, need 'x = coercion(x)'", coercedExpr);
+        return ArgFail(m, argName, stmt);
 
     *type = VarType(coercion);
     return true;
 }
 
 static bool
 CheckArguments(ModuleCompiler &m, ParseNode *fn, MIRTypeVector *argTypes, ParseNode **stmtIter)
 {
@@ -2737,17 +2802,17 @@ CheckArguments(ModuleCompiler &m, ParseN
         return false;
 
     for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
         PropertyName *argName;
         if (!CheckArgument(m, argpn, &argName))
             return false;
 
         if (dupSet.has(argName))
-            return m.fail("asm.js arguments must have distinct names", argpn);
+            return m.failName(argpn, "duplicate argument name '%s' not allowed", argName);
         if (!dupSet.putNew(argName))
             return false;
 
         VarType argType;
         if (!CheckArgumentType(m, fn, argName, stmt, &argType))
             return false;
 
         if (!argTypes->append(argType.toMIRType()))
@@ -2768,17 +2833,17 @@ CheckReturnType(ModuleCompiler &m, Parse
     }
 
     ParseNode *coercionNode = UnaryKid(stmt);
 
     if (IsNumericLiteral(coercionNode)) {
         switch (ExtractNumericLiteral(coercionNode).which()) {
           case NumLit::BigUnsigned:
           case NumLit::OutOfRangeInt:
-            return m.fail("Returned literal is out of integer range", coercionNode);
+            return m.fail(coercionNode, "returned literal is out of integer range");
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
             *returnType = RetType::Signed;
             break;
           case NumLit::Double:
             *returnType = RetType::Double;
             break;
         }
@@ -2826,17 +2891,17 @@ CheckFunctionSignatures(ModuleCompiler &
     ParseNode *fn = SkipEmptyStatements(*stmtIter);
 
     for (; fn && fn->isKind(PNK_FUNCTION); fn = NextNonEmptyStatement(fn)) {
         if (!CheckFunctionSignature(m, fn))
             return false;
     }
 
     if (fn && fn->isKind(PNK_NOP))
-        return m.fail("duplicate function names are not allowed", fn);
+        return m.fail(fn, "duplicate function names are not allowed");
 
     *stmtIter = fn;
     return true;
 }
 
 static bool
 SameSignature(const ModuleCompiler::Func &a, const ModuleCompiler::Func &b)
 {
@@ -2848,47 +2913,47 @@ SameSignature(const ModuleCompiler::Func
     }
     return true;
 }
 
 static bool
 CheckFuncPtrTable(ModuleCompiler &m, ParseNode *var)
 {
     if (!IsDefinition(var))
-        return m.fail("Function-pointer table name must be unique", var);
+        return m.fail(var, "function-pointer table name must be unique");
 
     PropertyName *name = var->name();
 
     if (!CheckModuleLevelName(m, name, var))
         return false;
 
     ParseNode *arrayLiteral = MaybeDefinitionInitializer(var);
     if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY))
-        return m.fail("Function-pointer table's initializer must be an array literal", var);
+        return m.fail(var, "function-pointer table's initializer must be an array literal");
 
     unsigned length = ListLength(arrayLiteral);
 
     if (!IsPowerOfTwo(length))
-        return m.fail("Function-pointer table's length must be a power of 2", arrayLiteral);
+        return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
 
     ModuleCompiler::FuncPtrVector funcPtrs(m.cx());
     const ModuleCompiler::Func *firstFunction = NULL;
 
     for (ParseNode *elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
         if (!elem->isKind(PNK_NAME))
-            return m.fail("Function-pointer table's elements must be names of functions", elem);
+            return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         PropertyName *funcName = elem->name();
         const ModuleCompiler::Func *func = m.lookupFunction(funcName);
         if (!func)
-            return m.fail("Function-pointer table's elements must be names of functions", elem);
+            return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         if (firstFunction) {
             if (!SameSignature(*firstFunction, *func))
-                return m.fail("All functions in table must have same signature", elem);
+                return m.fail(elem, "all functions in table must have same signature");
         } else {
             firstFunction = func;
         }
 
         if (!funcPtrs.append(func))
             return false;
     }
 
@@ -2910,67 +2975,67 @@ CheckFuncPtrTables(ModuleCompiler &m, Pa
     *stmtIter = stmt;
     return true;
 }
 
 static bool
 CheckModuleExportFunction(ModuleCompiler &m, ParseNode *returnExpr)
 {
     if (!returnExpr->isKind(PNK_NAME))
-        return m.fail("an asm.js export statement must be of the form 'return name'", returnExpr);
+        return m.fail(returnExpr, "export statement must be of the form 'return name'");
 
     PropertyName *funcName = returnExpr->name();
 
     const ModuleCompiler::Func *func = m.lookupFunction(funcName);
     if (!func)
-        return m.fail("exported function name not found", returnExpr);
+        return m.failName(returnExpr, "exported function name '%s' not found", funcName);
 
     return m.addExportedFunction(func, /* maybeFieldName = */ NULL);
 }
 
 static bool
 CheckModuleExportObject(ModuleCompiler &m, ParseNode *object)
 {
     JS_ASSERT(object->isKind(PNK_OBJECT));
 
     for (ParseNode *pn = ListHead(object); pn; pn = NextNode(pn)) {
         if (!IsNormalObjectField(m.cx(), pn))
-            return m.fail("Only normal object properties may be used in the export object literal", pn);
+            return m.fail(pn, "only normal object properties may be used in the export object literal");
 
         PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn);
 
         ParseNode *initNode = ObjectFieldInitializer(pn);
         if (!initNode->isKind(PNK_NAME))
-            return m.fail("Initializer of exported object literal must be name of function", initNode);
+            return m.fail(initNode, "initializer of exported object literal must be name of function");
 
         PropertyName *funcName = initNode->name();
 
         const ModuleCompiler::Func *func = m.lookupFunction(funcName);
         if (!func)
-            return m.fail("exported function name not found", initNode);
+            return m.failName(initNode, "exported function name '%s' not found", funcName);
 
         if (!m.addExportedFunction(func, fieldName))
             return false;
     }
 
     return true;
 }
 
 static bool
 CheckModuleExports(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter)
 {
     ParseNode *returnNode = SkipEmptyStatements(*stmtIter);
 
     if (!returnNode || !returnNode->isKind(PNK_RETURN))
-        return m.fail("asm.js must end with a return export statement", fn);
+        return m.fail(fn, "asm.js module must end with a return export statement");
 
     ParseNode *returnExpr = UnaryKid(returnNode);
 
     if (!returnExpr)
-        return m.fail("an asm.js export statement must return something", returnNode);
+        return m.fail(returnNode, "export statement must return something");
 
     if (returnExpr->isKind(PNK_OBJECT)) {
         if (!CheckModuleExportObject(m, returnExpr))
             return false;
     } else {
         if (!CheckModuleExportFunction(m, returnExpr))
             return false;
     }
@@ -2990,17 +3055,17 @@ CheckNumericLiteral(FunctionCompiler &f,
 
     switch (literal.which()) {
       case NumLit::Fixnum:
       case NumLit::NegativeInt:
       case NumLit::BigUnsigned:
       case NumLit::Double:
         break;
       case NumLit::OutOfRangeInt:
-        return f.fail("Numeric literal out of representable integer range", num);
+        return f.fail(num, "numeric literal out of representable integer range");
     }
 
     *type = literal.type();
     *def = f.constant(literal.value());
     return true;
 }
 
 static bool
@@ -3024,73 +3089,76 @@ CheckVarRef(FunctionCompiler &f, ParseNo
             *def = f.loadGlobalVar(*global);
             *type = global->varType().toType();
             break;
           case ModuleCompiler::Global::Function:
           case ModuleCompiler::Global::FFI:
           case ModuleCompiler::Global::MathBuiltin:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
-            return f.fail("Global may not be accessed by ordinary expressions", varRef);
+            return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
-    return f.fail("Name not found in scope", varRef);
+    return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
 static bool
 CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType,
                  MDefinition **def)
 {
     ParseNode *viewName = ElemBase(elem);
     ParseNode *indexExpr = ElemIndex(elem);
 
     if (!viewName->isKind(PNK_NAME))
-        return f.fail("Left-hand side of x[y] must be a name", viewName);
+        return f.fail(viewName, "base of array access must be a typed array view name");
 
     const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name());
     if (!global || global->which() != ModuleCompiler::Global::ArrayView)
-        return f.fail("Left-hand side of x[y] must be typed array view name", viewName);
+        return f.fail(viewName, "base of array access must be a typed array view name");
 
     *viewType = global->viewType();
 
     uint32_t pointer;
     if (IsLiteralUint32(indexExpr, &pointer)) {
         pointer <<= TypedArrayShift(*viewType);
         *def = f.constant(Int32Value(pointer));
         return true;
     }
 
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
         ParseNode *shiftNode = BinaryRight(indexExpr);
         ParseNode *pointerNode = BinaryLeft(indexExpr);
 
         uint32_t shift;
-        if (!IsLiteralUint32(shiftNode, &shift) || shift != TypedArrayShift(*viewType))
-            return f.fail("The shift amount must be a constant matching the array "
-                          "element size", shiftNode);
+        if (!IsLiteralUint32(shiftNode, &shift))
+            return f.failf(shiftNode, "shift amount must be constant");
+
+        unsigned requiredShift = TypedArrayShift(*viewType);
+        if (shift != requiredShift)
+            return f.failf(shiftNode, "shift amount must be %u", requiredShift);
 
         Type pointerType;
         if (!CheckExpr(f, pointerNode, Use::ToInt32, &pointerDef, &pointerType))
             return false;
 
         if (!pointerType.isIntish())
-            return f.fail("Pointer input must be intish", pointerNode);
+            return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
     } else {
         if (TypedArrayShift(*viewType) != 0)
-            return f.fail("The shift amount is 0 so this must be a Int8/Uint8 array", indexExpr);
+            return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
 
         Type pointerType;
         if (!CheckExpr(f, indexExpr, Use::ToInt32, &pointerDef, &pointerType))
             return false;
 
         if (!pointerType.isInt())
-            return f.fail("Pointer input must be int", indexExpr);
+            return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
     }
 
     // Mask off the low bits to account for clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
     // loses the low two bits.
     int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
     *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
     return true;
@@ -3120,21 +3188,21 @@ CheckStoreArray(FunctionCompiler &f, Par
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType))
         return false;
 
     switch (TypedArrayStoreType(viewType)) {
       case ArrayStore_Intish:
         if (!rhsType.isIntish())
-            return f.fail("Right-hand side of store must be intish", lhs);
+            return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
         break;
       case ArrayStore_Double:
         if (rhsType != Type::Double)
-            return f.fail("Right-hand side of store must be double", lhs);
+            return f.failf(lhs, "%s is not double", rhsType.toChars());
         break;
     }
 
     f.storeHeap(viewType, pointerDef, rhsDef);
 
     *def = rhsDef;
     *type = rhsType;
     return true;
@@ -3146,27 +3214,31 @@ CheckAssignName(FunctionCompiler &f, Par
     Rooted<PropertyName *> name(f.cx(), lhs->name());
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType))
         return false;
 
     if (const FunctionCompiler::Local *lhsVar = f.lookupLocal(name)) {
-        if (!(rhsType <= lhsVar->type))
-            return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs);
+        if (!(rhsType <= lhsVar->type)) {
+            return f.failf(lhs, "%s is not a subtype of %s",
+                           rhsType.toChars(), lhsVar->type.toType().toChars());
+        }
         f.assign(*lhsVar, rhsDef);
     } else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) {
         if (global->which() != ModuleCompiler::Global::Variable)
-            return f.fail("Only global variables are mutable, not FFI functions etc", lhs);
-        if (!(rhsType <= global->varType()))
-            return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs);
+            return f.failName(lhs, "'%s' is not a mutable variable", name);
+        if (!(rhsType <= global->varType())) {
+            return f.failf(lhs, "%s is not a subtype of %s",
+                           rhsType.toChars(), global->varType().toType().toChars());
+        }
         f.storeGlobalVar(*global, rhsDef);
     } else {
-        return f.fail("Variable name in left-hand side of assignment not found", lhs);
+        return f.failName(lhs, "'%s' not found in local or asm.js module scope", name);
     }
 
     *def = rhsDef;
     *type = rhsType;
     return true;
 }
 
 static bool
@@ -3177,51 +3249,53 @@ CheckAssign(FunctionCompiler &f, ParseNo
     ParseNode *rhs = BinaryRight(assign);
 
     if (lhs->getKind() == PNK_ELEM)
         return CheckStoreArray(f, lhs, rhs, def, type);
 
     if (lhs->getKind() == PNK_NAME)
         return CheckAssignName(f, lhs, rhs, def, type);
 
-    return f.fail("Left-hand side of assignment must be a variable or heap", assign);
+    return f.fail(assign, "left-hand side of assignment must be a variable or array access");
 }
 
 static bool
 CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 2)
-        return f.fail("Math.imul must be passed 2 arguments", call);
+        return f.fail(call, "Math.imul must be passed 2 arguments");
 
     ParseNode *lhs = CallArgList(call);
     ParseNode *rhs = NextNode(lhs);
 
     MDefinition *lhsDef;
     Type lhsType;
     if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType))
         return false;
 
-    if (!lhsType.isIntish() || !rhsType.isIntish())
-        return f.fail("Math.imul calls must be passed 2 intish arguments", call);
+    if (!lhsType.isIntish())
+        return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
+    if (!rhsType.isIntish())
+        return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
 
     *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 1)
-        return f.fail("Math.abs must be passed 1 argument", call);
+        return f.fail(call, "Math.abs must be passed 1 argument");
 
     ParseNode *arg = CallArgList(call);
 
     MDefinition *argDef;
     Type argType;
     if (!CheckExpr(f, arg, Use::ToNumber, &argDef, &argType))
         return false;
 
@@ -3232,33 +3306,33 @@ CheckMathAbs(FunctionCompiler &f, ParseN
     }
 
     if (argType.isDoublish()) {
         *def = f.unary<MAbs>(argDef, MIRType_Double);
         *type = Type::Double;
         return true;
     }
 
-    return f.fail("Math.abs must be passed either a signed or doublish argument", call);
+    return f.failf(call, "%s is not a subtype of signed or doublish", argType.toChars());
 }
 
 static bool
 CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, Use use, FunctionCompiler::Args *args)
 {
     f.startCallArgs(args);
 
     ParseNode *argNode = CallArgList(callNode);
     for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) {
         MDefinition *argDef;
         Type argType;
         if (!CheckExpr(f, argNode, use, &argDef, &argType))
             return false;
 
         if (argType.isVoid())
-            return f.fail("Void cannot be passed as an argument", argNode);
+            return f.fail(argNode, "void is not a valid argument type");
 
         if (!f.passArg(argDef, argType, args))
             return false;
     }
 
     f.finishCallArgs(args);
     return true;
 }
@@ -3267,22 +3341,28 @@ static bool
 CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, const ModuleCompiler::Func &callee,
                   Use use, MDefinition **def, Type *type)
 {
     FunctionCompiler::Args args(f);
 
     if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args))
         return false;
 
-    if (args.length() != callee.numArgs())
-        return f.fail("Wrong number of arguments", callNode);
+    if (args.length() != callee.numArgs()) {
+        return f.failf(callNode, "%u arguments passed to function taking %u",
+                       args.length(), callee.numArgs());
+    }
 
     for (unsigned i = 0; i < args.length(); i++) {
-        if (!(args.type(i) <= callee.argType(i)))
-            return f.fail("actual arg type is not subtype of formal arg type", callNode);
+        Type actual = args.type(i);
+        VarType formal = callee.argType(i);
+        if (!(actual <= formal)) {
+            return f.failf(callNode, "argument %u: %s is not a subtype of %s",
+                           i, actual.toChars(), formal.toType().toChars());
+        }
     }
 
     if (!f.internalCall(callee, args, def))
         return false;
 
     // Note: the eventual goal for this code is to perform single-pass type
     // checking. A single pass means that we may not have seen the callee's definition
     // when we encounter a callsite. To address this, asm.js requires call
@@ -3295,92 +3375,105 @@ CheckInternalCall(FunctionCompiler &f, P
     //    of 'void', 'intish', 'doublish').
     //  - later checking that use.toReturnType was conservative, i.e., that the
     //    declared return type was a subtype of use.toReturnType.
     // For now, we check both conditions here. With a single-pass type checking
     // algorithm, we'd accumulate all the uses for a given callee name
     // (detecting inconsistencies between uses) and check this meet-over-uses
     // against the declared return type when the definition was encountered.
 
-    if (!(callee.returnType() <= use))
-        return f.fail("return type of callee incompatible with use", callNode);
+    RetType declared = callee.returnType();
+    if (!(declared <= use)) {
+        return f.failf(callNode, "%s is not a subtype of %s",
+                       declared.toType().toChars(), use.toReturnType().toChars());
+    }
 
     *type = use.toReturnType();
     return true;
 }
 
 static bool
 CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, Use use, MDefinition **def, Type *type)
 {
     ParseNode *callee = CallCallee(callNode);
     ParseNode *elemBase = ElemBase(callee);
     ParseNode *indexExpr = ElemIndex(callee);
 
     if (!elemBase->isKind(PNK_NAME))
-        return f.fail("Expecting name (of function-pointer array)", elemBase);
+        return f.fail(elemBase, "expecting name of function-pointer array");
 
     const ModuleCompiler::FuncPtrTable *table = f.m().lookupFuncPtrTable(elemBase->name());
     if (!table)
-        return f.fail("Expecting name of function-pointer array", elemBase);
+        return f.fail(elemBase, "expecting name of function-pointer array");
 
     if (!indexExpr->isKind(PNK_BITAND))
-        return f.fail("Function-pointer table index expression needs & mask", indexExpr);
+        return f.fail(indexExpr, "function-pointer table index expression needs & mask");
 
     ParseNode *indexNode = BinaryLeft(indexExpr);
     ParseNode *maskNode = BinaryRight(indexExpr);
 
     uint32_t mask;
     if (!IsLiteralUint32(maskNode, &mask) || mask != table->mask())
-        return f.fail("Function-pointer table index mask must be the table length minus 1", maskNode);
+        return f.failf(maskNode, "function-pointer table index mask value must be %u", table->mask());
 
     MDefinition *indexDef;
     Type indexType;
     if (!CheckExpr(f, indexNode, Use::ToInt32, &indexDef, &indexType))
         return false;
 
     if (!indexType.isIntish())
-        return f.fail("Function-pointer table index expression must be intish", indexNode);
+        return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars());
 
     FunctionCompiler::Args args(f);
 
     if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args))
         return false;
 
-    if (args.length() != table->sig().numArgs())
-        return f.fail("Wrong number of arguments", callNode);
+    if (args.length() != table->sig().numArgs()) {
+        return f.failf(callNode, "%u arguments passed to function taking %u",
+                       args.length(), table->sig().numArgs());
+    }
 
     for (unsigned i = 0; i < args.length(); i++) {
-        if (!(args.type(i) <= table->sig().argType(i)))
-            return f.fail("actual arg type is not subtype of formal arg type", callNode);
+        Type actual = args.type(i);
+        VarType formal = table->sig().argType(i);
+        if (!(actual <= formal)) {
+            return f.failf(callNode, "argument %u: %s is not a subtype of %s",
+                           i, actual.toChars(), formal.toType().toChars());
+        }
     }
 
     if (!f.funcPtrCall(*table, indexDef, args, def))
         return false;
 
-    if (!(table->sig().returnType() <= use))
-        return f.fail("return type of callee incompatible with use", callNode);
+    RetType declared = table->sig().returnType();
+    if (!(declared <= use)) {
+        return f.failf(callNode, "%s is not a subtype of %s",
+                       declared.toType().toChars(), use.toReturnType().toChars());
+    }
 
     *type = use.toReturnType();
     return true;
 }
 
 static bool
 CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, Use use,
              MDefinition **def, Type *type)
 {
     FunctionCompiler::Args args(f);
 
     if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args))
         return false;
 
     MIRTypeVector argMIRTypes(f.cx());
     for (unsigned i = 0; i < args.length(); i++) {
-        if (!args.type(i).isExtern())
-            return f.fail("args to FFI call must be <: extern", callNode);
-        if (!argMIRTypes.append(args.type(i).toMIRType()))
+        Type argType = args.type(i);
+        if (!argType.isExtern())
+            return f.failf(callNode, "%s is not a subtype of extern", argType.toChars());
+        if (!argMIRTypes.append(argType.toMIRType()))
             return false;
     }
 
     unsigned exitIndex;
     if (!f.m().addExit(ffiIndex, CallCallee(callNode)->name(), Move(argMIRTypes), use, &exitIndex))
         return false;
 
     if (!f.ffiCall(exitIndex, args, use.toMIRType(), def))
@@ -3426,22 +3519,24 @@ CheckMathBuiltinCall(FunctionCompiler &f
       case AsmJSMathBuiltin_atan2: arity = 2; callee = BinaryMathFunCast(ecmaAtan2); break;
     }
 
     FunctionCompiler::Args args(f);
 
     if (!CheckCallArgs(f, callNode, Use::ToNumber, &args))
         return false;
 
-    if (args.length() != arity)
-        return f.fail("Math builtin call passed wrong number of argument", callNode);
+    if (args.length() != arity) {
+        return f.failf(callNode, "Math builtin call passed %u arguments, expected %u",
+                       args.length(), arity);
+    }
 
     for (unsigned i = 0; i < args.length(); i++) {
         if (!args.type(i).isDoublish())
-            return f.fail("Builtin calls must be passed 1 doublish argument", callNode);
+            return f.failf(callNode, "%s is not a subtype of doublish", args.type(i).toChars());
     }
 
     if (!f.builtinCall(callee, args, MIRType_Double, def))
         return false;
 
     *type = Type::Double;
     return true;
 }
@@ -3450,35 +3545,35 @@ static bool
 CheckCall(FunctionCompiler &f, ParseNode *call, Use use, MDefinition **def, Type *type)
 {
     ParseNode *callee = CallCallee(call);
 
     if (callee->isKind(PNK_ELEM))
         return CheckFuncPtrCall(f, call, use, def, type);
 
     if (!callee->isKind(PNK_NAME))
-        return f.fail("Unexpected callee expression type", callee);
+        return f.fail(callee, "unexpected callee expression type");
 
     if (const ModuleCompiler::Global *global = f.lookupGlobal(callee->name())) {
         switch (global->which()) {
           case ModuleCompiler::Global::Function:
             return CheckInternalCall(f, call, f.m().function(global->funcIndex()), use, def, type);
           case ModuleCompiler::Global::FFI:
             return CheckFFICall(f, call, global->ffiIndex(), use, def, type);
           case ModuleCompiler::Global::MathBuiltin:
             return CheckMathBuiltinCall(f, call, global->mathBuiltin(), def, type);
           case ModuleCompiler::Global::Constant:
           case ModuleCompiler::Global::Variable:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
-            return f.fail("Global is not callable function", callee);
+            return f.failName(callee, "'%s' is not callable function", callee->name());
         }
     }
 
-    return f.fail("Call target not found in global scope", callee);
+    return f.failName(callee, "'%s' not found in local or asm.js module scope", callee->name());
 }
 
 static bool
 CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type)
 {
     JS_ASSERT(pos->isKind(PNK_POS));
     ParseNode *operand = UnaryKid(pos);
 
@@ -3489,17 +3584,17 @@ CheckPos(FunctionCompiler &f, ParseNode 
 
     if (operandType.isSigned())
         *def = f.unary<MToDouble>(operandDef);
     else if (operandType.isUnsigned())
         *def = f.unary<MAsmJSUnsignedToDouble>(operandDef);
     else if (operandType.isDoublish())
         *def = operandDef;
     else
-        return f.fail("Operand to unary + must be signed, unsigned or doubleish", operand);
+        return f.failf(operand, "%s is not a subtype of signed, unsigned or doublish", operandType.toChars());
 
     *type = Type::Double;
     return true;
 }
 
 static bool
 CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
@@ -3507,17 +3602,17 @@ CheckNot(FunctionCompiler &f, ParseNode 
     ParseNode *operand = UnaryKid(expr);
 
     MDefinition *operandDef;
     Type operandType;
     if (!CheckExpr(f, operand, Use::NoCoercion, &operandDef, &operandType))
         return false;
 
     if (!operandType.isInt())
-        return f.fail("Operand to ! must be int", operand);
+        return f.failf(operand, "%s is not a subtype of int", operandType.toChars());
 
     *def = f.unary<MNot>(operandDef);
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
@@ -3537,17 +3632,17 @@ CheckNeg(FunctionCompiler &f, ParseNode 
     }
 
     if (operandType.isDoublish()) {
         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double);
         *type = Type::Double;
         return true;
     }
 
-    return f.fail("Operand to unary - must be an int", operand);
+    return f.failf(operand, "%s is not a subtype of int or doublish", operandType.toChars());
 }
 
 static bool
 CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     JS_ASSERT(expr->isKind(PNK_BITNOT));
     ParseNode *operand = UnaryKid(expr);
 
@@ -3558,17 +3653,17 @@ CheckCoerceToInt(FunctionCompiler &f, Pa
 
     if (operandType.isDouble()) {
         *def = f.unary<MTruncateToInt32>(operandDef);
         *type = Type::Signed;
         return true;
     }
 
     if (!operandType.isIntish())
-        return f.fail("Operand to ~ must be intish or double", operand);
+        return f.failf(operand, "%s is not a subtype of double or intish", operandType.toChars());
 
     *def = operandDef;
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type)
@@ -3580,17 +3675,17 @@ CheckBitNot(FunctionCompiler &f, ParseNo
         return CheckCoerceToInt(f, operand, def, type);
 
     MDefinition *operandDef;
     Type operandType;
     if (!CheckExpr(f, operand, Use::ToInt32, &operandDef, &operandType))
         return false;
 
     if (!operandType.isIntish())
-        return f.fail("Operand to ~ must be intish", operand);
+        return f.failf(operand, "%s is not a subtype of intish", operandType.toChars());
 
     *def = f.bitwise<MBitNot>(operandDef);
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckComma(FunctionCompiler &f, ParseNode *comma, Use use, MDefinition **def, Type *type)
@@ -3619,17 +3714,17 @@ CheckConditional(FunctionCompiler &f, Pa
     ParseNode *elseExpr = TernaryKid3(ternary);
 
     MDefinition *condDef;
     Type condType;
     if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType))
         return false;
 
     if (!condType.isInt())
-        return f.fail("Condition of if must be boolish", cond);
+        return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     MBasicBlock *thenBlock, *elseBlock;
     if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock))
         return false;
 
     MDefinition *thenDef;
     Type thenType;
     if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType))
@@ -3643,22 +3738,24 @@ CheckConditional(FunctionCompiler &f, Pa
     if (!CheckExpr(f, elseExpr, Use::NoCoercion, &elseDef, &elseType))
         return false;
 
     f.pushPhiInput(elseDef);
     if (!f.joinIfElse(thenEnd))
         return false;
     *def = f.popPhiOutput();
 
-    if (thenType.isInt() && elseType.isInt())
+    if (thenType.isInt() && elseType.isInt()) {
         *type = Type::Int;
-    else if (thenType.isDouble() && elseType.isDouble())
+    } else if (thenType.isDouble() && elseType.isDouble()) {
         *type = Type::Double;
-    else
-        return f.fail("Then/else branches of conditional must both be int or double", ternary);
+    } else {
+        return f.failf(ternary, "then/else branches of conditional must both produce int or double, "
+                       "current types are %s and %s", thenType.toChars(), elseType.toChars());
+    }
 
     return true;
 }
 
 static bool
 IsValidIntMultiplyConstant(ParseNode *expr)
 {
     if (!IsNumericLiteral(expr))
@@ -3695,43 +3792,44 @@ CheckMultiply(FunctionCompiler &f, Parse
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, Use::ToNumber, &rhsDef, &rhsType))
         return false;
 
     if (lhsType.isInt() && rhsType.isInt()) {
         if (!IsValidIntMultiplyConstant(lhs) && !IsValidIntMultiplyConstant(rhs))
-            return f.fail("One arg to int multiply must be small (-2^20, 2^20) int literal", star);
+            return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
         *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
         *type = Type::Signed;
         return true;
     }
 
-    if (lhsType.isDoublish() && rhsType.isDoublish()) {
-        *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
-        *type = Type::Double;
-        return true;
-    }
-
-    return f.fail("Arguments to * must both be doubles", star);
+    if (!lhsType.isDoublish())
+        return f.failf(lhs, "%s is not a subtype of doublish", lhsType.toChars());
+    if (!rhsType.isDoublish())
+        return f.failf(rhs, "%s is not a subtype of doublish", rhsType.toChars());
+
+    *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
+    *type = Type::Double;
+    return true;
 }
 
 static bool
 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, Use use, MDefinition **def, Type *type)
 {
     JS_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
     ParseNode *lhs = BinaryLeft(expr);
     ParseNode *rhs = BinaryRight(expr);
 
     Use argUse;
     unsigned addOrSubCount = 1;
     if (use.which() == Use::AddOrSub) {
         if (++use.addOrSubCount() > (1<<20))
-            return f.fail("Too many + or - without intervening coercion", expr);
+            return f.fail(expr, "too many + or - without intervening coercion");
         argUse = use;
     } else {
         argUse = Use(&addOrSubCount);
     }
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, argUse, &lhsDef, &lhsType))
@@ -3745,18 +3843,20 @@ CheckAddOrSub(FunctionCompiler &f, Parse
                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
         if (use.which() == Use::AddOrSub)
             *type = Type::Int;
         else
             *type = Type::Intish;
         return true;
     }
 
-    if (!lhsType.isDouble() || !rhsType.isDouble())
-        return f.fail("Arguments to + or - must both be ints or doubles", expr);
+    if (!lhsType.isDouble())
+        return f.failf(lhs, "%s is not a subtype of double", lhsType.toChars());
+    if (!rhsType.isDouble())
+        return f.failf(rhs, "%s is not a subtype of double", rhsType.toChars());
 
     *def = expr->isKind(PNK_ADD)
            ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
            : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
     *type = Type::Double;
     return true;
 }
 
@@ -3799,17 +3899,18 @@ CheckDivOrMod(FunctionCompiler &f, Parse
             *type = Type::Intish;
         } else {
             *def = f.binary<MAsmJSUMod>(lhsDef, rhsDef);
             *type = Type::Int;
         }
         return true;
     }
 
-    return f.fail("Arguments to / or % must both be double, signed, or unsigned", expr);
+    return f.failf(expr, "arguments to / or &% must both be double, signed, or unsigned, "
+                   "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
 {
     JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
               comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
     ParseNode *lhs = BinaryLeft(comp);
@@ -3832,17 +3933,18 @@ CheckComparison(FunctionCompiler &f, Par
     }
 
     if (lhsType.isDouble() && rhsType.isDouble()) {
         *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double);
         *type = Type::Int;
         return true;
     }
 
-    return f.fail("The arguments to a comparison must both be signed, unsigned or doubles", comp);
+    return f.failf(comp, "arguments to a comparison must both be signed, unsigned or doubles, "
+                   "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
 {
     ParseNode *lhs = BinaryLeft(bitwise);
     ParseNode *rhs = BinaryRight(bitwise);
 
@@ -3858,41 +3960,43 @@ CheckBitwise(FunctionCompiler &f, ParseN
       default: JS_NOT_REACHED("not a bitwise op");
     }
 
     if (!onlyOnRight && IsBits32(lhs, identityElement)) {
         Type rhsType;
         if (!CheckExpr(f, rhs, Use::ToInt32, def, &rhsType))
             return false;
         if (!rhsType.isIntish())
-            return f.fail("Operands to bitwise ops must be intish", bitwise);
+            return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars());
         return true;
     }
 
     if (IsBits32(rhs, identityElement)) {
         Type lhsType;
         if (!CheckExpr(f, lhs, Use::ToInt32, def, &lhsType))
             return false;
         if (!lhsType.isIntish())
-            return f.fail("Operands to bitwise ops must be intish", bitwise);
+            return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars());
         return true;
     }
 
     MDefinition *lhsDef;
     Type lhsType;
     if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType))
         return false;
 
-    if (!lhsType.isIntish() || !rhsType.isIntish())
-        return f.fail("Operands to bitwise ops must be intish", bitwise);
+    if (!lhsType.isIntish())
+        return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
+    if (!rhsType.isIntish())
+        return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
 
     switch (bitwise->getKind()) {
       case PNK_BITOR:  *def = f.bitwise<MBitOr>(lhsDef, rhsDef); break;
       case PNK_BITAND: *def = f.bitwise<MBitAnd>(lhsDef, rhsDef); break;
       case PNK_BITXOR: *def = f.bitwise<MBitXor>(lhsDef, rhsDef); break;
       case PNK_LSH:    *def = f.bitwise<MLsh>(lhsDef, rhsDef); break;
       case PNK_RSH:    *def = f.bitwise<MRsh>(lhsDef, rhsDef); break;
       case PNK_URSH:   *def = f.bitwise<MUrsh>(lhsDef, rhsDef); break;
@@ -3945,17 +4049,17 @@ CheckExpr(FunctionCompiler &f, ParseNode
       case PNK_BITXOR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:        return CheckBitwise(f, expr, def, type);
 
       default:;
     }
 
-    return f.fail("Unsupported expression", expr);
+    return f.fail(expr, "unsupported expression");
 }
 
 static bool
 CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels = NULL);
 
 static bool
 CheckExprStatement(FunctionCompiler &f, ParseNode *exprStmt)
 {
@@ -3985,17 +4089,17 @@ CheckWhile(FunctionCompiler &f, ParseNod
         return false;
 
     MDefinition *condDef;
     Type condType;
     if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType))
         return false;
 
     if (!condType.isInt())
-        return f.fail("Condition of while loop must be boolish", cond);
+        return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     MBasicBlock *afterLoop;
     if (!f.branchAndStartLoopBody(condDef, &afterLoop))
         return false;
 
     if (!CheckStatement(f, body))
         return false;
 
@@ -4008,17 +4112,17 @@ CheckWhile(FunctionCompiler &f, ParseNod
 static bool
 CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels)
 {
     JS_ASSERT(forStmt->isKind(PNK_FOR));
     ParseNode *forHead = BinaryLeft(forStmt);
     ParseNode *body = BinaryRight(forStmt);
 
     if (!forHead->isKind(PNK_FORHEAD))
-        return f.fail("Unsupported for-loop statement", forHead);
+        return f.fail(forHead, "unsupported for-loop statement");
 
     ParseNode *maybeInit = TernaryKid1(forHead);
     ParseNode *maybeCond = TernaryKid2(forHead);
     ParseNode *maybeInc = TernaryKid3(forHead);
 
     if (maybeInit) {
         MDefinition *_1;
         Type _2;
@@ -4032,17 +4136,17 @@ CheckFor(FunctionCompiler &f, ParseNode 
 
     MDefinition *condDef;
     if (maybeCond) {
         Type condType;
         if (!CheckExpr(f, maybeCond, Use::NoCoercion, &condDef, &condType))
             return false;
 
         if (!condType.isInt())
-            return f.fail("Condition of while loop must be boolish", maybeCond);
+            return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars());
     } else {
         condDef = f.constant(Int32Value(1));
     }
 
     MBasicBlock *afterLoop;
     if (!f.branchAndStartLoopBody(condDef, &afterLoop))
         return false;
 
@@ -4080,17 +4184,17 @@ CheckDoWhile(FunctionCompiler &f, ParseN
         return false;
 
     MDefinition *condDef;
     Type condType;
     if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType))
         return false;
 
     if (!condType.isInt())
-        return f.fail("Condition of while loop must be boolish", cond);
+        return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     return f.branchAndCloseDoWhileLoop(condDef, loopEntry);
 }
 
 static bool
 CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels)
 {
     JS_ASSERT(labeledStmt->isKind(PNK_COLON));
@@ -4124,17 +4228,17 @@ CheckIf(FunctionCompiler &f, ParseNode *
     ParseNode *elseStmt = TernaryKid3(ifStmt);
 
     MDefinition *condDef;
     Type condType;
     if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType))
         return false;
 
     if (!condType.isInt())
-        return f.fail("Condition of if must be boolish", cond);
+        return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     MBasicBlock *thenBlock, *elseBlock;
     if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock))
         return false;
 
     if (!CheckStatement(f, thenStmt))
         return false;
 
@@ -4150,41 +4254,41 @@ CheckIf(FunctionCompiler &f, ParseNode *
 
     return true;
 }
 
 static bool
 CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value)
 {
     if (!IsNumericLiteral(caseExpr))
-        return f.fail("Switch case expression must be an integer literal", caseExpr);
+        return f.fail(caseExpr, "switch case expression must be an integer literal");
 
     NumLit literal = ExtractNumericLiteral(caseExpr);
     switch (literal.which()) {
       case NumLit::Fixnum:
       case NumLit::NegativeInt:
         *value = literal.toInt32();
         break;
       case NumLit::OutOfRangeInt:
       case NumLit::BigUnsigned:
-        return f.fail("Switch case expression out of integer range", caseExpr);
+        return f.fail(caseExpr, "switch case expression out of integer range");
       case NumLit::Double:
-        return f.fail("Switch case expression must be an integer literal", caseExpr);
+        return f.fail(caseExpr, "switch case expression must be an integer literal");
     }
 
     return true;
 }
 
 static bool
 CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt)
 {
     for (; stmt; stmt = NextNode(stmt)) {
         JS_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT));
         if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != NULL)
-            return f.fail("default label must be at the end", stmt);
+            return f.fail(stmt, "default label must be at the end");
     }
 
     return true;
 }
 
 static bool
 CheckSwitchRange(FunctionCompiler &f, ParseNode *stmt, int32_t *low, int32_t *high,
                  int32_t *tableLength)
@@ -4207,40 +4311,40 @@ CheckSwitchRange(FunctionCompiler &f, Pa
         if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
             return false;
 
         *low = Min(*low, i);
         *high = Max(*high, i);
     }
 
     int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
-    if (i64 > 512*1024*1024)
-        return f.fail("All switch statements generate tables; this table would be bigger than 512MiB", stmt);
+    if (i64 > 128*1024*1024)
+        return f.fail(stmt, "all switch statements generate tables; this table would be too big");
 
     *tableLength = int32_t(i64);
     return true;
 }
 
 static bool
 CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
 {
     JS_ASSERT(switchStmt->isKind(PNK_SWITCH));
     ParseNode *switchExpr = BinaryLeft(switchStmt);
     ParseNode *switchBody = BinaryRight(switchStmt);
 
     if (!switchBody->isKind(PNK_STATEMENTLIST))
-        return f.fail("Switch body may not contain 'let' declarations", switchBody);
+        return f.fail(switchBody, "switch body may not contain 'let' declarations");
 
     MDefinition *exprDef;
     Type exprType;
     if (!CheckExpr(f, switchExpr, Use::NoCoercion, &exprDef, &exprType))
         return false;
 
     if (!exprType.isSigned())
-        return f.fail("Switch expression must be a signed integer", switchExpr);
+        return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
 
     ParseNode *stmt = ListHead(switchBody);
 
     if (!CheckDefaultAtEnd(f, stmt))
         return false;
 
     if (!stmt)
         return true;
@@ -4257,17 +4361,17 @@ CheckSwitch(FunctionCompiler &f, ParseNo
     if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock))
         return false;
 
     for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
         int32_t caseValue = ExtractNumericLiteral(CaseExpr(stmt)).toInt32();
         unsigned caseIndex = caseValue - low;
 
         if (cases[caseIndex])
-            return f.fail("No duplicate case labels", stmt);
+            return f.fail(stmt, "no duplicate case labels");
 
         if (!f.startSwitchCase(switchBlock, &cases[caseIndex]))
             return false;
 
         if (!CheckStatement(f, CaseBody(stmt)))
             return false;
     }
 
@@ -4285,30 +4389,33 @@ CheckSwitch(FunctionCompiler &f, ParseNo
 
 static bool
 CheckReturn(FunctionCompiler &f, ParseNode *returnStmt)
 {
     JS_ASSERT(returnStmt->isKind(PNK_RETURN));
     ParseNode *expr = UnaryKid(returnStmt);
 
     if (!expr) {
-        if (f.func().returnType().which() != RetType::Void)
-            return f.fail("All return statements must return void", returnStmt);
+        if (f.func().returnType().which() != RetType::Void) {
+            return f.failName(returnStmt, "all return statements in %s must return void",
+                              FunctionName(f.func().fn()));
+        }
 
         f.returnVoid();
         return true;
     }
 
     MDefinition *def;
     Type type;
     if (!CheckExpr(f, expr, Use::NoCoercion, &def, &type))
         return false;
 
-    if (!(type <= f.func().returnType()))
-        return f.fail("All returns must return the same type", expr);
+    RetType retType = f.func().returnType();
+    if (!(type <= retType))
+        return f.failf(expr, "%s is not a subtype of %s", type.toChars(), retType.toType().toChars());
 
     if (f.func().returnType().which() == RetType::Void)
         f.returnVoid();
     else
         f.returnExpr(def);
     return true;
 }
 
@@ -4348,55 +4455,55 @@ CheckStatement(FunctionCompiler &f, Pars
       case PNK_SWITCH:        return CheckSwitch(f, stmt);
       case PNK_RETURN:        return CheckReturn(f, stmt);
       case PNK_STATEMENTLIST: return CheckStatementList(f, stmt);
       case PNK_BREAK:         return f.addBreak(LoopControlMaybeLabel(stmt));
       case PNK_CONTINUE:      return f.addContinue(LoopControlMaybeLabel(stmt));
       default:;
     }
 
-    return f.fail("Unexpected statement kind", stmt);
+    return f.fail(stmt, "unexpected statement kind");
 }
 
 static bool
 CheckVariableDecl(ModuleCompiler &m, ParseNode *var, FunctionCompiler::LocalMap *locals)
 {
     if (!IsDefinition(var))
-        return m.fail("Local variable names must not restate argument names", var);
+        return m.fail(var, "local variable names must not restate argument names");
 
     PropertyName *name = var->name();
 
     if (!CheckIdentifier(m, name, var))
         return false;
 
     ParseNode *initNode = MaybeDefinitionInitializer(var);
     if (!initNode)
-        return m.fail("Variable needs explicit type declaration via an initial value", var);
+        return m.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
 
     if (!IsNumericLiteral(initNode))
-        return m.fail("Variable initialization value needs to be a numeric literal", initNode);
+        return m.failName(initNode, "initializer for '%s' needs to be a numeric literal", name);
 
     NumLit literal = ExtractNumericLiteral(initNode);
     VarType type;
     switch (literal.which()) {
       case NumLit::Fixnum:
       case NumLit::NegativeInt:
       case NumLit::BigUnsigned:
         type = VarType::Int;
         break;
       case NumLit::Double:
         type = VarType::Double;
         break;
       case NumLit::OutOfRangeInt:
-        return m.fail("Variable initializer is out of representable integer range", initNode);
+        return m.failName(initNode, "initializer for '%s' is out of range", name);
     }
 
     FunctionCompiler::LocalMap::AddPtr p = locals->lookupForAdd(name);
     if (p)
-        return m.fail("Local names must be unique", initNode);
+        return m.failName(initNode, "duplicate local name '%s' not allowed", name);
 
     unsigned slot = locals->count();
     if (!locals->add(p, name, FunctionCompiler::Local(type, slot, literal.value())))
         return false;
 
     return true;
 }
 
@@ -4466,17 +4573,17 @@ CheckFunctionBody(ModuleCompiler &m, Mod
 static bool
 GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func,
                   MIRGenerator &mirGen, LIRGraph &lir)
 {
     m.masm().bind(func.codeLabel());
 
     ScopedJSDeletePtr<CodeGenerator> codegen(GenerateCode(&mirGen, &lir, &m.masm()));
     if (!codegen)
-        return m.fail("Internal codegen failure (probably out of memory)", func.fn());
+        return m.fail(func.fn(), "internal codegen failure (probably out of memory)");
 
     if (!m.collectAccesses(mirGen))
         return false;
 
     ion::IonScriptCounts *counts = codegen->extractUnassociatedScriptCounts();
     if (counts && !m.addFunctionCounts(counts)) {
         js_delete(counts);
         return false;
@@ -4517,21 +4624,21 @@ CheckFunctionBodiesSequential(ModuleComp
         if (!mirGen)
             return false;
 
         IonSpewNewFunction(&mirGen->graph(), NullPtr());
 
         IonContext icx(m.cx()->compartment, &mirGen->temp());
 
         if (!OptimizeMIR(mirGen))
-            return m.fail("Internal compiler failure (probably out of memory)", func.fn());
+            return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
 
         LIRGraph *lir = GenerateLIR(mirGen);
         if (!lir)
-            return m.fail("Internal compiler failure (probably out of memory)", func.fn());
+            return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
 
         if (!GenerateAsmJSCode(m, func, *mirGen, *lir))
             return false;
 
         IonSpewEndFunction();
     }
 
     return true;
@@ -4710,17 +4817,17 @@ CheckFunctionBodiesParallel(ModuleCompil
     ParallelGroupState group(state, tasks);
     if (!CheckFunctionBodiesParallelImpl(m, group)) {
         CancelOutstandingJobs(m, group);
 
         // If failure was triggered by a worker thread, report error.
         int32_t maybeFailureIndex = state.maybeGetAsmJSFailedFunctionIndex();
         if (maybeFailureIndex >= 0) {
             ParseNode *fn = m.function(maybeFailureIndex).fn();
-            return m.fail("Internal compiler failure (probably out of memory)", fn);
+            return m.fail(fn, "internal compiler failure (probably out of memory)");
         }
 
         // Otherwise, the error occurred on the main thread and was already reported.
         return false;
     }
     return true;
 }
 #endif // JS_PARALLEL_COMPILATION
@@ -5499,17 +5606,17 @@ CheckModule(JSContext *cx, TokenStream &
 
     if (!CheckFuncPtrTables(m, &stmtIter))
         return false;
 
     if (!CheckModuleExports(m, fn, &stmtIter))
         return false;
 
     if (stmtIter)
-        return m.fail("The top-level export (return) must be the last statement.", stmtIter);
+        return m.fail(stmtIter, "top-level export (return) must be the last statement");
 
     m.setFirstPassComplete();
 
 #ifdef JS_PARALLEL_COMPILATION
     if (OffThreadCompilationEnabled(cx)) {
         if (!CheckFunctionBodiesParallel(m))
             return false;
     } else {
--- a/js/src/jit-test/lib/asm.js
+++ b/js/src/jit-test/lib/asm.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const ASM_OK_STRING = "Successfully compiled asm.js code";
+const ASM_OK_STRING = "successfully compiled asm.js code";
 const ASM_TYPE_FAIL_STRING = "asm.js type error:";
 
 const USE_ASM = "'use asm';";
 const HEAP_IMPORTS = "var i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+
                      "var i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+
                      "var i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+
                      "var f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);";
 const BUF_64KB = new ArrayBuffer(64 * 1024);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -389,14 +389,14 @@ MSG_DEF(JSMSG_UNDEFINED_CURRENCY,     33
 MSG_DEF(JSMSG_INVALID_TIME_ZONE,      336, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
 MSG_DEF(JSMSG_DATE_NOT_FINITE,        337, 0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_MODULE_STATEMENT,       338, 0, JSEXN_SYNTAXERR, "module declarations may only appear at the top level of a program or module body")
 MSG_DEF(JSMSG_CURLY_BEFORE_MODULE,    339, 0, JSEXN_SYNTAXERR, "missing { before module body")
 MSG_DEF(JSMSG_CURLY_AFTER_MODULE,     340, 0, JSEXN_SYNTAXERR, "missing } after module body")
 MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 341, 0, JSEXN_SYNTAXERR, "'use asm' directive only works on function code")
 MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,      342, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,      343, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
-MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 0, JSEXN_ERR,     "Successfully compiled asm.js code")
+MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 0, JSEXN_ERR,     "successfully compiled asm.js code")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,         345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_YIELD_IN_ARROW,         346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
 MSG_DEF(JSMSG_WRONG_VALUE,            347, 2, JSEXN_ERR, "expected {0} but found {1}")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")