Upgrade V8 to 5.1.281.57  DO NOT MERGE

FPIIM-449

Change-Id: Id981b686b4d587ac31697662eb98bb34be42ad90
(cherry picked from commit 3b9bc31999c9787eb726ecdbfd5796bfdec32a18)
diff --git a/src/wasm/asm-wasm-builder.cc b/src/wasm/asm-wasm-builder.cc
index ee5427b..d16d3a8 100644
--- a/src/wasm/asm-wasm-builder.cc
+++ b/src/wasm/asm-wasm-builder.cc
@@ -4,6 +4,12 @@
 
 #include "src/v8.h"
 
+// Required to get M_E etc. in MSVC.
+#if defined(_WIN32)
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+
 #include "src/wasm/asm-wasm-builder.h"
 #include "src/wasm/wasm-macro-gen.h"
 #include "src/wasm/wasm-opcodes.h"
@@ -28,7 +34,7 @@
 class AsmWasmBuilderImpl : public AstVisitor {
  public:
   AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal,
-                     Handle<Object> foreign)
+                     Handle<Object> foreign, AsmTyper* typer)
       : local_variables_(HashMap::PointersMatch,
                          ZoneHashMap::kDefaultHashMapCapacity,
                          ZoneAllocationPolicy(zone)),
@@ -46,6 +52,7 @@
         isolate_(isolate),
         zone_(zone),
         foreign_(foreign),
+        typer_(typer),
         cache_(TypeCache::Get()),
         breakable_blocks_(zone),
         block_size_(0),
@@ -59,12 +66,10 @@
   }
 
   void InitializeInitFunction() {
-    unsigned char init[] = "__init__";
     init_function_index_ = builder_->AddFunction();
     current_function_builder_ = builder_->FunctionAt(init_function_index_);
-    current_function_builder_->SetName(init, 8);
     current_function_builder_->ReturnType(kAstStmt);
-    current_function_builder_->Exported(1);
+    builder_->MarkStartFunction(init_function_index_);
     current_function_builder_ = nullptr;
   }
 
@@ -133,13 +138,14 @@
         : builder_(builder) {
       builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop));
       builder_->current_function_builder_->Emit(opcode);
-      index_ = builder_->current_function_builder_->EmitEditableImmediate(0);
+      index_ =
+          builder_->current_function_builder_->EmitEditableVarIntImmediate();
       prev_block_size_ = builder_->block_size_;
       builder_->block_size_ = initial_block_size;
     }
     ~BlockVisitor() {
-      builder_->current_function_builder_->EditImmediate(index_,
-                                                         builder_->block_size_);
+      builder_->current_function_builder_->EditVarIntImmediate(
+          index_, builder_->block_size_);
       builder_->block_size_ = prev_block_size_;
       builder_->breakable_blocks_.pop_back();
     }
@@ -188,7 +194,7 @@
       }
     }
     DCHECK(i >= 0);
-    current_function_builder_->EmitWithU8(kExprBr, block_distance);
+    current_function_builder_->EmitWithVarInt(kExprBr, block_distance);
     current_function_builder_->Emit(kExprNop);
   }
 
@@ -211,7 +217,7 @@
       }
     }
     DCHECK(i >= 0);
-    current_function_builder_->EmitWithU8(kExprBr, block_distance);
+    current_function_builder_->EmitWithVarInt(kExprBr, block_distance);
     current_function_builder_->Emit(kExprNop);
   }
 
@@ -232,7 +238,8 @@
   void SetLocalTo(uint16_t index, int value) {
     current_function_builder_->Emit(kExprSetLocal);
     AddLeb128(index, true);
-    byte code[] = {WASM_I32(value)};
+    // TODO(bradnelson): variable size
+    byte code[] = {WASM_I32V(value)};
     current_function_builder_->EmitCode(code, sizeof(code));
     block_size_++;
   }
@@ -286,7 +293,7 @@
     RECURSE(Visit(stmt->body()));
     current_function_builder_->Emit(kExprIf);
     RECURSE(Visit(stmt->cond()));
-    current_function_builder_->EmitWithU8(kExprBr, 0);
+    current_function_builder_->EmitWithVarInt(kExprBr, 0);
     current_function_builder_->Emit(kExprNop);
   }
 
@@ -296,7 +303,7 @@
                          1);
     current_function_builder_->Emit(kExprIf);
     RECURSE(Visit(stmt->cond()));
-    current_function_builder_->EmitWithU8(kExprBr, 0);
+    current_function_builder_->EmitWithVarInt(kExprBr, 0);
     RECURSE(Visit(stmt->body()));
   }
 
@@ -311,9 +318,9 @@
     if (stmt->cond() != nullptr) {
       block_size_++;
       current_function_builder_->Emit(kExprIf);
-      current_function_builder_->Emit(kExprBoolNot);
+      current_function_builder_->Emit(kExprI32Eqz);
       RECURSE(Visit(stmt->cond()));
-      current_function_builder_->EmitWithU8(kExprBr, 1);
+      current_function_builder_->EmitWithVarInt(kExprBr, 1);
       current_function_builder_->Emit(kExprNop);
     }
     if (stmt->body() != nullptr) {
@@ -325,7 +332,7 @@
       RECURSE(Visit(stmt->next()));
     }
     block_size_++;
-    current_function_builder_->EmitWithU8(kExprBr, 0);
+    current_function_builder_->EmitWithVarInt(kExprBr, 0);
     current_function_builder_->Emit(kExprNop);
   }
 
@@ -371,6 +378,58 @@
     RECURSE(Visit(expr->else_expression()));
   }
 
+  bool VisitStdlibConstant(Variable* var) {
+    AsmTyper::StandardMember standard_object =
+        typer_->VariableAsStandardMember(var);
+    double value;
+    switch (standard_object) {
+      case AsmTyper::kInfinity: {
+        value = std::numeric_limits<double>::infinity();
+        break;
+      }
+      case AsmTyper::kNaN: {
+        value = std::numeric_limits<double>::quiet_NaN();
+        break;
+      }
+      case AsmTyper::kMathE: {
+        value = M_E;
+        break;
+      }
+      case AsmTyper::kMathLN10: {
+        value = M_LN10;
+        break;
+      }
+      case AsmTyper::kMathLN2: {
+        value = M_LN2;
+        break;
+      }
+      case AsmTyper::kMathLOG10E: {
+        value = M_LOG10E;
+        break;
+      }
+      case AsmTyper::kMathLOG2E: {
+        value = M_LOG2E;
+        break;
+      }
+      case AsmTyper::kMathPI: {
+        value = M_PI;
+        break;
+      }
+      case AsmTyper::kMathSQRT1_2: {
+        value = M_SQRT1_2;
+        break;
+      }
+      case AsmTyper::kMathSQRT2: {
+        value = M_SQRT2;
+        break;
+      }
+      default: { return false; }
+    }
+    byte code[] = {WASM_F64(value)};
+    current_function_builder_->EmitCode(code, sizeof(code));
+    return true;
+  }
+
   void VisitVariableProxy(VariableProxy* expr) {
     if (in_function_) {
       Variable* var = expr->var();
@@ -382,6 +441,9 @@
         }
         is_set_op_ = false;
       } else {
+        if (VisitStdlibConstant(var)) {
+          return;
+        }
         if (var->IsContextSlot()) {
           current_function_builder_->Emit(kExprLoadGlobal);
         } else {
@@ -399,32 +461,32 @@
   }
 
   void VisitLiteral(Literal* expr) {
-    if (in_function_) {
-      if (expr->raw_value()->IsNumber()) {
-        LocalType type = TypeOf(expr);
-        switch (type) {
-          case kAstI32: {
-            int val = static_cast<int>(expr->raw_value()->AsNumber());
-            byte code[] = {WASM_I32(val)};
-            current_function_builder_->EmitCode(code, sizeof(code));
-            break;
-          }
-          case kAstF32: {
-            float val = static_cast<float>(expr->raw_value()->AsNumber());
-            byte code[] = {WASM_F32(val)};
-            current_function_builder_->EmitCode(code, sizeof(code));
-            break;
-          }
-          case kAstF64: {
-            double val = static_cast<double>(expr->raw_value()->AsNumber());
-            byte code[] = {WASM_F64(val)};
-            current_function_builder_->EmitCode(code, sizeof(code));
-            break;
-          }
-          default:
-            UNREACHABLE();
-        }
+    Handle<Object> value = expr->value();
+    if (!in_function_ || !value->IsNumber()) {
+      return;
+    }
+    Type* type = expr->bounds().upper;
+    if (type->Is(cache_.kAsmSigned)) {
+      int32_t i = 0;
+      if (!value->ToInt32(&i)) {
+        UNREACHABLE();
       }
+      byte code[] = {WASM_I32V(i)};
+      current_function_builder_->EmitCode(code, sizeof(code));
+    } else if (type->Is(cache_.kAsmUnsigned) || type->Is(cache_.kAsmFixnum)) {
+      uint32_t u = 0;
+      if (!value->ToUint32(&u)) {
+        UNREACHABLE();
+      }
+      int32_t i = static_cast<int32_t>(u);
+      byte code[] = {WASM_I32V(i)};
+      current_function_builder_->EmitCode(code, sizeof(code));
+    } else if (type->Is(cache_.kAsmDouble)) {
+      double val = expr->raw_value()->AsNumber();
+      byte code[] = {WASM_F64(val)};
+      current_function_builder_->EmitCode(code, sizeof(code));
+    } else {
+      UNREACHABLE();
     }
   }
 
@@ -589,29 +651,33 @@
         UnLoadInitFunction();
         return;
       }
-      // TODO(bradnelson): Get rid of this.
-      if (TypeOf(expr->value()) == kAstStmt) {
-        Property* prop = expr->value()->AsProperty();
-        if (prop != nullptr) {
-          VariableProxy* vp = prop->obj()->AsVariableProxy();
-          if (vp != nullptr && vp->var()->IsParameter() &&
-              vp->var()->index() == 1) {
-            VariableProxy* target = expr->target()->AsVariableProxy();
-            if (target->bounds().lower->Is(Type::Function())) {
-              const AstRawString* name =
-                  prop->key()->AsLiteral()->AsRawPropertyName();
-              imported_function_table_.AddImport(
-                  target->var(), name->raw_data(), name->length());
-            }
+      Property* prop = expr->value()->AsProperty();
+      if (prop != nullptr) {
+        VariableProxy* vp = prop->obj()->AsVariableProxy();
+        if (vp != nullptr && vp->var()->IsParameter() &&
+            vp->var()->index() == 1) {
+          VariableProxy* target = expr->target()->AsVariableProxy();
+          if (target->bounds().lower->Is(Type::Function())) {
+            const AstRawString* name =
+                prop->key()->AsLiteral()->AsRawPropertyName();
+            imported_function_table_.AddImport(target->var(), name->raw_data(),
+                                               name->length());
           }
         }
-        ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
-        if (funcs != nullptr &&
-            funcs->bounds().lower->AsArray()->Element()->IsFunction()) {
-          VariableProxy* target = expr->target()->AsVariableProxy();
-          DCHECK_NOT_NULL(target);
-          AddFunctionTable(target, funcs);
-        }
+        // Property values in module scope don't emit code, so return.
+        return;
+      }
+      ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
+      if (funcs != nullptr &&
+          funcs->bounds().lower->AsArray()->Element()->IsFunction()) {
+        VariableProxy* target = expr->target()->AsVariableProxy();
+        DCHECK_NOT_NULL(target);
+        AddFunctionTable(target, funcs);
+        // Only add to the function table. No init needed.
+        return;
+      }
+      if (expr->value()->IsCallNew()) {
+        // No init code to emit for CallNew nodes.
         return;
       }
       in_init = true;
@@ -630,6 +696,12 @@
     is_set_op_ = true;
     RECURSE(Visit(expr->target()));
     DCHECK(!is_set_op_);
+    // Assignment to heapf32 from float64 converts.
+    if (TypeOf(expr->value()) == kAstF64 && expr->target()->IsProperty() &&
+        expr->target()->AsProperty()->obj()->bounds().lower->Is(
+            cache_.kFloat32Array)) {
+      current_function_builder_->Emit(kExprF32ConvertF64);
+    }
     RECURSE(Visit(expr->value()));
     if (in_init) {
       UnLoadInitFunction();
@@ -672,7 +744,8 @@
             Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
             if (nvalue->IsNumber()) {
               int32_t val = static_cast<int32_t>(nvalue->Number());
-              byte code[] = {WASM_I32(val)};
+              // TODO(bradnelson): variable size
+              byte code[] = {WASM_I32V(val)};
               current_function_builder_->EmitCode(code, sizeof(code));
               return;
             }
@@ -684,7 +757,7 @@
       byte code[] = {WASM_F64(std::numeric_limits<double>::quiet_NaN())};
       current_function_builder_->EmitCode(code, sizeof(code));
     } else {
-      byte code[] = {WASM_I32(0)};
+      byte code[] = {WASM_I32V_1(0)};
       current_function_builder_->EmitCode(code, sizeof(code));
     }
   }
@@ -725,9 +798,9 @@
     } else {
       UNREACHABLE();
     }
-    current_function_builder_->EmitWithU8(
-        WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_),
-        WasmOpcodes::LoadStoreAccessOf(false));
+    // TODO(titzer): use special asm-compatibility opcodes?
+    current_function_builder_->EmitWithU8U8(
+        WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_), 0, 0);
     is_set_op_ = false;
     if (size == 1) {
       // Allow more general expression in byte arrays than the spec
@@ -742,7 +815,8 @@
         DCHECK(value->raw_value()->IsNumber());
         DCHECK_EQ(kAstI32, TypeOf(value));
         int val = static_cast<int>(value->raw_value()->AsNumber());
-        byte code[] = {WASM_I32(val * size)};
+        // TODO(bradnelson): variable size
+        byte code[] = {WASM_I32V(val * size)};
         current_function_builder_->EmitCode(code, sizeof(code));
         return;
       }
@@ -765,11 +839,209 @@
     UNREACHABLE();
   }
 
+  bool VisitStdlibFunction(Call* call, VariableProxy* expr) {
+    Variable* var = expr->var();
+    AsmTyper::StandardMember standard_object =
+        typer_->VariableAsStandardMember(var);
+    ZoneList<Expression*>* args = call->arguments();
+    LocalType call_type = TypeOf(call);
+    switch (standard_object) {
+      case AsmTyper::kNone: {
+        return false;
+      }
+      case AsmTyper::kMathAcos: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Acos);
+        break;
+      }
+      case AsmTyper::kMathAsin: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Asin);
+        break;
+      }
+      case AsmTyper::kMathAtan: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Atan);
+        break;
+      }
+      case AsmTyper::kMathCos: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Cos);
+        break;
+      }
+      case AsmTyper::kMathSin: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Sin);
+        break;
+      }
+      case AsmTyper::kMathTan: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Tan);
+        break;
+      }
+      case AsmTyper::kMathExp: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Exp);
+        break;
+      }
+      case AsmTyper::kMathLog: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Log);
+        break;
+      }
+      case AsmTyper::kMathCeil: {
+        if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Ceil);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Ceil);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathFloor: {
+        if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Floor);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Floor);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathSqrt: {
+        if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Sqrt);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Sqrt);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathAbs: {
+        // TODO(bradnelson): Should this be cast to float?
+        if (call_type == kAstI32) {
+          current_function_builder_->Emit(kExprIfElse);
+          current_function_builder_->Emit(kExprI32LtS);
+          Visit(args->at(0));
+          byte code[] = {WASM_I8(0)};
+          current_function_builder_->EmitCode(code, sizeof(code));
+          current_function_builder_->Emit(kExprI32Sub);
+          current_function_builder_->EmitCode(code, sizeof(code));
+          Visit(args->at(0));
+        } else if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Abs);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Abs);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathMin: {
+        // TODO(bradnelson): Change wasm to match Math.min in asm.js mode.
+        if (call_type == kAstI32) {
+          current_function_builder_->Emit(kExprIfElse);
+          current_function_builder_->Emit(kExprI32LeS);
+          Visit(args->at(0));
+          Visit(args->at(1));
+        } else if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Min);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Min);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathMax: {
+        // TODO(bradnelson): Change wasm to match Math.max in asm.js mode.
+        if (call_type == kAstI32) {
+          current_function_builder_->Emit(kExprIfElse);
+          current_function_builder_->Emit(kExprI32GtS);
+          Visit(args->at(0));
+          Visit(args->at(1));
+        } else if (call_type == kAstF32) {
+          current_function_builder_->Emit(kExprF32Max);
+        } else if (call_type == kAstF64) {
+          current_function_builder_->Emit(kExprF64Max);
+        } else {
+          UNREACHABLE();
+        }
+        break;
+      }
+      case AsmTyper::kMathAtan2: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Atan2);
+        break;
+      }
+      case AsmTyper::kMathPow: {
+        DCHECK_EQ(kAstF64, call_type);
+        current_function_builder_->Emit(kExprF64Pow);
+        break;
+      }
+      case AsmTyper::kMathImul: {
+        current_function_builder_->Emit(kExprI32Mul);
+        break;
+      }
+      case AsmTyper::kMathFround: {
+        DCHECK(args->length() == 1);
+        Literal* literal = args->at(0)->AsLiteral();
+        if (literal != nullptr) {
+          if (literal->raw_value()->IsNumber()) {
+            float val = static_cast<float>(literal->raw_value()->AsNumber());
+            byte code[] = {WASM_F32(val)};
+            current_function_builder_->EmitCode(code, sizeof(code));
+            return true;
+          }
+        }
+        switch (TypeIndexOf(args->at(0))) {
+          case kInt32:
+          case kFixnum:
+            current_function_builder_->Emit(kExprF32SConvertI32);
+            break;
+          case kUint32:
+            current_function_builder_->Emit(kExprF32UConvertI32);
+            break;
+          case kFloat32:
+            break;
+          case kFloat64:
+            current_function_builder_->Emit(kExprF32ConvertF64);
+            break;
+          default:
+            UNREACHABLE();
+        }
+        break;
+      }
+      default: {
+        UNREACHABLE();
+        break;
+      }
+    }
+    VisitCallArgs(call);
+    return true;
+  }
+
+  void VisitCallArgs(Call* expr) {
+    ZoneList<Expression*>* args = expr->arguments();
+    for (int i = 0; i < args->length(); ++i) {
+      Expression* arg = args->at(i);
+      RECURSE(Visit(arg));
+    }
+  }
+
   void VisitCall(Call* expr) {
     Call::CallType call_type = expr->GetCallType(isolate_);
     switch (call_type) {
       case Call::OTHER_CALL: {
         DCHECK(in_function_);
+        VariableProxy* proxy = expr->expression()->AsVariableProxy();
+        if (proxy != nullptr) {
+          if (VisitStdlibFunction(expr, proxy)) {
+            return;
+          }
+        }
         uint16_t index;
         VariableProxy* vp = expr->expression()->AsVariableProxy();
         if (vp != nullptr &&
@@ -802,10 +1074,11 @@
         VariableProxy* var = p->obj()->AsVariableProxy();
         DCHECK_NOT_NULL(var);
         FunctionTableIndices* indices = LookupFunctionTable(var->var());
-        current_function_builder_->EmitWithU8(kExprCallIndirect,
-                                              indices->signature_index);
+        current_function_builder_->EmitWithVarInt(kExprCallIndirect,
+                                                  indices->signature_index);
         current_function_builder_->Emit(kExprI32Add);
-        byte code[] = {WASM_I32(indices->start_index)};
+        // TODO(bradnelson): variable size
+        byte code[] = {WASM_I32V(indices->start_index)};
         current_function_builder_->EmitCode(code, sizeof(code));
         RECURSE(Visit(p->key()));
         break;
@@ -813,11 +1086,7 @@
       default:
         UNREACHABLE();
     }
-    ZoneList<Expression*>* args = expr->arguments();
-    for (int i = 0; i < args->length(); ++i) {
-      Expression* arg = args->at(i);
-      RECURSE(Visit(arg));
-    }
+    VisitCallArgs(expr);
   }
 
   void VisitCallNew(CallNew* expr) { UNREACHABLE(); }
@@ -828,7 +1097,7 @@
     switch (expr->op()) {
       case Token::NOT: {
         DCHECK_EQ(kAstI32, TypeOf(expr->expression()));
-        current_function_builder_->Emit(kExprBoolNot);
+        current_function_builder_->Emit(kExprI32Eqz);
         break;
       }
       default:
@@ -1022,7 +1291,7 @@
           } else if (type == kUint32) {
             current_function_builder_->Emit(kExprI32RemU);
           } else if (type == kFloat64) {
-            ModF64(expr);
+            current_function_builder_->Emit(kExprF64Mod);
             return;
           } else {
             UNREACHABLE();
@@ -1030,7 +1299,7 @@
           break;
         }
         case Token::COMMA: {
-          current_function_builder_->EmitWithU8(kExprBlock, 2);
+          current_function_builder_->EmitWithVarInt(kExprBlock, 2);
           break;
         }
         default:
@@ -1041,32 +1310,6 @@
     }
   }
 
-  void ModF64(BinaryOperation* expr) {
-    current_function_builder_->EmitWithU8(kExprBlock, 3);
-    uint16_t index_0 = current_function_builder_->AddLocal(kAstF64);
-    uint16_t index_1 = current_function_builder_->AddLocal(kAstF64);
-    current_function_builder_->Emit(kExprSetLocal);
-    AddLeb128(index_0, true);
-    RECURSE(Visit(expr->left()));
-    current_function_builder_->Emit(kExprSetLocal);
-    AddLeb128(index_1, true);
-    RECURSE(Visit(expr->right()));
-    current_function_builder_->Emit(kExprF64Sub);
-    current_function_builder_->Emit(kExprGetLocal);
-    AddLeb128(index_0, true);
-    current_function_builder_->Emit(kExprF64Mul);
-    current_function_builder_->Emit(kExprGetLocal);
-    AddLeb128(index_1, true);
-    // Use trunc instead of two casts
-    current_function_builder_->Emit(kExprF64SConvertI32);
-    current_function_builder_->Emit(kExprI32SConvertF64);
-    current_function_builder_->Emit(kExprF64Div);
-    current_function_builder_->Emit(kExprGetLocal);
-    AddLeb128(index_0, true);
-    current_function_builder_->Emit(kExprGetLocal);
-    AddLeb128(index_1, true);
-  }
-
   void AddLeb128(uint32_t index, bool is_local) {
     std::vector<uint8_t> index_vec = UnsignedLEB128From(index);
     if (is_local) {
@@ -1262,6 +1505,7 @@
   Isolate* isolate_;
   Zone* zone_;
   Handle<Object> foreign_;
+  AsmTyper* typer_;
   TypeCache const& cache_;
   ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
   int block_size_;
@@ -1277,13 +1521,18 @@
 };
 
 AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
-                               FunctionLiteral* literal, Handle<Object> foreign)
-    : isolate_(isolate), zone_(zone), literal_(literal), foreign_(foreign) {}
+                               FunctionLiteral* literal, Handle<Object> foreign,
+                               AsmTyper* typer)
+    : isolate_(isolate),
+      zone_(zone),
+      literal_(literal),
+      foreign_(foreign),
+      typer_(typer) {}
 
 // TODO(aseemgarg): probably should take zone (to write wasm to) as input so
 // that zone in constructor may be thrown away once wasm module is written.
 WasmModuleIndex* AsmWasmBuilder::Run() {
-  AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_);
+  AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_, typer_);
   impl.Compile();
   WasmModuleWriter* writer = impl.builder_->Build(zone_);
   return writer->WriteTo(zone_);
diff --git a/src/wasm/asm-wasm-builder.h b/src/wasm/asm-wasm-builder.h
index 9b761f9..09645ee 100644
--- a/src/wasm/asm-wasm-builder.h
+++ b/src/wasm/asm-wasm-builder.h
@@ -7,6 +7,7 @@
 
 #include "src/allocation.h"
 #include "src/objects.h"
