Revert "Revert "Upgrade to 5.0.71.48"" DO NOT MERGE

This reverts commit f2e3994fa5148cc3d9946666f0b0596290192b0e,
and updates the x64 makefile properly so it doesn't break that
build.

FPIIM-449

Change-Id: Ib83e35bfbae6af627451c926a9650ec57c045605
(cherry picked from commit 109988c7ccb6f3fd1a58574fa3dfb88beaef6632)
diff --git a/src/wasm/asm-wasm-builder.cc b/src/wasm/asm-wasm-builder.cc
index 30f8464..ee5427b 100644
--- a/src/wasm/asm-wasm-builder.cc
+++ b/src/wasm/asm-wasm-builder.cc
@@ -27,7 +27,8 @@
 
 class AsmWasmBuilderImpl : public AstVisitor {
  public:
-  AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal)
+  AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal,
+                     Handle<Object> foreign)
       : local_variables_(HashMap::PointersMatch,
                          ZoneHashMap::kDefaultHashMapCapacity,
                          ZoneAllocationPolicy(zone)),
@@ -44,17 +45,23 @@
         literal_(literal),
         isolate_(isolate),
         zone_(zone),
+        foreign_(foreign),
         cache_(TypeCache::Get()),
         breakable_blocks_(zone),
         block_size_(0),
-        init_function_index(0) {
+        init_function_index_(0),
+        next_table_index_(0),
+        function_tables_(HashMap::PointersMatch,
+                         ZoneHashMap::kDefaultHashMapCapacity,
+                         ZoneAllocationPolicy(zone)),
+        imported_function_table_(this) {
     InitializeAstVisitor(isolate);
   }
 
   void InitializeInitFunction() {
     unsigned char init[] = "__init__";
-    init_function_index = builder_->AddFunction();
-    current_function_builder_ = builder_->FunctionAt(init_function_index);
+    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);
@@ -70,7 +77,7 @@
 
   void VisitFunctionDeclaration(FunctionDeclaration* decl) {
     DCHECK(!in_function_);
-    DCHECK(current_function_builder_ == nullptr);
+    DCHECK_NULL(current_function_builder_);
     uint16_t index = LookupOrInsertFunction(decl->proxy()->var());
     current_function_builder_ = builder_->FunctionAt(index);
     in_function_ = true;
@@ -103,11 +110,15 @@
         }
       }
     }
-    DCHECK(in_function_);
-    BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false,
-                         static_cast<byte>(stmt->statements()->length()));
-    RECURSE(VisitStatements(stmt->statements()));
-    DCHECK(block_size_ >= 0);
+    if (in_function_) {
+      BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock,
+                           false,
+                           static_cast<byte>(stmt->statements()->length()));
+      RECURSE(VisitStatements(stmt->statements()));
+      DCHECK(block_size_ >= 0);
+    } else {
+      RECURSE(VisitStatements(stmt->statements()));
+    }
   }
 
   class BlockVisitor {
@@ -162,7 +173,7 @@
 
   void VisitContinueStatement(ContinueStatement* stmt) {
     DCHECK(in_function_);
-    DCHECK(stmt->target() != NULL);
+    DCHECK_NOT_NULL(stmt->target());
     int i = static_cast<int>(breakable_blocks_.size()) - 1;
     int block_distance = 0;
     for (; i >= 0; i--) {
@@ -183,7 +194,7 @@
 
   void VisitBreakStatement(BreakStatement* stmt) {
     DCHECK(in_function_);
-    DCHECK(stmt->target() != NULL);
+    DCHECK_NOT_NULL(stmt->target());
     int i = static_cast<int>(breakable_blocks_.size()) - 1;
     int block_distance = 0;
     for (; i >= 0; i--) {
@@ -229,7 +240,7 @@
   void CompileCase(CaseClause* clause, uint16_t fall_through,
                    VariableProxy* tag) {
     Literal* label = clause->label()->AsLiteral();
-    DCHECK(label != nullptr);
+    DCHECK_NOT_NULL(label);
     block_size_++;
     current_function_builder_->Emit(kExprIf);
     current_function_builder_->Emit(kExprI32Ior);
@@ -247,7 +258,7 @@
 
   void VisitSwitchStatement(SwitchStatement* stmt) {
     VariableProxy* tag = stmt->tag()->AsVariableProxy();
-    DCHECK(tag != NULL);
+    DCHECK_NOT_NULL(tag);
     BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false,
                          0);
     uint16_t fall_through = current_function_builder_->AddLocal(kAstI32);
@@ -332,20 +343,20 @@
     Scope* scope = expr->scope();
     if (in_function_) {
       if (expr->bounds().lower->IsFunction()) {
-        Type::FunctionType* func_type = expr->bounds().lower->AsFunction();
+        FunctionType* func_type = expr->bounds().lower->AsFunction();
         LocalType return_type = TypeFrom(func_type->Result());
         current_function_builder_->ReturnType(return_type);
         for (int i = 0; i < expr->parameter_count(); i++) {
           LocalType type = TypeFrom(func_type->Parameter(i));
-          DCHECK(type != kAstStmt);
+          DCHECK_NE(kAstStmt, type);
           LookupOrInsertLocal(scope->parameter(i), type);
         }
       } else {
         UNREACHABLE();
       }
     }
-    RECURSE(VisitDeclarations(scope->declarations()));
     RECURSE(VisitStatements(expr->body()));
+    RECURSE(VisitDeclarations(scope->declarations()));
   }
 
   void VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
@@ -363,34 +374,26 @@
   void VisitVariableProxy(VariableProxy* expr) {
     if (in_function_) {
       Variable* var = expr->var();
-      if (var->is_function()) {
-        DCHECK(!is_set_op_);
-        std::vector<uint8_t> index =
-            UnsignedLEB128From(LookupOrInsertFunction(var));
-        current_function_builder_->EmitCode(
-            &index[0], static_cast<uint32_t>(index.size()));
-      } else {
-        if (is_set_op_) {
-          if (var->IsContextSlot()) {
-            current_function_builder_->Emit(kExprStoreGlobal);
-          } else {
-            current_function_builder_->Emit(kExprSetLocal);
-          }
-          is_set_op_ = false;
-        } else {
-          if (var->IsContextSlot()) {
-            current_function_builder_->Emit(kExprLoadGlobal);
-          } else {
-            current_function_builder_->Emit(kExprGetLocal);
-          }
-        }
-        LocalType var_type = TypeOf(expr);
-        DCHECK(var_type != kAstStmt);
+      if (is_set_op_) {
         if (var->IsContextSlot()) {
-          AddLeb128(LookupOrInsertGlobal(var, var_type), false);
+          current_function_builder_->Emit(kExprStoreGlobal);
         } else {
-          AddLeb128(LookupOrInsertLocal(var, var_type), true);
+          current_function_builder_->Emit(kExprSetLocal);
         }
+        is_set_op_ = false;
+      } else {
+        if (var->IsContextSlot()) {
+          current_function_builder_->Emit(kExprLoadGlobal);
+        } else {
+          current_function_builder_->Emit(kExprGetLocal);
+        }
+      }
+      LocalType var_type = TypeOf(expr);
+      DCHECK_NE(kAstStmt, var_type);
+      if (var->IsContextSlot()) {
+        AddLeb128(LookupOrInsertGlobal(var, var_type), false);
+      } else {
+        AddLeb128(LookupOrInsertLocal(var, var_type), true);
       }
     }
   }
@@ -433,10 +436,10 @@
       ObjectLiteralProperty* prop = props->at(i);
       DCHECK(marking_exported);
       VariableProxy* expr = prop->value()->AsVariableProxy();
-      DCHECK(expr != nullptr);
+      DCHECK_NOT_NULL(expr);
       Variable* var = expr->var();
       Literal* name = prop->key()->AsLiteral();
-      DCHECK(name != nullptr);
+      DCHECK_NOT_NULL(name);
       DCHECK(name->IsPropertyName());
       const AstRawString* raw_name = name->AsRawPropertyName();
       if (var->is_function()) {
@@ -451,7 +454,7 @@
   void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); }
 
   void LoadInitFunction() {
-    current_function_builder_ = builder_->FunctionAt(init_function_index);
+    current_function_builder_ = builder_->FunctionAt(init_function_index_);
     in_function_ = true;
   }
 
@@ -460,11 +463,155 @@
     current_function_builder_ = nullptr;
   }
 
+  void AddFunctionTable(VariableProxy* table, ArrayLiteral* funcs) {
+    FunctionType* func_type =
+        funcs->bounds().lower->AsArray()->Element()->AsFunction();
+    LocalType return_type = TypeFrom(func_type->Result());
+    FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1,
+                             func_type->Arity());
+    if (return_type != kAstStmt) {
+      sig.AddReturn(static_cast<LocalType>(return_type));
+    }
+    for (int i = 0; i < func_type->Arity(); i++) {
+      sig.AddParam(TypeFrom(func_type->Parameter(i)));
+    }
+    uint16_t signature_index = builder_->AddSignature(sig.Build());
+    InsertFunctionTable(table->var(), next_table_index_, signature_index);
+    next_table_index_ += funcs->values()->length();
+    for (int i = 0; i < funcs->values()->length(); i++) {
+      VariableProxy* func = funcs->values()->at(i)->AsVariableProxy();
+      DCHECK_NOT_NULL(func);
+      builder_->AddIndirectFunction(LookupOrInsertFunction(func->var()));
+    }
+  }
+
+  struct FunctionTableIndices : public ZoneObject {
+    uint32_t start_index;
+    uint16_t signature_index;
+  };
+
+  void InsertFunctionTable(Variable* v, uint32_t start_index,
+                           uint16_t signature_index) {
+    FunctionTableIndices* container = new (zone()) FunctionTableIndices();
+    container->start_index = start_index;
+    container->signature_index = signature_index;
+    ZoneHashMap::Entry* entry = function_tables_.LookupOrInsert(
+        v, ComputePointerHash(v), ZoneAllocationPolicy(zone()));
+    entry->value = container;
+  }
+
+  FunctionTableIndices* LookupFunctionTable(Variable* v) {
+    ZoneHashMap::Entry* entry =
+        function_tables_.Lookup(v, ComputePointerHash(v));
+    DCHECK_NOT_NULL(entry);
+    return reinterpret_cast<FunctionTableIndices*>(entry->value);
+  }
+
+  class ImportedFunctionTable {
+   private:
+    class ImportedFunctionIndices : public ZoneObject {
+     public:
+      const unsigned char* name_;
+      int name_length_;
+      WasmModuleBuilder::SignatureMap signature_to_index_;
+
+      ImportedFunctionIndices(const unsigned char* name, int name_length,
+                              Zone* zone)
+          : name_(name), name_length_(name_length), signature_to_index_(zone) {}
+    };
+    ZoneHashMap table_;
+    AsmWasmBuilderImpl* builder_;
+
+   public:
+    explicit ImportedFunctionTable(AsmWasmBuilderImpl* builder)
+        : table_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity,
+                 ZoneAllocationPolicy(builder->zone())),
+          builder_(builder) {}
+
+    void AddImport(Variable* v, const unsigned char* name, int name_length) {
+      ImportedFunctionIndices* indices = new (builder_->zone())
+          ImportedFunctionIndices(name, name_length, builder_->zone());
+      ZoneHashMap::Entry* entry = table_.LookupOrInsert(
+          v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone()));
+      entry->value = indices;
+    }
+
+    uint16_t GetFunctionIndex(Variable* v, FunctionSig* sig) {
+      ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v));
+      DCHECK_NOT_NULL(entry);
+      ImportedFunctionIndices* indices =
+          reinterpret_cast<ImportedFunctionIndices*>(entry->value);
+      WasmModuleBuilder::SignatureMap::iterator pos =
+          indices->signature_to_index_.find(sig);
+      if (pos != indices->signature_to_index_.end()) {
+        return pos->second;
+      } else {
+        uint16_t index = builder_->builder_->AddFunction();
+        indices->signature_to_index_[sig] = index;
+        WasmFunctionBuilder* function = builder_->builder_->FunctionAt(index);
+        function->External(1);
+        function->SetName(indices->name_, indices->name_length_);
+        if (sig->return_count() > 0) {
+          function->ReturnType(sig->GetReturn());
+        }
+        for (size_t i = 0; i < sig->parameter_count(); i++) {
+          function->AddParam(sig->GetParam(i));
+        }
+        return index;
+      }
+    }
+  };
+
   void VisitAssignment(Assignment* expr) {
     bool in_init = false;
     if (!in_function_) {
+      BinaryOperation* binop = expr->value()->AsBinaryOperation();
+      if (binop != nullptr) {
+        Property* prop = binop->left()->AsProperty();
+        DCHECK_NOT_NULL(prop);
+        LoadInitFunction();
+        is_set_op_ = true;
+        RECURSE(Visit(expr->target()));
+        DCHECK(!is_set_op_);
+        if (binop->op() == Token::MUL) {
+          DCHECK(binop->right()->IsLiteral());
+          DCHECK_EQ(1.0, binop->right()->AsLiteral()->raw_value()->AsNumber());
+          DCHECK(binop->right()->AsLiteral()->raw_value()->ContainsDot());
+          VisitForeignVariable(true, prop);
+        } else if (binop->op() == Token::BIT_OR) {
+          DCHECK(binop->right()->IsLiteral());
+          DCHECK_EQ(0.0, binop->right()->AsLiteral()->raw_value()->AsNumber());
+          DCHECK(!binop->right()->AsLiteral()->raw_value()->ContainsDot());
+          VisitForeignVariable(false, prop);
+        } else {
+          UNREACHABLE();
+        }
+        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());
+            }
+          }
+        }
+        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);
+        }
         return;
       }
       in_init = true;
@@ -493,10 +640,59 @@
 
   void VisitThrow(Throw* expr) { UNREACHABLE(); }
 
+  void VisitForeignVariable(bool is_float, Property* expr) {
+    DCHECK(expr->obj()->AsVariableProxy());
+    DCHECK(VariableLocation::PARAMETER ==
+           expr->obj()->AsVariableProxy()->var()->location());
+    DCHECK_EQ(1, expr->obj()->AsVariableProxy()->var()->index());
+    Literal* key_literal = expr->key()->AsLiteral();
+    DCHECK_NOT_NULL(key_literal);
+    if (!key_literal->value().is_null() && !foreign_.is_null() &&
+        foreign_->IsObject()) {
+      Handle<Name> name =
+          i::Object::ToName(isolate_, key_literal->value()).ToHandleChecked();
+      MaybeHandle<Object> maybe_value = i::Object::GetProperty(foreign_, name);
+      if (!maybe_value.is_null()) {
+        Handle<Object> value = maybe_value.ToHandleChecked();
+        if (is_float) {
+          MaybeHandle<Object> maybe_nvalue = i::Object::ToNumber(value);
+          if (!maybe_nvalue.is_null()) {
+            Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
+            if (nvalue->IsNumber()) {
+              double val = nvalue->Number();
+              byte code[] = {WASM_F64(val)};
+              current_function_builder_->EmitCode(code, sizeof(code));
+              return;
+            }
+          }
+        } else {
+          MaybeHandle<Object> maybe_nvalue =
+              i::Object::ToInt32(isolate_, value);
+          if (!maybe_nvalue.is_null()) {
+            Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
+            if (nvalue->IsNumber()) {
+              int32_t val = static_cast<int32_t>(nvalue->Number());
+              byte code[] = {WASM_I32(val)};
+              current_function_builder_->EmitCode(code, sizeof(code));
+              return;
+            }
+          }
+        }
+      }
+    }
+    if (is_float) {
+      byte code[] = {WASM_F64(std::numeric_limits<double>::quiet_NaN())};
+      current_function_builder_->EmitCode(code, sizeof(code));
+    } else {
+      byte code[] = {WASM_I32(0)};
+      current_function_builder_->EmitCode(code, sizeof(code));
+    }
+  }
+
   void VisitProperty(Property* expr) {
     Expression* obj = expr->obj();
-    DCHECK(obj->bounds().lower == obj->bounds().upper);
-    TypeImpl<ZoneTypeConfig>* type = obj->bounds().lower;
+    DCHECK_EQ(obj->bounds().lower, obj->bounds().upper);
+    Type* type = obj->bounds().lower;
     MachineType mtype;
     int size;
     if (type->Is(cache_.kUint8Array)) {
@@ -533,29 +729,38 @@
         WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_),
         WasmOpcodes::LoadStoreAccessOf(false));
     is_set_op_ = false;
