Bug 1247104 - BaldrMonkey: Parsing, encoding, and decoding for load+store. r=luke
authorDan Gohman <sunfish@mozilla.com>
Thu, 11 Feb 2016 14:49:57 -0800
changeset 330576 5469639356d344f45cf7e611165eea47c043d88a
parent 330575 a3b4e84a812fa570188911d2596b0709bad1874e
child 330577 aac070f65cc757df30b4ba4b904e890195ec59d0
push id10773
push userjyavenard@mozilla.com
push dateThu, 11 Feb 2016 23:46:51 +0000
reviewersluke
bugs1247104
milestone47.0a1
Bug 1247104 - BaldrMonkey: Parsing, encoding, and decoding for load+store. r=luke
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmText.cpp
js/src/jit-test/tests/wasm/basic-memory.js
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -342,16 +342,43 @@ DecodeIfElse(FunctionDecoder& f, bool ha
     return DecodeExpr(f, ExprType::I32) &&
            DecodeExpr(f, expected) &&
            (hasElse
             ? DecodeExpr(f, expected)
             : CheckType(f, ExprType::Void, expected));
 }
 
 static bool
+DecodeLoadStoreAddress(FunctionDecoder &f)
+{
+    uint32_t offset, align;
+    return DecodeExpr(f, ExprType::I32) &&
+           f.d().readVarU32(&offset) &&
+           f.d().readVarU32(&align) &&
+           mozilla::IsPowerOfTwo(align) &&
+           (offset == 0 || f.fail("NYI: address offsets")) &&
+           f.fail("NYI: wasm loads and stores");
+}
+
+static bool
+DecodeLoad(FunctionDecoder& f, ExprType expected, ExprType type)
+{
+    return DecodeLoadStoreAddress(f) &&
+           CheckType(f, type, expected);
+}
+
+static bool
+DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type)
+{
+    return DecodeLoadStoreAddress(f) &&
+           DecodeExpr(f, expected) &&
+           CheckType(f, type, expected);
+}
+
+static bool
 DecodeExpr(FunctionDecoder& f, ExprType expected)
 {
     Expr expr;
     if (!f.d().readExpr(&expr))
         return f.fail("unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
@@ -534,16 +561,48 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I32);
       case Expr::F64ConvertSI64:
       case Expr::F64ConvertUI64:
       case Expr::F64ReinterpretI64:
         return f.fail("NYI: i64") &&
                DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I64);
       case Expr::F64PromoteF32:
         return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::F32);
+      case Expr::I32LoadMem:
+      case Expr::I32LoadMem8S:
+      case Expr::I32LoadMem8U:
+      case Expr::I32LoadMem16S:
+      case Expr::I32LoadMem16U:
+        return DecodeLoad(f, expected, ExprType::I32);
+      case Expr::I64LoadMem:
+      case Expr::I64LoadMem8S:
+      case Expr::I64LoadMem8U:
+      case Expr::I64LoadMem16S:
+      case Expr::I64LoadMem16U:
+      case Expr::I64LoadMem32S:
+      case Expr::I64LoadMem32U:
+        return DecodeLoad(f, expected, ExprType::I64);
+      case Expr::F32LoadMem:
+        return DecodeLoad(f, expected, ExprType::F32);
+      case Expr::F64LoadMem:
+        return DecodeLoad(f, expected, ExprType::F64);
+      case Expr::I32StoreMem:
+      case Expr::I32StoreMem8:
+      case Expr::I32StoreMem16:
+        return DecodeStore(f, expected, ExprType::I32);
+      case Expr::I64StoreMem:
+      case Expr::I64StoreMem8:
+      case Expr::I64StoreMem16:
+      case Expr::I64StoreMem32:
+        return f.fail("NYI: i64") &&
+               DecodeStore(f, expected, ExprType::I64);
+      case Expr::F32StoreMem:
+        return DecodeStore(f, expected, ExprType::F32);
+      case Expr::F64StoreMem:
+        return DecodeStore(f, expected, ExprType::F64);
       default:
         break;
     }
 
     return f.fail("bad expression code");
 }
 
 static bool
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -105,18 +105,20 @@ enum class WasmAstExprKind
     BinaryOperator,
     Block,
     Call,
     ComparisonOperator,
     Const,
     ConversionOperator,
     GetLocal,
     IfElse,