+#include "src/typing-asm.h"
 #include "src/wasm/encoder.h"
 #include "src/zone.h"
 
@@ -20,7 +21,7 @@
 class AsmWasmBuilder {
  public:
   explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
-                          Handle<Object> foreign);
+                          Handle<Object> foreign, AsmTyper* typer);
   WasmModuleIndex* Run();
 
  private:
@@ -28,6 +29,7 @@
   Zone* zone_;
   FunctionLiteral* literal_;
   Handle<Object> foreign_;
+  AsmTyper* typer_;
 };
 }  // namespace wasm
 }  // namespace internal
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc
index c97c781..e2f6a04 100644
--- a/src/wasm/ast-decoder.cc
+++ b/src/wasm/ast-decoder.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "src/base/platform/elapsed-timer.h"
 #include "src/signature.h"
 
 #include "src/bit-vector.h"
@@ -15,6 +14,8 @@
 #include "src/wasm/wasm-module.h"
 #include "src/wasm/wasm-opcodes.h"
 
+#include "src/ostreams.h"
+
 #include "src/compiler/wasm-compiler.h"
 
 namespace v8 {
@@ -52,7 +53,6 @@
   Tree* last() const { return index > 0 ? tree->children[index - 1] : nullptr; }
 };
 
-
 // An SsaEnv environment carries the current local variable renaming
 // as well as the current effect and control dependency in the TF graph.
 // It maintains a control state that tracks whether the environment
@@ -74,14 +74,12 @@
   }
 };
 
-
 // An entry in the stack of blocks during decoding.
 struct Block {
   SsaEnv* ssa_env;  // SSA renaming environment.
   int stack_depth;  // production stack depth.
 };
 
-
 // An entry in the stack of ifs during decoding.
 struct IfEnv {
   SsaEnv* false_env;
@@ -89,27 +87,27 @@
   SsaEnv** case_envs;
 };
 
-
 // Macros that build nodes only if there is a graph and the current SSA
 // environment is reachable from start. This avoids problems with malformed
 // TF graphs when decoding inputs that have unreachable code.
 #define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr)
 #define BUILD0(func) (build() ? builder_->func() : nullptr)
 
-
 // Generic Wasm bytecode decoder with utilities for decoding operands,
 // lengths, etc.
 class WasmDecoder : public Decoder {
  public:
-  WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {}
-  WasmDecoder(FunctionEnv* env, const byte* start, const byte* end)
-      : Decoder(start, end), function_env_(env) {}
-  FunctionEnv* function_env_;
-
-  void Reset(FunctionEnv* function_env, const byte* start, const byte* end) {
-    Decoder::Reset(start, end);
-    function_env_ = function_env;
-  }
+  WasmDecoder(ModuleEnv* module, FunctionSig* sig, const byte* start,
+              const byte* end)
+      : Decoder(start, end),
+        module_(module),
+        sig_(sig),
+        total_locals_(0),
+        local_types_(nullptr) {}
+  ModuleEnv* module_;
+  FunctionSig* sig_;
+  size_t total_locals_;
+  ZoneVector<LocalType>* local_types_;
 
   byte ByteOperand(const byte* pc, const char* msg = "missing 1-byte operand") {
     if ((pc + sizeof(byte)) >= limit_) {
@@ -136,8 +134,12 @@
   }
 
   inline bool Validate(const byte* pc, LocalIndexOperand& operand) {
-    if (operand.index < function_env_->total_locals) {
-      operand.type = function_env_->GetLocalType(operand.index);
+    if (operand.index < total_locals_) {
+      if (local_types_) {
+        operand.type = local_types_->at(operand.index);
+      } else {
+        operand.type = kAstStmt;
+      }
       return true;
     }
     error(pc, pc + 1, "invalid local index");
@@ -145,9 +147,9 @@
   }
 
   inline bool Validate(const byte* pc, GlobalIndexOperand& operand) {
-    ModuleEnv* m = function_env_->module;
-    if (m && m->module && operand.index < m->module->globals->size()) {
-      operand.machine_type = m->module->globals->at(operand.index).type;
+    ModuleEnv* m = module_;
+    if (m && m->module && operand.index < m->module->globals.size()) {
+      operand.machine_type = m->module->globals[operand.index].type;
       operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type);
       return true;
     }
@@ -156,9 +158,9 @@
   }
 
   inline bool Validate(const byte* pc, FunctionIndexOperand& operand) {
-    ModuleEnv* m = function_env_->module;
-    if (m && m->module && operand.index < m->module->functions->size()) {
-      operand.sig = m->module->functions->at(operand.index).sig;
+    ModuleEnv* m = module_;
+    if (m && m->module && operand.index < m->module->functions.size()) {
+      operand.sig = m->module->functions[operand.index].sig;
       return true;
     }
     error(pc, pc + 1, "invalid function index");
@@ -166,9 +168,9 @@
   }
 
   inline bool Validate(const byte* pc, SignatureIndexOperand& operand) {
-    ModuleEnv* m = function_env_->module;
-    if (m && m->module && operand.index < m->module->signatures->size()) {
-      operand.sig = m->module->signatures->at(operand.index);
+    ModuleEnv* m = module_;
+    if (m && m->module && operand.index < m->module->signatures.size()) {
+      operand.sig = m->module->signatures[operand.index];
       return true;
     }
     error(pc, pc + 1, "invalid signature index");
@@ -176,9 +178,9 @@
   }
 
   inline bool Validate(const byte* pc, ImportIndexOperand& operand) {
-    ModuleEnv* m = function_env_->module;
-    if (m && m->module && operand.index < m->module->import_table->size()) {
-      operand.sig = m->module->import_table->at(operand.index).sig;
+    ModuleEnv* m = module_;
+    if (m && m->module && operand.index < m->module->import_table.size()) {
+      operand.sig = m->module->import_table[operand.index].sig;
       return true;
     }
     error(pc, pc + 1, "invalid signature index");
@@ -195,26 +197,14 @@
     return false;
   }
 
-  bool Validate(const byte* pc, TableSwitchOperand& operand,
+  bool Validate(const byte* pc, BranchTableOperand& operand,
                 size_t block_depth) {
-    if (operand.table_count == 0) {
-      error(pc, "tableswitch with 0 entries");
-      return false;
-    }
     // Verify table.
-    for (uint32_t i = 0; i < operand.table_count; i++) {
-      uint16_t target = operand.read_entry(this, i);
-      if (target >= 0x8000) {
-        size_t depth = target - 0x8000;
-        if (depth > block_depth) {
-          error(operand.table + i * 2, "improper branch in tableswitch");
-          return false;
-        }
-      } else {
-        if (target >= operand.case_count) {
-          error(operand.table + i * 2, "invalid case target in tableswitch");
-          return false;
-        }
+    for (uint32_t i = 0; i < operand.table_count + 1; i++) {
+      uint32_t target = operand.read_entry(this, i);
+      if (target >= block_depth) {
+        error(operand.table + i * 2, "improper branch in br_table");
+        return false;
       }
     }
     return true;
@@ -262,27 +252,23 @@
       case kExprCallFunction: {
         FunctionIndexOperand operand(this, pc);
         return static_cast<int>(
-            function_env_->module->GetFunctionSignature(operand.index)
-                ->parameter_count());
+            module_->GetFunctionSignature(operand.index)->parameter_count());
       }
       case kExprCallIndirect: {
         SignatureIndexOperand operand(this, pc);
         return 1 + static_cast<int>(
-                       function_env_->module->GetSignature(operand.index)
-                           ->parameter_count());
+                       module_->GetSignature(operand.index)->parameter_count());
       }
       case kExprCallImport: {
         ImportIndexOperand operand(this, pc);
         return static_cast<int>(
-            function_env_->module->GetImportSignature(operand.index)
-                ->parameter_count());
+            module_->GetImportSignature(operand.index)->parameter_count());
       }
       case kExprReturn: {
-        return static_cast<int>(function_env_->sig->return_count());
+        return static_cast<int>(sig_->return_count());
       }
-      case kExprTableSwitch: {
-        TableSwitchOperand operand(this, pc);
-        return 1 + operand.case_count;
+      case kExprBrTable: {
+        return 1;
       }
 
 #define DECLARE_OPCODE_CASE(name, opcode, sig) \
@@ -293,10 +279,13 @@
         FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
         FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
         FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
+        FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE)
 #undef DECLARE_OPCODE_CASE
+      case kExprDeclLocals:
+      default:
+        UNREACHABLE();
+        return 0;
     }
-    UNREACHABLE();
-    return 0;
   }
 
   int OpcodeLength(const byte* pc) {
@@ -343,16 +332,22 @@
         LocalIndexOperand operand(this, pc);
         return 1 + operand.length;
       }
-      case kExprTableSwitch: {
-        TableSwitchOperand operand(this, pc);
+      case kExprBrTable: {
+        BranchTableOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprI32Const: {
+        ImmI32Operand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprI64Const: {
+        ImmI64Operand operand(this, pc);
         return 1 + operand.length;
       }
       case kExprI8Const:
         return 2;
-      case kExprI32Const:
       case kExprF32Const:
         return 5;
-      case kExprI64Const:
       case kExprF64Const:
         return 9;
 
@@ -365,35 +360,28 @@
 
 // A shift-reduce-parser strategy for decoding Wasm code that uses an explicit
 // shift-reduce strategy with multiple internal stacks.
-class LR_WasmDecoder : public WasmDecoder {
+class SR_WasmDecoder : public WasmDecoder {
  public:
-  LR_WasmDecoder(Zone* zone, TFBuilder* builder)
-      : zone_(zone),
+  SR_WasmDecoder(Zone* zone, TFBuilder* builder, FunctionBody& body)
+      : WasmDecoder(body.module, body.sig, body.start, body.end),
+        zone_(zone),
         builder_(builder),
+        base_(body.base),
+        local_type_vec_(zone),
         trees_(zone),
         stack_(zone),
         blocks_(zone),
-        ifs_(zone) {}
+        ifs_(zone) {
+    local_types_ = &local_type_vec_;
+  }
 
-  TreeResult Decode(FunctionEnv* function_env, const byte* base, const byte* pc,
-                    const byte* end) {
-    base::ElapsedTimer decode_timer;
-    if (FLAG_trace_wasm_decode_time) {
-      decode_timer.Start();
-    }
-    trees_.clear();
-    stack_.clear();
-    blocks_.clear();
-    ifs_.clear();
-
-    if (end < pc) {
-      error(pc, "function body end < start");
+  TreeResult Decode() {
+    if (end_ < pc_) {
+      error(pc_, "function body end < start");
       return result_;
     }
 
-    base_ = base;
-    Reset(function_env, pc, end);
-
+    DecodeLocalDecls();
     InitSsaEnv();
     DecodeFunctionBody();
 
@@ -401,12 +389,12 @@
     if (ok()) {
       if (ssa_env_->go()) {
         if (stack_.size() > 0) {
-          error(stack_.back().pc(), end, "fell off end of code");
+          error(stack_.back().pc(), end_, "fell off end of code");
         }
         AddImplicitReturnAtEnd();
       }
       if (trees_.size() == 0) {
-        if (function_env_->sig->return_count() > 0) {
+        if (sig_->return_count() > 0) {
           error(start_, "no trees created");
         }
       } else {
@@ -415,15 +403,7 @@
     }
 
     if (ok()) {
-      if (FLAG_trace_wasm_ast) {
-        PrintAst(function_env, pc, end);
-      }
-      if (FLAG_trace_wasm_decode_time) {
-        double ms = decode_timer.Elapsed().InMillisecondsF();
-        PrintF("wasm-decode ok (%0.3f ms)\n\n", ms);
-      } else {
-        TRACE("wasm-decode ok\n\n");
-      }
+      TRACE("wasm-decode ok\n");
     } else {
       TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_),
             startrel(error_pc_), error_msg_.get());
@@ -432,6 +412,36 @@
     return toResult(tree);
   }
 
+  bool DecodeLocalDecls(AstLocalDecls& decls) {
+    DecodeLocalDecls();
+    if (failed()) return false;
+    decls.decls_encoded_size = pc_offset();
+    decls.total_local_count = 0;
+    decls.local_types.reserve(local_type_vec_.size());
+    for (size_t pos = 0; pos < local_type_vec_.size();) {
+      uint32_t count = 0;
+      LocalType type = local_type_vec_[pos];
+      while (pos < local_type_vec_.size() && local_type_vec_[pos] == type) {
+        pos++;
+        count++;
+      }
+      decls.total_local_count += count;
+      decls.local_types.push_back(std::pair<LocalType, uint32_t>(type, count));
+    }
+    return true;
+  }
+
+  BitVector* AnalyzeLoopAssignmentForTesting(const byte* pc,
+                                             size_t num_locals) {
+    total_locals_ = num_locals;
+    local_type_vec_.reserve(num_locals);
+    if (num_locals > local_type_vec_.size()) {
+      local_type_vec_.insert(local_type_vec_.end(),
+                             num_locals - local_type_vec_.size(), kAstI32);
+    }
+    return AnalyzeLoopAssignment(pc);
+  }
+
  private:
   static const size_t kErrorMsgSize = 128;
 
@@ -442,6 +452,7 @@
 
   SsaEnv* ssa_env_;
 
+  ZoneVector<LocalType> local_type_vec_;
   ZoneVector<Tree*> trees_;
   ZoneVector<Production> stack_;
   ZoneVector<Block> blocks_;
@@ -450,8 +461,6 @@
   inline bool build() { return builder_ && ssa_env_->go(); }
 
   void InitSsaEnv() {
-    FunctionSig* sig = function_env_->sig;
-    int param_count = static_cast<int>(sig->parameter_count());
     TFNode* start = nullptr;
     SsaEnv* ssa_env = reinterpret_cast<SsaEnv*>(zone_->New(sizeof(SsaEnv)));
     size_t size = sizeof(TFNode*) * EnvironmentCount();
@@ -459,50 +468,46 @@
     ssa_env->locals =
         size > 0 ? reinterpret_cast<TFNode**>(zone_->New(size)) : nullptr;
 
-    int pos = 0;
     if (builder_) {
-      start = builder_->Start(param_count + 1);
-      // Initialize parameters.
-      for (int i = 0; i < param_count; i++) {
-        ssa_env->locals[pos++] = builder_->Param(i, sig->GetParam(i));
+      start = builder_->Start(static_cast<int>(sig_->parameter_count() + 1));
+      // Initialize local variables.
+      uint32_t index = 0;
+      while (index < sig_->parameter_count()) {
+        ssa_env->locals[index] = builder_->Param(index, local_type_vec_[index]);
+        index++;
       }
-      // Initialize int32 locals.
-      if (function_env_->local_i32_count > 0) {
-        TFNode* zero = builder_->Int32Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_i32_count; i++) {
-          ssa_env->locals[pos++] = zero;
+      while (index < local_type_vec_.size()) {
+        LocalType type = local_type_vec_[index];
+        TFNode* node = DefaultValue(type);
+        while (index < local_type_vec_.size() &&
+               local_type_vec_[index] == type) {
+          // Do a whole run of like-typed locals at a time.
+          ssa_env->locals[index++] = node;
         }
       }
-      // Initialize int64 locals.
-      if (function_env_->local_i64_count > 0) {
-        TFNode* zero = builder_->Int64Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_i64_count; i++) {
-          ssa_env->locals[pos++] = zero;
-        }
-      }
-      // Initialize float32 locals.
-      if (function_env_->local_f32_count > 0) {
-        TFNode* zero = builder_->Float32Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_f32_count; i++) {
-          ssa_env->locals[pos++] = zero;
-        }
-      }
-      // Initialize float64 locals.
-      if (function_env_->local_f64_count > 0) {
-        TFNode* zero = builder_->Float64Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_f64_count; i++) {
-          ssa_env->locals[pos++] = zero;
-        }
-      }
-      DCHECK_EQ(function_env_->total_locals, pos);
-      DCHECK_EQ(EnvironmentCount(), pos);
-      builder_->set_module(function_env_->module);
+      builder_->set_module(module_);
     }
     ssa_env->control = start;
     ssa_env->effect = start;
     SetEnv("initial", ssa_env);
   }
 
+  TFNode* DefaultValue(LocalType type) {
+    switch (type) {
+      case kAstI32:
+        return builder_->Int32Constant(0);
+      case kAstI64:
+        return builder_->Int64Constant(0);
+      case kAstF32:
+        return builder_->Float32Constant(0);
+      case kAstF64:
+        return builder_->Float64Constant(0);
+      default:
+        UNREACHABLE();
+        return nullptr;
+    }
+  }
+
   void Leaf(LocalType type, TFNode* node = nullptr) {
     size_t size = sizeof(Tree);
     Tree* tree = reinterpret_cast<Tree*>(zone_->New(size));
@@ -561,6 +566,45 @@
     return bytes;
   }
 
+  // Decodes the locals declarations, if any, populating {local_type_vec_}.
+  void DecodeLocalDecls() {
+    DCHECK_EQ(0, local_type_vec_.size());
+    // Initialize {local_type_vec} from signature.
+    if (sig_) {
+      local_type_vec_.reserve(sig_->parameter_count());
+      for (size_t i = 0; i < sig_->parameter_count(); i++) {
+        local_type_vec_.push_back(sig_->GetParam(i));
+      }
+    }
+    // Decode local declarations, if any.
+    int length;
+    uint32_t entries = consume_u32v(&length, "local decls count");
+    while (entries-- > 0 && pc_ < limit_) {
+      uint32_t count = consume_u32v(&length, "local count");
+      byte code = consume_u8("local type");
+      LocalType type;
+      switch (code) {
+        case kLocalI32:
+          type = kAstI32;
+          break;
+        case kLocalI64:
+          type = kAstI64;
+          break;
+        case kLocalF32:
+          type = kAstF32;
+          break;
+        case kLocalF64:
+          type = kAstF64;
+          break;
+        default:
+          error(pc_ - 1, "invalid local type");
+          return;
+      }
+      local_type_vec_.insert(local_type_vec_.end(), count, type);
+    }
+    total_locals_ = local_type_vec_.size();
+  }
+
   // Decodes the body of a function, producing reduced trees into {result}.
   void DecodeFunctionBody() {
     TRACE("wasm-decode %p...%p (%d bytes) %s\n",
@@ -621,7 +665,7 @@
             PushBlock(break_env);
             SsaEnv* cont_env = Steal(break_env);
             // The continue environment is the inner environment.
-            PrepareForLoop(cont_env);
+            PrepareForLoop(pc_, cont_env);
             SetEnv("loop:start", Split(cont_env));
             if (ssa_env_->go()) ssa_env_->state = SsaEnv::kReached;
             PushBlock(cont_env);
@@ -655,16 +699,16 @@
           len = 1 + operand.length;
           break;
         }
-        case kExprTableSwitch: {
-          TableSwitchOperand operand(this, pc_);
+        case kExprBrTable: {
+          BranchTableOperand operand(this, pc_);
           if (Validate(pc_, operand, blocks_.size())) {
-            Shift(kAstEnd, 1 + operand.case_count);
+            Shift(kAstEnd, 1);
           }
           len = 1 + operand.length;
           break;
         }
         case kExprReturn: {
-          int count = static_cast<int>(function_env_->sig->return_count());
+          int count = static_cast<int>(sig_->return_count());
           if (count == 0) {
             BUILD(Return, 0, builder_->Buffer(0));
             ssa_env_->Kill();
@@ -821,6 +865,7 @@
           len = 1 + operand.length;
           break;
         }
+        case kExprDeclLocals:
         default:
           error("Invalid opcode");
           return;
@@ -853,7 +898,7 @@
   }
 
   void AddImplicitReturnAtEnd() {
-    int retcount = static_cast<int>(function_env_->sig->return_count());
+    int retcount = static_cast<int>(sig_->return_count());
     if (retcount == 0) {
       BUILD0(ReturnVoid);
       return;
@@ -872,7 +917,7 @@
     for (int index = 0; index < retcount; index++) {
       Tree* tree = trees_[trees_.size() - 1 - index];
       if (buffer) buffer[index] = tree->node;
-      LocalType expected = function_env_->sig->GetReturn(index);
+      LocalType expected = sig_->GetReturn(index);
       if (tree->type != expected) {
         error(limit_, tree->pc,
               "ImplicitReturn[%d] expected type %s, found %s of type %s", index,
@@ -1043,73 +1088,42 @@
         }
         break;
       }
-      case kExprTableSwitch: {
+      case kExprBrTable: {
         if (p->index == 1) {
           // Switch key finished.
           TypeCheckLast(p, kAstI32);
           if (failed()) break;
 
-          TableSwitchOperand operand(this, p->pc());
+          BranchTableOperand operand(this, p->pc());
           DCHECK(Validate(p->pc(), operand, blocks_.size()));
 
-          // Build the switch only if it has more than just a default target.
-          bool build_switch = operand.table_count > 1;
+          // Build a switch only if it has more than just a default target.
+          bool build_switch = operand.table_count > 0;
           TFNode* sw = nullptr;
-          if (build_switch)
-            sw = BUILD(Switch, operand.table_count, p->last()->node);
-
-          // Allocate environments for each case.
-          SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count);
-          for (uint32_t i = 0; i < operand.case_count; i++) {
-            case_envs[i] = UnreachableEnv();
+          if (build_switch) {
+            sw = BUILD(Switch, operand.table_count + 1, p->last()->node);
           }
 
-          ifs_.push_back({nullptr, nullptr, case_envs});
-          SsaEnv* break_env = ssa_env_;
-          PushBlock(break_env);
-          SsaEnv* copy = Steal(break_env);
-          ssa_env_ = copy;
-
-          // Build the environments for each case based on the table.
-          for (uint32_t i = 0; i < operand.table_count; i++) {
-            uint16_t target = operand.read_entry(this, i);
+          // Process the targets of the break table.
+          SsaEnv* prev = ssa_env_;
+          SsaEnv* copy = Steal(prev);
+          for (uint32_t i = 0; i < operand.table_count + 1; i++) {
+            uint32_t target = operand.read_entry(this, i);
             SsaEnv* env = copy;
             if (build_switch) {
-              env = Split(env);
-              env->control = (i == operand.table_count - 1)
-                                 ? BUILD(IfDefault, sw)
-                                 : BUILD(IfValue, i, sw);
+              ssa_env_ = env = Split(env);
+              env->control = i == operand.table_count ? BUILD(IfDefault, sw)
+                                                      : BUILD(IfValue, i, sw);
             }
-            if (target >= 0x8000) {
-              // Targets an outer block.
-              int depth = target - 0x8000;
-              SsaEnv* tenv = blocks_[blocks_.size() - depth - 1].ssa_env;
-              Goto(env, tenv);
-            } else {
-              // Targets a case.
-              Goto(env, case_envs[target]);
-            }
+            SsaEnv* tenv = blocks_[blocks_.size() - target - 1].ssa_env;
+            Goto(env, tenv);
           }
-        }
-
-        if (p->done()) {
-          // Last case. Fall through to the end.
-          Block* block = &blocks_.back();
-          if (p->index > 1) ReduceBreakToExprBlock(p, block);
-          SsaEnv* next = block->ssa_env;
-          blocks_.pop_back();
-          ifs_.pop_back();
-          SetEnv("switch:end", next);
-        } else {
-          // Interior case. Maybe fall through to the next case.
-          SsaEnv* next = ifs_.back().case_envs[p->index - 1];
-          if (p->index > 1 && ssa_env_->go()) Goto(ssa_env_, next);
-          SetEnv("switch:case", next);
+          ssa_env_ = prev;
         }
         break;
       }
       case kExprReturn: {
-        TypeCheckLast(p, function_env_->sig->GetReturn(p->index - 1));
+        TypeCheckLast(p, sig_->GetReturn(p->index - 1));
         if (p->done()) {
           if (build()) {
             int count = p->tree->count;
@@ -1346,6 +1360,7 @@
   }
 
   void SetEnv(const char* reason, SsaEnv* env) {
+#if DEBUG
     TRACE("  env = %p, block depth = %d, reason = %s", static_cast<void*>(env),
           static_cast<int>(blocks_.size()), reason);
     if (FLAG_trace_wasm_decoder && env && env->control) {
@@ -1353,6 +1368,7 @@
       compiler::WasmGraphBuilder::PrintDebugName(env->control);
     }
     TRACE("\n");
+#endif
     ssa_env_ = env;
     if (builder_) {
       builder_->set_control_ptr(&env->control);
@@ -1389,8 +1405,7 @@
           TFNode* b = from->locals[i];
           if (a != b) {
             TFNode* vals[] = {a, b};
-            to->locals[i] =
-                builder_->Phi(function_env_->GetLocalType(i), 2, vals, merge);
+            to->locals[i] = builder_->Phi(local_type_vec_[i], 2, vals, merge);
           }
         }
         break;
@@ -1425,8 +1440,8 @@
               vals[j] = tnode;
             }
             vals[count - 1] = fnode;
-            to->locals[i] = builder_->Phi(function_env_->GetLocalType(i), count,
-                                          vals, merge);
+            to->locals[i] =
+                builder_->Phi(local_type_vec_[i], count, vals, merge);
           }
         }
         break;
@@ -1451,29 +1466,32 @@
     return tnode;
   }
 
-  void BuildInfiniteLoop() {
-    if (ssa_env_->go()) {
-      PrepareForLoop(ssa_env_);
-      SsaEnv* cont_env = ssa_env_;
-      ssa_env_ = Split(ssa_env_);
-      ssa_env_->state = SsaEnv::kReached;
-      Goto(ssa_env_, cont_env);
-    }
-  }
+  void PrepareForLoop(const byte* pc, SsaEnv* env) {
+    if (!env->go()) return;
+    env->state = SsaEnv::kMerged;
+    if (!builder_) return;
 
-  void PrepareForLoop(SsaEnv* env) {
-    if (env->go()) {
-      env->state = SsaEnv::kMerged;
-      if (builder_) {
-        env->control = builder_->Loop(env->control);
-        env->effect = builder_->EffectPhi(1, &env->effect, env->control);
-        builder_->Terminate(env->effect, env->control);
+    env->control = builder_->Loop(env->control);
+    env->effect = builder_->EffectPhi(1, &env->effect, env->control);
+    builder_->Terminate(env->effect, env->control);
+    if (FLAG_wasm_loop_assignment_analysis) {
+      BitVector* assigned = AnalyzeLoopAssignment(pc);
+      if (assigned != nullptr) {
+        // Only introduce phis for variables assigned in this loop.
         for (int i = EnvironmentCount() - 1; i >= 0; i--) {
-          env->locals[i] = builder_->Phi(function_env_->GetLocalType(i), 1,
-                                         &env->locals[i], env->control);
+          if (!assigned->Contains(i)) continue;
+          env->locals[i] = builder_->Phi(local_type_vec_[i], 1, &env->locals[i],
+                                         env->control);
         }
+        return;
       }
     }
+
+    // Conservatively introduce phis for all local variables.
+    for (int i = EnvironmentCount() - 1; i >= 0; i--) {
+      env->locals[i] =
+          builder_->Phi(local_type_vec_[i], 1, &env->locals[i], env->control);
+    }
   }
 
   // Create a complete copy of the {from}.
@@ -1524,7 +1542,7 @@
   }
 
   int EnvironmentCount() {
-    if (builder_) return static_cast<int>(function_env_->GetLocalCount());
+    if (builder_) return static_cast<int>(local_type_vec_.size());
     return 0;  // if we aren't building a graph, don't bother with SSA renaming.
   }
 
@@ -1560,23 +1578,84 @@
     PrintProduction(depth + 1);
   }
 #endif
+
+  BitVector* AnalyzeLoopAssignment(const byte* pc) {
+    if (pc >= limit_) return nullptr;
+    if (*pc != kExprLoop) return nullptr;
+
+    BitVector* assigned =
+        new (zone_) BitVector(static_cast<int>(total_locals_), zone_);
+    // Keep a stack to model the nesting of expressions.
+    std::vector<int> arity_stack;
+    arity_stack.push_back(OpcodeArity(pc));
+    pc += OpcodeLength(pc);
+
+    // Iteratively process all AST nodes nested inside the loop.
+    while (pc < limit_) {
+      WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
+      int arity = 0;
+      int length = 1;
+      int assigned_index = -1;
+      if (opcode == kExprSetLocal) {
+        LocalIndexOperand operand(this, pc);
+        if (assigned->length() > 0 &&
+            static_cast<int>(operand.index) < assigned->length()) {
+          // Unverified code might have an out-of-bounds index.
+          // Ignore out-of-bounds indices, as the main verification will fail.
+          assigned->Add(operand.index);
+          assigned_index = operand.index;
+        }
+        arity = 1;
+        length = 1 + operand.length;
+      } else {
+        arity = OpcodeArity(pc);
+        length = OpcodeLength(pc);
+      }
+
+      TRACE("loop-assign module+%-6d %s func+%d: 0x%02x %s", baserel(pc),
+            indentation(), startrel(pc), opcode,
+            WasmOpcodes::OpcodeName(opcode));
+
+      if (assigned_index >= 0) {
+        TRACE(" (assigned local #%d)\n", assigned_index);
+      } else {
+        TRACE("\n");
+      }
+
+      pc += length;
+      arity_stack.push_back(arity);
+      while (arity_stack.back() == 0) {
+        arity_stack.pop_back();
+        if (arity_stack.empty()) return assigned;  // reached end of loop
+        arity_stack.back()--;
+      }
+    }
+    return assigned;
+  }
 };
 
+bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
+                      const byte* end) {
+  base::AccountingAllocator allocator;
+  Zone tmp(&allocator);
+  FunctionBody body = {nullptr, nullptr, nullptr, start, end};
+  SR_WasmDecoder decoder(&tmp, nullptr, body);
+  return decoder.DecodeLocalDecls(decls);
+}
 
-TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
-                          const byte* end) {
-  Zone zone;
-  LR_WasmDecoder decoder(&zone, nullptr);
-  TreeResult result = decoder.Decode(env, base, start, end);
+TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+                          FunctionBody& body) {
+  Zone zone(allocator);
+  SR_WasmDecoder decoder(&zone, nullptr, body);
+  TreeResult result = decoder.Decode();
   return result;
 }
 
-
-TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
-                        const byte* start, const byte* end) {
-  Zone zone;
-  LR_WasmDecoder decoder(&zone, builder);
-  TreeResult result = decoder.Decode(env, base, start, end);
+TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+                        TFBuilder* builder, FunctionBody& body) {
+  Zone zone(allocator);
+  SR_WasmDecoder decoder(&zone, builder, body);
+  TreeResult result = decoder.Decode();
   return result;
 }
 
@@ -1608,20 +1687,49 @@
 }
 
 int OpcodeLength(const byte* pc, const byte* end) {
-  WasmDecoder decoder(nullptr, pc, end);
+  WasmDecoder decoder(nullptr, nullptr, pc, end);
   return decoder.OpcodeLength(pc);
 }
 
-int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) {
-  WasmDecoder decoder(env, pc, end);
+int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
+                const byte* end) {
+  WasmDecoder decoder(module, sig, pc, end);
   return decoder.OpcodeArity(pc);
 }
 
-void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
-  WasmDecoder decoder(env, start, end);
-  const byte* pc = start;
+void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
+  Zone zone(allocator);
+  SR_WasmDecoder decoder(&zone, nullptr, body);
+
+  OFStream os(stdout);
+
+  // Print the function signature.
+  if (body.sig) {
+    os << "// signature: " << *body.sig << std::endl;
+  }
+
+  // Print the local declarations.
+  AstLocalDecls decls(&zone);
+  decoder.DecodeLocalDecls(decls);
+  const byte* pc = decoder.pc();
+  if (body.start != decoder.pc()) {
+    printf("// locals:");
+    for (auto p : decls.local_types) {
+      LocalType type = p.first;
+      uint32_t count = p.second;
+      os << " " << count << " " << WasmOpcodes::TypeName(type);
+    }
+    os << std::endl;
+
+    for (const byte* locals = body.start; locals < pc; locals++) {
+      printf(" 0x%02x,", *locals);
+    }
+    printf("\n");
+  }
+
+  printf("// body: \n");
   std::vector<int> arity_stack;
-  while (pc < end) {
+  while (pc < body.end) {
     int arity = decoder.OpcodeArity(pc);
     size_t length = decoder.OpcodeLength(pc);
 
@@ -1636,6 +1744,35 @@
     for (size_t i = 1; i < length; i++) {
       printf(" 0x%02x,", pc[i]);
     }
+
+    if (body.module) {
+      switch (opcode) {
+        case kExprCallIndirect: {
+          SignatureIndexOperand operand(&decoder, pc);
+          if (decoder.Validate(pc, operand)) {
+            os << " // sig #" << operand.index << ": " << *operand.sig;
+          }
+          break;
+        }
+        case kExprCallImport: {
+          ImportIndexOperand operand(&decoder, pc);
+          if (decoder.Validate(pc, operand)) {
+            os << " // import #" << operand.index << ": " << *operand.sig;
+          }
+          break;
+        }
+        case kExprCallFunction: {
+          FunctionIndexOperand operand(&decoder, pc);
+          if (decoder.Validate(pc, operand)) {
+            os << " // function #" << operand.index << ": " << *operand.sig;
+          }
+          break;
+        }
+        default:
+          break;
+      }
+    }
+
     pc += length;
     printf("\n");
 
@@ -1648,65 +1785,11 @@
   }
 }
 
-// Analyzes loop bodies for static assignments to locals, which helps in
-// reducing the number of phis introduced at loop headers.
-class LoopAssignmentAnalyzer : public WasmDecoder {
- public:
-  LoopAssignmentAnalyzer(Zone* zone, FunctionEnv* function_env) : zone_(zone) {
-    function_env_ = function_env;
-  }
-
-  BitVector* Analyze(const byte* pc, const byte* limit) {
-    Decoder::Reset(pc, limit);
-    if (pc_ >= limit_) return nullptr;
-    if (*pc_ != kExprLoop) return nullptr;
-
-    BitVector* assigned =
-        new (zone_) BitVector(function_env_->total_locals, zone_);
-    // Keep a stack to model the nesting of expressions.
-    std::vector<int> arity_stack;
-    arity_stack.push_back(OpcodeArity(pc_));
-    pc_ += OpcodeLength(pc_);
-
-    // Iteratively process all AST nodes nested inside the loop.
-    while (pc_ < limit_) {
-      WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
-      int arity = 0;
-      int length = 1;
-      if (opcode == kExprSetLocal) {
-        LocalIndexOperand operand(this, pc_);
-        if (assigned->length() > 0 &&
-            static_cast<int>(operand.index) < assigned->length()) {
-          // Unverified code might have an out-of-bounds index.
-          assigned->Add(operand.index);
-        }
-        arity = 1;
-        length = 1 + operand.length;
-      } else {
-        arity = OpcodeArity(pc_);
-        length = OpcodeLength(pc_);
-      }
-
-      pc_ += length;
-      arity_stack.push_back(arity);
-      while (arity_stack.back() == 0) {
-        arity_stack.pop_back();
-        if (arity_stack.empty()) return assigned;  // reached end of loop
-        arity_stack.back()--;
-      }
-    }
-    return assigned;
-  }
-
- private:
-  Zone* zone_;
-};
-
-
-BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
                                            const byte* start, const byte* end) {
-  LoopAssignmentAnalyzer analyzer(zone, env);
-  return analyzer.Analyze(start, end);
+  FunctionBody body = {nullptr, nullptr, nullptr, start, end};
+  SR_WasmDecoder decoder(zone, nullptr, body);
+  return decoder.AnalyzeLoopAssignmentForTesting(start, num_locals);
 }
 
 }  // namespace wasm
diff --git a/src/wasm/ast-decoder.h b/src/wasm/ast-decoder.h
index 465baca..5376e7b 100644
--- a/src/wasm/ast-decoder.h
+++ b/src/wasm/ast-decoder.h
@@ -46,8 +46,7 @@
   int32_t value;
   int length;
   inline ImmI32Operand(Decoder* decoder, const byte* pc) {
-    value = bit_cast<int32_t>(decoder->checked_read_u32(pc, 1, "immi32"));
-    length = 4;
+    value = decoder->checked_read_i32v(pc, 1, &length, "immi32");
   }
 };
 
@@ -55,8 +54,7 @@
   int64_t value;
   int length;
   inline ImmI64Operand(Decoder* decoder, const byte* pc) {
-    value = bit_cast<int64_t>(decoder->checked_read_u64(pc, 1, "immi64"));
-    length = 8;
+    value = decoder->checked_read_i64v(pc, 1, &length, "immi64");
   }
 };
 
@@ -97,8 +95,7 @@
   Block* target;
   int length;
   inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
-    depth = decoder->checked_read_u8(pc, 1, "break depth");
-    length = 1;
+    depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
     target = nullptr;
   }
 };
@@ -107,8 +104,7 @@
   uint32_t count;
   int length;
   inline BlockCountOperand(Decoder* decoder, const byte* pc) {
-    count = decoder->checked_read_u8(pc, 1, "block count");
-    length = 1;
+    count = decoder->checked_read_u32v(pc, 1, &length, "block count");
   }
 };
 
@@ -142,103 +138,55 @@
   }
 };
 
-struct TableSwitchOperand {
-  uint32_t case_count;
+struct BranchTableOperand {
   uint32_t table_count;
   const byte* table;
   int length;
-  inline TableSwitchOperand(Decoder* decoder, const byte* pc) {
-    case_count = decoder->checked_read_u16(pc, 1, "expected #cases");
-    table_count = decoder->checked_read_u16(pc, 3, "expected #entries");
-    length = 4 + table_count * 2;
+  inline BranchTableOperand(Decoder* decoder, const byte* pc) {
+    int varint_length;
+    table_count =
+        decoder->checked_read_u32v(pc, 1, &varint_length, "expected #entries");
+    length = varint_length + (table_count + 1) * sizeof(uint32_t);
 
-    if (decoder->check(pc, 5, table_count * 2, "expected <table entries>")) {
-      table = pc + 5;
+    uint32_t table_start = 1 + varint_length;
+    if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
+                       "expected <table entries>")) {
+      table = pc + table_start;
     } else {
       table = nullptr;
     }
   }
-  inline uint16_t read_entry(Decoder* decoder, int i) {
-    DCHECK(i >= 0 && static_cast<uint32_t>(i) < table_count);
-    return table ? decoder->read_u16(table + i * sizeof(uint16_t)) : 0;
+  inline uint32_t read_entry(Decoder* decoder, int i) {
+    DCHECK(i >= 0 && static_cast<uint32_t>(i) <= table_count);
+    return table ? decoder->read_u32(table + i * sizeof(uint32_t)) : 0;
   }
 };
 
 struct MemoryAccessOperand {
-  bool aligned;
+  uint32_t alignment;
   uint32_t offset;
   int length;
   inline MemoryAccessOperand(Decoder* decoder, const byte* pc) {
-    byte bitfield = decoder->checked_read_u8(pc, 1, "memory access byte");
-    aligned = MemoryAccess::AlignmentField::decode(bitfield);
-    if (MemoryAccess::OffsetField::decode(bitfield)) {
-      offset = decoder->checked_read_u32v(pc, 2, &length, "memory offset");
-      length++;
-    } else {
-      offset = 0;
-      length = 1;
-    }
+    int alignment_length;
+    alignment =
+        decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment");
+    int offset_length;
+    offset = decoder->checked_read_u32v(pc, 1 + alignment_length,
+                                        &offset_length, "offset");
+    length = alignment_length + offset_length;
   }
 };
 
 typedef compiler::WasmGraphBuilder TFBuilder;
 struct ModuleEnv;  // forward declaration of module interface.
 
-// Interface the function environment during decoding, include the signature
-// and number of locals.
-struct FunctionEnv {
-  ModuleEnv* module;             // module environment
-  FunctionSig* sig;              // signature of this function
-  uint32_t local_i32_count;      // number of int32 locals
-  uint32_t local_i64_count;      // number of int64 locals
-  uint32_t local_f32_count;      // number of float32 locals
-  uint32_t local_f64_count;      // number of float64 locals
-  uint32_t total_locals;         // sum of parameters and all locals
-
-  uint32_t GetLocalCount() { return total_locals; }
-  LocalType GetLocalType(uint32_t index) {
-    if (index < static_cast<uint32_t>(sig->parameter_count())) {
-      return sig->GetParam(index);
-    }
-    index -= static_cast<uint32_t>(sig->parameter_count());
-    if (index < local_i32_count) return kAstI32;
-    index -= local_i32_count;
-    if (index < local_i64_count) return kAstI64;
-    index -= local_i64_count;
-    if (index < local_f32_count) return kAstF32;
-    index -= local_f32_count;
-    if (index < local_f64_count) return kAstF64;
-    return kAstStmt;
-  }
-
-  void AddLocals(LocalType type, uint32_t count) {
-    switch (type) {
-      case kAstI32:
-        local_i32_count += count;
-        break;
-      case kAstI64:
-        local_i64_count += count;
-        break;
-      case kAstF32:
-        local_f32_count += count;
-        break;
-      case kAstF64:
-        local_f64_count += count;
-        break;
-      default:
-        UNREACHABLE();
-    }
-    total_locals += count;
-    DCHECK_EQ(total_locals,
-              (sig->parameter_count() + local_i32_count + local_i64_count +
-               local_f32_count + local_f64_count));
-  }
-
-  void SumLocals() {
-    total_locals = static_cast<uint32_t>(sig->parameter_count()) +
-                   local_i32_count + local_i64_count + local_f32_count +
-                   local_f64_count;
-  }
+// All of the various data structures necessary to decode a function body.
+struct FunctionBody {
+  ModuleEnv* module;  // module environment
+  FunctionSig* sig;   // function signature
+  const byte* base;   // base of the module bytes, for error reporting
+  const byte* start;  // start of the function body
+  const byte* end;    // end of the function body
 };
 
 struct Tree;
@@ -246,21 +194,25 @@
 
 std::ostream& operator<<(std::ostream& os, const Tree& tree);
 
-TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
-                          const byte* end);
-TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
-                        const byte* start, const byte* end);
+TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+                          FunctionBody& body);
+TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+                        TFBuilder* builder, FunctionBody& body);
+void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body);
 
-void PrintAst(FunctionEnv* env, const byte* start, const byte* end);
-
-inline TreeResult VerifyWasmCode(FunctionEnv* env, const byte* start,
-                                 const byte* end) {
-  return VerifyWasmCode(env, nullptr, start, end);
+inline TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+                                 ModuleEnv* module, FunctionSig* sig,
+                                 const byte* start, const byte* end) {
+  FunctionBody body = {module, sig, nullptr, start, end};
+  return VerifyWasmCode(allocator, body);
 }
 
-inline TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env,
-                               const byte* start, const byte* end) {
-  return BuildTFGraph(builder, env, nullptr, start, end);
+inline TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+                               TFBuilder* builder, ModuleEnv* module,
+                               FunctionSig* sig, const byte* start,
+                               const byte* end) {
+  FunctionBody body = {module, sig, nullptr, start, end};
+  return BuildTFGraph(allocator, builder, body);
 }
 
 enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 };