-    Literal* value = expr->key()->AsLiteral();
-    if (value) {
-      DCHECK(value->raw_value()->IsNumber());
-      DCHECK(kAstI32 == TypeOf(value));
-      int val = static_cast<int>(value->raw_value()->AsNumber());
-      byte code[] = {WASM_I32(val * size)};
-      current_function_builder_->EmitCode(code, sizeof(code));
+    if (size == 1) {
+      // Allow more general expression in byte arrays than the spec
+      // strictly permits.
+      // Early versions of Emscripten emit HEAP8[HEAP32[..]|0] in
+      // places that strictly should be HEAP8[HEAP32[..]>>0].
+      RECURSE(Visit(expr->key()));
       return;
-    }
-    BinaryOperation* binop = expr->key()->AsBinaryOperation();
-    if (binop) {
-      DCHECK(Token::SAR == binop->op());
-      DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber());
-      DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral()));
-      DCHECK(size ==
-             1 << static_cast<int>(
-                 binop->right()->AsLiteral()->raw_value()->AsNumber()));
-      // Mask bottom bits to match asm.js behavior.
-      current_function_builder_->Emit(kExprI32And);
-      byte code[] = {WASM_I8(~(size - 1))};
-      current_function_builder_->EmitCode(code, sizeof(code));
-      RECURSE(Visit(binop->left()));
-      return;
+    } else {
+      Literal* value = expr->key()->AsLiteral();
+      if (value) {
+        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)};
+        current_function_builder_->EmitCode(code, sizeof(code));
+        return;
+      }
+      BinaryOperation* binop = expr->key()->AsBinaryOperation();
+      if (binop) {
+        DCHECK_EQ(Token::SAR, binop->op());
+        DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber());
+        DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral()));
+        DCHECK_EQ(size,
+                  1 << static_cast<int>(
+                      binop->right()->AsLiteral()->raw_value()->AsNumber()));
+        // Mask bottom bits to match asm.js behavior.
+        current_function_builder_->Emit(kExprI32And);
+        byte code[] = {WASM_I8(~(size - 1))};
+        current_function_builder_->EmitCode(code, sizeof(code));
+        RECURSE(Visit(binop->left()));
+        return;
+      }
     }
     UNREACHABLE();
   }
@@ -565,18 +770,54 @@
     switch (call_type) {
       case Call::OTHER_CALL: {
         DCHECK(in_function_);
-        current_function_builder_->Emit(kExprCallFunction);
-        RECURSE(Visit(expr->expression()));
-        ZoneList<Expression*>* args = expr->arguments();
-        for (int i = 0; i < args->length(); ++i) {
-          Expression* arg = args->at(i);
-          RECURSE(Visit(arg));
+        uint16_t index;
+        VariableProxy* vp = expr->expression()->AsVariableProxy();
+        if (vp != nullptr &&
+            Type::Any()->Is(vp->bounds().lower->AsFunction()->Result())) {
+          LocalType return_type = TypeOf(expr);
+          ZoneList<Expression*>* args = expr->arguments();
+          FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1,
+                                   args->length());
+          if (return_type != kAstStmt) {
+            sig.AddReturn(return_type);
+          }
+          for (int i = 0; i < args->length(); i++) {
+            sig.AddParam(TypeOf(args->at(i)));
+          }
+          index =
+              imported_function_table_.GetFunctionIndex(vp->var(), sig.Build());
+        } else {
+          index = LookupOrInsertFunction(vp->var());
         }
+        current_function_builder_->Emit(kExprCallFunction);
+        std::vector<uint8_t> index_arr = UnsignedLEB128From(index);
+        current_function_builder_->EmitCode(
+            &index_arr[0], static_cast<uint32_t>(index_arr.size()));
+        break;
+      }
+      case Call::KEYED_PROPERTY_CALL: {
+        DCHECK(in_function_);
+        Property* p = expr->expression()->AsProperty();
+        DCHECK_NOT_NULL(p);
+        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_->Emit(kExprI32Add);
+        byte code[] = {WASM_I32(indices->start_index)};
+        current_function_builder_->EmitCode(code, sizeof(code));
+        RECURSE(Visit(p->key()));
         break;
       }
       default:
         UNREACHABLE();
     }
+    ZoneList<Expression*>* args = expr->arguments();
+    for (int i = 0; i < args->length(); ++i) {
+      Expression* arg = args->at(i);
+      RECURSE(Visit(arg));
+    }
   }
 
   void VisitCallNew(CallNew* expr) { UNREACHABLE(); }