+    Load,
     Nop,
     SetLocal,
+    Store,
     UnaryOperator,
 };
 
 class WasmAstExpr : public WasmAstNode
 {
     const WasmAstExprKind kind_;
 
   protected:
@@ -240,16 +242,73 @@ class WasmAstIfElse : public WasmAstExpr
 
     bool hasElse() const { return expr_ == Expr::IfElse; }
     Expr expr() const { return expr_; }
     WasmAstExpr& cond() const { return *cond_; }
     WasmAstExpr& ifBody() const { return *ifBody_; }
     WasmAstExpr& elseBody() const { return *elseBody_; }
 };
 
+class WasmAstLoadStoreAddress
+{
+    WasmAstExpr* base_;
+    int32_t offset_;
+    int32_t align_;
+
+  public:
+    explicit WasmAstLoadStoreAddress(WasmAstExpr* base, int32_t offset,
+                                     int32_t align)
+      : base_(base),
+        offset_(offset),
+        align_(align)
+    {}
+
+    WasmAstExpr& base() const { return *base_; }
+    int32_t offset() const { return offset_; }
+    int32_t align() const { return align_; }
+};
+
+class WasmAstLoad : public WasmAstExpr
+{
+    Expr expr_;
+    WasmAstLoadStoreAddress address_;
+
+  public:
+    static const WasmAstExprKind Kind = WasmAstExprKind::Load;
+    explicit WasmAstLoad(Expr expr, const WasmAstLoadStoreAddress &address)
+      : WasmAstExpr(Kind),
+        expr_(expr),
+        address_(address)
+    {}
+
+    Expr expr() const { return expr_; }
+    const WasmAstLoadStoreAddress& address() const { return address_; }
+};
+
+class WasmAstStore : public WasmAstExpr
+{
+    Expr expr_;
+    WasmAstLoadStoreAddress address_;
+    WasmAstExpr* value_;
+
+  public:
+    static const WasmAstExprKind Kind = WasmAstExprKind::Store;
+    explicit WasmAstStore(Expr expr, const WasmAstLoadStoreAddress &address,
+                          WasmAstExpr* value)
+      : WasmAstExpr(Kind),
+        expr_(expr),
+        address_(address),
+        value_(value)
+    {}
+
+    Expr expr() const { return expr_; }
+    const WasmAstLoadStoreAddress& address() const { return address_; }
+    WasmAstExpr& value() const { return *value_; }
+};
+
 class WasmAstFunc : public WasmAstNode
 {
     const uint32_t sigIndex_;
     WasmAstValTypeVector varTypes_;
     WasmAstExpr* const maybeBody_;
 
   public:
     WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExpr* maybeBody)
@@ -484,46 +543,51 @@ class WasmToken
         HexNumber,
         DecNumber,
         Infinity,
         NaN
     };
 
     enum Kind
     {
+        Align,
         BinaryOpcode,
         Block,
         Call,
         CallImport,
         CloseParen,
         ComparisonOpcode,
         Const,
         ConversionOpcode,
         EndOfFile,
+        Equal,
         Error,
         Export,
         Float,
         Func,
         GetLocal,
         If,
         IfElse,
         Import,
         Index,
         UnsignedInteger,
         SignedInteger,
         Memory,
+        Load,
         Local,
         Module,
         Name,
         Nop,
+        Offset,
         OpenParen,
         Param,
         Result,
         Segment,
         SetLocal,
+        Store,
         Text,
         UnaryOpcode,
         ValueType
     };
   private:
     Kind kind_;
     const char16_t* begin_;
     const char16_t* end_;