@@ -268,14 +220,31 @@
 ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte*, const byte*,
                                                       int*, uint32_t*);
 
-BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
+struct AstLocalDecls {
+  // The size of the encoded declarations.
+  uint32_t decls_encoded_size;  // size of encoded declarations
+
+  // Total number of locals.
+  uint32_t total_local_count;
+
+  // List of {local type, count} pairs.
+  ZoneVector<std::pair<LocalType, uint32_t>> local_types;
+
+  // Constructor initializes the vector.
+  explicit AstLocalDecls(Zone* zone)
+      : decls_encoded_size(0), total_local_count(0), local_types(zone) {}
+};
+
+bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, const byte* end);
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
                                            const byte* start, const byte* end);
 
 // Computes the length of the opcode at the given address.
 int OpcodeLength(const byte* pc, const byte* end);
 
 // Computes the arity (number of sub-nodes) of the opcode at the given address.
-int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end);
+int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
+                const byte* end);
 }  // namespace wasm
 }  // namespace internal
 }  // namespace v8
diff --git a/src/wasm/decoder.h b/src/wasm/decoder.h
index 0e88eda..f9de2e1 100644
--- a/src/wasm/decoder.h
+++ b/src/wasm/decoder.h
@@ -77,33 +77,44 @@
     return check(base, offset, 8, msg) ? read_u64(base + offset) : 0;
   }
 
+  // Reads a variable-length unsigned integer (little endian).
   uint32_t checked_read_u32v(const byte* base, int offset, int* length,
-                             const char* msg = "expected LEB128") {
-    if (!check(base, offset, 1, msg)) {
-      *length = 0;
-      return 0;
-    }
+                             const char* msg = "expected LEB32") {
+    return checked_read_leb<uint32_t, false>(base, offset, length, msg);
+  }
 