@@ -586,7 +827,7 @@
   void VisitUnaryOperation(UnaryOperation* expr) {
     switch (expr->op()) {
       case Token::NOT: {
-        DCHECK(TypeOf(expr->expression()) == kAstI32);
+        DCHECK_EQ(kAstI32, TypeOf(expr->expression()));
         current_function_builder_->Emit(kExprBoolNot);
         break;
       }
@@ -600,7 +841,7 @@
 
   bool MatchIntBinaryOperation(BinaryOperation* expr, Token::Value op,
                                int32_t val) {
-    DCHECK(expr->right() != nullptr);
+    DCHECK_NOT_NULL(expr->right());
     if (expr->op() == op && expr->right()->IsLiteral() &&
         TypeOf(expr) == kAstI32) {
       Literal* right = expr->right()->AsLiteral();
@@ -614,7 +855,7 @@
 
   bool MatchDoubleBinaryOperation(BinaryOperation* expr, Token::Value op,
                                   double val) {
-    DCHECK(expr->right() != nullptr);
+    DCHECK_NOT_NULL(expr->right());
     if (expr->op() == op && expr->right()->IsLiteral() &&
         TypeOf(expr) == kAstF64) {
       Literal* right = expr->right()->AsLiteral();
@@ -629,8 +870,9 @@
   enum ConvertOperation { kNone, kAsIs, kToInt, kToDouble };
 
   ConvertOperation MatchOr(BinaryOperation* expr) {
-    if (MatchIntBinaryOperation(expr, Token::BIT_OR, 0)) {
-      return (TypeOf(expr->left()) == kAstI32) ? kAsIs : kToInt;
+    if (MatchIntBinaryOperation(expr, Token::BIT_OR, 0) &&
+        (TypeOf(expr->left()) == kAstI32)) {
+      return kAsIs;
     } else {
       return kNone;
     }
@@ -647,12 +889,12 @@
 
   ConvertOperation MatchXor(BinaryOperation* expr) {
     if (MatchIntBinaryOperation(expr, Token::BIT_XOR, 0xffffffff)) {
-      DCHECK(TypeOf(expr->left()) == kAstI32);
-      DCHECK(TypeOf(expr->right()) == kAstI32);
+      DCHECK_EQ(kAstI32, TypeOf(expr->left()));
+      DCHECK_EQ(kAstI32, TypeOf(expr->right()));
       BinaryOperation* op = expr->left()->AsBinaryOperation();
       if (op != nullptr) {
         if (MatchIntBinaryOperation(op, Token::BIT_XOR, 0xffffffff)) {
-          DCHECK(TypeOf(op->right()) == kAstI32);
+          DCHECK_EQ(kAstI32, TypeOf(op->right()));
           if (TypeOf(op->left()) != kAstI32) {
             return kToInt;
           } else {
@@ -666,7 +908,7 @@
 
   ConvertOperation MatchMul(BinaryOperation* expr) {
     if (MatchDoubleBinaryOperation(expr, Token::MUL, 1.0)) {
-      DCHECK(TypeOf(expr->right()) == kAstF64);
+      DCHECK_EQ(kAstF64, TypeOf(expr->right()));
       if (TypeOf(expr->left()) != kAstF64) {
         return kToDouble;
       } else {
@@ -768,6 +1010,7 @@
         BINOP_CASE(Token::MUL, Mul, NON_SIGNED_BINOP, true);
         BINOP_CASE(Token::DIV, Div, SIGNED_BINOP, false);
         BINOP_CASE(Token::BIT_OR, Ior, NON_SIGNED_INT_BINOP, true);
+        BINOP_CASE(Token::BIT_AND, And, NON_SIGNED_INT_BINOP, true);
         BINOP_CASE(Token::BIT_XOR, Xor, NON_SIGNED_INT_BINOP, true);
         BINOP_CASE(Token::SHL, Shl, NON_SIGNED_INT_BINOP, true);
         BINOP_CASE(Token::SAR, ShrS, NON_SIGNED_INT_BINOP, true);
@@ -786,6 +1029,10 @@
           }
           break;
         }
+        case Token::COMMA: {
+          current_function_builder_->EmitWithU8(kExprBlock, 2);
+          break;
+        }
         default:
           UNREACHABLE();
       }
@@ -879,8 +1126,8 @@
   }
 
   TypeIndex TypeIndexOf(Expression* expr) {
-    DCHECK(expr->bounds().lower == expr->bounds().upper);
-    TypeImpl<ZoneTypeConfig>* type = expr->bounds().lower;
+    DCHECK_EQ(expr->bounds().lower, expr->bounds().upper);
+    Type* type = expr->bounds().lower;
     if (type->Is(cache_.kAsmFixnum)) {
       return kFixnum;
     } else if (type->Is(cache_.kAsmSigned)) {
@@ -929,17 +1176,14 @@
 
   void VisitDoExpression(DoExpression* expr) { UNREACHABLE(); }
 
-  void VisitRewritableAssignmentExpression(
-      RewritableAssignmentExpression* expr) {
-    UNREACHABLE();
-  }
+  void VisitRewritableExpression(RewritableExpression* expr) { UNREACHABLE(); }
 
   struct IndexContainer : public ZoneObject {
     uint16_t index;
   };
 
   uint16_t LookupOrInsertLocal(Variable* v, LocalType type) {
-    DCHECK(current_function_builder_ != nullptr);
+    DCHECK_NOT_NULL(current_function_builder_);
     ZoneHashMap::Entry* entry =
         local_variables_.Lookup(v, ComputePointerHash(v));
     if (entry == nullptr) {
@@ -974,7 +1218,7 @@
   }
 
   uint16_t LookupOrInsertFunction(Variable* v) {
-    DCHECK(builder_ != nullptr);
+    DCHECK_NOT_NULL(builder_);
     ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v));
     if (entry == nullptr) {
       uint16_t index = builder_->AddFunction();
@@ -988,11 +1232,11 @@
   }
 
   LocalType TypeOf(Expression* expr) {
-    DCHECK(expr->bounds().lower == expr->bounds().upper);
+    DCHECK_EQ(expr->bounds().lower, expr->bounds().upper);
     return TypeFrom(expr->bounds().lower);
   }
 
-  LocalType TypeFrom(TypeImpl<ZoneTypeConfig>* type) {
+  LocalType TypeFrom(Type* type) {
     if (type->Is(cache_.kAsmInt)) {
       return kAstI32;
     } else if (type->Is(cache_.kAsmFloat)) {
@@ -1017,10 +1261,14 @@
   FunctionLiteral* literal_;
   Isolate* isolate_;
   Zone* zone_;
+  Handle<Object> foreign_;
   TypeCache const& cache_;
   ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
   int block_size_;
-  uint16_t init_function_index;
+  uint16_t init_function_index_;
+  uint32_t next_table_index_;
+  ZoneHashMap function_tables_;
+  ImportedFunctionTable imported_function_table_;
 
   DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
 
@@ -1029,13 +1277,13 @@
 };
 
 AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
-                               FunctionLiteral* literal)
-    : isolate_(isolate), zone_(zone), literal_(literal) {}
+                               FunctionLiteral* literal, Handle<Object> foreign)
+    : isolate_(isolate), zone_(zone), literal_(literal), foreign_(foreign) {}
 
 // 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_);
+  AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_);
   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 cb568db..9b761f9 100644
--- a/src/wasm/asm-wasm-builder.h
+++ b/src/wasm/asm-wasm-builder.h
@@ -6,6 +6,7 @@
 #define V8_WASM_ASM_WASM_BUILDER_H_
 
 #include "src/allocation.h"
+#include "src/objects.h"
 #include "src/wasm/encoder.h"
 #include "src/zone.h"
 
@@ -18,13 +19,15 @@
 
 class AsmWasmBuilder {
  public:
-  explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root);
+  explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
+                          Handle<Object> foreign);
   WasmModuleIndex* Run();
 
  private:
   Isolate* isolate_;
   Zone* zone_;
   FunctionLiteral* literal_;
+  Handle<Object> foreign_;
 };
 }  // namespace wasm
 }  // namespace internal
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc
index ffb8157..c97c781 100644
--- a/src/wasm/ast-decoder.cc
+++ b/src/wasm/ast-decoder.cc
@@ -5,6 +5,7 @@
 #include "src/base/platform/elapsed-timer.h"
 #include "src/signature.h"
 
+#include "src/bit-vector.h"
 #include "src/flags.h"
 #include "src/handles.h"
 #include "src/zone-containers.h"
@@ -40,7 +41,6 @@
   WasmOpcode opcode() const { return static_cast<WasmOpcode>(*pc); }
 };
 
-
 // A production represents an incomplete decoded tree in the LR decoder.
 struct Production {
   Tree* tree;  // the root of the syntax tree.
@@ -97,13 +97,278 @@
 #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;
+  }
+
+  byte ByteOperand(const byte* pc, const char* msg = "missing 1-byte operand") {
+    if ((pc + sizeof(byte)) >= limit_) {
+      error(pc, msg);
+      return 0;
+    }
+    return pc[1];
+  }
+
+  uint32_t Uint32Operand(const byte* pc) {
+    if ((pc + sizeof(uint32_t)) >= limit_) {
+      error(pc, "missing 4-byte operand");
+      return 0;
+    }
+    return read_u32(pc + 1);
+  }
+
+  uint64_t Uint64Operand(const byte* pc) {
+    if ((pc + sizeof(uint64_t)) >= limit_) {
+      error(pc, "missing 8-byte operand");
+      return 0;
+    }
+    return read_u64(pc + 1);
+  }
+
+  inline bool Validate(const byte* pc, LocalIndexOperand& operand) {
+    if (operand.index < function_env_->total_locals) {
+      operand.type = function_env_->GetLocalType(operand.index);
+      return true;
+    }
+    error(pc, pc + 1, "invalid local index");
+    return false;
+  }
+
+  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;
+      operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type);
+      return true;
+    }
+    error(pc, pc + 1, "invalid global index");
+    return false;
+  }
+
+  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;
+      return true;
+    }
+    error(pc, pc + 1, "invalid function index");
+    return false;
+  }
+
+  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);
+      return true;
+    }
+    error(pc, pc + 1, "invalid signature index");
+    return false;
+  }
+
+  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;
+      return true;
+    }
+    error(pc, pc + 1, "invalid signature index");
+    return false;
+  }
+
+  inline bool Validate(const byte* pc, BreakDepthOperand& operand,
+                       ZoneVector<Block>& blocks) {
+    if (operand.depth < blocks.size()) {
+      operand.target = &blocks[blocks.size() - operand.depth - 1];
+      return true;
+    }
+    error(pc, pc + 1, "invalid break depth");
+    return false;
+  }
+
+  bool Validate(const byte* pc, TableSwitchOperand& 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;
+        }
+      }
+    }
+    return true;
+  }
+
+  int OpcodeArity(const byte* pc) {
+#define DECLARE_ARITY(name, ...)                          \
+  static const LocalType kTypes_##name[] = {__VA_ARGS__}; \
+  static const int kArity_##name =                        \
+      static_cast<int>(arraysize(kTypes_##name) - 1);
+
+    FOREACH_SIGNATURE(DECLARE_ARITY);
+#undef DECLARE_ARITY
+
+    switch (static_cast<WasmOpcode>(*pc)) {
+      case kExprI8Const:
+      case kExprI32Const:
+      case kExprI64Const:
+      case kExprF64Const:
+      case kExprF32Const:
+      case kExprGetLocal:
+      case kExprLoadGlobal:
+      case kExprNop:
+      case kExprUnreachable:
+        return 0;
+
+      case kExprBr:
+      case kExprStoreGlobal:
+      case kExprSetLocal:
+        return 1;
+
+      case kExprIf:
+      case kExprBrIf:
+        return 2;
+      case kExprIfElse:
+      case kExprSelect:
+        return 3;
+
+      case kExprBlock:
+      case kExprLoop: {
+        BlockCountOperand operand(this, pc);
+        return operand.count;
+      }
+
+      case kExprCallFunction: {
+        FunctionIndexOperand operand(this, pc);
+        return static_cast<int>(
+            function_env_->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());
+      }
+      case kExprCallImport: {
+        ImportIndexOperand operand(this, pc);
+        return static_cast<int>(
+            function_env_->module->GetImportSignature(operand.index)
+                ->parameter_count());
+      }
+      case kExprReturn: {
+        return static_cast<int>(function_env_->sig->return_count());
+      }
+      case kExprTableSwitch: {
+        TableSwitchOperand operand(this, pc);
+        return 1 + operand.case_count;
+      }
+
+#define DECLARE_OPCODE_CASE(name, opcode, sig) \
+  case kExpr##name:                            \
+    return kArity_##sig;
+
+        FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
+        FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
+        FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
+        FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
+#undef DECLARE_OPCODE_CASE
+    }
+    UNREACHABLE();
+    return 0;
+  }
+
+  int OpcodeLength(const byte* pc) {
+    switch (static_cast<WasmOpcode>(*pc)) {
+#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
+      FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
+      FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
+#undef DECLARE_OPCODE_CASE
+      {
+        MemoryAccessOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprBlock:
+      case kExprLoop: {
+        BlockCountOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprBr:
+      case kExprBrIf: {
+        BreakDepthOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprStoreGlobal:
+      case kExprLoadGlobal: {
+        GlobalIndexOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+
+      case kExprCallFunction: {
+        FunctionIndexOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprCallIndirect: {
+        SignatureIndexOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprCallImport: {
+        ImportIndexOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+
+      case kExprSetLocal:
+      case kExprGetLocal: {
+        LocalIndexOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprTableSwitch: {
+        TableSwitchOperand operand(this, pc);
+        return 1 + operand.length;
+      }
+      case kExprI8Const:
+        return 2;
+      case kExprI32Const:
+      case kExprF32Const:
+        return 5;
+      case kExprI64Const:
+      case kExprF64Const:
+        return 9;
+
+      default:
+        return 1;
+    }
+  }
+};
+
+
 // A shift-reduce-parser strategy for decoding Wasm code that uses an explicit
 // shift-reduce strategy with multiple internal stacks.
-class LR_WasmDecoder : public Decoder {
+class LR_WasmDecoder : public WasmDecoder {
  public:
   LR_WasmDecoder(Zone* zone, TFBuilder* builder)
-      : Decoder(nullptr, nullptr),
-        zone_(zone),
+      : zone_(zone),
         builder_(builder),
         trees_(zone),
         stack_(zone),
@@ -127,8 +392,7 @@
     }
 
     base_ = base;
-    Reset(pc, end);
-    function_env_ = function_env;
+    Reset(function_env, pc, end);
 
     InitSsaEnv();
     DecodeFunctionBody();
@@ -151,15 +415,20 @@
     }
 
     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(" - decoding took %0.3f ms\n", ms);
+        PrintF("wasm-decode ok (%0.3f ms)\n\n", ms);
+      } else {
+        TRACE("wasm-decode ok\n\n");
       }
-      TRACE("wasm-decode ok\n\n");
     } else {
       TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_),
             startrel(error_pc_), error_msg_.get());
     }
+
     return toResult(tree);
   }
 
@@ -172,7 +441,6 @@
   TreeResult result_;
 
   SsaEnv* ssa_env_;
-  FunctionEnv* function_env_;
 
   ZoneVector<Tree*> trees_;
   ZoneVector<Production> stack_;
@@ -199,30 +467,30 @@
         ssa_env->locals[pos++] = builder_->Param(i, sig->GetParam(i));
       }
       // Initialize int32 locals.
-      if (function_env_->local_int32_count > 0) {
+      if (function_env_->local_i32_count > 0) {
         TFNode* zero = builder_->Int32Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_int32_count; i++) {
+        for (uint32_t i = 0; i < function_env_->local_i32_count; i++) {
           ssa_env->locals[pos++] = zero;
         }
       }
       // Initialize int64 locals.
-      if (function_env_->local_int64_count > 0) {
+      if (function_env_->local_i64_count > 0) {
         TFNode* zero = builder_->Int64Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_int64_count; i++) {
+        for (uint32_t i = 0; i < function_env_->local_i64_count; i++) {
           ssa_env->locals[pos++] = zero;
         }
       }
       // Initialize float32 locals.
-      if (function_env_->local_float32_count > 0) {
+      if (function_env_->local_f32_count > 0) {
         TFNode* zero = builder_->Float32Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_float32_count; i++) {
+        for (uint32_t i = 0; i < function_env_->local_f32_count; i++) {
           ssa_env->locals[pos++] = zero;
         }
       }
       // Initialize float64 locals.
-      if (function_env_->local_float64_count > 0) {
+      if (function_env_->local_f64_count > 0) {
         TFNode* zero = builder_->Float64Constant(0);
-        for (uint32_t i = 0; i < function_env_->local_float64_count; i++) {
+        for (uint32_t i = 0; i < function_env_->local_f64_count; i++) {
           ssa_env->locals[pos++] = zero;
         }
       }
@@ -329,25 +597,25 @@
           Leaf(kAstStmt);
           break;
         case kExprBlock: {
-          int length = Operand<uint8_t>(pc_);
-          if (length < 1) {
+          BlockCountOperand operand(this, pc_);
+          if (operand.count < 1) {
             Leaf(kAstStmt);
           } else {
-            Shift(kAstEnd, length);
+            Shift(kAstEnd, operand.count);
             // The break environment is the outer environment.
             SsaEnv* break_env = ssa_env_;
             PushBlock(break_env);
             SetEnv("block:start", Steal(break_env));
           }
-          len = 2;
+          len = 1 + operand.length;
           break;
         }
         case kExprLoop: {
-          int length = Operand<uint8_t>(pc_);
-          if (length < 1) {
+          BlockCountOperand operand(this, pc_);
+          if (operand.count < 1) {
             Leaf(kAstStmt);
           } else {
-            Shift(kAstEnd, length);
+            Shift(kAstEnd, operand.count);
             // The break environment is the outer environment.
             SsaEnv* break_env = ssa_env_;
             PushBlock(break_env);
@@ -359,7 +627,7 @@
             PushBlock(cont_env);
             blocks_.back().stack_depth = -1;  // no production for inner block.
           }
-          len = 2;
+          len = 1 + operand.length;
           break;
         }
         case kExprIf:
@@ -372,59 +640,27 @@
           Shift(kAstStmt, 3);  // Result type is typeof(x) in {c ? x : y}.
           break;
         case kExprBr: {
-          uint32_t depth = Operand<uint8_t>(pc_);
-          Shift(kAstEnd, 1);
-          if (depth >= blocks_.size()) {
-            error("improperly nested branch");
+          BreakDepthOperand operand(this, pc_);
+          if (Validate(pc_, operand, blocks_)) {
+            Shift(kAstEnd, 1);
           }
-          len = 2;
+          len = 1 + operand.length;
           break;
         }
         case kExprBrIf: {
-          uint32_t depth = Operand<uint8_t>(pc_);
-          Shift(kAstStmt, 2);
-          if (depth >= blocks_.size()) {
-            error("improperly nested conditional branch");
+          BreakDepthOperand operand(this, pc_);
+          if (Validate(pc_, operand, blocks_)) {
+            Shift(kAstStmt, 2);
           }
-          len = 2;
+          len = 1 + operand.length;
           break;
         }
         case kExprTableSwitch: {
-          if (!checkAvailable(5)) {
-            error("expected #tableswitch <cases> <table>, fell off end");
-            break;
+          TableSwitchOperand operand(this, pc_);
+          if (Validate(pc_, operand, blocks_.size())) {
+            Shift(kAstEnd, 1 + operand.case_count);
           }
-          uint16_t case_count = *reinterpret_cast<const uint16_t*>(pc_ + 1);
-          uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc_ + 3);
-          len = 5 + table_count * 2;
-
-          if (table_count == 0) {
-            error("tableswitch with 0 entries");
-            break;
-          }
-
-          if (!checkAvailable(len)) {
-            error("expected #tableswitch <cases> <table>, fell off end");
-            break;
-          }
-
-          Shift(kAstEnd, 1 + case_count);
-
-          // Verify table.
-          for (int i = 0; i < table_count; i++) {
-            uint16_t target =
-                *reinterpret_cast<const uint16_t*>(pc_ + 5 + i * 2);
-            if (target >= 0x8000) {
-              size_t depth = target - 0x8000;
-              if (depth > blocks_.size()) {
-                error(pc_ + 5 + i * 2, "improper branch in tableswitch");
-              }
-            } else {
-              if (target >= case_count) {
-                error(pc_ + 5 + i * 2, "invalid case target in tableswitch");
-              }
-            }
-          }
+          len = 1 + operand.length;
           break;
         }
         case kExprReturn: {
@@ -445,59 +681,66 @@
           break;
         }
         case kExprI8Const: {
-          int32_t value = Operand<int8_t>(pc_);
-          Leaf(kAstI32, BUILD(Int32Constant, value));
-          len = 2;
+          ImmI8Operand operand(this, pc_);
+          Leaf(kAstI32, BUILD(Int32Constant, operand.value));
+          len = 1 + operand.length;
           break;
         }
         case kExprI32Const: {
-          int32_t value = Operand<int32_t>(pc_);
-          Leaf(kAstI32, BUILD(Int32Constant, value));
-          len = 5;
+          ImmI32Operand operand(this, pc_);
+          Leaf(kAstI32, BUILD(Int32Constant, operand.value));
+          len = 1 + operand.length;
           break;
         }
         case kExprI64Const: {
-          int64_t value = Operand<int64_t>(pc_);
-          Leaf(kAstI64, BUILD(Int64Constant, value));
-          len = 9;
+          ImmI64Operand operand(this, pc_);
+          Leaf(kAstI64, BUILD(Int64Constant, operand.value));
+          len = 1 + operand.length;
           break;
         }
         case kExprF32Const: {
-          float value = Operand<float>(pc_);
-          Leaf(kAstF32, BUILD(Float32Constant, value));
-          len = 5;
+          ImmF32Operand operand(this, pc_);
+          Leaf(kAstF32, BUILD(Float32Constant, operand.value));
+          len = 1 + operand.length;
           break;
         }
         case kExprF64Const: {
-          double value = Operand<double>(pc_);
-          Leaf(kAstF64, BUILD(Float64Constant, value));
-          len = 9;
+          ImmF64Operand operand(this, pc_);
+          Leaf(kAstF64, BUILD(Float64Constant, operand.value));
+          len = 1 + operand.length;
           break;
         }
         case kExprGetLocal: {
-          uint32_t index;
-          LocalType type = LocalOperand(pc_, &index, &len);
-          TFNode* val =
-              build() && type != kAstStmt ? ssa_env_->locals[index] : nullptr;
-          Leaf(type, val);
+          LocalIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            TFNode* val = build() ? ssa_env_->locals[operand.index] : nullptr;
+            Leaf(operand.type, val);
+          }
+          len = 1 + operand.length;
           break;
         }
         case kExprSetLocal: {
-          uint32_t index;
-          LocalType type = LocalOperand(pc_, &index, &len);
-          Shift(type, 1);
+          LocalIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            Shift(operand.type, 1);
+          }
+          len = 1 + operand.length;
           break;
         }
         case kExprLoadGlobal: {
-          uint32_t index;
-          LocalType type = GlobalOperand(pc_, &index, &len);
-          Leaf(type, BUILD(LoadGlobal, index));
+          GlobalIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            Leaf(operand.type, BUILD(LoadGlobal, operand.index));
+          }
+          len = 1 + operand.length;
           break;
         }
         case kExprStoreGlobal: {
-          uint32_t index;
-          LocalType type = GlobalOperand(pc_, &index, &len);
-          Shift(type, 1);
+          GlobalIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            Shift(operand.type, 1);
+          }
+          len = 1 + operand.length;
           break;
         }
         case kExprI32LoadMem8S:
@@ -546,27 +789,36 @@
           Shift(kAstI32, 1);
           break;
         case kExprCallFunction: {
-          uint32_t unused;
-          FunctionSig* sig = FunctionSigOperand(pc_, &unused, &len);
-          if (sig) {
-            LocalType type =
-                sig->return_count() == 0 ? kAstStmt : sig->GetReturn();
-            Shift(type, static_cast<int>(sig->parameter_count()));
-          } else {
-            Leaf(kAstI32);  // error
+          FunctionIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            LocalType type = operand.sig->return_count() == 0
+                                 ? kAstStmt
+                                 : operand.sig->GetReturn();
+            Shift(type, static_cast<int>(operand.sig->parameter_count()));
           }
+          len = 1 + operand.length;
           break;
         }
         case kExprCallIndirect: {
-          uint32_t unused;
-          FunctionSig* sig = SigOperand(pc_, &unused, &len);
-          if (sig) {
-            LocalType type =
-                sig->return_count() == 0 ? kAstStmt : sig->GetReturn();
-            Shift(type, static_cast<int>(1 + sig->parameter_count()));
-          } else {
-            Leaf(kAstI32);  // error
+          SignatureIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            LocalType type = operand.sig->return_count() == 0
+                                 ? kAstStmt
+                                 : operand.sig->GetReturn();
+            Shift(type, static_cast<int>(1 + operand.sig->parameter_count()));
           }
+          len = 1 + operand.length;
+          break;
+        }
+        case kExprCallImport: {
+          ImportIndexOperand operand(this, pc_);
+          if (Validate(pc_, operand)) {
+            LocalType type = operand.sig->return_count() == 0
+                                 ? kAstStmt
+                                 : operand.sig->GetReturn();
+            Shift(type, static_cast<int>(operand.sig->parameter_count()));
+          }
+          len = 1 + operand.length;
           break;
         }
         default:
@@ -589,19 +841,15 @@
   }
 
   int DecodeLoadMem(const byte* pc, LocalType type) {
-    int length = 2;
-    uint32_t offset;
-    MemoryAccessOperand(pc, &length, &offset);
+    MemoryAccessOperand operand(this, pc);
     Shift(type, 1);
-    return length;
+    return 1 + operand.length;
   }
 
   int DecodeStoreMem(const byte* pc, LocalType type) {
-    int length = 2;
-    uint32_t offset;
-    MemoryAccessOperand(pc, &length, &offset);
+    MemoryAccessOperand operand(this, pc);
     Shift(type, 2);
-    return length;
+    return 1 + operand.length;
   }
 
   void AddImplicitReturnAtEnd() {
@@ -747,26 +995,26 @@
       }
       case kExprSelect: {
         if (p->index == 1) {
-          // Condition done.
-          TypeCheckLast(p, kAstI32);
-        } else if (p->index == 2) {
           // True expression done.
           p->tree->type = p->last()->type;
           if (p->tree->type == kAstStmt) {
             error(p->pc(), p->tree->children[1]->pc,
                   "select operand should be expression");
           }
-        } else {
+        } else if (p->index == 2) {
           // False expression done.
-          DCHECK(p->done());
           TypeCheckLast(p, p->tree->type);
+        } else {
+          // Condition done.
+          DCHECK(p->done());
+          TypeCheckLast(p, kAstI32);
           if (build()) {
             TFNode* controls[2];
-            builder_->Branch(p->tree->children[0]->node, &controls[0],
+            builder_->Branch(p->tree->children[2]->node, &controls[0],
                              &controls[1]);
             TFNode* merge = builder_->Merge(2, controls);
-            TFNode* vals[2] = {p->tree->children[1]->node,
-                               p->tree->children[2]->node};
+            TFNode* vals[2] = {p->tree->children[0]->node,
+                               p->tree->children[1]->node};
             TFNode* phi = builder_->Phi(p->tree->type, 2, vals, merge);
             p->tree->node = phi;
             ssa_env_->control = merge;
@@ -775,64 +1023,44 @@
         break;
       }
       case kExprBr: {
-        uint32_t depth = Operand<uint8_t>(p->pc());
-        if (depth >= blocks_.size()) {
-          error("improperly nested branch");
-          break;
-        }
-        Block* block = &blocks_[blocks_.size() - depth - 1];
-        ReduceBreakToExprBlock(p, block);
+        BreakDepthOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand, blocks_));
+        ReduceBreakToExprBlock(p, operand.target);
         break;
       }
       case kExprBrIf: {
-        if (p->index == 1) {
+        if (p->done()) {
           TypeCheckLast(p, kAstI32);
-        } else if (p->done()) {
-          uint32_t depth = Operand<uint8_t>(p->pc());
-          if (depth >= blocks_.size()) {
-            error("improperly nested branch");
-            break;
-          }
-          Block* block = &blocks_[blocks_.size() - depth - 1];
+          BreakDepthOperand operand(this, p->pc());
+          CHECK(Validate(p->pc(), operand, blocks_));
           SsaEnv* fenv = ssa_env_;
           SsaEnv* tenv = Split(fenv);
-          BUILD(Branch, p->tree->children[0]->node, &tenv->control,
+          BUILD(Branch, p->tree->children[1]->node, &tenv->control,
                 &fenv->control);
           ssa_env_ = tenv;
-          ReduceBreakToExprBlock(p, block);
+          ReduceBreakToExprBlock(p, operand.target, p->tree->children[0]);
           ssa_env_ = fenv;
         }
         break;
       }
       case kExprTableSwitch: {
-        uint16_t table_count = *reinterpret_cast<const uint16_t*>(p->pc() + 3);
-        if (table_count == 1) {
-          // Degenerate switch with only a default target.
-          if (p->index == 1) {
-            SsaEnv* break_env = ssa_env_;
-            PushBlock(break_env);
-            SetEnv("switch:default", Steal(break_env));
-          }
-          if (p->done()) {
-            Block* block = &blocks_.back();
-            // fall through to the end.
-            ReduceBreakToExprBlock(p, block);
-            SetEnv("switch:end", block->ssa_env);
-            blocks_.pop_back();
-          }
-          break;
-        }
-
         if (p->index == 1) {
           // Switch key finished.
           TypeCheckLast(p, kAstI32);
+          if (failed()) break;
 
-          TFNode* sw = BUILD(Switch, table_count, p->last()->node);
+          TableSwitchOperand 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;
+          TFNode* sw = nullptr;
+          if (build_switch)
+            sw = BUILD(Switch, operand.table_count, p->last()->node);
 
           // Allocate environments for each case.
-          uint16_t case_count = *reinterpret_cast<const uint16_t*>(p->pc() + 1);
-          SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(case_count);
-          for (int i = 0; i < case_count; i++) {
+          SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count);
+          for (uint32_t i = 0; i < operand.case_count; i++) {
             case_envs[i] = UnreachableEnv();
           }
 
@@ -843,13 +1071,15 @@
           ssa_env_ = copy;
 
           // Build the environments for each case based on the table.
-          const uint16_t* table =
-              reinterpret_cast<const uint16_t*>(p->pc() + 5);
-          for (int i = 0; i < table_count; i++) {
-            uint16_t target = table[i];
-            SsaEnv* env = Split(copy);
-            env->control = (i == table_count - 1) ? BUILD(IfDefault, sw)
-                                                  : BUILD(IfValue, i, sw);
+          for (uint32_t i = 0; i < operand.table_count; i++) {
+            uint16_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);
+            }
             if (target >= 0x8000) {
               // Targets an outer block.
               int depth = target - 0x8000;
@@ -860,25 +1090,21 @@
               Goto(env, case_envs[target]);
             }
           }
+        }
 
-          // Switch to the environment for the first case.
-          SetEnv("switch:case", case_envs[0]);
+        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 {
-          // Switch case finished.
-          if (p->done()) {
-            // Last case. Fall through to the end.
-            Block* block = &blocks_.back();
-            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 (ssa_env_->go()) Goto(ssa_env_, next);
-            SetEnv("switch:case", next);
-          }
+          // 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);
         }
         break;
       }
@@ -898,12 +1124,11 @@
         break;
       }
       case kExprSetLocal: {
-        int unused = 0;
-        uint32_t index;
-        LocalType type = LocalOperand(p->pc(), &index, &unused);
+        LocalIndexOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand));
         Tree* val = p->last();
-        if (type == val->type) {
-          if (build()) ssa_env_->locals[index] = val->node;
+        if (operand.type == val->type) {
+          if (build()) ssa_env_->locals[operand.index] = val->node;
           p->tree->node = val->node;
         } else {
           error(p->pc(), val->pc, "Typecheck failed in SetLocal");
@@ -911,12 +1136,11 @@
         break;
       }
       case kExprStoreGlobal: {
-        int unused = 0;
-        uint32_t index;
-        LocalType type = GlobalOperand(p->pc(), &index, &unused);
+        GlobalIndexOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand));
         Tree* val = p->last();
-        if (type == val->type) {
-          BUILD(StoreGlobal, index, val->node);
+        if (operand.type == val->type) {
+          BUILD(StoreGlobal, operand.index, val->node);
           p->tree->node = val->node;
         } else {
           error(p->pc(), val->pc, "Typecheck failed in StoreGlobal");
@@ -985,34 +1209,29 @@
         return;
 
       case kExprCallFunction: {
-        int len;
-        uint32_t index;
-        FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len);
-        if (!sig) break;
+        FunctionIndexOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand));
         if (p->index > 0) {
-          TypeCheckLast(p, sig->GetParam(p->index - 1));
+          TypeCheckLast(p, operand.sig->GetParam(p->index - 1));
         }
         if (p->done() && build()) {
           uint32_t count = p->tree->count + 1;
           TFNode** buffer = builder_->Buffer(count);
-          FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len);
-          USE(sig);
           buffer[0] = nullptr;  // reserved for code object.
           for (uint32_t i = 1; i < count; i++) {
             buffer[i] = p->tree->children[i - 1]->node;
           }
-          p->tree->node = builder_->CallDirect(index, buffer);
+          p->tree->node = builder_->CallDirect(operand.index, buffer);
         }
         break;
       }
       case kExprCallIndirect: {
-        int len;
-        uint32_t index;
-        FunctionSig* sig = SigOperand(p->pc(), &index, &len);
+        SignatureIndexOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand));
         if (p->index == 1) {
           TypeCheckLast(p, kAstI32);
         } else {
-          TypeCheckLast(p, sig->GetParam(p->index - 2));
+          TypeCheckLast(p, operand.sig->GetParam(p->index - 2));
         }
         if (p->done() && build()) {
           uint32_t count = p->tree->count;
@@ -1020,7 +1239,24 @@
           for (uint32_t i = 0; i < count; i++) {
             buffer[i] = p->tree->children[i]->node;
           }
-          p->tree->node = builder_->CallIndirect(index, buffer);
+          p->tree->node = builder_->CallIndirect(operand.index, buffer);
+        }
+        break;
+      }
+      case kExprCallImport: {
+        ImportIndexOperand operand(this, p->pc());
+        CHECK(Validate(p->pc(), operand));
+        if (p->index > 0) {
+          TypeCheckLast(p, operand.sig->GetParam(p->index - 1));
+        }
+        if (p->done() && build()) {
+          uint32_t count = p->tree->count + 1;
+          TFNode** buffer = builder_->Buffer(count);
+          buffer[0] = nullptr;  // reserved for code object.
+          for (uint32_t i = 1; i < count; i++) {
+            buffer[i] = p->tree->children[i - 1]->node;
+          }
+          p->tree->node = builder_->CallImport(operand.index, buffer);
         }
         break;
       }
@@ -1030,13 +1266,17 @@
   }
 
   void ReduceBreakToExprBlock(Production* p, Block* block) {
+    ReduceBreakToExprBlock(p, block, p->tree->count > 0 ? p->last() : nullptr);
+  }
+
+  void ReduceBreakToExprBlock(Production* p, Block* block, Tree* val) {
     if (block->stack_depth < 0) {
       // This is the inner loop block, which does not have a value.
       Goto(ssa_env_, block->ssa_env);
     } else {
       // Merge the value into the production for the block.
       Production* bp = &stack_[block->stack_depth];
-      MergeIntoProduction(bp, block->ssa_env, p->last());
+      MergeIntoProduction(bp, block->ssa_env, val);
     }
   }
 
@@ -1045,7 +1285,7 @@
 
     bool first = target->state == SsaEnv::kUnreachable;
     Goto(ssa_env_, target);
-    if (expr->type == kAstEnd) return;
+    if (expr == nullptr || expr->type == kAstEnd) return;
 
     if (first) {
       // first merge to this environment; set the type and the node.
@@ -1069,11 +1309,9 @@
     DCHECK_EQ(1, p->index);
     TypeCheckLast(p, kAstI32);  // index
     if (build()) {
-      int length = 0;
-      uint32_t offset = 0;
-      MemoryAccessOperand(p->pc(), &length, &offset);
+      MemoryAccessOperand operand(this, p->pc());
       p->tree->node =
-          builder_->LoadMem(type, mem_type, p->last()->node, offset);
+          builder_->LoadMem(type, mem_type, p->last()->node, operand.offset);
     }
   }
 
@@ -1084,11 +1322,10 @@
       DCHECK_EQ(2, p->index);
       TypeCheckLast(p, type);
       if (build()) {
-        int length = 0;
-        uint32_t offset = 0;
-        MemoryAccessOperand(p->pc(), &length, &offset);
+        MemoryAccessOperand operand(this, p->pc());
         TFNode* val = p->tree->children[1]->node;
-        builder_->StoreMem(mem_type, p->tree->children[0]->node, offset, val);
+        builder_->StoreMem(mem_type, p->tree->children[0]->node, operand.offset,
+                           val);
         p->tree->node = val;
       }
     }
@@ -1111,7 +1348,7 @@
   void SetEnv(const char* reason, SsaEnv* env) {
     TRACE("  env = %p, block depth = %d, reason = %s", static_cast<void*>(env),
           static_cast<int>(blocks_.size()), reason);
-    if (env->control != nullptr && FLAG_trace_wasm_decoder) {
+    if (FLAG_trace_wasm_decoder && env && env->control) {
       TRACE(", control = ");
       compiler::WasmGraphBuilder::PrintDebugName(env->control);
     }
@@ -1286,94 +1523,11 @@
     return result;
   }
 
-  // Load an operand at [pc + 1].
-  template <typename V>
-  V Operand(const byte* pc) {
-    if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) {
-      const char* msg = "Expected operand following opcode";
-      switch (sizeof(V)) {
-        case 1:
-          msg = "Expected 1-byte operand following opcode";
-          break;
-        case 2:
-          msg = "Expected 2-byte operand following opcode";
-          break;
-        case 4:
-          msg = "Expected 4-byte operand following opcode";
-          break;
-        default:
-          break;
-      }
-      error(pc, msg);
-      return -1;
-    }
-    return *reinterpret_cast<const V*>(pc + 1);
-  }
-
   int EnvironmentCount() {
     if (builder_) return static_cast<int>(function_env_->GetLocalCount());
     return 0;  // if we aren't building a graph, don't bother with SSA renaming.
   }
 
-  LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) {
-    *index = UnsignedLEB128Operand(pc, length);
-    if (function_env_->IsValidLocal(*index)) {
-      return function_env_->GetLocalType(*index);
-    }
-    error(pc, "invalid local variable index");
-    return kAstStmt;
-  }
-
-  LocalType GlobalOperand(const byte* pc, uint32_t* index, int* length) {
-    *index = UnsignedLEB128Operand(pc, length);
-    if (function_env_->module->IsValidGlobal(*index)) {
-      return WasmOpcodes::LocalTypeFor(
-          function_env_->module->GetGlobalType(*index));
-    }
-    error(pc, "invalid global variable index");
-    return kAstStmt;
-  }
-
-  FunctionSig* FunctionSigOperand(const byte* pc, uint32_t* index,
-                                  int* length) {
-    *index = UnsignedLEB128Operand(pc, length);
-    if (function_env_->module->IsValidFunction(*index)) {
-      return function_env_->module->GetFunctionSignature(*index);
-    }
-    error(pc, "invalid function index");
-    return nullptr;
-  }
-
-  FunctionSig* SigOperand(const byte* pc, uint32_t* index, int* length) {
-    *index = UnsignedLEB128Operand(pc, length);
-    if (function_env_->module->IsValidSignature(*index)) {
-      return function_env_->module->GetSignature(*index);
-    }
-    error(pc, "invalid signature index");
-    return nullptr;
-  }
-
-  uint32_t UnsignedLEB128Operand(const byte* pc, int* length) {
-    uint32_t result = 0;
-    ReadUnsignedLEB128ErrorCode error_code =
-        ReadUnsignedLEB128Operand(pc + 1, limit_, length, &result);
-    if (error_code == kInvalidLEB128) error(pc, "invalid LEB128 varint");
-    if (error_code == kMissingLEB128) error(pc, "expected LEB128 varint");
-    (*length)++;
-    return result;
-  }
-
-  void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) {
-    byte bitfield = Operand<uint8_t>(pc);
-    if (MemoryAccess::OffsetField::decode(bitfield)) {
-      *offset = UnsignedLEB128Operand(pc + 1, length);
-      (*length)++;  // to account for the memory access byte
-    } else {
-      *offset = 0;
-      *length = 2;
-    }
-  }
-
   virtual void onFirstError() {
     limit_ = start_;     // Terminate decoding loop.
     builder_ = nullptr;  // Don't build any more nodes.
@@ -1447,137 +1601,114 @@
                                                       const byte* limit,
                                                       int* length,
                                                       uint32_t* result) {
-  *result = 0;
-  const byte* ptr = pc;
-  const byte* end = pc + 5;  // maximum 5 bytes.
-  if (end > limit) end = limit;
-  int shift = 0;
-  byte b = 0;
-  while (ptr < end) {
-    b = *ptr++;
-    *result = *result | ((b & 0x7F) << shift);
-    if ((b & 0x80) == 0) break;
-    shift += 7;
-  }
-  DCHECK_LE(ptr - pc, 5);
-  *length = static_cast<int>(ptr - pc);
-  if (ptr == end && (b & 0x80)) {
-    return kInvalidLEB128;
-  } else if (*length == 0) {
-    return kMissingLEB128;
-  } else {
-    return kNoError;
+  Decoder decoder(pc, limit);
+  *result = decoder.checked_read_u32v(pc, 0, length);
+  if (decoder.ok()) return kNoError;
+  return (limit - pc) > 1 ? kInvalidLEB128 : kMissingLEB128;
+}
+
+int OpcodeLength(const byte* pc, const byte* end) {
+  WasmDecoder decoder(nullptr, pc, end);
+  return decoder.OpcodeLength(pc);
+}
+
+int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) {
+  WasmDecoder decoder(env, 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;
+  std::vector<int> arity_stack;
+  while (pc < end) {
+    int arity = decoder.OpcodeArity(pc);
+    size_t length = decoder.OpcodeLength(pc);
+
+    for (auto arity : arity_stack) {
+      printf("  ");
+      USE(arity);
+    }
+
+    WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
+    printf("k%s,", WasmOpcodes::OpcodeName(opcode));
+
+    for (size_t i = 1; i < length; i++) {
+      printf(" 0x%02x,", pc[i]);
+    }
+    pc += length;
+    printf("\n");
+
+    arity_stack.push_back(arity);
+    while (arity_stack.back() == 0) {
+      arity_stack.pop_back();
+      if (arity_stack.empty()) break;
+      arity_stack.back()--;
+    }
   }
 }
 
-
-int OpcodeLength(const byte* pc) {
-  switch (static_cast<WasmOpcode>(*pc)) {
-#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
-    FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
-    FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
-#undef DECLARE_OPCODE_CASE
-
-    case kExprI8Const:
-    case kExprBlock:
-    case kExprLoop:
-    case kExprBr:
-    case kExprBrIf:
-      return 2;
-    case kExprI32Const:
-    case kExprF32Const:
-      return 5;
-    case kExprI64Const:
-    case kExprF64Const:
-      return 9;
-    case kExprStoreGlobal:
-    case kExprSetLocal:
-    case kExprLoadGlobal:
-    case kExprCallFunction:
-    case kExprCallIndirect:
-    case kExprGetLocal: {
-      int length;
-      uint32_t result = 0;
-      ReadUnsignedLEB128Operand(pc + 1, pc + 6, &length, &result);
-      return 1 + length;
-    }
-    case kExprTableSwitch: {
-      uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc + 3);
-      return 5 + table_count * 2;
-    }
-
-    default:
-      return 1;
+// 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,
+                                           const byte* start, const byte* end) {
+  LoopAssignmentAnalyzer analyzer(zone, env);
+  return analyzer.Analyze(start, end);
 }
 
-
-int OpcodeArity(FunctionEnv* env, const byte* pc) {
-#define DECLARE_ARITY(name, ...)                          \
-  static const LocalType kTypes_##name[] = {__VA_ARGS__}; \
-  static const int kArity_##name =                        \
-      static_cast<int>(arraysize(kTypes_##name) - 1);
-
-  FOREACH_SIGNATURE(DECLARE_ARITY);
-#undef DECLARE_ARITY
-
-  switch (static_cast<WasmOpcode>(*pc)) {
-    case kExprI8Const:
-    case kExprI32Const:
-    case kExprI64Const:
-    case kExprF64Const:
-    case kExprF32Const:
-    case kExprGetLocal:
-    case kExprLoadGlobal:
-    case kExprNop:
-    case kExprUnreachable:
-      return 0;
-
-    case kExprBr:
-    case kExprStoreGlobal:
-    case kExprSetLocal:
-      return 1;
-
-    case kExprIf:
-    case kExprBrIf:
-      return 2;
-    case kExprIfElse:
-    case kExprSelect:
-      return 3;
-    case kExprBlock:
-    case kExprLoop:
-      return *(pc + 1);
-
-    case kExprCallFunction: {
-      int index = *(pc + 1);
-      return static_cast<int>(
-          env->module->GetFunctionSignature(index)->parameter_count());
-    }
-    case kExprCallIndirect: {
-      int index = *(pc + 1);
-      return 1 + static_cast<int>(
-                     env->module->GetSignature(index)->parameter_count());
-    }
-    case kExprReturn:
-      return static_cast<int>(env->sig->return_count());
-    case kExprTableSwitch: {
-      uint16_t case_count = *reinterpret_cast<const uint16_t*>(pc + 1);
-      return 1 + case_count;
-    }
-
-#define DECLARE_OPCODE_CASE(name, opcode, sig) \
-  case kExpr##name:                            \
-    return kArity_##sig;
-
-      FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
-      FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
-      FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
-      FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
-#undef DECLARE_OPCODE_CASE
-  }
-  UNREACHABLE();
-  return 0;
-}
 }  // namespace wasm
 }  // namespace internal
 }  // namespace v8