@@ -589,17 +653,17 @@ class WasmToken
     }
     explicit WasmToken(Kind kind, Expr expr, const char16_t* begin, const char16_t* end)
       : kind_(kind),
         begin_(begin),
         end_(end)
     {
         MOZ_ASSERT(begin != end);
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
-                   kind_ == ConversionOpcode);
+                   kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
         u.expr_ = expr;
     }
     explicit WasmToken(const char16_t* begin)
       : kind_(Error),
         begin_(begin),
         end_(begin)
     {}
     Kind kind() const {
@@ -635,17 +699,17 @@ class WasmToken
         return u.floatLiteralKind_;
     }
     ValType valueType() const {
         MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
         return u.valueType_;
     }
     Expr expr() const {
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
-                   kind_ == ConversionOpcode);
+                   kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
         return u.expr_;
     }
 };
 
 static bool
 IsWasmNewLine(char16_t c)
 {
     return c == '\n';
@@ -941,16 +1005,20 @@ WasmToken WasmTokenStream::next()
       case '(':
         cur_++;
         return WasmToken(WasmToken::OpenParen, begin, cur_);
 
       case ')':
         cur_++;
         return WasmToken(WasmToken::CloseParen, begin, cur_);
 
+      case '=':
+        cur_++;
+        return WasmToken(WasmToken::Equal, begin, cur_);
+
       case '+': case '-':
         cur_++;
         if (consume(MOZ_UTF16("infinity")))
             goto infinity;
         if (consume(MOZ_UTF16("nan")))
             goto nan;
         if (!IsWasmDigit(*cur_))
             break;
@@ -997,16 +1065,21 @@ WasmToken WasmTokenStream::next()
 
         CheckedInt<uint32_t> index = u.value();
         if (index.isValid())
             return WasmToken(index.value(), begin, cur_);
 
         return WasmToken(value, begin, cur_);
       }
 