-    const ptrdiff_t kMaxDiff = 5;  // maximum 5 bytes.
-    const byte* ptr = base + offset;
-    const byte* end = ptr + kMaxDiff;
-    if (end > limit_) end = limit_;
-    int shift = 0;
-    byte b = 0;
-    uint32_t result = 0;
-    while (ptr < end) {
-      b = *ptr++;
-      result = result | ((b & 0x7F) << shift);
-      if ((b & 0x80) == 0) break;
-      shift += 7;
+  // Reads a variable-length signed integer (little endian).
+  int32_t checked_read_i32v(const byte* base, int offset, int* length,
+                            const char* msg = "expected SLEB32") {
+    uint32_t result =
+        checked_read_leb<uint32_t, true>(base, offset, length, msg);
+    if (*length == 5) return bit_cast<int32_t>(result);
+    if (*length > 0) {
+      int shift = 32 - 7 * *length;
+      // Perform sign extension.
+      return bit_cast<int32_t>(result << shift) >> shift;
     }
-    DCHECK_LE(ptr - (base + offset), kMaxDiff);
-    *length = static_cast<int>(ptr - (base + offset));
-    if (ptr == end && (b & 0x80)) {
-      error(base, ptr, msg);
-      return 0;
+    return 0;
+  }
+
+  // Reads a variable-length unsigned integer (little endian).
+  uint64_t checked_read_u64v(const byte* base, int offset, int* length,
+                             const char* msg = "expected LEB64") {
+    return checked_read_leb<uint64_t, false>(base, offset, length, msg);
+  }
+
+  // Reads a variable-length signed integer (little endian).
+  int64_t checked_read_i64v(const byte* base, int offset, int* length,
+                            const char* msg = "expected SLEB64") {
+    uint64_t result =
+        checked_read_leb<uint64_t, true>(base, offset, length, msg);
+    if (*length == 10) return bit_cast<int64_t>(result);
+    if (*length > 0) {
+      int shift = 64 - 7 * *length;
+      // Perform sign extension.
+      return bit_cast<int64_t>(result << shift) >> shift;
     }
-    return result;
+    return 0;
   }
 
   // Reads a single 16-bit unsigned integer (little endian).
@@ -214,6 +225,8 @@
       *length = static_cast<int>(pc_ - pos);
       if (pc_ == end && (b & 0x80)) {
         error(pc_ - 1, "varint too large");
+      } else if (*length == 0) {
+        error(pc_, "varint of length 0");
       } else {
         TRACE("= %u\n", result);
       }
@@ -222,9 +235,22 @@
     return traceOffEnd<uint32_t>();
   }
 
+  // Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
+  void consume_bytes(int size) {
+    if (checkAvailable(size)) {
+      pc_ += size;
+    } else {
+      pc_ = limit_;
+    }
+  }
+
   // Check that at least {size} bytes exist between {pc_} and {limit_}.
   bool checkAvailable(int size) {
-    if (pc_ < start_ || (pc_ + size) > limit_) {
+    intptr_t pc_overflow_value = std::numeric_limits<intptr_t>::max() - size;
+    if (size < 0 || (intptr_t)pc_ > pc_overflow_value) {
+      error(pc_, nullptr, "reading %d bytes would underflow/overflow", size);
+      return false;
+    } else if (pc_ < start_ || limit_ < (pc_ + size)) {
       error(pc_, nullptr, "expected %d bytes, fell off end", size);
       return false;
     } else {
@@ -232,12 +258,6 @@
     }
   }
 
-  bool RangeOk(const byte* pc, int length) {
-    if (pc < start_ || pc_ >= limit_) return false;
-    if ((pc + length) >= limit_) return false;
-    return true;
-  }
-
   void error(const char* msg) { error(pc_, nullptr, msg); }
 
   void error(const byte* pc, const char* msg) { error(pc, nullptr, msg); }
@@ -283,12 +303,13 @@
   Result<T> toResult(T val) {
     Result<T> result;
     if (error_pc_) {
+      TRACE("Result error: %s\n", error_msg_.get());
       result.error_code = kError;
       result.start = start_;
       result.error_pc = error_pc_;
       result.error_pt = error_pt_;
-      result.error_msg = error_msg_;
-      error_msg_.Reset(nullptr);
+      // transfer ownership of the error to the result.
+      result.error_msg.Reset(error_msg_.Detach());
     } else {
       result.error_code = kSuccess;
     }
@@ -308,7 +329,12 @@
   }
 
   bool ok() const { return error_pc_ == nullptr; }
-  bool failed() const { return error_pc_ != nullptr; }
+  bool failed() const { return !error_msg_.is_empty(); }
+  bool more() const { return pc_ < limit_; }
+
+  const byte* start() { return start_; }
+  const byte* pc() { return pc_; }
+  uint32_t pc_offset() { return static_cast<uint32_t>(pc_ - start_); }
 
  protected:
   const byte* start_;
@@ -318,6 +344,60 @@
   const byte* error_pc_;
   const byte* error_pt_;
   base::SmartArrayPointer<char> error_msg_;
+
+ private:
+  template <typename IntType, bool is_signed>
+  IntType checked_read_leb(const byte* base, int offset, int* length,
+                           const char* msg) {
+    if (!check(base, offset, 1, msg)) {
+      *length = 0;
+      return 0;
+    }
+
+    const int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
+    const byte* ptr = base + offset;
+    const byte* end = ptr + kMaxLength;
+    if (end > limit_) end = limit_;
+    int shift = 0;
+    byte b = 0;
+    IntType result = 0;
+    while (ptr < end) {
+      b = *ptr++;
+      result = result | (static_cast<IntType>(b & 0x7F) << shift);
+      if ((b & 0x80) == 0) break;
+      shift += 7;
+    }
+    DCHECK_LE(ptr - (base + offset), kMaxLength);
+    *length = static_cast<int>(ptr - (base + offset));
+    if (ptr == end) {
+      // Check there are no bits set beyond the bitwidth of {IntType}.
+      const int kExtraBits = (1 + kMaxLength * 7) - (sizeof(IntType) * 8);
+      const byte kExtraBitsMask =
+          static_cast<byte>((0xFF << (8 - kExtraBits)) & 0xFF);
+      int extra_bits_value;
+      if (is_signed) {
+        // A signed-LEB128 must sign-extend the final byte, excluding its
+        // most-signifcant bit. e.g. for a 32-bit LEB128:
+        //   kExtraBits = 4
+        //   kExtraBitsMask = 0xf0
+        // If b is 0x0f, the value is negative, so extra_bits_value is 0x70.
+        // If b is 0x03, the value is positive, so extra_bits_value is 0x00.
+        extra_bits_value = (static_cast<int8_t>(b << kExtraBits) >> 8) &
+                           kExtraBitsMask & ~0x80;
+      } else {
+        extra_bits_value = 0;
+      }
+      if (*length == kMaxLength && (b & kExtraBitsMask) != extra_bits_value) {
+        error(base, ptr, "extra bits in varint");
+        return 0;
+      }
+      if ((b & 0x80) != 0) {
+        error(base, ptr, msg);
+        return 0;
+      }
+    }
+    return result;
+  }
 };
 
 #undef TRACE
diff --git a/src/wasm/encoder.cc b/src/wasm/encoder.cc
index d80a275..92e6b11 100644
--- a/src/wasm/encoder.cc
+++ b/src/wasm/encoder.cc
@@ -10,11 +10,21 @@
 
 #include "src/wasm/ast-decoder.h"
 #include "src/wasm/encoder.h"
+#include "src/wasm/wasm-macro-gen.h"
 #include "src/wasm/wasm-module.h"
 #include "src/wasm/wasm-opcodes.h"
 
 #include "src/v8memory.h"
 
+#if DEBUG
+#define TRACE(...)                                    \
+  do {                                                \
+    if (FLAG_trace_wasm_encoder) PrintF(__VA_ARGS__); \
+  } while (false)
+#else
+#define TRACE(...)
+#endif
+
 namespace v8 {
 namespace internal {
 namespace wasm {
@@ -40,6 +50,11 @@
   *b += 4;
 }
 
+// Sections all start with a size, but it's unknown at the start.
+// We generate a large varint which we then fixup later when the size is known.
+//
+// TODO(jfb) Not strictly necessary since sizes are calculated ahead of time.
+const size_t padded_varint = 5;
 
 void EmitVarInt(byte** b, size_t val) {
   while (true) {
@@ -54,8 +69,47 @@
     }
   }
 }
-}  // namespace
 
+size_t SizeOfVarInt(size_t value) {
+  size_t size = 0;
+  do {
+    size++;
+    value = value >> 7;
+  } while (value > 0);
+  return size;
+}
+
+void FixupSection(byte* start, byte* end) {
+  // Same as EmitVarInt, but fixed-width with zeroes in the MSBs.
+  size_t val = end - start - padded_varint;
+  TRACE("  fixup %u\n", (unsigned)val);
+  for (size_t pos = 0; pos != padded_varint; ++pos) {
+    size_t next = val >> 7;
+    byte out = static_cast<byte>(val & 0x7f);
+    if (pos != padded_varint - 1) {
+      *(start++) = 0x80 | out;
+      val = next;
+    } else {
+      *(start++) = out;
+      // TODO(jfb) check that the pre-allocated fixup size isn't overflowed.
+    }
+  }
+}
+
+// Returns the start of the section, where the section VarInt size is.
+byte* EmitSection(WasmSection::Code code, byte** b) {
+  byte* start = *b;
+  const char* name = WasmSection::getName(code);
+  size_t length = WasmSection::getNameLength(code);
+  TRACE("emit section: %s\n", name);
+  for (size_t padding = 0; padding != padded_varint; ++padding) {
+    EmitUint8(b, 0xff);  // Will get fixed up later.
+  }
+  EmitVarInt(b, length);  // Section name string size.
+  for (size_t i = 0; i != length; ++i) EmitUint8(b, name[i]);
+  return start;
+}
+}  // namespace
 
 struct WasmFunctionBuilder::Type {
   bool param_;
@@ -120,16 +174,48 @@
   body_.push_back(immediate);
 }
 
+void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
+                                       const byte imm2) {
+  body_.push_back(static_cast<byte>(opcode));
+  body_.push_back(imm1);
+  body_.push_back(imm2);
+}
 
-uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) {
-  body_.push_back(immediate);
+void WasmFunctionBuilder::EmitWithVarInt(WasmOpcode opcode,
+                                         uint32_t immediate) {
+  body_.push_back(static_cast<byte>(opcode));
+  size_t immediate_size = SizeOfVarInt(immediate);
+  body_.insert(body_.end(), immediate_size, 0);
+  byte* p = &body_[body_.size() - immediate_size];
+  EmitVarInt(&p, immediate);
+}
+
+uint32_t WasmFunctionBuilder::EmitEditableVarIntImmediate() {
+  // Guess that the immediate will be 1 byte. If it is more, we'll have to
+  // shift everything down.
+  body_.push_back(0);
   return static_cast<uint32_t>(body_.size()) - 1;
 }
 
+void WasmFunctionBuilder::EditVarIntImmediate(uint32_t offset,
+                                              const uint32_t immediate) {
+  uint32_t immediate_size = static_cast<uint32_t>(SizeOfVarInt(immediate));
+  // In EmitEditableVarIntImmediate, we guessed that we'd only need one byte.
+  // If we need more, shift everything down to make room for the larger
+  // immediate.
+  if (immediate_size > 1) {
+    uint32_t diff = immediate_size - 1;
+    body_.insert(body_.begin() + offset, diff, 0);
 
-void WasmFunctionBuilder::EditImmediate(uint32_t offset, const byte immediate) {
-  DCHECK(offset < body_.size());
-  body_[offset] = immediate;
+    for (size_t i = 0; i < local_indices_.size(); ++i) {
+      if (local_indices_[i] >= offset) {
+        local_indices_[i] += diff;
+      }
+    }
+  }
+  DCHECK(offset + immediate_size <= body_.size());
+  byte* p = &body_[offset];
+  EmitVarInt(&p, immediate);
 }
 
 
@@ -144,7 +230,6 @@
     for (int i = 0; i < name_length; i++) {
       name_.push_back(*(name + i));
     }
-    name_.push_back('\0');
   }
 }
 
@@ -250,15 +335,25 @@
 
 uint32_t WasmFunctionEncoder::HeaderSize() const {
   uint32_t size = 3;
-  if (HasLocals()) size += 8;
   if (!external_) size += 2;
-  if (HasName()) size += 4;
+  if (HasName()) {
+    uint32_t name_size = NameSize();
+    size += static_cast<uint32_t>(SizeOfVarInt(name_size)) + name_size;
+  }
   return size;
 }
 
 
 uint32_t WasmFunctionEncoder::BodySize(void) const {
-  return external_ ? 0 : static_cast<uint32_t>(body_.size());
+  // TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
+  LocalDeclEncoder local_decl;
+  local_decl.AddLocals(local_i32_count_, kAstI32);
+  local_decl.AddLocals(local_i64_count_, kAstI64);
+  local_decl.AddLocals(local_f32_count_, kAstF32);
+  local_decl.AddLocals(local_f64_count_, kAstF64);
+
+  return external_ ? 0
+                   : static_cast<uint32_t>(body_.size() + local_decl.Size());
 }
 
 