diff --git a/src/wasm/ast-decoder.h b/src/wasm/ast-decoder.h
index 5b95ad9..465baca 100644
--- a/src/wasm/ast-decoder.h
+++ b/src/wasm/ast-decoder.h
@@ -6,18 +6,181 @@
 #define V8_WASM_AST_DECODER_H_
 
 #include "src/signature.h"
+#include "src/wasm/decoder.h"
 #include "src/wasm/wasm-opcodes.h"
 #include "src/wasm/wasm-result.h"
 
 namespace v8 {
 namespace internal {
 
+class BitVector;  // forward declaration
+
 namespace compiler {  // external declarations from compiler.
 class WasmGraphBuilder;
 }
 
 namespace wasm {
 
+// Helpers for decoding different kinds of operands which follow bytecodes.
+struct LocalIndexOperand {
+  uint32_t index;
+  LocalType type;
+  int length;
+
+  inline LocalIndexOperand(Decoder* decoder, const byte* pc) {
+    index = decoder->checked_read_u32v(pc, 1, &length, "local index");
+    type = kAstStmt;
+  }
+};
+
+struct ImmI8Operand {
+  int8_t value;
+  int length;
+  inline ImmI8Operand(Decoder* decoder, const byte* pc) {
+    value = bit_cast<int8_t>(decoder->checked_read_u8(pc, 1, "immi8"));
+    length = 1;
+  }
+};
+
+struct ImmI32Operand {
+  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;
+  }
+};
+
+struct ImmI64Operand {
+  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;
+  }
+};
+
+struct ImmF32Operand {
+  float value;
+  int length;
+  inline ImmF32Operand(Decoder* decoder, const byte* pc) {
+    value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32"));
+    length = 4;
+  }
+};
+
+struct ImmF64Operand {
+  double value;
+  int length;
+  inline ImmF64Operand(Decoder* decoder, const byte* pc) {
+    value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64"));
+    length = 8;
+  }
+};
+
+struct GlobalIndexOperand {
+  uint32_t index;
+  LocalType type;
+  MachineType machine_type;
+  int length;
+
+  inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
+    index = decoder->checked_read_u32v(pc, 1, &length, "global index");
+    type = kAstStmt;
+    machine_type = MachineType::None();
+  }
+};
+
+struct Block;
+struct BreakDepthOperand {
+  uint32_t depth;
+  Block* target;
+  int length;
+  inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
+    depth = decoder->checked_read_u8(pc, 1, "break depth");
+    length = 1;
+    target = nullptr;
+  }
+};
+
+struct BlockCountOperand {
+  uint32_t count;
+  int length;
+  inline BlockCountOperand(Decoder* decoder, const byte* pc) {
+    count = decoder->checked_read_u8(pc, 1, "block count");
+    length = 1;
+  }
+};
+
+struct SignatureIndexOperand {
+  uint32_t index;
+  FunctionSig* sig;
+  int length;
+  inline SignatureIndexOperand(Decoder* decoder, const byte* pc) {
+    index = decoder->checked_read_u32v(pc, 1, &length, "signature index");
+    sig = nullptr;
+  }
+};
+
+struct FunctionIndexOperand {
+  uint32_t index;
+  FunctionSig* sig;
+  int length;
+  inline FunctionIndexOperand(Decoder* decoder, const byte* pc) {
+    index = decoder->checked_read_u32v(pc, 1, &length, "function index");
+    sig = nullptr;
+  }
+};
+
+struct ImportIndexOperand {
+  uint32_t index;
+  FunctionSig* sig;
+  int length;
+  inline ImportIndexOperand(Decoder* decoder, const byte* pc) {
+    index = decoder->checked_read_u32v(pc, 1, &length, "import index");
+    sig = nullptr;
+  }
+};
+
+struct TableSwitchOperand {
+  uint32_t case_count;
+  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;
+
+    if (decoder->check(pc, 5, table_count * 2, "expected <table entries>")) {
+      table = pc + 5;
+    } 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;
+  }
+};
+
+struct MemoryAccessOperand {
+  bool aligned;
+  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;
+    }
+  }
+};
+
 typedef compiler::WasmGraphBuilder TFBuilder;
 struct ModuleEnv;  // forward declaration of module interface.
 