+      case 'a':
+        if (consume(MOZ_UTF16("align")))
+            return WasmToken(WasmToken::Align, begin, cur_);
+        break;
+
       case 'b':
         if (consume(MOZ_UTF16("block")))
             return WasmToken(WasmToken::Block, begin, cur_);
         break;
 
       case 'c':
         if (consume(MOZ_UTF16("call"))) {
             if (consume(MOZ_UTF16("_import")))
@@ -1070,16 +1143,18 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("gt")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_);
                 break;
               case 'l':
                 if (consume(MOZ_UTF16("le")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_);
                 if (consume(MOZ_UTF16("lt")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_);
+                if (consume(MOZ_UTF16("load")))
+                    return WasmToken(WasmToken::Load, Expr::F32LoadMem, begin, cur_);
                 break;
               case 'm':
                 if (consume(MOZ_UTF16("max")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_);
                 if (consume(MOZ_UTF16("min")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_);
                 if (consume(MOZ_UTF16("mul")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_);
@@ -1097,16 +1172,18 @@ WasmToken WasmTokenStream::next()
                     return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32,
                                      begin, cur_);
                 break;
               case 's':
                 if (consume(MOZ_UTF16("sqrt")))
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_);
                 if (consume(MOZ_UTF16("sub")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_);
+                if (consume(MOZ_UTF16("store")))
+                    return WasmToken(WasmToken::Store, Expr::F32StoreMem, begin, cur_);
                 break;
               case 't':
                 if (consume(MOZ_UTF16("trunc")))
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_);
                 break;
             }
             break;
         }
@@ -1153,16 +1230,18 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("gt")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_);
                 break;
               case 'l':
                 if (consume(MOZ_UTF16("le")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_);
                 if (consume(MOZ_UTF16("lt")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_);
+                if (consume(MOZ_UTF16("load")))
+                    return WasmToken(WasmToken::Load, Expr::F64LoadMem, begin, cur_);
                 break;
               case 'm':
                 if (consume(MOZ_UTF16("max")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_);
                 if (consume(MOZ_UTF16("min")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_);
                 if (consume(MOZ_UTF16("mul")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_);
@@ -1180,16 +1259,18 @@ WasmToken WasmTokenStream::next()
                     return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32,
                                      begin, cur_);
                 break;
               case 's':
                 if (consume(MOZ_UTF16("sqrt")))
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_);
                 if (consume(MOZ_UTF16("sub")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_);
+                if (consume(MOZ_UTF16("store")))
+                    return WasmToken(WasmToken::Store, Expr::F64StoreMem, begin, cur_);
                 break;
               case 't':
                 if (consume(MOZ_UTF16("trunc")))
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
                 break;
             }
             break;
         }
@@ -1244,16 +1325,29 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("le_s")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_);
                 if (consume(MOZ_UTF16("le_u")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_);
                 if (consume(MOZ_UTF16("lt_s")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_);
                 if (consume(MOZ_UTF16("lt_u")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_);
+                if (consume(MOZ_UTF16("load"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8_s")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem8S, begin, cur_);
+                    if (consume(MOZ_UTF16("8_u")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem8U, begin, cur_);
+                    if (consume(MOZ_UTF16("16_s")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem16S, begin, cur_);
+                    if (consume(MOZ_UTF16("16_u")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem16U, begin, cur_);
+                    break;
+                }
                 break;
               case 'm':
                 if (consume(MOZ_UTF16("mul")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_);
                 break;
               case 'n':
                 if (consume(MOZ_UTF16("ne")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_);
@@ -1279,16 +1373,25 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("sub")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_);
                 if (consume(MOZ_UTF16("shl")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_);
                 if (consume(MOZ_UTF16("shr_s")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_);
                 if (consume(MOZ_UTF16("shr_u")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_);
+                if (consume(MOZ_UTF16("store"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8")))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem8, begin, cur_);
+                    if (consume(MOZ_UTF16("16")))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem16, begin, cur_);
+                    break;
+                }
                 break;
               case 't':
                 if (consume(MOZ_UTF16("trunc_s/f32")))
                     return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32,
                                      begin, cur_);
                 if (consume(MOZ_UTF16("trunc_s/f64")))
                     return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64,
                                      begin, cur_);
@@ -1360,16 +1463,33 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("le_s")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_);
                 if (consume(MOZ_UTF16("le_u")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_);
                 if (consume(MOZ_UTF16("lt_s")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_);
                 if (consume(MOZ_UTF16("lt_u")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_);
+                if (consume(MOZ_UTF16("load"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem8S, begin, cur_);
+                    if (consume(MOZ_UTF16("8_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem8U, begin, cur_);
+                    if (consume(MOZ_UTF16("16_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem16S, begin, cur_);
+                    if (consume(MOZ_UTF16("16_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem16U, begin, cur_);
+                    if (consume(MOZ_UTF16("32_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem32S, begin, cur_);
+                    if (consume(MOZ_UTF16("32_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem32U, begin, cur_);
+                    break;
+                }
                 break;
               case 'm':
                 if (consume(MOZ_UTF16("mul")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_);
                 break;
               case 'n':
                 if (consume(MOZ_UTF16("ne")))
                     return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_);
@@ -1395,16 +1515,27 @@ WasmToken WasmTokenStream::next()
                 if (consume(MOZ_UTF16("sub")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_);
                 if (consume(MOZ_UTF16("shl")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_);
                 if (consume(MOZ_UTF16("shr_s")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_);
                 if (consume(MOZ_UTF16("shr_u")))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_);
+                if (consume(MOZ_UTF16("store"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem8, begin, cur_);
+                    if (consume(MOZ_UTF16("16")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem16, begin, cur_);
+                    if (consume(MOZ_UTF16("32")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem32, begin, cur_);
+                    break;
+                }
                 break;
               case 't':
                 if (consume(MOZ_UTF16("trunc_s/f32")))
                     return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32,
                                      begin, cur_);
                 if (consume(MOZ_UTF16("trunc_s/f64")))
                     return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64,
                                      begin, cur_);
@@ -1458,16 +1589,21 @@ WasmToken WasmTokenStream::next()
                     cur_++;
             }
             return WasmToken(WasmToken::NaN, begin, cur_);
         }
         if (consume(MOZ_UTF16("nop")))
             return WasmToken(WasmToken::Nop, begin, cur_);
         break;
 
+      case 'o':
+        if (consume(MOZ_UTF16("offset")))
+            return WasmToken(WasmToken::Offset, begin, cur_);
+        break;
+
       case 'p':
         if (consume(MOZ_UTF16("param")))
             return WasmToken(WasmToken::Param, begin, cur_);
         break;
 
       case 'r':
         if (consume(MOZ_UTF16("result")))
             return WasmToken(WasmToken::Result, begin, cur_);
@@ -1766,37 +1902,42 @@ ParseFloatLiteral(WasmParseContext& c, W
     if (*cur == '-' || *cur == '+')
         isNegated = *cur++ == '-';
 
     switch (token.floatLiteralKind()) {
       case WasmToken::Infinity:
         *result = PositiveInfinity<Float>();
         break;
       case WasmToken::NaN:
-        if (!ParseNaNLiteral(cur, end, result))
+        if (!ParseNaNLiteral(cur, end, result)) {
+            c.ts.generateError(token, c.error);
             return false;
+        }
         break;
       case WasmToken::HexNumber:
-        if (!ParseHexFloatLiteral(cur, end, result))
+        if (!ParseHexFloatLiteral(cur, end, result)) {
+            c.ts.generateError(token, c.error);
             return false;
+        }
         break;
       case WasmToken::DecNumber: {
         // Call into JS' strtod. Tokenization has already required that the
         // string is well-behaved.
         LifoAlloc::Mark mark = c.lifo.mark();
         char* buffer = c.lifo.newArray<char>(end - begin + 1);
         if (!buffer)
             return false;
         for (ptrdiff_t i = 0; i < end - cur; ++i)
             buffer[i] = char(cur[i]);
         char* strtod_end;
         int err;
         Float d = (Float)js_strtod_harder(c.dtoaState, buffer, &strtod_end, &err);
         if (err != 0 || strtod_end == buffer) {
             c.lifo.release(mark);
+            c.ts.generateError(token, c.error);
             return false;
         }
         c.lifo.release(mark);
         *result = d;
         break;
       }
     }
 
@@ -1810,64 +1951,61 @@ static WasmAstConst*
 ParseConst(WasmParseContext& c, WasmToken constToken)
 {
     WasmToken val = c.ts.get();
     switch (constToken.valueType()) {
       case ValType::I32: {
         switch (val.kind()) {
           case WasmToken::Index:
             return new(c.lifo) WasmAstConst(Val(val.index()));
-          case WasmToken::UnsignedInteger: {
-            CheckedInt<uint32_t> uint = val.uint();
-            if (uint.isValid())
-                return new(c.lifo) WasmAstConst(Val(uint.value()));
-            return nullptr;
-          }
           case WasmToken::SignedInteger: {
             CheckedInt<int32_t> sint = val.sint();
-            if (sint.isValid())
-                return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
-            return nullptr;
+            if (!sint.isValid())
+                break;
+            return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
           }
           default:
-            return nullptr;
+            break;
         }
+        break;
       }
       case ValType::I64: {
         switch (val.kind()) {
           case WasmToken::Index:
             return new(c.lifo) WasmAstConst(Val(val.index()));
           case WasmToken::UnsignedInteger:
             return new(c.lifo) WasmAstConst(Val(val.uint()));
           case WasmToken::SignedInteger:
             return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint())));
           default:
-            return nullptr;
+            break;
         }
+        break;
       }
       case ValType::F32: {
         if (val.kind() != WasmToken::Float)
-            return nullptr;
+            break;
         float result;
         if (!ParseFloatLiteral(c, val, &result))
-            return nullptr;
+            break;
         return new(c.lifo) WasmAstConst(Val(result));
       }
       case ValType::F64: {
         if (val.kind() != WasmToken::Float)
-            return nullptr;
+            break;
         double result;
         if (!ParseFloatLiteral(c, val, &result))
-            return nullptr;
+            break;
         return new(c.lifo) WasmAstConst(Val(result));
       }
       default:
-        c.ts.generateError(constToken, c.error);
-        return nullptr;
+        break;
     }
+    c.ts.generateError(constToken, c.error);
+    return nullptr;
 }
 
 static WasmAstGetLocal*
 ParseGetLocal(WasmParseContext& c)
 {
     WasmToken localIndex;
     if (!c.ts.match(WasmToken::Index, &localIndex, c.error))
         return nullptr;
@@ -1953,16 +2091,145 @@ ParseIfElse(WasmParseContext& c, Expr ex
         elseBody = ParseExpr(c);
         if (!elseBody)
             return nullptr;
     }
 
     return new(c.lifo) WasmAstIfElse(expr, cond, ifBody, elseBody);
 }
 
+static bool
+ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base,
+                      int32_t* offset, int32_t* align)
+{
+    *base = ParseExpr(c);
+    if (!*base)
+        return false;
+
+    WasmToken token = c.ts.get();
+
+    *offset = 0;
+    if (token.kind() == WasmToken::Offset) {
+        if (!c.ts.match(WasmToken::Equal, c.error))
+            return false;
+        WasmToken val = c.ts.get();
+        switch (val.kind()) {
+          case WasmToken::Index:
+            *offset = val.index();
+            break;
+          default:
+            c.ts.generateError(val, c.error);
+            return false;
+        }
+
+        token = c.ts.get();
+    }
+
+    *align = 0;
+    if (token.kind() == WasmToken::Align) {
+        if (!c.ts.match(WasmToken::Equal, c.error))
+            return false;
+        WasmToken val = c.ts.get();
+        switch (val.kind()) {
+          case WasmToken::Index:
+            *align = val.index();
+            break;
+          default:
+            c.ts.generateError(val, c.error);
+            return false;
+        }
+
+        token = c.ts.get();
+    }
+
+    c.ts.unget(token);
+    return true;
+}
+
+static WasmAstLoad*
+ParseLoad(WasmParseContext& c, Expr expr)
+{
+    WasmAstExpr* base;
+    int32_t offset;
+    int32_t align;
+    if (!ParseLoadStoreAddress(c, &base, &offset, &align))
+        return nullptr;
+
+    if (align == 0) {
+        switch (expr) {
+          case Expr::I32LoadMem8S:
+          case Expr::I32LoadMem8U:
+          case Expr::I64LoadMem8S:
+          case Expr::I64LoadMem8U:
+            align = 1;
+            break;
+          case Expr::I32LoadMem16S:
+          case Expr::I32LoadMem16U:
+          case Expr::I64LoadMem16S:
+          case Expr::I64LoadMem16U:
+            align = 2;
+            break;
+          case Expr::I32LoadMem:
+          case Expr::F32LoadMem:
+          case Expr::I64LoadMem32S:
+          case Expr::I64LoadMem32U:
+            align = 4;
+            break;
+          case Expr::I64LoadMem:
+          case Expr::F64LoadMem:
+            align = 8;
+            break;
+          default:
+            MOZ_CRASH("Bad load expr");
+        }
+    }
+
+    return new(c.lifo) WasmAstLoad(expr, WasmAstLoadStoreAddress(base, offset, align));
+}
+
+static WasmAstStore*
+ParseStore(WasmParseContext& c, Expr expr)
+{
+    WasmAstExpr* base;
+    int32_t offset;
+    int32_t align;
+    if (!ParseLoadStoreAddress(c, &base, &offset, &align))
+        return nullptr;
+
+    if (align == 0) {
+        switch (expr) {
+          case Expr::I32StoreMem8:
+          case Expr::I64StoreMem8:
+            align = 1;
+            break;
+          case Expr::I32StoreMem16:
+          case Expr::I64StoreMem16:
+            align = 2;
+            break;
+          case Expr::I32StoreMem:
+          case Expr::F32StoreMem:
+          case Expr::I64StoreMem32:
+            align = 4;
+            break;
+          case Expr::I64StoreMem:
+          case Expr::F64StoreMem:
+            align = 8;
+            break;
+          default:
+            MOZ_CRASH("Bad load expr");
+        }
+    }
+
+    WasmAstExpr* value = ParseExpr(c);
+    if (!value)
+        return nullptr;
+
+    return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value);
+}
+
 static WasmAstExpr*
 ParseExprInsideParens(WasmParseContext& c)
 {
     WasmToken token = c.ts.get();
 
     switch (token.kind()) {
       case WasmToken::Nop:
         return new(c.lifo) WasmAstNop;
@@ -1981,18 +2248,22 @@ ParseExprInsideParens(WasmParseContext& 
       case WasmToken::ConversionOpcode:
         return ParseConversionOperator(c, token.expr());
       case WasmToken::If:
         return ParseIfElse(c, Expr::If);
       case WasmToken::IfElse:
         return ParseIfElse(c, Expr::IfElse);
       case WasmToken::GetLocal:
         return ParseGetLocal(c);
+      case WasmToken::Load:
+        return ParseLoad(c, token.expr());
       case WasmToken::SetLocal:
         return ParseSetLocal(c);
+      case WasmToken::Store:
+        return ParseStore(c, token.expr());
       case WasmToken::UnaryOpcode:
         return ParseUnaryOperator(c, token.expr());
       default:
         c.ts.generateError(token, c.error);
         return nullptr;
     }
 }
 
@@ -2337,16 +2608,39 @@ EncodeIfElse(Encoder& e, WasmAstIfElse& 
 {
     return e.writeExpr(ie.expr()) &&
            EncodeExpr(e, ie.cond()) &&
            EncodeExpr(e, ie.ifBody()) &&
            (!ie.hasElse() || EncodeExpr(e, ie.elseBody()));
 }
 
 static bool
+EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address)
+{
+    return EncodeExpr(e, address.base()) &&
+           e.writeVarU32(address.offset()) &&
+           e.writeVarU32(address.align());
+}
+
+static bool
+EncodeLoad(Encoder& e, WasmAstLoad& l)
+{
+    return e.writeExpr(l.expr()) &&
+           EncodeLoadStoreAddress(e, l.address());
+}
+
+static bool
+EncodeStore(Encoder& e, WasmAstStore& s)
+{
+    return e.writeExpr(s.expr()) &&
+           EncodeLoadStoreAddress(e, s.address()) &&
+           EncodeExpr(e, s.value());
+}
+
+static bool
 EncodeExpr(Encoder& e, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
       case WasmAstExprKind::BinaryOperator:
         return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
@@ -2358,18 +2652,22 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr
       case WasmAstExprKind::Const:
         return EncodeConst(e, expr.as<WasmAstConst>());
       case WasmAstExprKind::ConversionOperator:
         return EncodeConversionOperator(e, expr.as<WasmAstConversionOperator>());
       case WasmAstExprKind::GetLocal:
         return EncodeGetLocal(e, expr.as<WasmAstGetLocal>());
       case WasmAstExprKind::IfElse:
         return EncodeIfElse(e, expr.as<WasmAstIfElse>());
+      case WasmAstExprKind::Load:
+        return EncodeLoad(e, expr.as<WasmAstLoad>());
       case WasmAstExprKind::SetLocal:
         return EncodeSetLocal(e, expr.as<WasmAstSetLocal>());
+      case WasmAstExprKind::Store:
+        return EncodeStore(e, expr.as<WasmAstStore>());
       case WasmAstExprKind::UnaryOperator:
         return EncodeUnaryOperator(e, expr.as<WasmAstUnaryOperator>());
       default:;
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 /*****************************************************************************/
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -0,0 +1,111 @@
+load(libdir + "wasm.js");
+
+quit(); // TODO: Loads and stores are NYI.
+
+if (!wasmIsSupported())
+    quit();
+
+function mismatchError(actual, expect) {
+    var str = "type mismatch: expression has type " + actual + " but expected " + expect;
+    return RegExp(str);
+}
+
+function testLoad(type, ext, base, offset, align, expect) {
+    print('(module' +
+    '  (memory 0x10000' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (result ' + type + ')' +
+    '    (' + type + '.load' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '    )' +
+    '  ) (export "" 0))');
+  assertEq(wasmEvalText(
+    '(module' +
+    '  (memory 0x10000' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (result ' + type + ')' +
+    '    (' + type + '.load' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '    )' +
+    '  ) (export "" 0))'
+  )(base), expect);
+}
+
+function testStore(type, ext, base, offset, align, value) {
+  assertEq(wasmEvalText(
+    '(module' +
+    '  (memory 0x10000)' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (param ' + type + ') (result ' + type + ')' +
+    '    (' + type + '.store' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '     (get_local 1)' +
+    '    )' +
+    '  ) (export "" 0))'
+  )(base, value), value);
+}
+
+function testConstError(type, str) {
+  // For now at least, we don't distinguish between parse errors and OOMs.
+  assertErrorMessage(() => wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), Error, /out of memory/);
+}
+
+testLoad('i32', '', 0, 0, 0, 0x00010203);
+testLoad('i32', '', 1, 0, 0, 0x01020304);
+//testLoad('i32', '', 0, 1, 0, 0x01020304); // TODO: NYI
+//testLoad('i32', '', 1, 1, 4, 0x02030405); // TODO: NYI
+//testLoad('i64', '', 0, 0, 0, 0x0001020304050607); // TODO: NYI
+//testLoad('i64', '', 1, 0, 0, 0x0102030405060708); // TODO: NYI
+//testLoad('i64', '', 0, 1, 0, 0x0102030405060708); // TODO: NYI
+//testLoad('i64', '', 1, 1, 4, 0x0203040506070809); // TODO: NYI
+testLoad('f32', '', 0, 0, 0, 0x00010203);
+testLoad('f32', '', 1, 0, 0, 0x01020304);
+//testLoad('f32', '', 0, 1, 0, 0x01020304); // TODO: NYI
+//testLoad('f32', '', 1, 1, 4, 0x02030405); // TODO: NYI
+testLoad('f64', '', 0, 0, 0, 0x00010203);
+testLoad('f64', '', 1, 0, 0, 0x01020304);
+//testLoad('f64', '', 0, 1, 0, 0x01020304); // TODO: NYI
+//testLoad('f64', '', 1, 1, 4, 0x02030405); // TODO: NYI
+
+testLoad('i32', '8_s', 16, 0, 0, -0x8);
+testLoad('i32', '8_u', 16, 0, 0, 0x8);
+testLoad('i32', '16_s', 16, 0, 0, -0x707);
+testLoad('i32', '16_u', 16, 0, 0, 0x8f9);
+testLoad('i64', '8_s', 16, 0, 0, -0x8);
+testLoad('i64', '8_u', 16, 0, 0, 0x8);
+//testLoad('i64', '16_s', 16, 0, 0, -0x707); // TODO: NYI
+//testLoad('i64', '16_u', 16, 0, 0, 0x8f9); // TODO: NYI
+//testLoad('i64', '32_s', 16, 0, 0, -0x7060505); // TODO: NYI
+//testLoad('i64', '32_u', 16, 0, 0, 0x8f9fafb); // TODO: NYI
+
+testStore('i32', '', 0, 0, 0, 0xc0c1d3d4);
+testStore('i32', '', 1, 0, 0, 0xc0c1d3d4);
+//testStore('i32', '', 0, 1, 0, 0xc0c1d3d4); // TODO: NYI
+//testStore('i32', '', 1, 1, 4, 0xc0c1d3d4); // TODO: NYI
+//testStore('i64', '', 0, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI
+//testStore('i64', '', 1, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI
+//testStore('i64', '', 0, 1, 0, 0xc0c1d3d4e6e7090a); // TODO: NYI
+//testStore('i64', '', 1, 1, 4, 0xc0c1d3d4e6e7090a); // TODO: NYI
+testStore('f32', '', 0, 0, 0, 0.01234567);
+testStore('f32', '', 1, 0, 0, 0.01234567);
+//testStore('f32', '', 0, 1, 0, 0.01234567); // TODO: NYI
+//testStore('f32', '', 1, 1, 4, 0.01234567); // TODO: NYI
+testStore('f64', '', 0, 0, 0, 0.89012345);
+testStore('f64', '', 1, 0, 0, 0.89012345);
+//testStore('f64', '', 0, 1, 0, 0.89012345); // TODO: NYI
+//testStore('f64', '', 1, 1, 4, 0.89012345); // TODO: NYI
+
+testStore('i32', '8', 0, 0, 0, 0x23);
+testStore('i32', '16', 0, 0, 0, 0x2345);
+//testStore('i64', '8', 0, 0, 0, 0x23); // TODO: NYI
+//testStore('i64', '16', 0, 0, 0, 0x23); // TODO: NYI
+//testStore('i64', '32', 0, 0, 0, 0x23); // TODO: NYI