@@ -271,28 +366,29 @@
                                     byte** body) const {
   uint8_t decl_bits = (exported_ ? kDeclFunctionExport : 0) |
                       (external_ ? kDeclFunctionImport : 0) |
-                      (HasLocals() ? kDeclFunctionLocals : 0) |
                       (HasName() ? kDeclFunctionName : 0);
 
   EmitUint8(header, decl_bits);
   EmitUint16(header, signature_index_);
 
   if (HasName()) {
-    uint32_t name_offset = static_cast<uint32_t>(*body - buffer);
-    EmitUint32(header, name_offset);
-    std::memcpy(*body, &name_[0], name_.size());
-    (*body) += name_.size();
+    EmitVarInt(header, NameSize());
+    for (size_t i = 0; i < name_.size(); ++i) {
+      EmitUint8(header, name_[i]);
+    }
   }
 
-  if (HasLocals()) {
-    EmitUint16(header, local_i32_count_);
-    EmitUint16(header, local_i64_count_);
-    EmitUint16(header, local_f32_count_);
-    EmitUint16(header, local_f64_count_);
-  }
 
   if (!external_) {
-    EmitUint16(header, static_cast<uint16_t>(body_.size()));
+    // TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
+    LocalDeclEncoder local_decl;
+    local_decl.AddLocals(local_i32_count_, kAstI32);
+    local_decl.AddLocals(local_i64_count_, kAstI64);
+    local_decl.AddLocals(local_f32_count_, kAstF32);
+    local_decl.AddLocals(local_f64_count_, kAstF64);
+
+    EmitUint16(header, static_cast<uint16_t>(body_.size() + local_decl.Size()));
+    (*header) += local_decl.Emit(*header);
     if (body_.size() > 0) {
       std::memcpy(*header, &body_[0], body_.size());
       (*header) += body_.size();
@@ -323,17 +419,13 @@
 
 void WasmDataSegmentEncoder::Serialize(byte* buffer, byte** header,
                                        byte** body) const {
-  uint32_t body_offset = static_cast<uint32_t>(*body - buffer);
-  EmitUint32(header, dest_);
-  EmitUint32(header, body_offset);
-  EmitUint32(header, static_cast<uint32_t>(data_.size()));
-  EmitUint8(header, 1);  // init
+  EmitVarInt(header, dest_);
+  EmitVarInt(header, static_cast<uint32_t>(data_.size()));
 
-  std::memcpy(*body, &data_[0], data_.size());
-  (*body) += data_.size();
+  std::memcpy(*header, &data_[0], data_.size());
+  (*header) += data_.size();
 }
 
-
 WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
     : zone_(zone),
       signatures_(zone),
@@ -341,8 +433,8 @@
       data_segments_(zone),
       indirect_functions_(zone),
       globals_(zone),
-      signature_map_(zone) {}
-
+      signature_map_(zone),
+      start_function_index_(-1) {}
 
 uint16_t WasmModuleBuilder::AddFunction() {
   functions_.push_back(new (zone_) WasmFunctionBuilder(zone_));
@@ -399,6 +491,9 @@
   indirect_functions_.push_back(index);
 }
 
+void WasmModuleBuilder::MarkStartFunction(uint16_t index) {
+  start_function_index_ = index;
+}
 
 WasmModuleWriter* WasmModuleBuilder::Build(Zone* zone) {
   WasmModuleWriter* writer = new (zone) WasmModuleWriter(zone);
@@ -417,6 +512,7 @@
   for (auto global : globals_) {
     writer->globals_.push_back(global);
   }
+  writer->start_function_index_ = start_function_index_;
   return writer;
 }
 
@@ -434,7 +530,6 @@
       indirect_functions_(zone),
       globals_(zone) {}
 
-
 struct Sizes {
   size_t header_size;
   size_t body_size;
@@ -446,80 +541,124 @@
     body_size += body;
   }
 
-  void AddSection(size_t size) {
-    if (size > 0) {
-      Add(1, 0);
-      while (size > 0) {
-        Add(1, 0);
-        size = size >> 7;
-      }
-    }
+  void AddSection(WasmSection::Code code, size_t other_size) {
+    Add(padded_varint + SizeOfVarInt(WasmSection::getNameLength(code)) +
+            WasmSection::getNameLength(code),
+        0);
+    if (other_size) Add(SizeOfVarInt(other_size), 0);
   }
 };
 
-
 WasmModuleIndex* WasmModuleWriter::WriteTo(Zone* zone) const {
   Sizes sizes = {0, 0};
 
-  sizes.Add(1, 0);
+  sizes.Add(2 * sizeof(uint32_t), 0);  // header
+
+  sizes.AddSection(WasmSection::Code::Memory, 0);
   sizes.Add(kDeclMemorySize, 0);
+  TRACE("Size after memory: %u, %u\n", (unsigned)sizes.header_size,
+        (unsigned)sizes.body_size);
 
-  sizes.AddSection(signatures_.size());
-  for (auto sig : signatures_) {
-    sizes.Add(2 + sig->parameter_count(), 0);
-  }
-
-  sizes.AddSection(globals_.size());
   if (globals_.size() > 0) {
-    sizes.Add(kDeclGlobalSize * globals_.size(), 0);
+    sizes.AddSection(WasmSection::Code::Globals, globals_.size());
+    /* These globals never have names, so are always 3 bytes. */
+    sizes.Add(3 * globals_.size(), 0);
+    TRACE("Size after globals: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
   }
 
-  sizes.AddSection(functions_.size());
-  for (auto function : functions_) {
-    sizes.Add(function->HeaderSize() + function->BodySize(),
-              function->NameSize());
+  if (signatures_.size() > 0) {
+    sizes.AddSection(WasmSection::Code::Signatures, signatures_.size());
+    for (auto sig : signatures_) {
+      sizes.Add(
+          1 + SizeOfVarInt(sig->parameter_count()) + sig->parameter_count(), 0);
+    }
+    TRACE("Size after signatures: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
   }
 
-  sizes.AddSection(data_segments_.size());
-  for (auto segment : data_segments_) {
-    sizes.Add(segment->HeaderSize(), segment->BodySize());
+  if (functions_.size() > 0) {
+    sizes.AddSection(WasmSection::Code::Functions, functions_.size());
+    for (auto function : functions_) {
+      sizes.Add(function->HeaderSize() + function->BodySize(),
+                function->NameSize());
+    }
+    TRACE("Size after functions: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
   }
 
-  sizes.AddSection(indirect_functions_.size());
-  sizes.Add(2 * static_cast<uint32_t>(indirect_functions_.size()), 0);
+  if (start_function_index_ >= 0) {
+    sizes.AddSection(WasmSection::Code::StartFunction, 0);
+    sizes.Add(SizeOfVarInt(start_function_index_), 0);
+    TRACE("Size after start: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
+  }
 
-  if (sizes.body_size > 0) sizes.Add(1, 0);
+  if (data_segments_.size() > 0) {
+    sizes.AddSection(WasmSection::Code::DataSegments, data_segments_.size());
+    for (auto segment : data_segments_) {
+      sizes.Add(segment->HeaderSize(), segment->BodySize());
+    }
+    TRACE("Size after data segments: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
+  }
+
+  if (indirect_functions_.size() > 0) {
+    sizes.AddSection(WasmSection::Code::FunctionTable,
+                     indirect_functions_.size());
+    for (auto function_index : indirect_functions_) {
+      sizes.Add(SizeOfVarInt(function_index), 0);
+    }
+    TRACE("Size after indirect functions: %u, %u\n",
+          (unsigned)sizes.header_size, (unsigned)sizes.body_size);
+  }
+
+  if (sizes.body_size > 0) {
+    sizes.AddSection(WasmSection::Code::End, 0);
+    TRACE("Size after end: %u, %u\n", (unsigned)sizes.header_size,
+          (unsigned)sizes.body_size);
+  }
 
   ZoneVector<uint8_t> buffer_vector(sizes.total(), zone);
   byte* buffer = &buffer_vector[0];
   byte* header = buffer;
   byte* body = buffer + sizes.header_size;
 
+  // -- emit magic -------------------------------------------------------------
+  TRACE("emit magic\n");
+  EmitUint32(&header, kWasmMagic);
+  EmitUint32(&header, kWasmVersion);
+
   // -- emit memory declaration ------------------------------------------------
-  EmitUint8(&header, kDeclMemory);
-  EmitUint8(&header, 16);  // min memory size
-  EmitUint8(&header, 16);  // max memory size
-  EmitUint8(&header, 0);   // memory export
+  {
+    byte* section = EmitSection(WasmSection::Code::Memory, &header);
+    EmitVarInt(&header, 16);  // min memory size
+    EmitVarInt(&header, 16);  // max memory size
+    EmitUint8(&header, 0);    // memory export
+    static_assert(kDeclMemorySize == 3, "memory size must match emit above");
+    FixupSection(section, header);
+  }
 
   // -- emit globals -----------------------------------------------------------
   if (globals_.size() > 0) {
-    EmitUint8(&header, kDeclGlobals);
+    byte* section = EmitSection(WasmSection::Code::Globals, &header);
     EmitVarInt(&header, globals_.size());
 
     for (auto global : globals_) {
-      EmitUint32(&header, 0);
+      EmitVarInt(&header, 0);  // Length of the global name.
       EmitUint8(&header, WasmOpcodes::MemTypeCodeFor(global.first));
       EmitUint8(&header, global.second);
     }
+    FixupSection(section, header);
   }
 
   // -- emit signatures --------------------------------------------------------
   if (signatures_.size() > 0) {
-    EmitUint8(&header, kDeclSignatures);
+    byte* section = EmitSection(WasmSection::Code::Signatures, &header);
     EmitVarInt(&header, signatures_.size());
 
     for (FunctionSig* sig : signatures_) {
-      EmitUint8(&header, static_cast<byte>(sig->parameter_count()));
+      EmitVarInt(&header, sig->parameter_count());
       if (sig->return_count() > 0) {
         EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetReturn()));
       } else {
@@ -529,39 +668,53 @@
         EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
       }
     }
+    FixupSection(section, header);
   }
 
   // -- emit functions ---------------------------------------------------------
   if (functions_.size() > 0) {
-    EmitUint8(&header, kDeclFunctions);
+    byte* section = EmitSection(WasmSection::Code::Functions, &header);
     EmitVarInt(&header, functions_.size());
 
     for (auto func : functions_) {
       func->Serialize(buffer, &header, &body);
     }
+    FixupSection(section, header);
+  }
+
+  // -- emit start function index ----------------------------------------------
+  if (start_function_index_ >= 0) {
+    byte* section = EmitSection(WasmSection::Code::StartFunction, &header);
+    EmitVarInt(&header, start_function_index_);
+    FixupSection(section, header);
   }
 
   // -- emit data segments -----------------------------------------------------
   if (data_segments_.size() > 0) {
-    EmitUint8(&header, kDeclDataSegments);
+    byte* section = EmitSection(WasmSection::Code::DataSegments, &header);
     EmitVarInt(&header, data_segments_.size());
 
     for (auto segment : data_segments_) {
       segment->Serialize(buffer, &header, &body);
     }
+    FixupSection(section, header);
   }
 
   // -- emit function table ----------------------------------------------------
   if (indirect_functions_.size() > 0) {
-    EmitUint8(&header, kDeclFunctionTable);
+    byte* section = EmitSection(WasmSection::Code::FunctionTable, &header);
     EmitVarInt(&header, indirect_functions_.size());
 
     for (auto index : indirect_functions_) {
-      EmitUint16(&header, index);
+      EmitVarInt(&header, index);
     }
+    FixupSection(section, header);
   }
 
-  if (sizes.body_size > 0) EmitUint8(&header, kDeclEnd);
+  if (sizes.body_size > 0) {
+    byte* section = EmitSection(WasmSection::Code::End, &header);
+    FixupSection(section, header);
+  }
 
   return new (zone) WasmModuleIndex(buffer, buffer + sizes.total());
 }
diff --git a/src/wasm/encoder.h b/src/wasm/encoder.h
index 7b651bf..49a7bf7 100644
--- a/src/wasm/encoder.h
+++ b/src/wasm/encoder.h
@@ -42,11 +42,6 @@
   ZoneVector<uint8_t> body_;
   ZoneVector<char> name_;
 
-  bool HasLocals() const {
-    return (local_i32_count_ + local_i64_count_ + local_f32_count_ +
-            local_f64_count_) > 0;
-  }
-
   bool HasName() const { return (exported_ || external_) && name_.size() > 0; }
 };
 
@@ -60,8 +55,10 @@
                 const uint32_t* local_indices, uint32_t indices_size);
   void Emit(WasmOpcode opcode);
   void EmitWithU8(WasmOpcode opcode, const byte immediate);
-  uint32_t EmitEditableImmediate(const byte immediate);
-  void EditImmediate(uint32_t offset, const byte immediate);
+  void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
+  void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
+  uint32_t EmitEditableVarIntImmediate();
+  void EditVarIntImmediate(uint32_t offset, const uint32_t immediate);
   void Exported(uint8_t flag);
   void External(uint8_t flag);
   void SetName(const unsigned char* name, int name_length);
@@ -120,6 +117,7 @@
   ZoneVector<FunctionSig*> signatures_;
   ZoneVector<uint16_t> indirect_functions_;
   ZoneVector<std::pair<MachineType, bool>> globals_;
+  int start_function_index_;
 };
 
 class WasmModuleBuilder : public ZoneObject {
@@ -131,6 +129,7 @@
   void AddDataSegment(WasmDataSegmentEncoder* data);
   uint16_t AddSignature(FunctionSig* sig);
   void AddIndirectFunction(uint16_t index);
+  void MarkStartFunction(uint16_t index);
   WasmModuleWriter* Build(Zone* zone);
 
   struct CompareFunctionSigs {
@@ -146,6 +145,7 @@
   ZoneVector<uint16_t> indirect_functions_;
   ZoneVector<std::pair<MachineType, bool>> globals_;
   SignatureMap signature_map_;
+  int start_function_index_;
 };
 
 std::vector<uint8_t> UnsignedLEB128From(uint32_t result);
diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc
index 62b000d..3e85a1b 100644
--- a/src/wasm/module-decoder.cc
+++ b/src/wasm/module-decoder.cc
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "src/wasm/module-decoder.h"
+
+#include "src/base/functional.h"
+#include "src/base/platform/platform.h"
 #include "src/macro-assembler.h"
 #include "src/objects.h"
 #include "src/v8.h"
 
 #include "src/wasm/decoder.h"
-#include "src/wasm/module-decoder.h"
 
 namespace v8 {
 namespace internal {
@@ -27,8 +30,8 @@
 class ModuleDecoder : public Decoder {
  public:
   ModuleDecoder(Zone* zone, const byte* module_start, const byte* module_end,
-                bool asm_js)
-      : Decoder(module_start, module_end), module_zone(zone), asm_js_(asm_js) {
+                ModuleOrigin origin)
+      : Decoder(module_start, module_end), module_zone(zone), origin_(origin) {
     result_.start = start_;
     if (limit_ < start_) {
       error(start_, "end is less than start");
@@ -40,86 +43,196 @@
     pc_ = limit_;  // On error, terminate section decoding loop.
   }
 
+  static void DumpModule(WasmModule* module, ModuleResult result) {
+    std::string path;
+    if (FLAG_dump_wasm_module_path) {
+      path = FLAG_dump_wasm_module_path;
+      if (path.size() &&
+          !base::OS::isDirectorySeparator(path[path.size() - 1])) {
+        path += base::OS::DirectorySeparator();
+      }
+    }
+    // File are named `HASH.{ok,failed}.wasm`.
+    size_t hash = base::hash_range(module->module_start, module->module_end);
+    char buf[32] = {'\0'};
+#if V8_OS_WIN && _MSC_VER < 1900
+#define snprintf sprintf_s
+#endif
+    snprintf(buf, sizeof(buf) - 1, "%016zx.%s.wasm", hash,
+             result.ok() ? "ok" : "failed");
+    std::string name(buf);
+    if (FILE* wasm_file = base::OS::FOpen((path + name).c_str(), "wb")) {
+      fwrite(module->module_start, module->module_end - module->module_start, 1,
+             wasm_file);
+      fclose(wasm_file);
+    }
+  }
+
   // Decodes an entire module.
   ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) {
     pc_ = start_;
     module->module_start = start_;
     module->module_end = limit_;
-    module->min_mem_size_log2 = 0;
-    module->max_mem_size_log2 = 0;
+    module->min_mem_pages = 0;
+    module->max_mem_pages = 0;
     module->mem_export = false;
     module->mem_external = false;
-    module->globals = new std::vector<WasmGlobal>();
-    module->signatures = new std::vector<FunctionSig*>();
-    module->functions = new std::vector<WasmFunction>();
-    module->data_segments = new std::vector<WasmDataSegment>();
-    module->function_table = new std::vector<uint16_t>();
-    module->import_table = new std::vector<WasmImport>();
+    module->origin = origin_;
 
-    bool sections[kMaxModuleSectionCode];
-    memset(sections, 0, sizeof(sections));
+    bool sections[(size_t)WasmSection::Code::Max] = {false};
+
+    const byte* pos = pc_;
+    uint32_t magic_word = consume_u32("wasm magic");
+#define BYTES(x) (x & 0xff), (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff
+    if (magic_word != kWasmMagic) {
+      error(pos, pos,
+            "expected magic word %02x %02x %02x %02x, "
+            "found %02x %02x %02x %02x",
+            BYTES(kWasmMagic), BYTES(magic_word));
+      goto done;
+    }
+
+    pos = pc_;
+    {
+      uint32_t magic_version = consume_u32("wasm version");
+      if (magic_version != kWasmVersion) {
+        error(pos, pos,
+              "expected version %02x %02x %02x %02x, "
+              "found %02x %02x %02x %02x",
+              BYTES(kWasmVersion), BYTES(magic_version));
+        goto done;
+      }
+    }
 
     // Decode the module sections.
     while (pc_ < limit_) {
       TRACE("DecodeSection\n");
-      WasmSectionDeclCode section =
-          static_cast<WasmSectionDeclCode>(consume_u8("section"));
-      // Each section should appear at most once.
-      if (section < kMaxModuleSectionCode) {
-        CheckForPreviousSection(sections, section, false);
-        sections[section] = true;
+      pos = pc_;
+
+      int length;
+      uint32_t section_length = consume_u32v(&length, "section size");
+
+      int section_string_leb_length = 0;
+      uint32_t section_string_length = 0;
+      WasmSection::Code section = consume_section_name(
+          &section_string_leb_length, &section_string_length);
+      uint32_t string_and_leb_length =
+          section_string_leb_length + section_string_length;
+      if (string_and_leb_length > section_length) {
+        error(pos, pos,
+              "section string of size %u longer than total section bytes %u",
+              string_and_leb_length, section_length);
+        break;
       }
 
+      if (section == WasmSection::Code::Max) {
+        // Skip unknown section.
+        uint32_t skip = section_length - string_and_leb_length;
+        TRACE("skipping %u bytes from unknown section\n", skip);
+        consume_bytes(skip);
+        continue;
+      }
+
+      // Each section should appear at most once.
+      CheckForPreviousSection(sections, section, false);
+      sections[(size_t)section] = true;
+
       switch (section) {
-        case kDeclEnd:
+        case WasmSection::Code::End:
           // Terminate section decoding.
           limit_ = pc_;
           break;
-        case kDeclMemory:
-          module->min_mem_size_log2 = consume_u8("min memory");
-          module->max_mem_size_log2 = consume_u8("max memory");
+        case WasmSection::Code::Memory:
+          int length;
+          module->min_mem_pages = consume_u32v(&length, "min memory");
+          module->max_mem_pages = consume_u32v(&length, "max memory");
           module->mem_export = consume_u8("export memory") != 0;
           break;
-        case kDeclSignatures: {
+        case WasmSection::Code::Signatures: {
           int length;
           uint32_t signatures_count = consume_u32v(&length, "signatures count");
-          module->signatures->reserve(SafeReserve(signatures_count));
+          module->signatures.reserve(SafeReserve(signatures_count));
           // Decode signatures.
           for (uint32_t i = 0; i < signatures_count; i++) {
             if (failed()) break;
             TRACE("DecodeSignature[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
             FunctionSig* s = consume_sig();  // read function sig.
-            module->signatures->push_back(s);
+            module->signatures.push_back(s);
           }
           break;
         }
-        case kDeclFunctions: {
+        case WasmSection::Code::FunctionSignatures: {
           // Functions require a signature table first.
-          CheckForPreviousSection(sections, kDeclSignatures, true);
+          CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+                                  true);
           int length;
           uint32_t functions_count = consume_u32v(&length, "functions count");
-          module->functions->reserve(SafeReserve(functions_count));
+          module->functions.reserve(SafeReserve(functions_count));
+          for (uint32_t i = 0; i < functions_count; i++) {
+            module->functions.push_back(
+                {nullptr, i, 0, 0, 0, 0, 0, 0, false, false});
+            WasmFunction* function = &module->functions.back();
+            function->sig_index = consume_sig_index(module, &function->sig);
+          }
+          break;
+        }
+        case WasmSection::Code::FunctionBodies: {
+          // Function bodies should follow signatures.
+          CheckForPreviousSection(sections,
+                                  WasmSection::Code::FunctionSignatures, true);
+          int length;
+          const byte* pos = pc_;
+          uint32_t functions_count = consume_u32v(&length, "functions count");
+          if (functions_count != module->functions.size()) {
+            error(pos, pos, "function body count %u mismatch (%u expected)",
+                  functions_count,
+                  static_cast<uint32_t>(module->functions.size()));
+            break;
+          }
+          for (uint32_t i = 0; i < functions_count; i++) {
+            WasmFunction* function = &module->functions[i];
+            int length;
+            uint32_t size = consume_u32v(&length, "body size");
+            function->code_start_offset = pc_offset();
+            function->code_end_offset = pc_offset() + size;
+
+            TRACE("  +%d  %-20s: (%d bytes)\n", pc_offset(), "function body",
+                  size);
+            pc_ += size;
+            if (pc_ > limit_) {
+              error(pc_, "function body extends beyond end of file");
+            }
+          }
+          break;
+        }
+        case WasmSection::Code::Functions: {
+          // Functions require a signature table first.
+          CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+                                  true);
+          int length;
+          uint32_t functions_count = consume_u32v(&length, "functions count");
+          module->functions.reserve(SafeReserve(functions_count));
           // Set up module environment for verification.
           ModuleEnv menv;
           menv.module = module;
           menv.instance = nullptr;
-          menv.asm_js = asm_js_;
+          menv.origin = origin_;
           // Decode functions.
           for (uint32_t i = 0; i < functions_count; i++) {
             if (failed()) break;
             TRACE("DecodeFunction[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
 
-            module->functions->push_back(
+            module->functions.push_back(
                 {nullptr, i, 0, 0, 0, 0, 0, 0, false, false});
-            WasmFunction* function = &module->functions->back();
+            WasmFunction* function = &module->functions.back();
             DecodeFunctionInModule(module, function, false);
           }
           if (ok() && verify_functions) {
             for (uint32_t i = 0; i < functions_count; i++) {
               if (failed()) break;
-              WasmFunction* function = &module->functions->at(i);
+              WasmFunction* function = &module->functions[i];
               if (!function->external) {
                 VerifyFunctionBody(i, &menv, function);
                 if (result_.failed())
@@ -129,132 +242,166 @@
           }
           break;
         }
-        case kDeclGlobals: {
+        case WasmSection::Code::Names: {
+          // Names correspond to functions.
+          CheckForPreviousSection(sections,
+                                  WasmSection::Code::FunctionSignatures, true);
+          int length;
+          const byte* pos = pc_;
+          uint32_t functions_count = consume_u32v(&length, "functions count");
+          if (functions_count != module->functions.size()) {
+            error(pos, pos, "function name count %u mismatch (%u expected)",
+                  functions_count,
+                  static_cast<uint32_t>(module->functions.size()));
+            break;
+          }
+
+          for (uint32_t i = 0; i < functions_count; i++) {
+            WasmFunction* function = &module->functions[i];
+            function->name_offset =
+                consume_string(&function->name_length, "function name");
+
+            uint32_t local_names_count =
+                consume_u32v(&length, "local names count");
+            for (uint32_t j = 0; j < local_names_count; j++) {
+              uint32_t unused = 0;
+              uint32_t offset = consume_string(&unused, "local name");
+              USE(unused);
+              USE(offset);
+            }
+          }
+          break;
+        }
+        case WasmSection::Code::Globals: {
           int length;
           uint32_t globals_count = consume_u32v(&length, "globals count");
-          module->globals->reserve(SafeReserve(globals_count));
+          module->globals.reserve(SafeReserve(globals_count));
           // Decode globals.
           for (uint32_t i = 0; i < globals_count; i++) {
             if (failed()) break;
             TRACE("DecodeGlobal[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
-            module->globals->push_back({0, MachineType::Int32(), 0, false});
-            WasmGlobal* global = &module->globals->back();
+            module->globals.push_back({0, 0, MachineType::Int32(), 0, false});
+            WasmGlobal* global = &module->globals.back();
             DecodeGlobalInModule(global);
           }
           break;
         }
-        case kDeclDataSegments: {
+        case WasmSection::Code::DataSegments: {
           int length;
           uint32_t data_segments_count =
               consume_u32v(&length, "data segments count");
-          module->data_segments->reserve(SafeReserve(data_segments_count));
+          module->data_segments.reserve(SafeReserve(data_segments_count));
           // Decode data segments.
           for (uint32_t i = 0; i < data_segments_count; i++) {
             if (failed()) break;
             TRACE("DecodeDataSegment[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
-            module->data_segments->push_back({0, 0, 0});
-            WasmDataSegment* segment = &module->data_segments->back();
+            module->data_segments.push_back({0, 0, 0});
+            WasmDataSegment* segment = &module->data_segments.back();
             DecodeDataSegmentInModule(module, segment);
           }
           break;
         }
-        case kDeclFunctionTable: {
+        case WasmSection::Code::FunctionTable: {
           // An indirect function table requires functions first.
-          CheckForPreviousSection(sections, kDeclFunctions, true);
+          CheckForFunctions(module, section);
           int length;
           uint32_t function_table_count =
               consume_u32v(&length, "function table count");
-          module->function_table->reserve(SafeReserve(function_table_count));
+          module->function_table.reserve(SafeReserve(function_table_count));
           // Decode function table.
           for (uint32_t i = 0; i < function_table_count; i++) {
             if (failed()) break;
             TRACE("DecodeFunctionTable[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
-            uint16_t index = consume_u16();
-            if (index >= module->functions->size()) {
+            uint16_t index = consume_u32v(&length);
+            if (index >= module->functions.size()) {
               error(pc_ - 2, "invalid function index");
               break;
             }
-            module->function_table->push_back(index);
+            module->function_table.push_back(index);
           }
           break;
         }
-        case kDeclStartFunction: {
+        case WasmSection::Code::StartFunction: {
           // Declares a start function for a module.
-          CheckForPreviousSection(sections, kDeclFunctions, true);
+          CheckForFunctions(module, section);
           if (module->start_function_index >= 0) {
             error("start function already declared");
             break;
           }
-          int length;
-          const byte* before = pc_;
-          uint32_t index = consume_u32v(&length, "start function index");
-          if (index >= module->functions->size()) {
-            error(before, "invalid start function index");
-            break;
-          }
-          module->start_function_index = static_cast<int>(index);
-          FunctionSig* sig =
-              module->signatures->at(module->functions->at(index).sig_index);
-          if (sig->parameter_count() > 0) {
-            error(before, "invalid start function: non-zero parameter count");
+          WasmFunction* func;
+          const byte* pos = pc_;
+          module->start_function_index = consume_func_index(module, &func);
+          if (func && func->sig->parameter_count() > 0) {
+            error(pos, "invalid start function: non-zero parameter count");
             break;
           }
           break;
         }
-        case kDeclImportTable: {
+        case WasmSection::Code::ImportTable: {
           // Declares an import table.
-          CheckForPreviousSection(sections, kDeclSignatures, true);
+          CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+                                  true);
           int length;
           uint32_t import_table_count =
               consume_u32v(&length, "import table count");
-          module->import_table->reserve(SafeReserve(import_table_count));
+          module->import_table.reserve(SafeReserve(import_table_count));
           // Decode import table.
           for (uint32_t i = 0; i < import_table_count; i++) {
             if (failed()) break;
             TRACE("DecodeImportTable[%d] module+%d\n", i,
                   static_cast<int>(pc_ - start_));
 
-            module->import_table->push_back({nullptr, 0, 0});
-            WasmImport* import = &module->import_table->back();
+            module->import_table.push_back({nullptr, 0, 0});
+            WasmImport* import = &module->import_table.back();
 
-            const byte* sigpos = pc_;
-            import->sig_index = consume_u16("signature index");
-
-            if (import->sig_index >= module->signatures->size()) {
-              error(sigpos, "invalid signature index");
-            } else {
-              import->sig = module->signatures->at(import->sig_index);
+            import->sig_index = consume_sig_index(module, &import->sig);
+            const byte* pos = pc_;
+            import->module_name_offset = consume_string(
+                &import->module_name_length, "import module name");
+            if (import->module_name_length == 0) {
+              error(pos, "import module name cannot be NULL");
             }
-            import->module_name_offset = consume_string("import module name");
-            import->function_name_offset =
-                consume_string("import function name");
+            import->function_name_offset = consume_string(
+                &import->function_name_length, "import function name");
           }
           break;
         }
-        case kDeclWLL: {
-          // Reserved for experimentation by the Web Low-level Language project
-          // which is augmenting the binary encoding with source code meta
-          // information. This section does not affect the semantics of the code
-          // and can be ignored by the runtime. https://github.com/JSStats/wll
-          int length = 0;
-          uint32_t section_size = consume_u32v(&length, "section size");
-          if (pc_ + section_size > limit_ || pc_ + section_size < pc_) {
-            error(pc_ - length, "invalid section size");
-            break;
+        case WasmSection::Code::ExportTable: {
+          // Declares an export table.
+          CheckForFunctions(module, section);
+          int length;
+          uint32_t export_table_count =
+              consume_u32v(&length, "export table count");
+          module->export_table.reserve(SafeReserve(export_table_count));
+          // Decode export table.
+          for (uint32_t i = 0; i < export_table_count; i++) {
+            if (failed()) break;
+            TRACE("DecodeExportTable[%d] module+%d\n", i,
+                  static_cast<int>(pc_ - start_));
+
+            module->export_table.push_back({0, 0});
+            WasmExport* exp = &module->export_table.back();
+
+            WasmFunction* func;
+            exp->func_index = consume_func_index(module, &func);
+            exp->name_offset = consume_string(&exp->name_length, "export name");
           }
-          pc_ += section_size;
           break;
         }
-        default:
-          error(pc_ - 1, nullptr, "unrecognized section 0x%02x", section);
-          break;
+        case WasmSection::Code::Max:
+          UNREACHABLE();  // Already skipped unknown sections.
       }
     }
 
-    return toResult(module);
+  done:
+    ModuleResult result = toResult(module);
+    if (FLAG_dump_wasm_module) {
+      DumpModule(module, result);
+    }
+    return result;
   }
 
   uint32_t SafeReserve(uint32_t count) {
@@ -263,38 +410,23 @@
     return count < kMaxReserve ? count : kMaxReserve;
   }
 
-  void CheckForPreviousSection(bool* sections, WasmSectionDeclCode section,
-                               bool present) {
-    if (section >= kMaxModuleSectionCode) return;
-    if (sections[section] == present) return;
-    const char* name = "";
-    switch (section) {
-      case kDeclMemory:
-        name = "memory";
-        break;
-      case kDeclSignatures:
-        name = "signatures";
-        break;
-      case kDeclFunctions:
-        name = "function declaration";
-        break;
-      case kDeclGlobals:
-        name = "global variable";
-        break;
-      case kDeclDataSegments:
-        name = "data segment";
-        break;
-      case kDeclFunctionTable:
-        name = "function table";
-        break;
-      default:
-        name = "";
-        break;
+  void CheckForFunctions(WasmModule* module, WasmSection::Code section) {
+    if (module->functions.size() == 0) {
+      error(pc_ - 1, nullptr, "functions must appear before section %s",
+            WasmSection::getName(section));
     }
+  }
+
+  void CheckForPreviousSection(bool* sections, WasmSection::Code section,
+                               bool present) {
+    if (section >= WasmSection::Code::Max) return;
+    if (sections[(size_t)section] == present) return;
     if (present) {
-      error(pc_ - 1, nullptr, "required %s section missing", name);
+      error(pc_ - 1, nullptr, "required %s section missing",
+            WasmSection::getName(section));
     } else {
-      error(pc_ - 1, nullptr, "%s section already present", name);
+      error(pc_ - 1, nullptr, "%s section already present",
+            WasmSection::getName(section));
     }
   }
 
@@ -302,16 +434,13 @@
   FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
                                       WasmFunction* function) {
     pc_ = start_;
-    function->sig = consume_sig();                  // read signature
-    function->name_offset = 0;                   // ---- name
-    function->code_start_offset = off(pc_ + 8);  // ---- code start
-    function->code_end_offset = off(limit_);     // ---- code end
-    function->local_i32_count = consume_u16();      // read u16
-    function->local_i64_count = consume_u16();      // read u16
-    function->local_f32_count = consume_u16();      // read u16
-    function->local_f64_count = consume_u16();      // read u16
-    function->exported = false;                  // ---- exported
-    function->external = false;                  // ---- external
+    function->sig = consume_sig();            // read signature
+    function->name_offset = 0;                // ---- name
+    function->name_length = 0;                // ---- name length
+    function->code_start_offset = off(pc_);   // ---- code start
+    function->code_end_offset = off(limit_);  // ---- code end
+    function->exported = false;               // ---- exported
+    function->external = false;               // ---- external
 
     if (ok()) VerifyFunctionBody(0, module_env, function);
 
@@ -331,19 +460,20 @@
  private:
   Zone* module_zone;
   ModuleResult result_;
-  bool asm_js_;
+  ModuleOrigin origin_;
 
   uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); }
 
   // Decodes a single global entry inside a module starting at {pc_}.
   void DecodeGlobalInModule(WasmGlobal* global) {
-    global->name_offset = consume_string("global name");
+    global->name_offset = consume_string(&global->name_length, "global name");
     global->type = mem_type();
     global->offset = 0;
     global->exported = consume_u8("exported") != 0;
   }
 
   // Decodes a single function entry inside a module starting at {pc_}.
+  // TODO(titzer): legacy function body; remove
   void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
                               bool verify_body = true) {
     byte decl_bits = consume_u8("function decl");
@@ -351,10 +481,10 @@
     const byte* sigpos = pc_;
     function->sig_index = consume_u16("signature index");
 
-    if (function->sig_index >= module->signatures->size()) {
+    if (function->sig_index >= module->signatures.size()) {
       return error(sigpos, "invalid signature index");
     } else {
-      function->sig = module->signatures->at(function->sig_index);
+      function->sig = module->signatures[function->sig_index];
     }
 
     TRACE("  +%d  <function attributes:%s%s%s%s%s>\n",
@@ -366,7 +496,8 @@
           (decl_bits & kDeclFunctionImport) == 0 ? " body" : "");
 
     if (decl_bits & kDeclFunctionName) {
-      function->name_offset = consume_string("function name");
+      function->name_offset =
+          consume_string(&function->name_length, "function name");
     }
 
     function->exported = decl_bits & kDeclFunctionExport;
@@ -406,25 +537,30 @@
 
   // Decodes a single data segment entry inside a module starting at {pc_}.
   void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) {
-    segment->dest_addr = consume_u32("destination");
-    segment->source_offset = consume_offset("source offset");
-    segment->source_size = consume_u32("source size");
-    segment->init = consume_u8("init");
+    const byte* start = pc_;
+    int length;
+    segment->dest_addr = consume_u32v(&length, "destination");
+    segment->source_size = consume_u32v(&length, "source size");
+    segment->source_offset = static_cast<uint32_t>(pc_ - start_);
+    segment->init = true;
 
     // Validate the data is in the module.
     uint32_t module_limit = static_cast<uint32_t>(limit_ - start_);
     if (!IsWithinLimit(module_limit, segment->source_offset,
                        segment->source_size)) {
-      error(pc_ - sizeof(uint32_t), "segment out of bounds of module");
+      error(start, "segment out of bounds of module");
     }
 
     // Validate that the segment will fit into the (minimum) memory.
     uint32_t memory_limit =
-        1 << (module ? module->min_mem_size_log2 : WasmModule::kMaxMemSize);
+        WasmModule::kPageSize * (module ? module->min_mem_pages
+                                        : WasmModule::kMaxMemPages);
     if (!IsWithinLimit(memory_limit, segment->dest_addr,
                        segment->source_size)) {
-      error(pc_ - sizeof(uint32_t), "segment out of bounds of memory");
+      error(start, "segment out of bounds of memory");
     }
+
+    consume_bytes(segment->source_size);
   }
 
   // Verifies the body (code) of a given function.
@@ -436,18 +572,10 @@
          << std::endl;
       os << std::endl;
     }
-    FunctionEnv fenv;
-    fenv.module = menv;
-    fenv.sig = function->sig;
-    fenv.local_i32_count = function->local_i32_count;
-    fenv.local_i64_count = function->local_i64_count;
-    fenv.local_f32_count = function->local_f32_count;
-    fenv.local_f64_count = function->local_f64_count;
-    fenv.SumLocals();
-
-    TreeResult result =
-        VerifyWasmCode(&fenv, start_, start_ + function->code_start_offset,
-                       start_ + function->code_end_offset);
+    FunctionBody body = {menv, function->sig, start_,
+                         start_ + function->code_start_offset,
+                         start_ + function->code_end_offset};
+    TreeResult result = VerifyWasmCode(module_zone->allocator(), body);
     if (result.failed()) {
       // Wrap the error message from the function decoder.
       std::ostringstream str;
@@ -476,11 +604,67 @@
     return offset;
   }
 
-  // Reads a single 32-bit unsigned integer interpreted as an offset into the
-  // data and validating the string there and advances.
-  uint32_t consume_string(const char* name = nullptr) {
-    // TODO(titzer): validate string
-    return consume_offset(name ? name : "string");
+  // Reads a length-prefixed string, checking that it is within bounds. Returns
+  // the offset of the string, and the length as an out parameter.
+  uint32_t consume_string(uint32_t* length, const char* name = nullptr) {
+    int varint_length;
+    *length = consume_u32v(&varint_length, "string length");
+    uint32_t offset = pc_offset();
+    TRACE("  +%u  %-20s: (%u bytes)\n", offset, "string", *length);
+    consume_bytes(*length);
+    return offset;
+  }
+
+  uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) {
+    const byte* pos = pc_;
+    int length;
+    uint32_t sig_index = consume_u32v(&length, "signature index");
+    if (sig_index >= module->signatures.size()) {
+      error(pos, pos, "signature index %u out of bounds (%d signatures)",
+            sig_index, static_cast<int>(module->signatures.size()));
+      *sig = nullptr;
+      return 0;
+    }
+    *sig = module->signatures[sig_index];
+    return sig_index;
+  }
+
+  uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
+    const byte* pos = pc_;
+    int length;
+    uint32_t func_index = consume_u32v(&length, "function index");
+    if (func_index >= module->functions.size()) {
+      error(pos, pos, "function index %u out of bounds (%d functions)",
+            func_index, static_cast<int>(module->functions.size()));
+      *func = nullptr;
+      return 0;
+    }
+    *func = &module->functions[func_index];
+    return func_index;
+  }
+
+  // Reads a section name.
+  WasmSection::Code consume_section_name(int* string_leb_length,
+                                         uint32_t* string_length) {
+    *string_length = consume_u32v(string_leb_length, "name length");
+    const byte* start = pc_;
+    consume_bytes(*string_length);
+    if (failed()) {
+      TRACE("Section name of length %u couldn't be read\n", *string_length);
+      return WasmSection::Code::Max;
+    }
+    // TODO(jfb) Linear search, it may be better to do a common-prefix search.
+    for (WasmSection::Code i = WasmSection::begin(); i != WasmSection::end();
+         i = WasmSection::next(i)) {
+      if (WasmSection::getNameLength(i) == *string_length &&
+          0 == memcmp(WasmSection::getName(i), start, *string_length)) {
+        return i;
+      }
+    }
+    TRACE("Unknown section: '");
+    for (uint32_t i = 0; i != *string_length; ++i) TRACE("%c", *(start + i));
+    TRACE("'\n");
+    return WasmSection::Code::Max;
   }
 
   // Reads a single 8-bit integer, interpreting it as a local type.
@@ -537,7 +721,8 @@
 
   // Parses an inline function signature.
   FunctionSig* consume_sig() {
-    byte count = consume_u8("param count");
+    int length;
+    byte count = consume_u32v(&length, "param count");
     LocalType ret = consume_local_type();
     FunctionSig::Builder builder(module_zone, ret == kAstStmt ? 0 : 1, count);
     if (ret != kAstStmt) builder.AddReturn(ret);
@@ -579,22 +764,21 @@
   }
 };
 
-
 ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
                               const byte* module_start, const byte* module_end,
-                              bool verify_functions, bool asm_js) {
+                              bool verify_functions, ModuleOrigin origin) {
   size_t size = module_end - module_start;
   if (module_start > module_end) return ModuleError("start > end");
   if (size >= kMaxModuleSize) return ModuleError("size > maximum module size");
   WasmModule* module = new WasmModule();
-  ModuleDecoder decoder(zone, module_start, module_end, asm_js);
+  ModuleDecoder decoder(zone, module_start, module_end, origin);
   return decoder.DecodeModule(module, verify_functions);
 }
 
 
 FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start,
                                            const byte* end) {
-  ModuleDecoder decoder(zone, start, end, false);
+  ModuleDecoder decoder(zone, start, end, kWasmOrigin);
   return decoder.DecodeFunctionSignature(start);
 }
 
@@ -608,7 +792,7 @@
   if (size > kMaxFunctionSize)
     return FunctionError("size > maximum function size");
   WasmFunction* function = new WasmFunction();
-  ModuleDecoder decoder(zone, function_start, function_end, false);
+  ModuleDecoder decoder(zone, function_start, function_end, kWasmOrigin);
   return decoder.DecodeSingleFunction(module_env, function);
 }
 }  // namespace wasm
diff --git a/src/wasm/module-decoder.h b/src/wasm/module-decoder.h
index 3f469a5..00a9b87 100644
--- a/src/wasm/module-decoder.h
+++ b/src/wasm/module-decoder.h
@@ -14,7 +14,7 @@
 // Decodes the bytes of a WASM module between {module_start} and {module_end}.
 ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
                               const byte* module_start, const byte* module_end,
-                              bool verify_functions, bool asm_js);
+                              bool verify_functions, ModuleOrigin origin);
 
 // Exposed for testing. Decodes a single function signature, allocating it
 // in the given zone. Returns {nullptr} upon failure.
diff --git a/src/wasm/wasm-external-refs.h b/src/wasm/wasm-external-refs.h
new file mode 100644
index 0000000..4aa452b
--- /dev/null
+++ b/src/wasm/wasm-external-refs.h
@@ -0,0 +1,181 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WASM_EXTERNAL_REFS_H
+#define WASM_EXTERNAL_REFS_H
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+static void f32_trunc_wrapper(float* param) { *param = truncf(*param); }
+
+static void f32_floor_wrapper(float* param) { *param = floorf(*param); }
+
+static void f32_ceil_wrapper(float* param) { *param = ceilf(*param); }
+
+static void f32_nearest_int_wrapper(float* param) {
+  *param = nearbyintf(*param);
+}
+
+static void f64_trunc_wrapper(double* param) { *param = trunc(*param); }
+
+static void f64_floor_wrapper(double* param) { *param = floor(*param); }
+
+static void f64_ceil_wrapper(double* param) { *param = ceil(*param); }
+
+static void f64_nearest_int_wrapper(double* param) {
+  *param = nearbyint(*param);
+}
+
+static void int64_to_float32_wrapper(int64_t* input, float* output) {
+  *output = static_cast<float>(*input);
+}
+
+static void uint64_to_float32_wrapper(uint64_t* input, float* output) {
+#if V8_CC_MSVC
+  // With MSVC we use static_cast<float>(uint32_t) instead of
+  // static_cast<float>(uint64_t) to achieve round-to-nearest-ties-even
+  // semantics. The idea is to calculate
+  // static_cast<float>(high_word) * 2^32 + static_cast<float>(low_word). To
+  // achieve proper rounding in all cases we have to adjust the high_word
+  // with a "rounding bit" sometimes. The rounding bit is stored in the LSB of
+  // the high_word if the low_word may affect the rounding of the high_word.
+  uint32_t low_word = static_cast<uint32_t>(*input & 0xffffffff);
+  uint32_t high_word = static_cast<uint32_t>(*input >> 32);
+
+  float shift = static_cast<float>(1ull << 32);
+  // If the MSB of the high_word is set, then we make space for a rounding bit.
+  if (high_word < 0x80000000) {
+    high_word <<= 1;
+    shift = static_cast<float>(1ull << 31);
+  }
+
+  if ((high_word & 0xfe000000) && low_word) {
+    // Set the rounding bit.
+    high_word |= 1;
+  }
+
+  float result = static_cast<float>(high_word);
+  result *= shift;
+  result += static_cast<float>(low_word);
+  *output = result;
+
+#else
+  *output = static_cast<float>(*input);
+#endif
+}
+
+static void int64_to_float64_wrapper(int64_t* input, double* output) {
+  *output = static_cast<double>(*input);
+}
+
+static void uint64_to_float64_wrapper(uint64_t* input, double* output) {
+#if V8_CC_MSVC
+  // With MSVC we use static_cast<double>(uint32_t) instead of
+  // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
+  // semantics. The idea is to calculate
+  // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
+  uint32_t low_word = static_cast<uint32_t>(*input & 0xffffffff);
+  uint32_t high_word = static_cast<uint32_t>(*input >> 32);
+
+  double shift = static_cast<double>(1ull << 32);
+
+  double result = static_cast<double>(high_word);
+  result *= shift;
+  result += static_cast<double>(low_word);
+  *output = result;
+
+#else
+  *output = static_cast<double>(*input);
+#endif
+}
+
+static int32_t float32_to_int64_wrapper(float* input, int64_t* output) {
+  // We use "<" here to check the upper bound because of rounding problems: With
+  // "<=" some inputs would be considered within int64 range which are actually
+  // not within int64 range.
+  if (*input >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
+      *input < static_cast<float>(std::numeric_limits<int64_t>::max())) {
+    *output = static_cast<int64_t>(*input);
+    return 1;
+  }
+  return 0;
+}
+
+static int32_t float32_to_uint64_wrapper(float* input, uint64_t* output) {
+  // We use "<" here to check the upper bound because of rounding problems: With
+  // "<=" some inputs would be considered within uint64 range which are actually
+  // not within uint64 range.
+  if (*input > -1.0 &&
+      *input < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
+    *output = static_cast<uint64_t>(*input);
+    return 1;
+  }
+  return 0;
+}
+
+static int32_t float64_to_int64_wrapper(double* input, int64_t* output) {
+  // We use "<" here to check the upper bound because of rounding problems: With
+  // "<=" some inputs would be considered within int64 range which are actually
+  // not within int64 range.
+  if (*input >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
+      *input < static_cast<double>(std::numeric_limits<int64_t>::max())) {
+    *output = static_cast<int64_t>(*input);
+    return 1;
+  }
+  return 0;
+}
+
+static int32_t float64_to_uint64_wrapper(double* input, uint64_t* output) {
+  // We use "<" here to check the upper bound because of rounding problems: With
+  // "<=" some inputs would be considered within uint64 range which are actually
+  // not within uint64 range.
+  if (*input > -1.0 &&
+      *input < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
+    *output = static_cast<uint64_t>(*input);
+    return 1;
+  }
+  return 0;
+}
+
+static int32_t int64_div_wrapper(int64_t* dst, int64_t* src) {
+  if (*src == 0) {
+    return 0;
+  }
+  if (*src == -1 && *dst == std::numeric_limits<int64_t>::min()) {
+    return -1;
+  }
+  *dst /= *src;
+  return 1;
+}
+
+static int32_t int64_mod_wrapper(int64_t* dst, int64_t* src) {
+  if (*src == 0) {
+    return 0;
+  }
+  *dst %= *src;
+  return 1;
+}
+
+static int32_t uint64_div_wrapper(uint64_t* dst, uint64_t* src) {
+  if (*src == 0) {
+    return 0;
+  }
+  *dst /= *src;
+  return 1;
+}
+
+static int32_t uint64_mod_wrapper(uint64_t* dst, uint64_t* src) {
+  if (*src == 0) {
+    return 0;
+  }
+  *dst %= *src;
+  return 1;
+}
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif
diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc
index 62a2676..83009d7 100644
--- a/src/wasm/wasm-js.cc
+++ b/src/wasm/wasm-js.cc
@@ -37,20 +37,43 @@
 
 RawBuffer GetRawBufferArgument(
     ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) {
-  // TODO(titzer): allow typed array views.
-  if (args.Length() < 1 || !args[0]->IsArrayBuffer()) {
+  if (args.Length() < 1) {
     thrower.Error("Argument 0 must be an array buffer");
     return {nullptr, nullptr};
   }
-  Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
-  ArrayBuffer::Contents contents = buffer->GetContents();
 
-  const byte* start = reinterpret_cast<const byte*>(contents.Data());
-  const byte* end = start + contents.ByteLength();
+  const byte* start = nullptr;
+  const byte* end = nullptr;
 
-  if (start == nullptr) {
-    thrower.Error("ArrayBuffer argument is empty");
+  if (args[0]->IsArrayBuffer()) {
+    // A raw array buffer was passed.
+    Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
+    ArrayBuffer::Contents contents = buffer->GetContents();
+
+    start = reinterpret_cast<const byte*>(contents.Data());
+    end = start + contents.ByteLength();
+
+    if (start == nullptr || end == start) {
+      thrower.Error("ArrayBuffer argument is empty");
+    }
+  } else if (args[0]->IsTypedArray()) {
+    // A TypedArray was passed.
+    Local<TypedArray> array = Local<TypedArray>::Cast(args[0]);
+    Local<ArrayBuffer> buffer = array->Buffer();
+
+    ArrayBuffer::Contents contents = buffer->GetContents();
+
+    start =
+        reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
+    end = start + array->ByteLength();
+
+    if (start == nullptr || end == start) {
+      thrower.Error("ArrayBuffer argument is empty");
+    }
+  } else {
+    thrower.Error("Argument 0 must be an ArrayBuffer or Uint8Array");
   }
+
   return {start, end};
 }
 
@@ -63,9 +86,10 @@
   RawBuffer buffer = GetRawBufferArgument(thrower, args);
   if (thrower.error()) return;
 
-  i::Zone zone;
-  internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
-      isolate, &zone, buffer.start, buffer.end, true, false);
+  i::Zone zone(isolate->allocator());
+  internal::wasm::ModuleResult result =
+      internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
+                                       true, internal::wasm::kWasmOrigin);
 
   if (result.failed()) {
     thrower.Failed("", result);
@@ -87,7 +111,7 @@
   {
     // Verification of a single function shouldn't allocate.
     i::DisallowHeapAllocation no_allocation;
-    i::Zone zone;
+    i::Zone zone(isolate->allocator());
     result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr,
                                                 buffer.start, buffer.end);
   }
@@ -123,25 +147,18 @@
     return nullptr;
   }
 
-  auto module = v8::internal::wasm::AsmWasmBuilder(
-                    info->isolate(), info->zone(), info->literal(), foreign)
-                    .Run();
-
-  if (i::FLAG_dump_asmjs_wasm) {
-    FILE* wasm_file = fopen(i::FLAG_asmjs_wasm_dumpfile, "wb");
-    if (wasm_file) {
-      fwrite(module->Begin(), module->End() - module->Begin(), 1, wasm_file);
-      fclose(wasm_file);
-    }
-  }
+  auto module =
+      v8::internal::wasm::AsmWasmBuilder(info->isolate(), info->zone(),
+                                         info->literal(), foreign, &typer)
+          .Run();
 
   return module;
 }
 
-
 void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
                              const byte* start, const byte* end,
-                             ErrorThrower* thrower, bool must_decode) {
+                             ErrorThrower* thrower,
+                             internal::wasm::ModuleOrigin origin) {
   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
 
   i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
@@ -153,11 +170,11 @@
 
   // Decode but avoid a redundant pass over function bodies for verification.
   // Verification will happen during compilation.
-  i::Zone zone;
+  i::Zone zone(isolate->allocator());
   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
-      isolate, &zone, start, end, false, false);
+      isolate, &zone, start, end, false, origin);
 
-  if (result.failed() && must_decode) {
+  if (result.failed() && origin == internal::wasm::kAsmJsOrigin) {
     thrower->Error("Asm.js converted module failed to decode");
   } else if (result.failed()) {
     thrower->Failed("", result);
@@ -192,7 +209,7 @@
   }
 
   i::Factory* factory = isolate->factory();
-  i::Zone zone;
+  i::Zone zone(isolate->allocator());
   Local<String> source = Local<String>::Cast(args[0]);
   i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
   i::ParseInfo info(&zone, script);
@@ -208,7 +225,8 @@
     return;
   }
 
-  InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, true);
+  InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower,
+                          internal::wasm::kAsmJsOrigin);
 }
 
 
@@ -220,7 +238,8 @@
   RawBuffer buffer = GetRawBufferArgument(thrower, args);
   if (buffer.start == nullptr) return;
 
-  InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, false);
+  InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower,
+                          internal::wasm::kWasmOrigin);
 }
 }  // namespace
 
@@ -260,7 +279,7 @@
 
   // Bind the WASM object.
   Factory* factory = isolate->factory();
-  Handle<String> name = v8_str(isolate, "_WASMEXP_");
+  Handle<String> name = v8_str(isolate, "Wasm");
   Handle<JSFunction> cons = factory->NewFunction(name);
   JSFunction::SetInstancePrototype(
       cons, Handle<Object>(context->initial_object_prototype(), isolate));
@@ -280,10 +299,26 @@
 
 void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
   if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
-    Handle<Map> wasm_function_map = isolate->factory()->NewMap(
-        JS_FUNCTION_TYPE, JSFunction::kSize + kPointerSize);
-    wasm_function_map->set_is_callable();
-    context->set_wasm_function_map(*wasm_function_map);
+    // TODO(titzer): Move this to bootstrapper.cc??
+    // TODO(titzer): Also make one for strict mode functions?
+    Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
+
+    InstanceType instance_type = prev_map->instance_type();
+    int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
+    CHECK_EQ(0, internal_fields);
+    int pre_allocated =
+        prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
+    int instance_size;
+    int in_object_properties;
+    JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields + 1,
+                                            0, &instance_size,
+                                            &in_object_properties);
+
+    int unused_property_fields = in_object_properties - pre_allocated;
+    Handle<Map> map = Map::CopyInitialMap(
+        prev_map, instance_size, in_object_properties, unused_property_fields);
+
+    context->set_wasm_function_map(*map);
   }
 }
 
diff --git a/src/wasm/wasm-macro-gen.h b/src/wasm/wasm-macro-gen.h
index dd653c1..d9199e8 100644
--- a/src/wasm/wasm-macro-gen.h
+++ b/src/wasm/wasm-macro-gen.h
@@ -7,6 +7,50 @@
 
 #include "src/wasm/wasm-opcodes.h"
 
+#define U32_LE(v)                                    \
+  static_cast<byte>(v), static_cast<byte>((v) >> 8), \
+      static_cast<byte>((v) >> 16), static_cast<byte>((v) >> 24)
+
+#define U16_LE(v) static_cast<byte>(v), static_cast<byte>((v) >> 8)
+
+#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion)
+
+#define SIG_INDEX(v) U16_LE(v)
+// TODO(binji): make SIG_INDEX match this.
+#define IMPORT_SIG_INDEX(v) U32V_1(v)
+#define FUNC_INDEX(v) U32V_1(v)
+#define NO_NAME U32V_1(0)
+#define NAME_LENGTH(v) U32V_1(v)
+
+#define ZERO_ALIGNMENT 0
+#define ZERO_OFFSET 0
+
+#define BR_TARGET(v) U32_LE(v)
+
+#define MASK_7 ((1 << 7) - 1)
+#define MASK_14 ((1 << 14) - 1)
+#define MASK_21 ((1 << 21) - 1)
+#define MASK_28 ((1 << 28) - 1)
+
+#define U32V_1(x) static_cast<byte>((x)&MASK_7)
+#define U32V_2(x) \
+  static_cast<byte>(((x)&MASK_7) | 0x80), static_cast<byte>(((x) >> 7) & MASK_7)
+#define U32V_3(x)                                      \
+  static_cast<byte>((((x)) & MASK_7) | 0x80),          \
+      static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
+      static_cast<byte>(((x) >> 14) & MASK_7)
+#define U32V_4(x)                                       \
+  static_cast<byte>(((x)&MASK_7) | 0x80),               \
+      static_cast<byte>((((x) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((x) >> 21) & MASK_7)
+#define U32V_5(x)                                       \
+  static_cast<byte>(((x)&MASK_7) | 0x80),               \
+      static_cast<byte>((((x) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>((((x) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>((((x) >> 28) & MASK_7))
+
 // Convenience macros for building Wasm bytecode directly into a byte array.
 
 //------------------------------------------------------------------------------
@@ -33,14 +77,8 @@
 #define WASM_RETURN(...) kExprReturn, __VA_ARGS__
 #define WASM_UNREACHABLE kExprUnreachable
 
-#define WASM_TABLESWITCH_OP(case_count, table_count, ...)                 \
-  kExprTableSwitch, static_cast<byte>(case_count),                        \
-      static_cast<byte>(case_count >> 8), static_cast<byte>(table_count), \
-      static_cast<byte>(table_count >> 8), __VA_ARGS__
-
-#define WASM_TABLESWITCH_BODY0(key) key
-
-#define WASM_TABLESWITCH_BODY(key, ...) key, __VA_ARGS__
+#define WASM_BR_TABLE(key, count, ...) \
+  kExprBrTable, U32V_1(count), __VA_ARGS__, key
 
 #define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
 #define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
@@ -52,18 +90,222 @@
 #define WASM_ZERO kExprI8Const, 0
 #define WASM_ONE kExprI8Const, 1
 #define WASM_I8(val) kExprI8Const, static_cast<byte>(val)
-#define WASM_I32(val)                                                 \
-  kExprI32Const, static_cast<byte>(val), static_cast<byte>(val >> 8), \
-      static_cast<byte>(val >> 16), static_cast<byte>(val >> 24)
-#define WASM_I64(val)                                           \
-  kExprI64Const, static_cast<byte>(static_cast<uint64_t>(val)), \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 8),       \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 16),      \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 24),      \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 32),      \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 40),      \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 48),      \
-      static_cast<byte>(static_cast<uint64_t>(val) >> 56)
+
+#define I32V_MIN(length) -(1 << (6 + (7 * ((length) - 1))))
+#define I32V_MAX(length) ((1 << (6 + (7 * ((length) - 1)))) - 1)
+#define I64V_MIN(length) -(1LL << (6 + (7 * ((length) - 1))))
+#define I64V_MAX(length) ((1LL << (6 + 7 * ((length) - 1))) - 1)
+
+#define I32V_IN_RANGE(value, length) \
+  ((value) >= I32V_MIN(length) && (value) <= I32V_MAX(length))
+#define I64V_IN_RANGE(value, length) \
+  ((value) >= I64V_MIN(length) && (value) <= I64V_MAX(length))
+
+#define WASM_NO_LOCALS 0
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+inline void CheckI32v(int32_t value, int length) {
+  DCHECK(length >= 1 && length <= 5);
+  DCHECK(length == 5 || I32V_IN_RANGE(value, length));
+}
+
+inline void CheckI64v(int64_t value, int length) {
+  DCHECK(length >= 1 && length <= 10);
+  DCHECK(length == 10 || I64V_IN_RANGE(value, length));
+}
+
+// A helper for encoding local declarations prepended to the body of a
+// function.
+class LocalDeclEncoder {
+ public:
+  // Prepend local declarations by creating a new buffer and copying data
+  // over. The new buffer must be delete[]'d by the caller.
+  void Prepend(const byte** start, const byte** end) const {
+    size_t size = (*end - *start);
+    byte* buffer = new byte[Size() + size];
+    size_t pos = Emit(buffer);
+    memcpy(buffer + pos, *start, size);
+    pos += size;
+    *start = buffer;
+    *end = buffer + pos;
+  }
+
+  size_t Emit(byte* buffer) const {
+    size_t pos = 0;
+    pos = WriteUint32v(buffer, pos, static_cast<uint32_t>(local_decls.size()));
+    for (size_t i = 0; i < local_decls.size(); i++) {
+      pos = WriteUint32v(buffer, pos, local_decls[i].first);
+      buffer[pos++] = WasmOpcodes::LocalTypeCodeFor(local_decls[i].second);
+    }
+    DCHECK_EQ(Size(), pos);
+    return pos;
+  }
+
+  // Add locals declarations to this helper. Return the index of the newly added
+  // local(s), with an optional adjustment for the parameters.
+  uint32_t AddLocals(uint32_t count, LocalType type,
+                     FunctionSig* sig = nullptr) {
+    if (count == 0) {
+      return static_cast<uint32_t>((sig ? sig->parameter_count() : 0) +
+                                   local_decls.size());
+    }
+    size_t pos = local_decls.size();
+    if (local_decls.size() > 0 && local_decls.back().second == type) {
+      count += local_decls.back().first;
+      local_decls.pop_back();
+    }
+    local_decls.push_back(std::pair<uint32_t, LocalType>(count, type));
+    return static_cast<uint32_t>(pos + (sig ? sig->parameter_count() : 0));
+  }
+
+  size_t Size() const {
+    size_t size = SizeofUint32v(static_cast<uint32_t>(local_decls.size()));
+    for (auto p : local_decls) size += 1 + SizeofUint32v(p.first);
+    return size;
+  }
+
+ private:
+  std::vector<std::pair<uint32_t, LocalType>> local_decls;
+
+  size_t SizeofUint32v(uint32_t val) const {
+    size_t size = 1;
+    while (true) {
+      byte b = val & MASK_7;
+      if (b == val) return size;
+      size++;
+      val = val >> 7;
+    }
+  }
+
+  // TODO(titzer): lift encoding of u32v to a common place.
+  size_t WriteUint32v(byte* buffer, size_t pos, uint32_t val) const {
+    while (true) {
+      byte b = val & MASK_7;
+      if (b == val) {
+        buffer[pos++] = b;
+        break;
+      }
+      buffer[pos++] = 0x80 | b;
+      val = val >> 7;
+    }
+    return pos;
+  }
+};
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+//------------------------------------------------------------------------------
+// Int32 Const operations
+//------------------------------------------------------------------------------
+#define WASM_I32V(val) kExprI32Const, U32V_5(val)
+
+#define WASM_I32V_1(val) \
+  static_cast<byte>(CheckI32v((val), 1), kExprI32Const), U32V_1(val)
+#define WASM_I32V_2(val) \
+  static_cast<byte>(CheckI32v((val), 2), kExprI32Const), U32V_2(val)
+#define WASM_I32V_3(val) \
+  static_cast<byte>(CheckI32v((val), 3), kExprI32Const), U32V_3(val)
+#define WASM_I32V_4(val) \
+  static_cast<byte>(CheckI32v((val), 4), kExprI32Const), U32V_4(val)
+#define WASM_I32V_5(val) \
+  static_cast<byte>(CheckI32v((val), 5), kExprI32Const), U32V_5(val)
+
+//------------------------------------------------------------------------------
+// Int64 Const operations
+//------------------------------------------------------------------------------
+#define WASM_I64V(val)                                                        \
+  kExprI64Const,                                                              \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 56) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 63) & MASK_7)
+
+#define WASM_I64V_1(val)                                                     \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 1), kExprI64Const), \
+      static_cast<byte>(static_cast<int64_t>(val) & MASK_7)
+#define WASM_I64V_2(val)                                                     \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 2), kExprI64Const), \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),        \
+      static_cast<byte>((static_cast<int64_t>(val) >> 7) & MASK_7)
+#define WASM_I64V_3(val)                                                     \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 3), kExprI64Const), \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),        \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 14) & MASK_7)
+#define WASM_I64V_4(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 4), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 21) & MASK_7)
+#define WASM_I64V_5(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 5), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 28) & MASK_7)
+#define WASM_I64V_6(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 6), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 35) & MASK_7)
+#define WASM_I64V_7(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 7), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 42) & MASK_7)
+#define WASM_I64V_8(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 8), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 49) & MASK_7)
+#define WASM_I64V_9(val)                                                      \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 9), kExprI64Const),  \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 56) & MASK_7)
+#define WASM_I64V_10(val)                                                     \
+  static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 10), kExprI64Const), \
+      static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80),         \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80),  \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+      static_cast<byte>(((static_cast<int64_t>(val) >> 56) & MASK_7) | 0x80), \
+      static_cast<byte>((static_cast<int64_t>(val) >> 63) & MASK_7)
+
 #define WASM_F32(val)                                                       \
   kExprF32Const,                                                            \
       static_cast<byte>(bit_cast<int32_t>(static_cast<float>(val))),        \