@@ -26,56 +189,55 @@
 struct FunctionEnv {
   ModuleEnv* module;             // module environment
   FunctionSig* sig;              // signature of this function
-  uint32_t local_int32_count;    // number of int32 locals
-  uint32_t local_int64_count;    // number of int64 locals
-  uint32_t local_float32_count;  // number of float32 locals
-  uint32_t local_float64_count;  // number of float64 locals
+  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
 
-  bool IsValidLocal(uint32_t index) { return index < total_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_int32_count) return kAstI32;
-    index -= local_int32_count;
-    if (index < local_int64_count) return kAstI64;
-    index -= local_int64_count;
-    if (index < local_float32_count) return kAstF32;
-    index -= local_float32_count;
-    if (index < local_float64_count) return kAstF64;
+    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_int32_count += count;
+        local_i32_count += count;
         break;
       case kAstI64:
-        local_int64_count += count;
+        local_i64_count += count;
         break;
       case kAstF32:
-        local_float32_count += count;
+        local_f32_count += count;
         break;
       case kAstF64:
-        local_float64_count += count;
+        local_f64_count += count;
         break;
       default:
         UNREACHABLE();
     }
     total_locals += count;
-    DCHECK(total_locals ==
-           (sig->parameter_count() + local_int32_count + local_int64_count +
-            local_float32_count + local_float64_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_int32_count + local_int64_count + local_float32_count +
-                   local_float64_count;
+                   local_i32_count + local_i64_count + local_f32_count +
+                   local_f64_count;
   }
 };
 
@@ -89,6 +251,8 @@
 TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
                         const byte* start, const byte* end);
 
+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);
@@ -104,11 +268,14 @@
 ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte*, const byte*,
                                                       int*, uint32_t*);
 
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
+                                           const byte* start, const byte* end);
+
 // Computes the length of the opcode at the given address.
-int OpcodeLength(const byte* pc);
+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);
+int OpcodeArity(FunctionEnv* env, 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 698919d..0e88eda 100644
--- a/src/wasm/decoder.h
+++ b/src/wasm/decoder.h
@@ -24,6 +24,12 @@
 #define TRACE(...)
 #endif
 
+#if !(V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM)
+#define UNALIGNED_ACCESS_OK 1
+#else
+#define UNALIGNED_ACCESS_OK 0
+#endif
+
 // A helper utility to decode bytes, integers, fields, varints, etc, from
 // a buffer of bytes.
 class Decoder {
@@ -32,107 +38,188 @@
       : start_(start),
         pc_(start),
         limit_(end),
+        end_(end),
         error_pc_(nullptr),
         error_pt_(nullptr) {}
 
   virtual ~Decoder() {}
 
+  inline bool check(const byte* base, int offset, int length, const char* msg) {
+    DCHECK_GE(base, start_);
+    if ((base + offset + length) > limit_) {
+      error(base, base + offset, msg);
+      return false;
+    }
+    return true;
+  }
+
+  // Reads a single 8-bit byte, reporting an error if out of bounds.
+  inline uint8_t checked_read_u8(const byte* base, int offset,
+                                 const char* msg = "expected 1 byte") {
+    return check(base, offset, 1, msg) ? base[offset] : 0;
+  }
+
+  // Reads 16-bit word, reporting an error if out of bounds.
+  inline uint16_t checked_read_u16(const byte* base, int offset,
+                                   const char* msg = "expected 2 bytes") {
+    return check(base, offset, 2, msg) ? read_u16(base + offset) : 0;
+  }
+
+  // Reads 32-bit word, reporting an error if out of bounds.
+  inline uint32_t checked_read_u32(const byte* base, int offset,
+                                   const char* msg = "expected 4 bytes") {
+    return check(base, offset, 4, msg) ? read_u32(base + offset) : 0;
+  }
+
+  // Reads 64-bit word, reporting an error if out of bounds.
+  inline uint64_t checked_read_u64(const byte* base, int offset,
+                                   const char* msg = "expected 8 bytes") {
+    return check(base, offset, 8, msg) ? read_u64(base + offset) : 0;
+  }
+
+  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 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;
+    }
+    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 result;
+  }
+
+  // Reads a single 16-bit unsigned integer (little endian).
+  inline uint16_t read_u16(const byte* ptr) {
+    DCHECK(ptr >= start_ && (ptr + 2) <= end_);
+#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK
+    return *reinterpret_cast<const uint16_t*>(ptr);
+#else
+    uint16_t b0 = ptr[0];
+    uint16_t b1 = ptr[1];
+    return (b1 << 8) | b0;
+#endif
+  }
+
+  // Reads a single 32-bit unsigned integer (little endian).
+  inline uint32_t read_u32(const byte* ptr) {
+    DCHECK(ptr >= start_ && (ptr + 4) <= end_);
+#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK
+    return *reinterpret_cast<const uint32_t*>(ptr);
+#else
+    uint32_t b0 = ptr[0];
+    uint32_t b1 = ptr[1];
+    uint32_t b2 = ptr[2];
+    uint32_t b3 = ptr[3];
+    return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
+#endif
+  }
+
+  // Reads a single 64-bit unsigned integer (little endian).
+  inline uint64_t read_u64(const byte* ptr) {
+    DCHECK(ptr >= start_ && (ptr + 8) <= end_);
+#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK
+    return *reinterpret_cast<const uint64_t*>(ptr);
+#else
+    uint32_t b0 = ptr[0];
+    uint32_t b1 = ptr[1];
+    uint32_t b2 = ptr[2];
+    uint32_t b3 = ptr[3];
+    uint32_t low = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
+    uint32_t b4 = ptr[4];
+    uint32_t b5 = ptr[5];
+    uint32_t b6 = ptr[6];
+    uint32_t b7 = ptr[7];
+    uint64_t high = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4;
+    return (high << 32) | low;
+#endif
+  }
+
   // Reads a 8-bit unsigned integer (byte) and advances {pc_}.
-  uint8_t u8(const char* name = nullptr) {
+  uint8_t consume_u8(const char* name = nullptr) {
     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
           name ? name : "uint8_t");
     if (checkAvailable(1)) {
       byte val = *(pc_++);
       TRACE("%02x = %d\n", val, val);
       return val;
-    } else {
-      error("expected 1 byte, but fell off end");
-      return traceOffEnd<uint8_t>();
     }
+    return traceOffEnd<uint8_t>();
   }
 
   // Reads a 16-bit unsigned integer (little endian) and advances {pc_}.
-  uint16_t u16(const char* name = nullptr) {
+  uint16_t consume_u16(const char* name = nullptr) {
     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
           name ? name : "uint16_t");
     if (checkAvailable(2)) {
-#ifdef V8_TARGET_LITTLE_ENDIAN
-      byte b0 = pc_[0];
-      byte b1 = pc_[1];
-#else
-      byte b1 = pc_[0];
-      byte b0 = pc_[1];
-#endif
-      uint16_t val = static_cast<uint16_t>(b1 << 8) | b0;
+      uint16_t val = read_u16(pc_);
       TRACE("%02x %02x = %d\n", pc_[0], pc_[1], val);
       pc_ += 2;
       return val;
-    } else {
-      error("expected 2 bytes, but fell off end");
-      return traceOffEnd<uint16_t>();
     }
+    return traceOffEnd<uint16_t>();
   }
 
   // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
-  uint32_t u32(const char* name = nullptr) {
+  uint32_t consume_u32(const char* name = nullptr) {
     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
           name ? name : "uint32_t");
     if (checkAvailable(4)) {
-#ifdef V8_TARGET_LITTLE_ENDIAN
-      byte b0 = pc_[0];
-      byte b1 = pc_[1];
-      byte b2 = pc_[2];
-      byte b3 = pc_[3];
-#else
-      byte b3 = pc_[0];
-      byte b2 = pc_[1];
-      byte b1 = pc_[2];
-      byte b0 = pc_[3];
-#endif
-      uint32_t val = static_cast<uint32_t>(b3 << 24) |
-                     static_cast<uint32_t>(b2 << 16) |
-                     static_cast<uint32_t>(b1 << 8) | b0;
+      uint32_t val = read_u32(pc_);
       TRACE("%02x %02x %02x %02x = %u\n", pc_[0], pc_[1], pc_[2], pc_[3], val);
       pc_ += 4;
       return val;
-    } else {
-      error("expected 4 bytes, but fell off end");
-      return traceOffEnd<uint32_t>();
     }
+    return traceOffEnd<uint32_t>();
   }
 
   // Reads a LEB128 variable-length 32-bit integer and advances {pc_}.
-  uint32_t u32v(int* length, const char* name = nullptr) {
+  uint32_t consume_u32v(int* length, const char* name = nullptr) {
     TRACE("  +%d  %-20s: ", static_cast<int>(pc_ - start_),
           name ? name : "varint");
 
-    if (!checkAvailable(1)) {
-      error("expected at least 1 byte, but fell off end");
-      return traceOffEnd<uint32_t>();
-    }
+    if (checkAvailable(1)) {
+      const byte* pos = pc_;
+      const byte* end = pc_ + 5;
+      if (end > limit_) end = limit_;
 
-    const byte* pos = pc_;
-    const byte* end = pc_ + 5;
-    if (end > limit_) end = limit_;
+      uint32_t result = 0;
+      int shift = 0;
+      byte b = 0;
+      while (pc_ < end) {
+        b = *pc_++;
+        TRACE("%02x ", b);
+        result = result | ((b & 0x7F) << shift);
+        if ((b & 0x80) == 0) break;
+        shift += 7;
+      }
 
-    uint32_t result = 0;
-    int shift = 0;
-    byte b = 0;
-    while (pc_ < end) {
-      b = *pc_++;
-      TRACE("%02x ", b);
-      result = result | ((b & 0x7F) << shift);
-      if ((b & 0x80) == 0) break;
-      shift += 7;
+      *length = static_cast<int>(pc_ - pos);
+      if (pc_ == end && (b & 0x80)) {
+        error(pc_ - 1, "varint too large");
+      } else {
+        TRACE("= %u\n", result);
+      }
+      return result;
     }
-
-    *length = static_cast<int>(pc_ - pos);
-    if (pc_ == end && (b & 0x80)) {
-      error(pc_ - 1, "varint too large");
-    } else {
-      TRACE("= %u\n", result);
-    }
-    return result;
+    return traceOffEnd<uint32_t>();
   }
 
   // Check that at least {size} bytes exist between {pc_} and {limit_}.
@@ -145,6 +232,12 @@
     }
   }
 
+  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); }
@@ -208,6 +301,7 @@
     start_ = start;
     pc_ = start;
     limit_ = end;
+    end_ = end;
     error_pc_ = nullptr;
     error_pt_ = nullptr;
     error_msg_.Reset(nullptr);
@@ -220,6 +314,7 @@
   const byte* start_;
   const byte* pc_;
   const byte* limit_;
+  const byte* end_;
   const byte* error_pc_;
   const byte* error_pt_;
   base::SmartArrayPointer<char> error_msg_;
diff --git a/src/wasm/encoder.cc b/src/wasm/encoder.cc
index d8d3633..d80a275 100644
--- a/src/wasm/encoder.cc
+++ b/src/wasm/encoder.cc
@@ -30,13 +30,13 @@
 
 
 void EmitUint16(byte** b, uint16_t x) {
-  Memory::uint16_at(*b) = x;
+  WriteUnalignedUInt16(*b, x);
   *b += 2;
 }
 
 
 void EmitUint32(byte** b, uint32_t x) {
-  Memory::uint32_at(*b) = x;
+  WriteUnalignedUInt32(*b, x);
   *b += 4;
 }
 
@@ -121,12 +121,6 @@
 }
 
 
-void WasmFunctionBuilder::EmitWithLocal(WasmOpcode opcode) {
-  body_.push_back(static_cast<byte>(opcode));
-  local_indices_.push_back(static_cast<uint32_t>(body_.size()) - 1);
-}
-
-
 uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) {
   body_.push_back(immediate);
   return static_cast<uint32_t>(body_.size()) - 1;
@@ -202,44 +196,44 @@
 void WasmFunctionBuilder::IndexVars(WasmFunctionEncoder* e,
                                     uint16_t* var_index) const {
   uint16_t param = 0;
-  uint16_t int32 = 0;
-  uint16_t int64 = 0;
-  uint16_t float32 = 0;
-  uint16_t float64 = 0;
+  uint16_t i32 = 0;
+  uint16_t i64 = 0;
+  uint16_t f32 = 0;
+  uint16_t f64 = 0;
   for (size_t i = 0; i < locals_.size(); i++) {
     if (locals_.at(i).param_) {
       param++;
     } else if (locals_.at(i).type_ == kAstI32) {
-      int32++;
+      i32++;
     } else if (locals_.at(i).type_ == kAstI64) {
-      int64++;
+      i64++;
     } else if (locals_.at(i).type_ == kAstF32) {
-      float32++;
+      f32++;
     } else if (locals_.at(i).type_ == kAstF64) {
-      float64++;
+      f64++;
     }
   }
-  e->local_int32_count_ = int32;
-  e->local_int64_count_ = int64;
-  e->local_float32_count_ = float32;
-  e->local_float64_count_ = float64;
-  float64 = param + int32 + int64 + float32;
-  float32 = param + int32 + int64;
-  int64 = param + int32;
-  int32 = param;
+  e->local_i32_count_ = i32;
+  e->local_i64_count_ = i64;
+  e->local_f32_count_ = f32;
+  e->local_f64_count_ = f64;
+  f64 = param + i32 + i64 + f32;
+  f32 = param + i32 + i64;
+  i64 = param + i32;
+  i32 = param;
   param = 0;
   for (size_t i = 0; i < locals_.size(); i++) {
     if (locals_.at(i).param_) {
       e->params_.push_back(locals_.at(i).type_);
       var_index[i] = param++;
     } else if (locals_.at(i).type_ == kAstI32) {
-      var_index[i] = int32++;
+      var_index[i] = i32++;
     } else if (locals_.at(i).type_ == kAstI64) {
-      var_index[i] = int64++;
+      var_index[i] = i64++;
     } else if (locals_.at(i).type_ == kAstF32) {
-      var_index[i] = float32++;
+      var_index[i] = f32++;
     } else if (locals_.at(i).type_ == kAstF64) {
-      var_index[i] = float64++;
+      var_index[i] = f64++;
     }
   }
 }
@@ -269,7 +263,7 @@
 
 
 uint32_t WasmFunctionEncoder::NameSize() const {
-  return exported_ ? static_cast<uint32_t>(name_.size()) : 0;
+  return HasName() ? static_cast<uint32_t>(name_.size()) : 0;
 }
 
 
@@ -291,10 +285,10 @@
   }
 
   if (HasLocals()) {
-    EmitUint16(header, local_int32_count_);
-    EmitUint16(header, local_int64_count_);
-    EmitUint16(header, local_float32_count_);
-    EmitUint16(header, local_float64_count_);
+    EmitUint16(header, local_i32_count_);
+    EmitUint16(header, local_i64_count_);
+    EmitUint16(header, local_f32_count_);
+    EmitUint16(header, local_f64_count_);
   }
 
   if (!external_) {
@@ -370,21 +364,21 @@
 }
 
 
-int WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a,
-                                                       FunctionSig* b) const {
-  if (a->return_count() < b->return_count()) return -1;
-  if (a->return_count() > b->return_count()) return 1;
-  if (a->parameter_count() < b->parameter_count()) return -1;
-  if (a->parameter_count() > b->parameter_count()) return 1;
+bool WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a,
+                                                        FunctionSig* b) const {
+  if (a->return_count() < b->return_count()) return true;
+  if (a->return_count() > b->return_count()) return false;
+  if (a->parameter_count() < b->parameter_count()) return true;
+  if (a->parameter_count() > b->parameter_count()) return false;
   for (size_t r = 0; r < a->return_count(); r++) {
-    if (a->GetReturn(r) < b->GetReturn(r)) return -1;
-    if (a->GetReturn(r) > b->GetReturn(r)) return 1;
+    if (a->GetReturn(r) < b->GetReturn(r)) return true;
+    if (a->GetReturn(r) > b->GetReturn(r)) return false;
   }
   for (size_t p = 0; p < a->parameter_count(); p++) {
-    if (a->GetParam(p) < b->GetParam(p)) return -1;
-    if (a->GetParam(p) > b->GetParam(p)) return 1;
+    if (a->GetParam(p) < b->GetParam(p)) return true;
+    if (a->GetParam(p) > b->GetParam(p)) return false;
   }
-  return 0;
+  return false;
 }
 
 
diff --git a/src/wasm/encoder.h b/src/wasm/encoder.h
index f0fabe9..7b651bf 100644
--- a/src/wasm/encoder.h
+++ b/src/wasm/encoder.h
@@ -33,21 +33,21 @@
   friend class WasmFunctionBuilder;
   uint16_t signature_index_;
   ZoneVector<LocalType> params_;
-  uint16_t local_int32_count_;
-  uint16_t local_int64_count_;
-  uint16_t local_float32_count_;
-  uint16_t local_float64_count_;
+  uint16_t local_i32_count_;
+  uint16_t local_i64_count_;
+  uint16_t local_f32_count_;
+  uint16_t local_f64_count_;
   bool exported_;
   bool external_;
   ZoneVector<uint8_t> body_;
   ZoneVector<char> name_;
 
   bool HasLocals() const {
-    return (local_int32_count_ + local_int64_count_ + local_float32_count_ +
-            local_float64_count_) > 0;
+    return (local_i32_count_ + local_i64_count_ + local_f32_count_ +
+            local_f64_count_) > 0;
   }
 
-  bool HasName() const { return exported_ && name_.size() > 0; }
+  bool HasName() const { return (exported_ || external_) && name_.size() > 0; }
 };
 
 class WasmFunctionBuilder : public ZoneObject {
@@ -60,7 +60,6 @@
                 const uint32_t* local_indices, uint32_t indices_size);
   void Emit(WasmOpcode opcode);
   void EmitWithU8(WasmOpcode opcode, const byte immediate);
-  void EmitWithLocal(WasmOpcode opcode);
   uint32_t EmitEditableImmediate(const byte immediate);
   void EditImmediate(uint32_t offset, const byte immediate);
   void Exported(uint8_t flag);
@@ -134,12 +133,12 @@
   void AddIndirectFunction(uint16_t index);
   WasmModuleWriter* Build(Zone* zone);
 
- private:
   struct CompareFunctionSigs {
-    int operator()(FunctionSig* a, FunctionSig* b) const;
+    bool operator()(FunctionSig* a, FunctionSig* b) const;
   };
   typedef ZoneMap<FunctionSig*, uint16_t, CompareFunctionSigs> SignatureMap;
 
+ private:
   Zone* zone_;
   ZoneVector<FunctionSig*> signatures_;
   ZoneVector<WasmFunctionBuilder*> functions_;
diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc
index 24f3982..62b000d 100644
--- a/src/wasm/module-decoder.cc
+++ b/src/wasm/module-decoder.cc
@@ -54,6 +54,7 @@
     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>();
 
     bool sections[kMaxModuleSectionCode];
     memset(sections, 0, sizeof(sections));
@@ -62,7 +63,7 @@
     while (pc_ < limit_) {
       TRACE("DecodeSection\n");
       WasmSectionDeclCode section =
-          static_cast<WasmSectionDeclCode>(u8("section"));
+          static_cast<WasmSectionDeclCode>(consume_u8("section"));
       // Each section should appear at most once.
       if (section < kMaxModuleSectionCode) {
         CheckForPreviousSection(sections, section, false);
@@ -75,20 +76,20 @@
           limit_ = pc_;
           break;
         case kDeclMemory:
-          module->min_mem_size_log2 = u8("min memory");
-          module->max_mem_size_log2 = u8("max memory");
-          module->mem_export = u8("export memory") != 0;
+          module->min_mem_size_log2 = consume_u8("min memory");
+          module->max_mem_size_log2 = consume_u8("max memory");
+          module->mem_export = consume_u8("export memory") != 0;
           break;
         case kDeclSignatures: {
           int length;
-          uint32_t signatures_count = u32v(&length, "signatures count");
+          uint32_t signatures_count = consume_u32v(&length, "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 = sig();  // read function sig.
+            FunctionSig* s = consume_sig();  // read function sig.
             module->signatures->push_back(s);
           }
           break;
@@ -97,15 +98,12 @@
           // Functions require a signature table first.
           CheckForPreviousSection(sections, kDeclSignatures, true);
           int length;
-          uint32_t functions_count = u32v(&length, "functions count");
+          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.globals_area = 0;
-          menv.mem_start = 0;
-          menv.mem_end = 0;
-          menv.function_code = nullptr;
+          menv.instance = nullptr;
           menv.asm_js = asm_js_;
           // Decode functions.
           for (uint32_t i = 0; i < functions_count; i++) {
@@ -114,7 +112,7 @@
                   static_cast<int>(pc_ - start_));
 
             module->functions->push_back(
-                {nullptr, 0, 0, 0, 0, 0, 0, false, false});
+                {nullptr, i, 0, 0, 0, 0, 0, 0, false, false});
             WasmFunction* function = &module->functions->back();
             DecodeFunctionInModule(module, function, false);
           }
@@ -133,7 +131,7 @@
         }
         case kDeclGlobals: {
           int length;
-          uint32_t globals_count = u32v(&length, "globals count");
+          uint32_t globals_count = consume_u32v(&length, "globals count");
           module->globals->reserve(SafeReserve(globals_count));
           // Decode globals.
           for (uint32_t i = 0; i < globals_count; i++) {
@@ -148,7 +146,8 @@
         }
         case kDeclDataSegments: {
           int length;
-          uint32_t data_segments_count = u32v(&length, "data segments count");
+          uint32_t data_segments_count =
+              consume_u32v(&length, "data segments count");
           module->data_segments->reserve(SafeReserve(data_segments_count));
           // Decode data segments.
           for (uint32_t i = 0; i < data_segments_count; i++) {
@@ -157,7 +156,7 @@
                   static_cast<int>(pc_ - start_));
             module->data_segments->push_back({0, 0, 0});
             WasmDataSegment* segment = &module->data_segments->back();
-            DecodeDataSegmentInModule(segment);
+            DecodeDataSegmentInModule(module, segment);
           }
           break;
         }
@@ -165,14 +164,15 @@
           // An indirect function table requires functions first.
           CheckForPreviousSection(sections, kDeclFunctions, true);
           int length;
-          uint32_t function_table_count = u32v(&length, "function table count");
+          uint32_t function_table_count =
+              consume_u32v(&length, "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 = u16();
+            uint16_t index = consume_u16();
             if (index >= module->functions->size()) {
               error(pc_ - 2, "invalid function index");
               break;
@@ -181,13 +181,66 @@
           }
           break;
         }
+        case kDeclStartFunction: {
+          // Declares a start function for a module.
+          CheckForPreviousSection(sections, kDeclFunctions, true);
+          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");
+            break;
+          }
+          break;
+        }
+        case kDeclImportTable: {
+          // Declares an import table.
+          CheckForPreviousSection(sections, kDeclSignatures, true);
+          int length;
+          uint32_t import_table_count =
+              consume_u32v(&length, "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();
+
+            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->module_name_offset = consume_string("import module name");
+            import->function_name_offset =
+                consume_string("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 = u32v(&length, "section size");
+          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;
@@ -249,14 +302,14 @@
   FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
                                       WasmFunction* function) {
     pc_ = start_;
-    function->sig = sig();                       // read signature
+    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_int32_count = u16();         // read u16
-    function->local_int64_count = u16();         // read u16
-    function->local_float32_count = u16();       // read u16
-    function->local_float64_count = u16();       // read u16
+    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
 
@@ -271,7 +324,7 @@
   // Decodes a single function signature at {start}.
   FunctionSig* DecodeFunctionSignature(const byte* start) {
     pc_ = start;
-    FunctionSig* result = sig();
+    FunctionSig* result = consume_sig();
     return ok() ? result : nullptr;
   }
 
@@ -284,19 +337,19 @@
 
   // Decodes a single global entry inside a module starting at {pc_}.
   void DecodeGlobalInModule(WasmGlobal* global) {
-    global->name_offset = string("global name");
+    global->name_offset = consume_string("global name");
     global->type = mem_type();
     global->offset = 0;
-    global->exported = u8("exported") != 0;
+    global->exported = consume_u8("exported") != 0;
   }
 
   // Decodes a single function entry inside a module starting at {pc_}.
   void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
                               bool verify_body = true) {
-    byte decl_bits = u8("function decl");
+    byte decl_bits = consume_u8("function decl");
 
     const byte* sigpos = pc_;
-    function->sig_index = u16("signature index");
+    function->sig_index = consume_u16("signature index");
 
     if (function->sig_index >= module->signatures->size()) {
       return error(sigpos, "invalid signature index");
@@ -313,7 +366,7 @@
           (decl_bits & kDeclFunctionImport) == 0 ? " body" : "");
 
     if (decl_bits & kDeclFunctionName) {
-      function->name_offset = string("function name");
+      function->name_offset = consume_string("function name");
     }
 
     function->exported = decl_bits & kDeclFunctionExport;
@@ -325,13 +378,13 @@
     }
 
     if (decl_bits & kDeclFunctionLocals) {
-      function->local_int32_count = u16("int32 count");
-      function->local_int64_count = u16("int64 count");
-      function->local_float32_count = u16("float32 count");
-      function->local_float64_count = u16("float64 count");
+      function->local_i32_count = consume_u16("i32 count");
+      function->local_i64_count = consume_u16("i64 count");
+      function->local_f32_count = consume_u16("f32 count");
+      function->local_f64_count = consume_u16("f64 count");
     }
 
-    uint16_t size = u16("body size");
+    uint16_t size = consume_u16("body size");
     if (ok()) {
       if ((pc_ + size) > limit_) {
         return error(pc_, limit_,
@@ -345,35 +398,51 @@
     }
   }
 
+  bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) {
+    if (offset > limit) return false;
+    if ((offset + size) < offset) return false;  // overflow
+    return (offset + size) <= limit;
+  }
+
   // Decodes a single data segment entry inside a module starting at {pc_}.
-  void DecodeDataSegmentInModule(WasmDataSegment* segment) {
-    segment->dest_addr =
-        u32("destination");  // TODO(titzer): check it's within the memory size.
-    segment->source_offset = offset("source offset");
-    segment->source_size =
-        u32("source size");  // TODO(titzer): check the size is reasonable.
-    segment->init = u8("init");
+  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");
+
+    // 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");
+    }
+
+    // Validate that the segment will fit into the (minimum) memory.
+    uint32_t memory_limit =
+        1 << (module ? module->min_mem_size_log2 : WasmModule::kMaxMemSize);
+    if (!IsWithinLimit(memory_limit, segment->dest_addr,
+                       segment->source_size)) {
+      error(pc_ - sizeof(uint32_t), "segment out of bounds of memory");
+    }
   }
 
   // Verifies the body (code) of a given function.
   void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
                           WasmFunction* function) {
     if (FLAG_trace_wasm_decode_time) {
-      // TODO(titzer): clean me up a bit.
       OFStream os(stdout);
-      os << "Verifying WASM function:";
-      if (function->name_offset > 0) {
-        os << menv->module->GetName(function->name_offset);
-      }
+      os << "Verifying WASM function " << WasmFunctionName(function, menv)
+         << std::endl;
       os << std::endl;
     }
     FunctionEnv fenv;
     fenv.module = menv;
     fenv.sig = function->sig;
-    fenv.local_int32_count = function->local_int32_count;
-    fenv.local_int64_count = function->local_int64_count;
-    fenv.local_float32_count = function->local_float32_count;
-    fenv.local_float64_count = function->local_float64_count;
+    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 =
@@ -382,8 +451,7 @@
     if (result.failed()) {
       // Wrap the error message from the function decoder.
       std::ostringstream str;
-      str << "in function #" << func_num << ": ";
-      // TODO(titzer): add function name for the user?
+      str << "in function " << WasmFunctionName(function, menv) << ": ";
       str << result;
       std::string strval = str.str();
       const char* raw = strval.c_str();
@@ -400,8 +468,8 @@
 
   // Reads a single 32-bit unsigned integer interpreted as an offset, checking
   // the offset is within bounds and advances.
-  uint32_t offset(const char* name = nullptr) {
-    uint32_t offset = u32(name ? name : "offset");
+  uint32_t consume_offset(const char* name = nullptr) {
+    uint32_t offset = consume_u32(name ? name : "offset");
     if (offset > static_cast<uint32_t>(limit_ - start_)) {
       error(pc_ - sizeof(uint32_t), "offset out of bounds of module");
     }
@@ -410,13 +478,14 @@
 
   // Reads a single 32-bit unsigned integer interpreted as an offset into the
   // data and validating the string there and advances.
-  uint32_t string(const char* name = nullptr) {
-    return offset(name ? name : "string");  // TODO(titzer): validate string
+  uint32_t consume_string(const char* name = nullptr) {
+    // TODO(titzer): validate string
+    return consume_offset(name ? name : "string");
   }
 
   // Reads a single 8-bit integer, interpreting it as a local type.
-  LocalType local_type() {
-    byte val = u8("local type");
+  LocalType consume_local_type() {
+    byte val = consume_u8("local type");
     LocalTypeCode t = static_cast<LocalTypeCode>(val);
     switch (t) {
       case kLocalVoid:
@@ -437,7 +506,7 @@
 
   // Reads a single 8-bit integer, interpreting it as a memory type.
   MachineType mem_type() {
-    byte val = u8("memory type");
+    byte val = consume_u8("memory type");
     MemTypeCode t = static_cast<MemTypeCode>(val);
     switch (t) {
       case kMemI8:
@@ -467,14 +536,14 @@
   }
 
   // Parses an inline function signature.
-  FunctionSig* sig() {
-    byte count = u8("param count");
-    LocalType ret = local_type();
+  FunctionSig* consume_sig() {
+    byte count = consume_u8("param count");
+    LocalType ret = consume_local_type();
     FunctionSig::Builder builder(module_zone, ret == kAstStmt ? 0 : 1, count);
     if (ret != kAstStmt) builder.AddReturn(ret);
 
     for (int i = 0; i < count; i++) {
-      LocalType param = local_type();
+      LocalType param = consume_local_type();
       if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type");
       builder.AddParam(param);
     }
diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc
index 80d8bdb..62a2676 100644
--- a/src/wasm/wasm-js.cc
+++ b/src/wasm/wasm-js.cc
@@ -37,6 +37,7 @@
 
 RawBuffer GetRawBufferArgument(
     ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) {
+  // TODO(titzer): allow typed array views.
   if (args.Length() < 1 || !args[0]->IsArrayBuffer()) {
     thrower.Error("Argument 0 must be an array buffer");
     return {nullptr, nullptr};
@@ -44,8 +45,6 @@
   Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
   ArrayBuffer::Contents contents = buffer->GetContents();
 
-  // TODO(titzer): allow offsets into buffers, views, etc.
-
   const byte* start = reinterpret_cast<const byte*>(contents.Data());
   const byte* end = start + contents.ByteLength();
 
@@ -100,33 +99,8 @@
   if (result.val) delete result.val;
 }
 
-
-void CompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  HandleScope scope(args.GetIsolate());
-  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
-  ErrorThrower thrower(isolate, "WASM.compileRun()");
-
-  RawBuffer buffer = GetRawBufferArgument(thrower, args);
-  if (thrower.error()) return;
-
-  // Decode and pre-verify the functions before compiling and running.
-  i::Zone zone;
-  internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
-      isolate, &zone, buffer.start, buffer.end, true, false);
-
-  if (result.failed()) {
-    thrower.Failed("", result);
-  } else {
-    // Success. Compile and run!
-    int32_t retval = i::wasm::CompileAndRunWasmModule(isolate, result.val);
-    args.GetReturnValue().Set(retval);
-  }
-
-  if (result.val) delete result.val;
-}
-
-
-v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info) {
+v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
+    i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) {
   info->set_global();
   info->set_lazy(false);
   info->set_allow_lazy_parsing(false);
@@ -141,105 +115,34 @@
 
   v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
                                info->literal());
+  if (i::FLAG_enable_simd_asmjs) {
+    typer.set_allow_simd(true);
+  }
   if (!typer.Validate()) {
+    thrower->Error("Asm.js validation failed: %s", typer.error_message());
     return nullptr;
   }
 
   auto module = v8::internal::wasm::AsmWasmBuilder(
-                    info->isolate(), info->zone(), info->literal())
+                    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);
+    }
+  }
+
   return module;
 }
 
 
-void AsmCompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  HandleScope scope(args.GetIsolate());
+void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
+                             const byte* start, const byte* end,
+                             ErrorThrower* thrower, bool must_decode) {
   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
-  ErrorThrower thrower(isolate, "WASM.asmCompileRun()");
-
-  if (args.Length() != 1) {
-    thrower.Error("Invalid argument count");
-    return;
-  }
-  if (!args[0]->IsString()) {
-    thrower.Error("Invalid argument count");
-    return;
-  }
-
-  i::Factory* factory = isolate->factory();
-  i::Zone zone;
-  Local<String> source = Local<String>::Cast(args[0]);
-  i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
-  i::ParseInfo info(&zone, script);
-
-  auto module = TranslateAsmModule(&info);
-  if (module == nullptr) {
-    thrower.Error("Asm.js validation failed");
-    return;
-  }
-
-  int32_t result = v8::internal::wasm::CompileAndRunWasmModule(
-      isolate, module->Begin(), module->End(), true);
-  args.GetReturnValue().Set(result);
-}
-
-
-// TODO(aseemgarg): deal with arraybuffer and foreign functions
-void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  HandleScope scope(args.GetIsolate());
-  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
-  ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()");
-
-  if (args.Length() != 1) {
-    thrower.Error("Invalid argument count");
-    return;
-  }
-  if (!args[0]->IsString()) {
-    thrower.Error("Invalid argument count");
-    return;
-  }
-
-  i::Factory* factory = isolate->factory();
-  i::Zone zone;
-  Local<String> source = Local<String>::Cast(args[0]);
-  i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
-  i::ParseInfo info(&zone, script);
-
-  auto module = TranslateAsmModule(&info);
-  if (module == nullptr) {
-    thrower.Error("Asm.js validation failed");
-    return;
-  }
-
-  i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
-  internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
-      isolate, &zone, module->Begin(), module->End(), false, false);
-
-  if (result.failed()) {
-    thrower.Failed("", result);
-  } else {
-    // Success. Instantiate the module and return the object.
-    i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
-
-    i::MaybeHandle<i::JSObject> object =
-        result.val->Instantiate(isolate, ffi, memory);
-
-    if (!object.is_null()) {
-      args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
-    }
-  }
-
-  if (result.val) delete result.val;
-}
-
-
-void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
-  HandleScope scope(args.GetIsolate());
-  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
-  ErrorThrower thrower(isolate, "WASM.instantiateModule()");
-
-  RawBuffer buffer = GetRawBufferArgument(thrower, args);
-  if (buffer.start == nullptr) return;
 
   i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
   if (args.Length() > 2 && args[2]->IsArrayBuffer()) {
@@ -252,10 +155,12 @@
   // Verification will happen during compilation.
   i::Zone zone;
   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
-      isolate, &zone, buffer.start, buffer.end, false, false);
+      isolate, &zone, start, end, false, false);
 
-  if (result.failed()) {
-    thrower.Failed("", result);
+  if (result.failed() && must_decode) {
+    thrower->Error("Asm.js converted module failed to decode");
+  } else if (result.failed()) {
+    thrower->Failed("", result);
   } else {
     // Success. Instantiate the module and return the object.
     i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
@@ -274,6 +179,49 @@
 
   if (result.val) delete result.val;
 }
+
+
+void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  HandleScope scope(args.GetIsolate());
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
+  ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()");
+
+  if (!args[0]->IsString()) {
+    thrower.Error("Asm module text should be a string");
+    return;
+  }
+
+  i::Factory* factory = isolate->factory();
+  i::Zone zone;
+  Local<String> source = Local<String>::Cast(args[0]);
+  i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
+  i::ParseInfo info(&zone, script);
+
+  i::Handle<i::Object> foreign;
+  if (args.Length() > 1 && args[1]->IsObject()) {
+    Local<Object> local_foreign = Local<Object>::Cast(args[1]);
+    foreign = v8::Utils::OpenHandle(*local_foreign);
+  }
+
+  auto module = TranslateAsmModule(&info, foreign, &thrower);
+  if (module == nullptr) {
+    return;
+  }
+
+  InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, true);
+}
+
+
+void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  HandleScope scope(args.GetIsolate());
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
+  ErrorThrower thrower(isolate, "WASM.instantiateModule()");
+
+  RawBuffer buffer = GetRawBufferArgument(thrower, args);
+  if (buffer.start == nullptr) return;
+
+  InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, false);
+}
 }  // namespace
 
 