@@ -87,21 +329,19 @@
 #define WASM_LOAD_MEM(type, index)                                      \
   static_cast<byte>(                                                    \
       v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, false)), \
-      v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(false), index
+      ZERO_ALIGNMENT, ZERO_OFFSET, index
 #define WASM_STORE_MEM(type, index, val)                               \
   static_cast<byte>(                                                   \
       v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
-      v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(false), index, val
+      ZERO_ALIGNMENT, ZERO_OFFSET, index, val
 #define WASM_LOAD_MEM_OFFSET(type, offset, index)                       \
   static_cast<byte>(                                                    \
       v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, false)), \
-      v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(true),         \
-      static_cast<byte>(offset), index
+      ZERO_ALIGNMENT, U32V_1(offset), index
 #define WASM_STORE_MEM_OFFSET(type, offset, index, val)                \
   static_cast<byte>(                                                   \
       v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
-      v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(true),        \
-      static_cast<byte>(offset), index, val
+      ZERO_ALIGNMENT, U32V_1(offset), index, val
 #define WASM_CALL_FUNCTION(index, ...) \
   kExprCallFunction, static_cast<byte>(index), __VA_ARGS__
 #define WASM_CALL_IMPORT(index, ...) \
@@ -112,7 +352,7 @@
 #define WASM_CALL_IMPORT0(index) kExprCallImport, static_cast<byte>(index)
 #define WASM_CALL_INDIRECT0(index, func) \
   kExprCallIndirect, static_cast<byte>(index), func
-#define WASM_NOT(x) kExprBoolNot, x
+#define WASM_NOT(x) kExprI32Eqz, x
 
 //------------------------------------------------------------------------------
 // Constructs that are composed of multiple bytecodes.
@@ -144,6 +384,8 @@
 #define WASM_I32_SHL(x, y) kExprI32Shl, x, y
 #define WASM_I32_SHR(x, y) kExprI32ShrU, x, y
 #define WASM_I32_SAR(x, y) kExprI32ShrS, x, y
+#define WASM_I32_ROR(x, y) kExprI32Ror, x, y
+#define WASM_I32_ROL(x, y) kExprI32Rol, x, y
 #define WASM_I32_EQ(x, y) kExprI32Eq, x, y
 #define WASM_I32_NE(x, y) kExprI32Ne, x, y
 #define WASM_I32_LTS(x, y) kExprI32LtS, x, y
@@ -157,6 +399,7 @@
 #define WASM_I32_CLZ(x) kExprI32Clz, x
 #define WASM_I32_CTZ(x) kExprI32Ctz, x
 #define WASM_I32_POPCNT(x) kExprI32Popcnt, x
+#define WASM_I32_EQZ(x) kExprI32Eqz, x
 
 //------------------------------------------------------------------------------
 // Int64 operations
@@ -174,6 +417,8 @@
 #define WASM_I64_SHL(x, y) kExprI64Shl, x, y
 #define WASM_I64_SHR(x, y) kExprI64ShrU, x, y
 #define WASM_I64_SAR(x, y) kExprI64ShrS, x, y
+#define WASM_I64_ROR(x, y) kExprI64Ror, x, y
+#define WASM_I64_ROL(x, y) kExprI64Rol, x, y
 #define WASM_I64_EQ(x, y) kExprI64Eq, x, y
 #define WASM_I64_NE(x, y) kExprI64Ne, x, y
 #define WASM_I64_LTS(x, y) kExprI64LtS, x, y
@@ -187,6 +432,7 @@
 #define WASM_I64_CLZ(x) kExprI64Clz, x
 #define WASM_I64_CTZ(x) kExprI64Ctz, x
 #define WASM_I64_POPCNT(x) kExprI64Popcnt, x
+#define WASM_I64_EQZ(x) kExprI64Eqz, x
 
 //------------------------------------------------------------------------------
 // Float32 operations
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
index 02d197c..a1c2a7a 100644
--- a/src/wasm/wasm-module.cc
+++ b/src/wasm/wasm-module.cc
@@ -19,13 +19,43 @@
 namespace internal {
 namespace wasm {
 
+static const char* wasmSections[] = {
+#define F(enumerator, string) string,
+    FOR_EACH_WASM_SECTION_TYPE(F)
+#undef F
+};
+
+static uint8_t wasmSectionsLengths[]{
+#define F(enumerator, string) sizeof(string) - 1,
+    FOR_EACH_WASM_SECTION_TYPE(F)
+#undef F
+};
+
+static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) ==
+                  (size_t)WasmSection::Code::Max,
+              "expected enum WasmSection::Code to be monotonic from 0");
+
+WasmSection::Code WasmSection::begin() { return (WasmSection::Code)0; }
+WasmSection::Code WasmSection::end() { return WasmSection::Code::Max; }
+WasmSection::Code WasmSection::next(WasmSection::Code code) {
+  return (WasmSection::Code)(1 + (uint32_t)code);
+}
+
+const char* WasmSection::getName(WasmSection::Code code) {
+  return wasmSections[(size_t)code];
+}
+
+size_t WasmSection::getNameLength(WasmSection::Code code) {
+  return wasmSectionsLengths[(size_t)code];
+}
+
 std::ostream& operator<<(std::ostream& os, const WasmModule& module) {
   os << "WASM module with ";
-  os << (1 << module.min_mem_size_log2) << " min mem";
-  os << (1 << module.max_mem_size_log2) << " max mem";
-  if (module.functions) os << module.functions->size() << " functions";
-  if (module.globals) os << module.functions->size() << " globals";
-  if (module.data_segments) os << module.functions->size() << " data segments";
+  os << (module.min_mem_pages * module.kPageSize) << " min mem";
+  os << (module.max_mem_pages * module.kPageSize) << " max mem";
+  os << module.functions.size() << " functions";
+  os << module.functions.size() << " globals";
+  os << module.functions.size() << " data segments";
   return os;
 }
 
@@ -48,7 +78,9 @@
   os << "#" << pair.function_->func_index << ":";
   if (pair.function_->name_offset > 0) {
     if (pair.module_) {
-      os << pair.module_->GetName(pair.function_->name_offset);
+      WasmName name = pair.module_->GetName(pair.function_->name_offset,
+                                            pair.function_->name_length);
+      os.write(name.name, name.length);
     } else {
       os << "+" << pair.function_->func_index;
     }
@@ -91,15 +123,15 @@
   }
 
   void Link(Handle<FixedArray> function_table,
-            std::vector<uint16_t>* functions) {
+            std::vector<uint16_t>& functions) {
     for (size_t i = 0; i < function_code_.size(); i++) {
       LinkFunction(function_code_[i]);
     }
-    if (functions && !function_table.is_null()) {
-      int table_size = static_cast<int>(functions->size());
+    if (!function_table.is_null()) {
+      int table_size = static_cast<int>(functions.size());
       DCHECK_EQ(function_table->length(), table_size * 2);
       for (int i = 0; i < table_size; i++) {
-        function_table->set(i + table_size, *function_code_[functions->at(i)]);
+        function_table->set(i + table_size, *function_code_[functions[i]]);
       }
     }
   }
@@ -151,11 +183,10 @@
 const int kWasmMemArrayBuffer = 2;
 const int kWasmGlobalsArrayBuffer = 3;
 
-
-size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>* globals) {
+size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>& globals) {
   uint32_t offset = 0;
-  if (!globals) return 0;
-  for (WasmGlobal& global : *globals) {
+  if (globals.size() == 0) return 0;
+  for (WasmGlobal& global : globals) {
     byte size = WasmOpcodes::MemSize(global.type);
     offset = (offset + size - 1) & ~(size - 1);  // align
     global.offset = offset;
@@ -166,8 +197,9 @@
 
 
 void LoadDataSegments(WasmModule* module, byte* mem_addr, size_t mem_size) {
-  for (const WasmDataSegment& segment : *module->data_segments) {
+  for (const WasmDataSegment& segment : module->data_segments) {
     if (!segment.init) continue;
+    if (!segment.source_size) continue;
     CHECK_LT(segment.dest_addr, mem_size);
     CHECK_LE(segment.source_size, mem_size);
     CHECK_LE(segment.dest_addr + segment.source_size, mem_size);
@@ -179,14 +211,13 @@
 
 
 Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) {
-  if (!module->function_table || module->function_table->size() == 0) {
+  if (module->function_table.size() == 0) {
     return Handle<FixedArray>::null();
   }
-  int table_size = static_cast<int>(module->function_table->size());
+  int table_size = static_cast<int>(module->function_table.size());
   Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size);
   for (int i = 0; i < table_size; i++) {
-    WasmFunction* function =
-        &module->functions->at(module->function_table->at(i));
+    WasmFunction* function = &module->functions[module->function_table[i]];
     fixed->set(i, Smi::FromInt(function->sig_index));
   }
   return fixed;
@@ -194,7 +225,7 @@
 
 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
                                      byte** backing_store) {
-  if (size > (1 << WasmModule::kMaxMemSize)) {
+  if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
     // TODO(titzer): lift restriction on maximum memory allocated here.
     *backing_store = nullptr;
     return Handle<JSArrayBuffer>::null();
@@ -236,12 +267,11 @@
   DCHECK(instance->module);
   DCHECK(instance->mem_buffer.is_null());
 
-  if (instance->module->min_mem_size_log2 > WasmModule::kMaxMemSize) {
+  if (instance->module->min_mem_pages > WasmModule::kMaxMemPages) {
     thrower->Error("Out of memory: wasm memory too large");
     return false;
   }
-  instance->mem_size = static_cast<size_t>(1)
-                       << instance->module->min_mem_size_log2;
+  instance->mem_size = WasmModule::kPageSize * instance->module->min_mem_pages;
   instance->mem_buffer =
       NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start);
   if (!instance->mem_start) {
@@ -273,50 +303,75 @@
     : shared_isolate(nullptr),
       module_start(nullptr),
       module_end(nullptr),
-      min_mem_size_log2(0),
-      max_mem_size_log2(0),
+      min_mem_pages(0),
+      max_mem_pages(0),
       mem_export(false),
       mem_external(false),
       start_function_index(-1),
-      globals(nullptr),
-      signatures(nullptr),
-      functions(nullptr),
-      data_segments(nullptr),
-      function_table(nullptr),
-      import_table(nullptr) {}
+      origin(kWasmOrigin) {}
 
-WasmModule::~WasmModule() {
-  if (globals) delete globals;
-  if (signatures) delete signatures;
-  if (functions) delete functions;
-  if (data_segments) delete data_segments;
-  if (function_table) delete function_table;
-  if (import_table) delete import_table;
+static MaybeHandle<JSFunction> ReportFFIError(ErrorThrower& thrower,
+                                              const char* error, uint32_t index,
+                                              wasm::WasmName module_name,
+                                              wasm::WasmName function_name) {
+  if (function_name.name) {
+    thrower.Error("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s",
+                  index, module_name.length, module_name.name,
+                  function_name.length, function_name.name, error);
+  } else {
+    thrower.Error("Import #%d module=\"%.*s\" error: %s", index,
+                  module_name.length, module_name.name, error);
+  }
+  thrower.Error("Import ");
+  return MaybeHandle<JSFunction>();
 }
 
-static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower,
-                                              Handle<JSObject> ffi,
-                                              uint32_t index,
-                                              Handle<String> name,
-                                              const char* cstr) {
-  if (!ffi.is_null()) {
-    MaybeHandle<Object> result = Object::GetProperty(ffi, name);
-    if (!result.is_null()) {
-      Handle<Object> obj = result.ToHandleChecked();
-      if (obj->IsJSFunction()) {
-        return Handle<JSFunction>::cast(obj);
-      } else {
-        thrower.Error("FFI function #%d:%s is not a JSFunction.", index, cstr);
-        return MaybeHandle<JSFunction>();
-      }
-    } else {
-      thrower.Error("FFI function #%d:%s not found.", index, cstr);
-      return MaybeHandle<JSFunction>();
-    }
-  } else {
-    thrower.Error("FFI table is not an object.");
-    return MaybeHandle<JSFunction>();
+static MaybeHandle<JSFunction> LookupFunction(
+    ErrorThrower& thrower, Factory* factory, Handle<JSObject> ffi,
+    uint32_t index, wasm::WasmName module_name, wasm::WasmName function_name) {
+  if (ffi.is_null()) {
+    return ReportFFIError(thrower, "FFI is not an object", index, module_name,
+                          function_name);
   }
+
+  // Look up the module first.
+  Handle<String> name = factory->InternalizeUtf8String(
+      Vector<const char>(module_name.name, module_name.length));
+  MaybeHandle<Object> result = Object::GetProperty(ffi, name);
+  if (result.is_null()) {
+    return ReportFFIError(thrower, "module not found", index, module_name,
+                          function_name);
+  }
+
+  Handle<Object> module = result.ToHandleChecked();
+
+  if (!module->IsJSReceiver()) {
+    return ReportFFIError(thrower, "module is not an object or function", index,
+                          module_name, function_name);
+  }
+
+  Handle<Object> function;
+  if (function_name.name) {
+    // Look up the function in the module.
+    Handle<String> name = factory->InternalizeUtf8String(
+        Vector<const char>(function_name.name, function_name.length));
+    MaybeHandle<Object> result = Object::GetProperty(module, name);
+    if (result.is_null()) {
+      return ReportFFIError(thrower, "function not found", index, module_name,
+                            function_name);
+    }
+    function = result.ToHandleChecked();
+  } else {
+    // No function specified. Use the "default export".
+    function = module;
+  }
+
+  if (!function->IsJSFunction()) {
+    return ReportFFIError(thrower, "not a function", index, module_name,
+                          function_name);
+  }
+
+  return Handle<JSFunction>::cast(function);
 }
 
 // Instantiates a wasm module as a JSObject.
@@ -338,11 +393,10 @@
       JS_OBJECT_TYPE,
       JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
   WasmModuleInstance instance(this);
-  std::vector<Handle<Code>> import_code;
   instance.context = isolate->native_context();
   instance.js_object = factory->NewJSObjectFromMap(map, TENURED);
   Handle<FixedArray> code_table =
-      factory->NewFixedArray(static_cast<int>(functions->size()), TENURED);
+      factory->NewFixedArray(static_cast<int>(functions.size()), TENURED);
   instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
 
   //-------------------------------------------------------------------------
@@ -359,13 +413,6 @@
                                        *instance.mem_buffer);
   LoadDataSegments(this, instance.mem_start, instance.mem_size);
 
-  if (mem_export) {
-    // Export the memory as a named property.
-    Handle<String> name = factory->InternalizeUtf8String("memory");
-    JSObject::AddProperty(instance.js_object, name, instance.mem_buffer,
-                          READ_ONLY);
-  }
-
   //-------------------------------------------------------------------------
   // Allocate the globals area if necessary.
   //-------------------------------------------------------------------------
@@ -382,25 +429,27 @@
   //-------------------------------------------------------------------------
   uint32_t index = 0;
   instance.function_table = BuildFunctionTable(isolate, this);
-  WasmLinker linker(isolate, functions->size());
+  WasmLinker linker(isolate, functions.size());
   ModuleEnv module_env;
   module_env.module = this;
   module_env.instance = &instance;
   module_env.linker = &linker;
-  module_env.asm_js = false;
+  module_env.origin = origin;
 
-  if (import_table->size() > 0) {
-    instance.import_code = &import_code;
-    instance.import_code->reserve(import_table->size());
-    for (const WasmImport& import : *import_table) {
-      const char* cstr = GetName(import.function_name_offset);
-      Handle<String> name = factory->InternalizeUtf8String(cstr);
-      MaybeHandle<JSFunction> function =
-          LookupFunction(thrower, ffi, index, name, cstr);
+  if (import_table.size() > 0) {
+    instance.import_code.reserve(import_table.size());
+    for (const WasmImport& import : import_table) {
+      WasmName module_name =
+          GetNameOrNull(import.module_name_offset, import.module_name_length);
+      WasmName function_name = GetNameOrNull(import.function_name_offset,
+                                             import.function_name_length);
+      MaybeHandle<JSFunction> function = LookupFunction(
+          thrower, factory, ffi, index, module_name, function_name);
       if (function.is_null()) return MaybeHandle<JSObject>();
       Handle<Code> code = compiler::CompileWasmToJSWrapper(
-          isolate, &module_env, function.ToHandleChecked(), import.sig, cstr);
-      instance.import_code->push_back(code);
+          isolate, &module_env, function.ToHandleChecked(), import.sig,
+          module_name, function_name);
+      instance.import_code.push_back(code);
       index++;
     }
   }
@@ -410,27 +459,32 @@
   //-------------------------------------------------------------------------
 
   // First pass: compile each function and initialize the code table.
-  index = 0;
-  for (const WasmFunction& func : *functions) {
+  index = FLAG_skip_compiling_wasm_funcs;
+  while (index < functions.size()) {
+    const WasmFunction& func = functions[index];
     if (thrower.error()) break;
     DCHECK_EQ(index, func.func_index);
 
-    const char* cstr = GetName(func.name_offset);
-    Handle<String> name = factory->InternalizeUtf8String(cstr);
+    WasmName str = GetName(func.name_offset, func.name_length);
+    WasmName str_null = {nullptr, 0};
+    Handle<String> name = factory->InternalizeUtf8String(
+        Vector<const char>(str.name, str.length));
     Handle<Code> code = Handle<Code>::null();
     Handle<JSFunction> function = Handle<JSFunction>::null();
     if (func.external) {
       // Lookup external function in FFI object.
       MaybeHandle<JSFunction> function =
-          LookupFunction(thrower, ffi, index, name, cstr);
+          LookupFunction(thrower, factory, ffi, index, str, str_null);
       if (function.is_null()) return MaybeHandle<JSObject>();
-      code = compiler::CompileWasmToJSWrapper(
-          isolate, &module_env, function.ToHandleChecked(), func.sig, cstr);
+      code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
+                                              function.ToHandleChecked(),
+                                              func.sig, str, str_null);
     } else {
       // Compile the function.
       code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
       if (code.is_null()) {
-        thrower.Error("Compilation of #%d:%s failed.", index, cstr);
+        thrower.Error("Compilation of #%d:%.*s failed.", index, str.length,
+                      str.name);
         return MaybeHandle<JSObject>();
       }
       if (func.exported) {
@@ -455,6 +509,40 @@
   instance.js_object->SetInternalField(kWasmModuleFunctionTable,
                                        Smi::FromInt(0));
 
+  //-------------------------------------------------------------------------
+  // Create and populate the exports object.
+  //-------------------------------------------------------------------------
+  if (export_table.size() > 0 || mem_export) {
+    index = 0;
+    // Create the "exports" object.
+    Handle<JSFunction> object_function = Handle<JSFunction>(
+        isolate->native_context()->object_function(), isolate);
+    Handle<JSObject> exports_object =
+        factory->NewJSObject(object_function, TENURED);
+    Handle<String> exports_name = factory->InternalizeUtf8String("exports");
+    JSObject::AddProperty(instance.js_object, exports_name, exports_object,
+                          READ_ONLY);
+
+    // Compile wrappers and add them to the exports object.
+    for (const WasmExport& exp : export_table) {
+      if (thrower.error()) break;
+      WasmName str = GetName(exp.name_offset, exp.name_length);
+      Handle<String> name = factory->InternalizeUtf8String(
+          Vector<const char>(str.name, str.length));
+      Handle<Code> code = linker.GetFunctionCode(exp.func_index);
+      Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
+          isolate, &module_env, name, code, instance.js_object, exp.func_index);
+      JSObject::AddProperty(exports_object, name, function, READ_ONLY);
+    }
+
+    if (mem_export) {
+      // Export the memory as a named property.
+      Handle<String> name = factory->InternalizeUtf8String("memory");
+      JSObject::AddProperty(exports_object, name, instance.mem_buffer,
+                            READ_ONLY);
+    }
+  }
+
   // Run the start function if one was specified.
   if (this->start_function_index >= 0) {
     HandleScope scope(isolate);
@@ -480,18 +568,12 @@
 Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
   DCHECK(IsValidFunction(index));
   if (linker) return linker->GetFunctionCode(index);
-  if (instance && instance->function_code) {
-    return instance->function_code->at(index);
-  }
-  return Handle<Code>::null();
+  return instance ? instance->function_code[index] : Handle<Code>::null();
 }
 
 Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
   DCHECK(IsValidImport(index));
-  if (instance && instance->import_code) {
-    return instance->import_code->at(index);
-  }
-  return Handle<Code>::null();
+  return instance ? instance->import_code[index] : Handle<Code>::null();
 }
 
 compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
@@ -499,7 +581,7 @@
   DCHECK(IsValidFunction(index));
   // Always make a direct call to whatever is in the table at that location.
   // A wrapper will be generated for FFI calls.
-  WasmFunction* function = &module->functions->at(index);
+  WasmFunction* function = &module->functions[index];
   return GetWasmCallDescriptor(zone, function->sig);
 }
 
@@ -507,12 +589,15 @@
 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
                                 const byte* module_end, bool asm_js) {
   HandleScope scope(isolate);
-  Zone zone;
+  Zone zone(isolate->allocator());
   // Decode the module, but don't verify function bodies, since we'll
   // be compiling them anyway.
-  ModuleResult result =
-      DecodeWasmModule(isolate, &zone, module_start, module_end, false, false);
+  ModuleResult result = DecodeWasmModule(isolate, &zone, module_start,
+                                         module_end, false, kWasmOrigin);
   if (result.failed()) {
+    if (result.val) {
+      delete result.val;
+    }
     // Module verification failed. throw.
     std::ostringstream str;
     str << "WASM.compileRun() failed: " << result;
@@ -546,18 +631,18 @@
   instance.function_table = BuildFunctionTable(isolate, module);
 
   // Create module environment.
-  WasmLinker linker(isolate, module->functions->size());
+  WasmLinker linker(isolate, module->functions.size());
   ModuleEnv module_env;
   module_env.module = module;
   module_env.instance = &instance;
   module_env.linker = &linker;
-  module_env.asm_js = false;
+  module_env.origin = module->origin;
 
   // Compile all functions.
   Handle<Code> main_code = Handle<Code>::null();  // record last code.
   uint32_t index = 0;
   int main_index = 0;
-  for (const WasmFunction& func : *module->functions) {
+  for (const WasmFunction& func : module->functions) {
     DCHECK_EQ(index, func.func_index);
     if (!func.external) {
       // Compile the function and install it in the code table.
diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h
index 5f5777c..4e5aa78 100644
--- a/src/wasm/wasm-module.h
+++ b/src/wasm/wasm-module.h
@@ -22,22 +22,81 @@
 const size_t kMaxModuleSize = 1024 * 1024 * 1024;
 const size_t kMaxFunctionSize = 128 * 1024;
 const size_t kMaxStringSize = 256;
+const uint32_t kWasmMagic = 0x6d736100;
+const uint32_t kWasmVersion = 0x0a;
 
-enum WasmSectionDeclCode {
-  kDeclMemory = 0x00,
-  kDeclSignatures = 0x01,
-  kDeclFunctions = 0x02,
-  kDeclGlobals = 0x03,
-  kDeclDataSegments = 0x04,
-  kDeclFunctionTable = 0x05,
-  kDeclEnd = 0x06,
-  kDeclStartFunction = 0x07,
-  kDeclImportTable = 0x08,
-  kDeclWLL = 0x11,
+// WebAssembly sections are named as strings in the binary format, but
+// internally V8 uses an enum to handle them.
+//
+// Entries have the form F(enumerator, string).
+#define FOR_EACH_WASM_SECTION_TYPE(F)          \
+  F(Memory, "memory")                          \
+  F(Signatures, "signatures")                  \
+  F(Functions, "functions")                    \
+  F(Globals, "globals")                        \
+  F(DataSegments, "data_segments")             \
+  F(FunctionTable, "function_table")           \
+  F(End, "end")                                \
+  F(StartFunction, "start_function")           \
+  F(ImportTable, "import_table")               \
+  F(ExportTable, "export_table")               \
+  F(FunctionSignatures, "function_signatures") \
+  F(FunctionBodies, "function_bodies")         \
+  F(Names, "names")
+
+// Contants for the above section types: {LEB128 length, characters...}.
+#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
+#define WASM_SECTION_SIGNATURES \
+  10, 's', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', 's'
+#define WASM_SECTION_FUNCTIONS 9, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', 's'
+#define WASM_SECTION_GLOBALS 7, 'g', 'l', 'o', 'b', 'a', 'l', 's'
+#define WASM_SECTION_DATA_SEGMENTS \
+  13, 'd', 'a', 't', 'a', '_', 's', 'e', 'g', 'm', 'e', 'n', 't', 's'
+#define WASM_SECTION_FUNCTION_TABLE \
+  14, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_END 3, 'e', 'n', 'd'
+#define WASM_SECTION_START_FUNCTION \
+  14, 's', 't', 'a', 'r', 't', '_', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
+#define WASM_SECTION_IMPORT_TABLE \
+  12, 'i', 'm', 'p', 'o', 'r', 't', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_EXPORT_TABLE \
+  12, 'e', 'x', 'p', 'o', 'r', 't', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_FUNCTION_SIGNATURES                                    \
+  19, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 's', 'i', 'g', 'n', 'a', \
+      't', 'u', 'r', 'e', 's'
+#define WASM_SECTION_FUNCTION_BODIES \
+  15, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 'b', 'o', 'd', 'i', 'e', 's'
+#define WASM_SECTION_NAMES 5, 'n', 'a', 'm', 'e', 's'
+
+// Constants for the above section headers' size (LEB128 + characters).
+#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
+#define WASM_SECTION_SIGNATURES_SIZE ((size_t)11)
+#define WASM_SECTION_FUNCTIONS_SIZE ((size_t)10)
+#define WASM_SECTION_GLOBALS_SIZE ((size_t)8)
+#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)14)
+#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)15)
+#define WASM_SECTION_END_SIZE ((size_t)4)
+#define WASM_SECTION_START_FUNCTION_SIZE ((size_t)15)
+#define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)13)
+#define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)13)
+#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)20)
+#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)16)
+#define WASM_SECTION_NAMES_SIZE ((size_t)6)
+
+struct WasmSection {
+  enum class Code : uint32_t {
+#define F(enumerator, string) enumerator,
+    FOR_EACH_WASM_SECTION_TYPE(F)
+#undef F
+        Max
+  };
+  static WasmSection::Code begin();
+  static WasmSection::Code end();
+  static WasmSection::Code next(WasmSection::Code code);
+  static const char* getName(Code code);
+  static size_t getNameLength(Code code);
 };
 
-static const int kMaxModuleSectionCode = 0x11;
-
 enum WasmFunctionDeclBit {
   kDeclFunctionName = 0x01,
   kDeclFunctionImport = 0x02,
@@ -47,15 +106,15 @@
 
 // Constants for fixed-size elements within a module.
 static const size_t kDeclMemorySize = 3;
-static const size_t kDeclGlobalSize = 6;
 static const size_t kDeclDataSegmentSize = 13;
 
 // Static representation of a WASM function.
 struct WasmFunction {
   FunctionSig* sig;      // signature of the function.
   uint32_t func_index;   // index into the function table.
-  uint16_t sig_index;    // index into the signature table.
+  uint32_t sig_index;    // index into the signature table.
   uint32_t name_offset;  // offset in the module bytes of the name, if any.
+  uint32_t name_length;  // length in bytes of the name.
   uint32_t code_start_offset;    // offset in the module bytes of code start.
   uint32_t code_end_offset;      // offset in the module bytes of code end.
   uint16_t local_i32_count;      // number of i32 local variables.
@@ -69,14 +128,24 @@
 // Static representation of an imported WASM function.
 struct WasmImport {
   FunctionSig* sig;               // signature of the function.
-  uint16_t sig_index;             // index into the signature table.
+  uint32_t sig_index;             // index into the signature table.
   uint32_t module_name_offset;    // offset in module bytes of the module name.
+  uint32_t module_name_length;    // length in bytes of the module name.
   uint32_t function_name_offset;  // offset in module bytes of the import name.
+  uint32_t function_name_length;  // length in bytes of the import name.
+};
+
+// Static representation of an exported WASM function.
+struct WasmExport {
+  uint32_t func_index;   // index into the function table.
+  uint32_t name_offset;  // offset in module bytes of the name to export.
+  uint32_t name_length;  // length in bytes of the exported name.
 };
 
 // Static representation of a wasm global variable.
 struct WasmGlobal {
   uint32_t name_offset;  // offset in the module bytes of the name, if any.
+  uint32_t name_length;  // length in bytes of the global name.
   MachineType type;      // type of the global.
   uint32_t offset;       // offset from beginning of globals area.
   bool exported;         // true if this global is exported.
@@ -90,35 +159,46 @@
   bool init;               // true if loaded upon instantiation.
 };
 
+enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
+
 // Static representation of a module.
 struct WasmModule {
-  static const uint8_t kMinMemSize = 12;  // Minimum memory size = 4kb
-  static const uint8_t kMaxMemSize = 30;  // Maximum memory size = 1gb
+  static const uint32_t kPageSize = 0x10000;    // Page size, 64kb.
+  static const uint32_t kMinMemPages = 1;       // Minimum memory size = 64kb
+  static const uint32_t kMaxMemPages = 16384;   // Maximum memory size =  1gb
 
   Isolate* shared_isolate;    // isolate for storing shared code.
   const byte* module_start;   // starting address for the module bytes.
   const byte* module_end;     // end address for the module bytes.
-  uint8_t min_mem_size_log2;  // minimum size of the memory (log base 2).
-  uint8_t max_mem_size_log2;  // maximum size of the memory (log base 2).
+  uint32_t min_mem_pages;     // minimum size of the memory in 64k pages.
+  uint32_t max_mem_pages;     // maximum size of the memory in 64k pages.
   bool mem_export;            // true if the memory is exported.
   bool mem_external;          // true if the memory is external.
   int start_function_index;   // start function, if any.
+  ModuleOrigin origin;        // origin of the module
 
-  std::vector<WasmGlobal>* globals;             // globals in this module.
-  std::vector<FunctionSig*>* signatures;        // signatures in this module.
-  std::vector<WasmFunction>* functions;         // functions in this module.
-  std::vector<WasmDataSegment>* data_segments;  // data segments in this module.
-  std::vector<uint16_t>* function_table;        // function table.
-  std::vector<WasmImport>* import_table;        // import table.
+  std::vector<WasmGlobal> globals;             // globals in this module.
+  std::vector<FunctionSig*> signatures;        // signatures in this module.
+  std::vector<WasmFunction> functions;         // functions in this module.
+  std::vector<WasmDataSegment> data_segments;  // data segments in this module.
+  std::vector<uint16_t> function_table;        // function table.
+  std::vector<WasmImport> import_table;        // import table.
+  std::vector<WasmExport> export_table;        // export table.
 
   WasmModule();
-  ~WasmModule();
 
-  // Get a pointer to a string stored in the module bytes representing a name.
-  const char* GetName(uint32_t offset) const {
-    if (offset == 0) return "<?>";  // no name.
-    CHECK(BoundsCheck(offset, offset + 1));
-    return reinterpret_cast<const char*>(module_start + offset);
+  // Get a string stored in the module bytes representing a name.
+  WasmName GetName(uint32_t offset, uint32_t length) const {
+    if (length == 0) return {"<?>", 3};  // no name.
+    CHECK(BoundsCheck(offset, offset + length));
+    return {reinterpret_cast<const char*>(module_start + offset), length};
+  }
+
+  // Get a string stored in the module bytes representing a name.
+  WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
+    if (length == 0) return {NULL, 0};  // no name.
+    CHECK(BoundsCheck(offset, offset + length));
+    return {reinterpret_cast<const char*>(module_start + offset), length};
   }
 
   // Checks the given offset range is contained within the module bytes.
@@ -141,8 +221,8 @@
   Handle<JSArrayBuffer> mem_buffer;      // Handle to array buffer of memory.
   Handle<JSArrayBuffer> globals_buffer;  // Handle to array buffer of globals.
   Handle<FixedArray> function_table;     // indirect function table.
-  std::vector<Handle<Code>>* function_code;  // code objects for each function.
-  std::vector<Handle<Code>>* import_code;   // code objects for each import.
+  std::vector<Handle<Code>> function_code;  // code objects for each function.
+  std::vector<Handle<Code>> import_code;    // code objects for each import.
   // -- raw memory ------------------------------------------------------------
   byte* mem_start;  // start of linear memory.
   size_t mem_size;  // size of the linear memory.
@@ -152,7 +232,6 @@
 
   explicit WasmModuleInstance(WasmModule* m)
       : module(m),
-        function_code(nullptr),
         mem_start(nullptr),
         mem_size(0),
         globals_start(nullptr),
@@ -168,41 +247,42 @@
   WasmModule* module;
   WasmModuleInstance* instance;
   WasmLinker* linker;
-  bool asm_js;  // true if the module originated from asm.js.
+  ModuleOrigin origin;
 
   bool IsValidGlobal(uint32_t index) {
-    return module && index < module->globals->size();
+    return module && index < module->globals.size();
   }
   bool IsValidFunction(uint32_t index) {
-    return module && index < module->functions->size();
+    return module && index < module->functions.size();
   }
   bool IsValidSignature(uint32_t index) {
-    return module && index < module->signatures->size();
+    return module && index < module->signatures.size();
   }
   bool IsValidImport(uint32_t index) {
-    return module && index < module->import_table->size();
+    return module && index < module->import_table.size();
   }
   MachineType GetGlobalType(uint32_t index) {
     DCHECK(IsValidGlobal(index));
-    return module->globals->at(index).type;
+    return module->globals[index].type;
   }
   FunctionSig* GetFunctionSignature(uint32_t index) {
     DCHECK(IsValidFunction(index));
-    return module->functions->at(index).sig;
+    return module->functions[index].sig;
   }
   FunctionSig* GetImportSignature(uint32_t index) {
     DCHECK(IsValidImport(index));
-    return module->import_table->at(index).sig;
+    return module->import_table[index].sig;
   }
   FunctionSig* GetSignature(uint32_t index) {
     DCHECK(IsValidSignature(index));
-    return module->signatures->at(index);
+    return module->signatures[index];
   }
   size_t FunctionTableSize() {
-    return module && module->function_table ? module->function_table->size()
-                                            : 0;
+    return module ? module->function_table.size() : 0;
   }
 
+  bool asm_js() { return origin == kAsmJsOrigin; }
+
   Handle<Code> GetFunctionCode(uint32_t index);
   Handle<Code> GetImportCode(uint32_t index);
   Handle<FixedArray> GetFunctionTable();
diff --git a/src/wasm/wasm-opcodes.cc b/src/wasm/wasm-opcodes.cc
index a609e03..736c4d9 100644
--- a/src/wasm/wasm-opcodes.cc
+++ b/src/wasm/wasm-opcodes.cc
@@ -66,6 +66,7 @@
 #define SET_SIG_TABLE(name, opcode, sig) \
   kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
   FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE);
+  FOREACH_ASMJS_COMPAT_OPCODE(SET_SIG_TABLE);
 #undef SET_SIG_TABLE
 }
 
@@ -103,6 +104,8 @@
     case kExprI64Shl:
     case kExprI64ShrU:
     case kExprI64ShrS:
+    case kExprI64Ror:
+    case kExprI64Rol:
     case kExprI64Eq:
     case kExprI64Ne:
     case kExprI64LtS:
diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h
index 7cb9c00..52f85aa 100644
--- a/src/wasm/wasm-opcodes.h
+++ b/src/wasm/wasm-opcodes.h
@@ -46,28 +46,14 @@
 // We use kTagged here because kNone is already used by kAstStmt.
 const LocalType kAstEnd = MachineRepresentation::kTagged;
 
-// Functionality related to encoding memory accesses.
-struct MemoryAccess {
-  // Atomicity annotations for access to the memory and globals.
-  enum Atomicity {
-    kNone = 0,        // non-atomic
-    kSequential = 1,  // sequential consistency
-    kAcquire = 2,     // acquire semantics
-    kRelease = 3      // release semantics
-  };
-
-  // Alignment annotations for memory accesses.
-  enum Alignment { kAligned = 0, kUnaligned = 1 };
-
-  // Bitfields for the various annotations for memory accesses.
-  typedef BitField<Alignment, 7, 1> AlignmentField;
-  typedef BitField<Atomicity, 5, 2> AtomicityField;
-  typedef BitField<bool, 4, 1> OffsetField;
-};
-
 typedef Signature<LocalType> FunctionSig;
 std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
 
+struct WasmName {
+  const char* name;
+  uint32_t length;
+};
+
 // TODO(titzer): Renumber all the opcodes to fill in holes.
 
 // Control expressions and blocks.
@@ -80,7 +66,7 @@
   V(Select, 0x05, _)              \
   V(Br, 0x06, _)                  \
   V(BrIf, 0x07, _)                \
-  V(TableSwitch, 0x08, _)         \
+  V(BrTable, 0x08, _)             \
   V(Return, 0x14, _)              \
   V(Unreachable, 0x15, _)
 
@@ -97,7 +83,8 @@
   V(StoreGlobal, 0x11, _)      \
   V(CallFunction, 0x12, _)     \
   V(CallIndirect, 0x13, _)     \
-  V(CallImport, 0x1F, _)
+  V(CallImport, 0x1F, _)       \
+  V(DeclLocals, 0x1E, _)
 
 // Load memory expressions.
 #define FOREACH_LOAD_MEM_OPCODE(V) \
@@ -161,7 +148,7 @@
   V(I32Clz, 0x57, i_i)            \
   V(I32Ctz, 0x58, i_i)            \
   V(I32Popcnt, 0x59, i_i)         \
-  V(BoolNot, 0x5a, i_i)           \
+  V(I32Eqz, 0x5a, i_i)            \
   V(I64Add, 0x5b, l_ll)           \
   V(I64Sub, 0x5c, l_ll)           \
   V(I64Mul, 0x5d, l_ll)           \
@@ -188,6 +175,7 @@
   V(I64Clz, 0x72, l_l)            \
   V(I64Ctz, 0x73, l_l)            \
   V(I64Popcnt, 0x74, l_l)         \
+  V(I64Eqz, 0xba, i_l)            \
   V(F32Add, 0x75, f_ff)           \
   V(F32Sub, 0x76, f_ff)           \
   V(F32Mul, 0x77, f_ff)           \
@@ -252,7 +240,47 @@
   V(F64ConvertF32, 0xb2, d_f)     \
   V(F64ReinterpretI64, 0xb3, d_l) \
   V(I32ReinterpretF32, 0xb4, i_f) \
-  V(I64ReinterpretF64, 0xb5, l_d)
+  V(I64ReinterpretF64, 0xb5, l_d) \
+  V(I32Ror, 0xb6, i_ii)           \
+  V(I32Rol, 0xb7, i_ii)           \
+  V(I64Ror, 0xb8, l_ll)           \
+  V(I64Rol, 0xb9, l_ll)
+
+// For compatibility with Asm.js.
+#define FOREACH_ASMJS_COMPAT_OPCODE(V) \
+  V(F64Acos, 0xc0, d_d)                \
+  V(F64Asin, 0xc1, d_d)                \
+  V(F64Atan, 0xc2, d_d)                \
+  V(F64Cos, 0xc3, d_d)                 \
+  V(F64Sin, 0xc4, d_d)                 \
+  V(F64Tan, 0xc5, d_d)                 \
+  V(F64Exp, 0xc6, d_d)                 \
+  V(F64Log, 0xc7, d_d)                 \
+  V(F64Atan2, 0xc8, d_dd)              \
+  V(F64Pow, 0xc9, d_dd)                \
+  V(F64Mod, 0xca, d_dd)
+
+// TODO(titzer): sketch of asm-js compatibility bytecodes
+/* V(I32AsmjsDivS, 0xd0, i_ii)          \ */
+/* V(I32AsmjsDivU, 0xd1, i_ii)          \ */
+/* V(I32AsmjsRemS, 0xd2, i_ii)          \ */
+/* V(I32AsmjsRemU, 0xd3, i_ii)          \ */
+/* V(I32AsmjsLoad8S, 0xd4, i_i)         \ */
+/* V(I32AsmjsLoad8U, 0xd5, i_i)         \ */
+/* V(I32AsmjsLoad16S, 0xd6, i_i)        \ */
+/* V(I32AsmjsLoad16U, 0xd7, i_i)        \ */
+/* V(I32AsmjsLoad, 0xd8, i_i)           \ */
+/* V(F32AsmjsLoad, 0xd9, f_i)           \ */
+/* V(F64AsmjsLoad, 0xda, d_i)           \ */
+/* V(I32AsmjsStore8, 0xdb, i_i)         \ */
+/* V(I32AsmjsStore16, 0xdc, i_i)        \ */
+/* V(I32AsmjsStore, 0xdd, i_ii)         \ */
+/* V(F32AsmjsStore, 0xde, i_if)         \ */
+/* V(F64AsmjsStore, 0xdf, i_id)         \ */
+/* V(I32SAsmjsConvertF32, 0xe0, i_f)    \ */
+/* V(I32UAsmjsConvertF32, 0xe1, i_f)    \ */
+/* V(I32SAsmjsConvertF64, 0xe2, i_d)    \ */
+/* V(I32SAsmjsConvertF64, 0xe3, i_d) */
 
 // All opcodes.
 #define FOREACH_OPCODE(V)     \
@@ -261,7 +289,8 @@
   FOREACH_SIMPLE_OPCODE(V)    \
   FOREACH_STORE_MEM_OPCODE(V) \
   FOREACH_LOAD_MEM_OPCODE(V)  \
-  FOREACH_MISC_MEM_OPCODE(V)
+  FOREACH_MISC_MEM_OPCODE(V)  \
+  FOREACH_ASMJS_COMPAT_OPCODE(V)
 
 // All signatures.
 #define FOREACH_SIGNATURE(V)         \
@@ -300,6 +329,19 @@
 #undef DECLARE_NAMED_ENUM
 };
 
+// The reason for a trap.
+enum TrapReason {
+  kTrapUnreachable,
+  kTrapMemOutOfBounds,
+  kTrapDivByZero,
+  kTrapDivUnrepresentable,
+  kTrapRemByZero,
+  kTrapFloatUnrepresentable,
+  kTrapFuncInvalid,
+  kTrapFuncSigMismatch,
+  kTrapCount
+};
+
 // A collection of opcode-related static methods.
 class WasmOpcodes {
  public:
@@ -428,10 +470,6 @@
     }
   }
 
-  static byte LoadStoreAccessOf(bool with_offset) {
-    return MemoryAccess::OffsetField::encode(with_offset);
-  }
-
   static char ShortNameOf(LocalType type) {
     switch (type) {
       case kAstI32:
@@ -470,6 +508,29 @@
         return "<unknown>";
     }
   }
+
+  static const char* TrapReasonName(TrapReason reason) {
+    switch (reason) {
+      case kTrapUnreachable:
+        return "unreachable";
+      case kTrapMemOutOfBounds:
+        return "memory access out of bounds";
+      case kTrapDivByZero:
+        return "divide by zero";
+      case kTrapDivUnrepresentable:
+        return "divide result unrepresentable";
+      case kTrapRemByZero:
+        return "remainder by zero";
+      case kTrapFloatUnrepresentable:
+        return "integer result unrepresentable";
+      case kTrapFuncInvalid:
+        return "invalid function";
+      case kTrapFuncSigMismatch:
+        return "function signature mismatch";
+      default:
+        return "<?>";
+    }
+  }
 };
 }  // namespace wasm
 }  // namespace internal