@@ -322,11 +270,9 @@
   JSObject::AddProperty(global, name, wasm_object, attributes);
 
   // Install functions on the WASM object.
-  InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule);
   InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule);
   InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction);
-  InstallFunc(isolate, wasm_object, "compileRun", CompileRun);
-  InstallFunc(isolate, wasm_object, "asmCompileRun", AsmCompileRun);
+  InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule);
   InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm",
               InstantiateModuleFromAsm);
 }
diff --git a/src/wasm/wasm-macro-gen.h b/src/wasm/wasm-macro-gen.h
index 470804a..dd653c1 100644
--- a/src/wasm/wasm-macro-gen.h
+++ b/src/wasm/wasm-macro-gen.h
@@ -22,10 +22,10 @@
 #define WASM_SELECT(cond, tval, fval) kExprSelect, cond, tval, fval
 #define WASM_BR(depth) kExprBr, static_cast<byte>(depth), kExprNop
 #define WASM_BR_IF(depth, cond) \
-  kExprBrIf, static_cast<byte>(depth), cond, kExprNop
+  kExprBrIf, static_cast<byte>(depth), kExprNop, cond
 #define WASM_BRV(depth, val) kExprBr, static_cast<byte>(depth), val
-#define WASM_BRV_IF(depth, cond, val) \
-  kExprBrIf, static_cast<byte>(depth), cond, val
+#define WASM_BRV_IF(depth, val, cond) \
+  kExprBrIf, static_cast<byte>(depth), val, cond
 #define WASM_BREAK(depth) kExprBr, static_cast<byte>(depth + 1), kExprNop
 #define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth), kExprNop
 #define WASM_BREAKV(depth, val) kExprBr, static_cast<byte>(depth + 1), val
@@ -104,9 +104,12 @@
       static_cast<byte>(offset), index, val
 #define WASM_CALL_FUNCTION(index, ...) \
   kExprCallFunction, static_cast<byte>(index), __VA_ARGS__
+#define WASM_CALL_IMPORT(index, ...) \
+  kExprCallImport, static_cast<byte>(index), __VA_ARGS__
 #define WASM_CALL_INDIRECT(index, func, ...) \
   kExprCallIndirect, static_cast<byte>(index), func, __VA_ARGS__
 #define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index)
+#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
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
index fd24280..02d197c 100644
--- a/src/wasm/wasm-module.cc
+++ b/src/wasm/wasm-module.cc
@@ -31,33 +31,32 @@
 
 
 std::ostream& operator<<(std::ostream& os, const WasmFunction& function) {
-  os << "WASM function with signature ";
+  os << "WASM function with signature " << *function.sig;
 
-  // TODO(titzer): factor out rendering of signatures.
-  if (function.sig->return_count() == 0) os << "v";
-  for (size_t i = 0; i < function.sig->return_count(); i++) {
-    os << WasmOpcodes::ShortNameOf(function.sig->GetReturn(i));
-  }
-  os << "_";
-  if (function.sig->parameter_count() == 0) os << "v";
-  for (size_t i = 0; i < function.sig->parameter_count(); i++) {
-    os << WasmOpcodes::ShortNameOf(function.sig->GetParam(i));
-  }
   os << " locals: ";
-  if (function.local_int32_count)
-    os << function.local_int32_count << " int32s ";
-  if (function.local_int64_count)
-    os << function.local_int64_count << " int64s ";
-  if (function.local_float32_count)
-    os << function.local_float32_count << " float32s ";
-  if (function.local_float64_count)
-    os << function.local_float64_count << " float64s ";
+  if (function.local_i32_count) os << function.local_i32_count << " i32s ";
+  if (function.local_i64_count) os << function.local_i64_count << " i64s ";
+  if (function.local_f32_count) os << function.local_f32_count << " f32s ";
+  if (function.local_f64_count) os << function.local_f64_count << " f64s ";
 
   os << " code bytes: "
      << (function.code_end_offset - function.code_start_offset);
   return os;
 }
 
+std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
+  os << "#" << pair.function_->func_index << ":";
+  if (pair.function_->name_offset > 0) {
+    if (pair.module_) {
+      os << pair.module_->GetName(pair.function_->name_offset);
+    } else {
+      os << "+" << pair.function_->func_index;
+    }
+  } else {
+    os << "?";
+  }
+  return os;
+}
 
 // A helper class for compiling multiple wasm functions that offers
 // placeholder code objects for calling functions that are not yet compiled.
@@ -193,35 +192,98 @@
   return fixed;
 }
 
-
-Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, int size,
+Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
                                      byte** backing_store) {
-  void* memory = isolate->array_buffer_allocator()->Allocate(size);
-  if (!memory) return Handle<JSArrayBuffer>::null();
+  if (size > (1 << WasmModule::kMaxMemSize)) {
+    // TODO(titzer): lift restriction on maximum memory allocated here.
+    *backing_store = nullptr;
+    return Handle<JSArrayBuffer>::null();
+  }
+  void* memory =
+      isolate->array_buffer_allocator()->Allocate(static_cast<int>(size));
+  if (!memory) {
+    *backing_store = nullptr;
+    return Handle<JSArrayBuffer>::null();
+  }
+
   *backing_store = reinterpret_cast<byte*>(memory);
 
 #if DEBUG
   // Double check the API allocator actually zero-initialized the memory.
-  for (int i = 0; i < size; i++) {
-    DCHECK_EQ(0, (*backing_store)[i]);
+  byte* bytes = reinterpret_cast<byte*>(*backing_store);
+  for (size_t i = 0; i < size; i++) {
+    DCHECK_EQ(0, bytes[i]);
   }
 #endif
 
   Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
-  JSArrayBuffer::Setup(buffer, isolate, false, memory, size);
+  JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size));
   buffer->set_is_neuterable(false);
   return buffer;
 }
+
+// Set the memory for a module instance to be the {memory} array buffer.
+void SetMemory(WasmModuleInstance* instance, Handle<JSArrayBuffer> memory) {
+  memory->set_is_neuterable(false);
+  instance->mem_start = reinterpret_cast<byte*>(memory->backing_store());
+  instance->mem_size = memory->byte_length()->Number();
+  instance->mem_buffer = memory;
+}
+
+// Allocate memory for a module instance as a new JSArrayBuffer.
+bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate,
+                    WasmModuleInstance* instance) {
+  DCHECK(instance->module);
+  DCHECK(instance->mem_buffer.is_null());
+
+  if (instance->module->min_mem_size_log2 > WasmModule::kMaxMemSize) {
+    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_buffer =
+      NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start);
+  if (!instance->mem_start) {
+    thrower->Error("Out of memory: wasm memory");
+    instance->mem_size = 0;
+    return false;
+  }
+  return true;
+}
+
+bool AllocateGlobals(ErrorThrower* thrower, Isolate* isolate,
+                     WasmModuleInstance* instance) {
+  instance->globals_size = AllocateGlobalsOffsets(instance->module->globals);
+
+  if (instance->globals_size > 0) {
+    instance->globals_buffer = NewArrayBuffer(isolate, instance->globals_size,
+                                              &instance->globals_start);
+    if (!instance->globals_start) {
+      // Not enough space for backing store of globals.
+      thrower->Error("Out of memory: wasm globals");
+      return false;
+    }
+  }
+  return true;
+}
 }  // namespace
 
-
 WasmModule::WasmModule()
-    : globals(nullptr),
+    : shared_isolate(nullptr),
+      module_start(nullptr),
+      module_end(nullptr),
+      min_mem_size_log2(0),
+      max_mem_size_log2(0),
+      mem_export(false),
+      mem_external(false),
+      start_function_index(-1),
+      globals(nullptr),
       signatures(nullptr),
       functions(nullptr),
       data_segments(nullptr),
-      function_table(nullptr) {}
-
+      function_table(nullptr),
+      import_table(nullptr) {}
 
 WasmModule::~WasmModule() {
   if (globals) delete globals;
@@ -229,8 +291,33 @@
   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> 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>();
+  }
+}
 
 // Instantiates a wasm module as a JSObject.
 //  * allocates a backing store of {mem_size} bytes.
@@ -242,95 +329,91 @@
                                               Handle<JSArrayBuffer> memory) {
   this->shared_isolate = isolate;  // TODO(titzer): have a real shared isolate.
   ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
-
   Factory* factory = isolate->factory();
-  // Memory is bigger than maximum supported size.
-  if (memory.is_null() && min_mem_size_log2 > kMaxMemSize) {
-    thrower.Error("Out of memory: wasm memory too large");
-    return MaybeHandle<JSObject>();
-  }
 
+  //-------------------------------------------------------------------------
+  // Allocate the instance and its JS counterpart.
+  //-------------------------------------------------------------------------
   Handle<Map> map = factory->NewMap(
       JS_OBJECT_TYPE,
       JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
-
-  //-------------------------------------------------------------------------
-  // Allocate the module object.
-  //-------------------------------------------------------------------------
-  Handle<JSObject> module = factory->NewJSObjectFromMap(map, TENURED);
+  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);
+  instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
 
   //-------------------------------------------------------------------------
-  // Allocate the linear memory.
+  // Allocate and initialize the linear memory.
   //-------------------------------------------------------------------------
-  uint32_t mem_size = 1 << min_mem_size_log2;
-  byte* mem_addr = nullptr;
-  Handle<JSArrayBuffer> mem_buffer;
-  if (!memory.is_null()) {
-    memory->set_is_neuterable(false);
-    mem_addr = reinterpret_cast<byte*>(memory->backing_store());
-    mem_size = memory->byte_length()->Number();
-    mem_buffer = memory;
-  } else {
-    mem_buffer = NewArrayBuffer(isolate, mem_size, &mem_addr);
-    if (!mem_addr) {
-      // Not enough space for backing store of memory
-      thrower.Error("Out of memory: wasm memory");
+  if (memory.is_null()) {
+    if (!AllocateMemory(&thrower, isolate, &instance)) {
       return MaybeHandle<JSObject>();
     }
+  } else {
+    SetMemory(&instance, memory);
   }
-
-  // Load initialized data segments.
-  LoadDataSegments(this, mem_addr, mem_size);
-
-  module->SetInternalField(kWasmMemArrayBuffer, *mem_buffer);
+  instance.js_object->SetInternalField(kWasmMemArrayBuffer,
+                                       *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(module, name, mem_buffer, READ_ONLY);
+    JSObject::AddProperty(instance.js_object, name, instance.mem_buffer,
+                          READ_ONLY);
   }
 
   //-------------------------------------------------------------------------
   // Allocate the globals area if necessary.
   //-------------------------------------------------------------------------
-  size_t globals_size = AllocateGlobalsOffsets(globals);
-  byte* globals_addr = nullptr;
-  if (globals_size > 0) {
-    Handle<JSArrayBuffer> globals_buffer =
-        NewArrayBuffer(isolate, mem_size, &globals_addr);
-    if (!globals_addr) {
-      // Not enough space for backing store of globals.
-      thrower.Error("Out of memory: wasm globals");
-      return MaybeHandle<JSObject>();
-    }
+  if (!AllocateGlobals(&thrower, isolate, &instance)) {
+    return MaybeHandle<JSObject>();
+  }
+  if (!instance.globals_buffer.is_null()) {
+    instance.js_object->SetInternalField(kWasmGlobalsArrayBuffer,
+                                         *instance.globals_buffer);
+  }
 
-    module->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer);
-  } else {
-    module->SetInternalField(kWasmGlobalsArrayBuffer, Smi::FromInt(0));
+  //-------------------------------------------------------------------------
+  // Compile wrappers to imported functions.
+  //-------------------------------------------------------------------------
+  uint32_t index = 0;
+  instance.function_table = BuildFunctionTable(isolate, this);
+  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;
+
+  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 (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);
+      index++;
+    }
   }
 
   //-------------------------------------------------------------------------
   // Compile all functions in the module.
   //-------------------------------------------------------------------------
-  int index = 0;
-  WasmLinker linker(isolate, functions->size());
-  ModuleEnv module_env;
-  module_env.module = this;
-  module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr);
-  module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr) + mem_size;
-  module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr);
-  module_env.linker = &linker;
-  module_env.function_code = nullptr;
-  module_env.function_table = BuildFunctionTable(isolate, this);
-  module_env.memory = memory;
-  module_env.context = isolate->native_context();
-  module_env.asm_js = false;
 
   // First pass: compile each function and initialize the code table.
+  index = 0;
   for (const WasmFunction& func : *functions) {
     if (thrower.error()) break;
+    DCHECK_EQ(index, func.func_index);
 
     const char* cstr = GetName(func.name_offset);
     Handle<String> name = factory->InternalizeUtf8String(cstr);
@@ -338,38 +421,21 @@
     Handle<JSFunction> function = Handle<JSFunction>::null();
     if (func.external) {
       // Lookup external function in FFI object.
-      if (!ffi.is_null()) {
-        MaybeHandle<Object> result = Object::GetProperty(ffi, name);
-        if (!result.is_null()) {
-          Handle<Object> obj = result.ToHandleChecked();
-          if (obj->IsJSFunction()) {
-            function = Handle<JSFunction>::cast(obj);
-            code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
-                                                    function, index);
-          } else {
-            thrower.Error("FFI function #%d:%s is not a JSFunction.", index,
-                          cstr);
-            return MaybeHandle<JSObject>();
-          }
-        } else {
-          thrower.Error("FFI function #%d:%s not found.", index, cstr);
-          return MaybeHandle<JSObject>();
-        }
-      } else {
-        thrower.Error("FFI table is not an object.");
-        return MaybeHandle<JSObject>();
-      }
+      MaybeHandle<JSFunction> function =
+          LookupFunction(thrower, ffi, index, name, cstr);
+      if (function.is_null()) return MaybeHandle<JSObject>();
+      code = compiler::CompileWasmToJSWrapper(
+          isolate, &module_env, function.ToHandleChecked(), func.sig, cstr);
     } else {
       // Compile the function.
-      code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func,
-                                           index);
+      code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
       if (code.is_null()) {
         thrower.Error("Compilation of #%d:%s failed.", index, cstr);
         return MaybeHandle<JSObject>();
       }
       if (func.exported) {
-        function = compiler::CompileJSToWasmWrapper(isolate, &module_env, name,
-                                                    code, module, index);
+        function = compiler::CompileJSToWasmWrapper(
+            isolate, &module_env, name, code, instance.js_object, index);
       }
     }
     if (!code.is_null()) {
@@ -379,27 +445,54 @@
     }
     if (func.exported) {
       // Exported functions are installed as read-only properties on the module.
-      JSObject::AddProperty(module, name, function, READ_ONLY);
+      JSObject::AddProperty(instance.js_object, name, function, READ_ONLY);
     }
     index++;
   }
 
   // Second pass: patch all direct call sites.
-  linker.Link(module_env.function_table, this->function_table);
+  linker.Link(instance.function_table, this->function_table);
+  instance.js_object->SetInternalField(kWasmModuleFunctionTable,
+                                       Smi::FromInt(0));
 
-  module->SetInternalField(kWasmModuleFunctionTable, Smi::FromInt(0));
-  module->SetInternalField(kWasmModuleCodeTable, *code_table);
-  return module;
+  // Run the start function if one was specified.
+  if (this->start_function_index >= 0) {
+    HandleScope scope(isolate);
+    uint32_t index = static_cast<uint32_t>(this->start_function_index);
+    Handle<String> name = isolate->factory()->NewStringFromStaticChars("start");
+    Handle<Code> code = linker.GetFunctionCode(index);
+    Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
+        isolate, &module_env, name, code, instance.js_object, index);
+
+    // Call the JS function.
+    Handle<Object> undefined(isolate->heap()->undefined_value(), isolate);
+    MaybeHandle<Object> retval =
+        Execution::Call(isolate, jsfunc, undefined, 0, nullptr);
+
+    if (retval.is_null()) {
+      thrower.Error("WASM.instantiateModule(): start function failed");
+    }
+  }
+  return instance.js_object;
 }
 
 
 Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
   DCHECK(IsValidFunction(index));
   if (linker) return linker->GetFunctionCode(index);
-  if (function_code) return function_code->at(index);
+  if (instance && instance->function_code) {
+    return instance->function_code->at(index);
+  }
   return 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();
+}
 
 compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
                                                        uint32_t index) {
@@ -436,43 +529,45 @@
 
 int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) {
   ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
+  WasmModuleInstance instance(module);
 
-  // Allocate temporary linear memory and globals.
-  size_t mem_size = 1 << module->min_mem_size_log2;
-  size_t globals_size = AllocateGlobalsOffsets(module->globals);
+  // Allocate and initialize the linear memory.
+  if (!AllocateMemory(&thrower, isolate, &instance)) {
+    return -1;
+  }
+  LoadDataSegments(module, instance.mem_start, instance.mem_size);
 
-  base::SmartArrayPointer<byte> mem_addr(new byte[mem_size]);
-  base::SmartArrayPointer<byte> globals_addr(new byte[globals_size]);
+  // Allocate the globals area if necessary.
+  if (!AllocateGlobals(&thrower, isolate, &instance)) {
+    return -1;
+  }
 
-  memset(mem_addr.get(), 0, mem_size);
-  memset(globals_addr.get(), 0, globals_size);
+  // Build the function table.
+  instance.function_table = BuildFunctionTable(isolate, module);
 
   // Create module environment.
   WasmLinker linker(isolate, module->functions->size());
   ModuleEnv module_env;
   module_env.module = module;
-  module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr.get());
-  module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr.get()) + mem_size;
-  module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr.get());
+  module_env.instance = &instance;
   module_env.linker = &linker;
-  module_env.function_code = nullptr;
-  module_env.function_table = BuildFunctionTable(isolate, module);
   module_env.asm_js = false;
 
-  // Load data segments.
-  // TODO(titzer): throw instead of crashing if segments don't fit in memory?
-  LoadDataSegments(module, mem_addr.get(), mem_size);
-
   // Compile all functions.
   Handle<Code> main_code = Handle<Code>::null();  // record last code.
-  int index = 0;
+  uint32_t index = 0;
+  int main_index = 0;
   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.
-      Handle<Code> code = compiler::CompileWasmFunction(
-          thrower, isolate, &module_env, func, index);
+      Handle<Code> code =
+          compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
       if (!code.is_null()) {
-        if (func.exported) main_code = code;
+        if (func.exported) {
+          main_code = code;
+          main_index = index;
+        }
         linker.Finish(index, code);
       }
       if (thrower.error()) return -1;
@@ -480,30 +575,37 @@
     index++;
   }
 
-  if (!main_code.is_null()) {
-    linker.Link(module_env.function_table, module->function_table);
-#if USE_SIMULATOR && V8_TARGET_ARCH_ARM64
-    // Run the main code on arm64 simulator.
-    Simulator* simulator = Simulator::current(isolate);
-    Simulator::CallArgument args[] = {Simulator::CallArgument(0),
-                                      Simulator::CallArgument::End()};
-    return static_cast<int32_t>(simulator->CallInt64(main_code->entry(), args));
-#elif USE_SIMULATOR
-    // Run the main code on simulator.
-    Simulator* simulator = Simulator::current(isolate);
-    return static_cast<int32_t>(
-        simulator->Call(main_code->entry(), 4, 0, 0, 0, 0));
-#else
-    // Run the main code as raw machine code.
-    int32_t (*raw_func)() = reinterpret_cast<int32_t (*)()>(
-        reinterpret_cast<uintptr_t>(main_code->entry()));
-    return raw_func();
-#endif
-  } else {
-    // No main code was found.
-    isolate->Throw(*isolate->factory()->NewStringFromStaticChars(
-        "WASM.compileRun() failed: no valid main code produced."));
+  if (main_code.is_null()) {
+    thrower.Error("WASM.compileRun() failed: no main code found");
+    return -1;
   }
+
+  linker.Link(instance.function_table, instance.module->function_table);
+
+  // Wrap the main code so it can be called as a JS function.
+  Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
+  Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
+  Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
+      isolate, &module_env, name, main_code, module_object, main_index);
+
+  // Call the JS function.
+  Handle<Object> undefined(isolate->heap()->undefined_value(), isolate);
+  MaybeHandle<Object> retval =
+      Execution::Call(isolate, jsfunc, undefined, 0, nullptr);
+
+  // The result should be a number.
+  if (retval.is_null()) {
+    thrower.Error("WASM.compileRun() failed: Invocation was null");
+    return -1;
+  }
+  Handle<Object> result = retval.ToHandleChecked();
+  if (result->IsSmi()) {
+    return Smi::cast(*result)->value();
+  }
+  if (result->IsHeapNumber()) {
+    return static_cast<int32_t>(HeapNumber::cast(*result)->value());
+  }
+  thrower.Error("WASM.compileRun() failed: Return value should be number");
   return -1;
 }
 }  // namespace wasm
diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h
index 5e2ba58..5f5777c 100644
--- a/src/wasm/wasm-module.h
+++ b/src/wasm/wasm-module.h
@@ -30,11 +30,13 @@
   kDeclGlobals = 0x03,
   kDeclDataSegments = 0x04,
   kDeclFunctionTable = 0x05,
-  kDeclWLL = 0x11,
   kDeclEnd = 0x06,
+  kDeclStartFunction = 0x07,
+  kDeclImportTable = 0x08,
+  kDeclWLL = 0x11,
 };
 
-static const int kMaxModuleSectionCode = 6;
+static const int kMaxModuleSectionCode = 0x11;
 
 enum WasmFunctionDeclBit {
   kDeclFunctionName = 0x01,
@@ -48,22 +50,29 @@
 static const size_t kDeclGlobalSize = 6;
 static const size_t kDeclDataSegmentSize = 13;
 
-// Static representation of a wasm function.
+// 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 name_offset;  // offset in the module bytes of the name, if any.
   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_int32_count;    // number of int32 local variables.
-  uint16_t local_int64_count;    // number of int64 local variables.
-  uint16_t local_float32_count;  // number of float32 local variables.
-  uint16_t local_float64_count;  // number of float64 local variables.
+  uint16_t local_i32_count;      // number of i32 local variables.
+  uint16_t local_i64_count;      // number of i64 local variables.
+  uint16_t local_f32_count;      // number of f32 local variables.
+  uint16_t local_f64_count;      // number of f64 local variables.
   bool exported;                 // true if this function is exported.
   bool external;  // true if this function is externally supplied.
 };
 
-struct ModuleEnv;  // forward declaration of decoder interface.
+// 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 module_name_offset;    // offset in module bytes of the module name.
+  uint32_t function_name_offset;  // offset in module bytes of the import name.
+};
 
 // Static representation of a wasm global variable.
 struct WasmGlobal {
@@ -93,25 +102,27 @@
   uint8_t max_mem_size_log2;  // maximum size of the memory (log base 2).
   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.
 
   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.
 
   WasmModule();
   ~WasmModule();
 
   // Get a pointer to a string stored in the module bytes representing a name.
-  const char* GetName(uint32_t offset) {
-    CHECK(BoundsCheck(offset, offset + 1));
+  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);
   }
 
   // Checks the given offset range is contained within the module bytes.
-  bool BoundsCheck(uint32_t start, uint32_t end) {
+  bool BoundsCheck(uint32_t start, uint32_t end) const {
     size_t size = module_end - module_start;
     return start < size && end < size;
   }
@@ -121,22 +132,42 @@
                                     Handle<JSArrayBuffer> memory);
 };
 
+// An instantiated WASM module, including memory, function table, etc.
+struct WasmModuleInstance {
+  WasmModule* module;  // static representation of the module.
+  // -- Heap allocated --------------------------------------------------------
+  Handle<JSObject> js_object;            // JavaScript module object.
+  Handle<Context> context;               // JavaScript native context.
+  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.
+  // -- raw memory ------------------------------------------------------------
+  byte* mem_start;  // start of linear memory.
+  size_t mem_size;  // size of the linear memory.
+  // -- raw globals -----------------------------------------------------------
+  byte* globals_start;  // start of the globals area.
+  size_t globals_size;  // size of the globals area.
+
+  explicit WasmModuleInstance(WasmModule* m)
+      : module(m),
+        function_code(nullptr),
+        mem_start(nullptr),
+        mem_size(0),
+        globals_start(nullptr),
+        globals_size(0) {}
+};
+
 // forward declaration.
 class WasmLinker;
 
 // Interface provided to the decoder/graph builder which contains only
 // minimal information about the globals, functions, and function tables.
 struct ModuleEnv {
-  uintptr_t globals_area;  // address of the globals area.
-  uintptr_t mem_start;     // address of the start of linear memory.
-  uintptr_t mem_end;       // address of the end of linear memory.
-
   WasmModule* module;
+  WasmModuleInstance* instance;
   WasmLinker* linker;
-  std::vector<Handle<Code>>* function_code;
-  Handle<FixedArray> function_table;
-  Handle<JSArrayBuffer> memory;
-  Handle<Context> context;
   bool asm_js;  // true if the module originated from asm.js.
 
   bool IsValidGlobal(uint32_t index) {
@@ -148,6 +179,9 @@
   bool IsValidSignature(uint32_t index) {
     return module && index < module->signatures->size();
   }
+  bool IsValidImport(uint32_t index) {
+    return module && index < module->import_table->size();
+  }
   MachineType GetGlobalType(uint32_t index) {
     DCHECK(IsValidGlobal(index));
     return module->globals->at(index).type;
@@ -156,23 +190,41 @@
     DCHECK(IsValidFunction(index));
     return module->functions->at(index).sig;
   }
+  FunctionSig* GetImportSignature(uint32_t index) {
+    DCHECK(IsValidImport(index));
+    return module->import_table->at(index).sig;
+  }
   FunctionSig* GetSignature(uint32_t index) {
     DCHECK(IsValidSignature(index));
     return module->signatures->at(index);
   }
   size_t FunctionTableSize() {
-    return module ? module->function_table->size() : 0;
+    return module && module->function_table ? module->function_table->size()
+                                            : 0;
   }
 
   Handle<Code> GetFunctionCode(uint32_t index);
+  Handle<Code> GetImportCode(uint32_t index);
   Handle<FixedArray> GetFunctionTable();
 
-  compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, FunctionSig* sig);
+  static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
+                                                         FunctionSig* sig);
+  static compiler::CallDescriptor* GetI32WasmCallDescriptor(
+      Zone* zone, compiler::CallDescriptor* descriptor);
   compiler::CallDescriptor* GetCallDescriptor(Zone* zone, uint32_t index);
 };
 
+// A helper for printing out the names of functions.
+struct WasmFunctionName {
+  const WasmFunction* function_;
+  const WasmModule* module_;
+  WasmFunctionName(const WasmFunction* function, const ModuleEnv* menv)
+      : function_(function), module_(menv ? menv->module : nullptr) {}
+};
+
 std::ostream& operator<<(std::ostream& os, const WasmModule& module);
 std::ostream& operator<<(std::ostream& os, const WasmFunction& function);
+std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
 
 typedef Result<WasmModule*> ModuleResult;
 typedef Result<WasmFunction*> FunctionResult;
@@ -185,6 +237,7 @@
 // For testing. Decode, verify, and run the last exported function in the
 // given decoded module.
 int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module);
+
 }  // namespace wasm
 }  // namespace internal
 }  // namespace v8
diff --git a/src/wasm/wasm-opcodes.cc b/src/wasm/wasm-opcodes.cc
index 25eef03..a609e03 100644
--- a/src/wasm/wasm-opcodes.cc
+++ b/src/wasm/wasm-opcodes.cc
@@ -25,6 +25,20 @@
 }
 
 
+std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
+  if (sig.return_count() == 0) os << "v";
+  for (size_t i = 0; i < sig.return_count(); i++) {
+    os << WasmOpcodes::ShortNameOf(sig.GetReturn(i));
+  }
+  os << "_";
+  if (sig.parameter_count() == 0) os << "v";
+  for (size_t i = 0; i < sig.parameter_count(); i++) {
+    os << WasmOpcodes::ShortNameOf(sig.GetParam(i));
+  }
+  return os;
+}
+
+
 #define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name,
 
 
diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h
index ae2843a..7cb9c00 100644
--- a/src/wasm/wasm-opcodes.h
+++ b/src/wasm/wasm-opcodes.h
@@ -66,6 +66,9 @@
 };
 
 typedef Signature<LocalType> FunctionSig;
+std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
+
+// TODO(titzer): Renumber all the opcodes to fill in holes.
 
 // Control expressions and blocks.
 #define FOREACH_CONTROL_OPCODE(V) \
@@ -80,7 +83,6 @@
   V(TableSwitch, 0x08, _)         \
   V(Return, 0x14, _)              \
   V(Unreachable, 0x15, _)
-// TODO(titzer): numbering
 
 // Constants, locals, globals, and calls.
 #define FOREACH_MISC_OPCODE(V) \
@@ -94,7 +96,8 @@
   V(LoadGlobal, 0x10, _)       \
   V(StoreGlobal, 0x11, _)      \
   V(CallFunction, 0x12, _)     \
-  V(CallIndirect, 0x13, _)
+  V(CallIndirect, 0x13, _)     \
+  V(CallImport, 0x1F, _)
 
 // Load memory expressions.
 #define FOREACH_LOAD_MEM_OPCODE(V) \
@@ -398,7 +401,6 @@
     }
   }
 
-  // TODO(titzer): remove this method
   static WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
     if (type == MachineType::Int8()) {
       return store ? kExprI32StoreMem8 : kExprI32LoadMem8S;