| Index: test/cctest/asmjs/test-asm-typer.cc
 | 
| diff --git a/test/cctest/asmjs/test-asm-typer.cc b/test/cctest/asmjs/test-asm-typer.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..2be20f189bf70b756c4d3a914470b13bd0f9a72a
 | 
| --- /dev/null
 | 
| +++ b/test/cctest/asmjs/test-asm-typer.cc
 | 
| @@ -0,0 +1,1766 @@
 | 
| +// Copyright 2016 the V8 project authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include <cstring>
 | 
| +#include <functional>
 | 
| +#include <iostream>
 | 
| +#include <memory>
 | 
| +
 | 
| +#include "src/asmjs/asm-typer.h"
 | 
| +#include "src/asmjs/asm-types.h"
 | 
| +#include "src/ast/ast-value-factory.h"
 | 
| +#include "src/ast/ast.h"
 | 
| +#include "src/ast/scopes.h"
 | 
| +#include "src/base/platform/platform.h"
 | 
| +#include "src/parsing/parser.h"
 | 
| +#include "src/v8.h"
 | 
| +#include "test/cctest/cctest.h"
 | 
| +
 | 
| +using namespace v8::internal;
 | 
| +namespace iw = v8::internal::wasm;
 | 
| +
 | 
| +namespace v8 {
 | 
| +namespace internal {
 | 
| +namespace wasm {
 | 
| +
 | 
| +namespace {
 | 
| +enum ValidationType {
 | 
| +  ValidateModule,
 | 
| +  ValidateGlobals,
 | 
| +  ValidateFunctionTables,
 | 
| +  ValidateExport,
 | 
| +  ValidateFunction,
 | 
| +  ValidateStatement,
 | 
| +  ValidateExpression,
 | 
| +};
 | 
| +}  // namespace
 | 
| +
 | 
| +class AsmTyperHarnessBuilder {
 | 
| + public:
 | 
| +  AsmTyperHarnessBuilder(const char* source, ValidationType type)
 | 
| +      : source_(source),
 | 
| +        validation_type_(type),
 | 
| +        handles_(),
 | 
| +        zone_(handles_.main_zone()),
 | 
| +        isolate_(CcTest::i_isolate()),
 | 
| +        ast_value_factory_(zone_, isolate_->heap()->HashSeed()),
 | 
| +        factory_(isolate_->factory()),
 | 
| +        source_code_(
 | 
| +            factory_->NewStringFromUtf8(CStrVector(source)).ToHandleChecked()),
 | 
| +        script_(factory_->NewScript(source_code_)) {
 | 
| +    ParseInfo info(zone_, script_);
 | 
| +    info.set_global();
 | 
| +    info.set_lazy(false);
 | 
| +    info.set_allow_lazy_parsing(false);
 | 
| +    info.set_toplevel(true);
 | 
| +    info.set_ast_value_factory(&ast_value_factory_);
 | 
| +    info.set_ast_value_factory_owned(false);
 | 
| +    Parser parser(&info);
 | 
| +
 | 
| +    if (!Compiler::ParseAndAnalyze(&info)) {
 | 
| +      std::cerr << "Failed to parse:\n" << source_ << "\n";
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +
 | 
| +    outer_scope_ = info.script_scope();
 | 
| +    module_ =
 | 
| +        info.scope()->declarations()->at(0)->AsFunctionDeclaration()->fun();
 | 
| +    typer_.reset(new AsmTyper(isolate_, zone_, *script_, module_));
 | 
| +
 | 
| +    if (validation_type_ == ValidateStatement ||
 | 
| +        validation_type_ == ValidateExpression) {
 | 
| +      fun_scope_.reset(new AsmTyper::FunctionScope(typer_.get()));
 | 
| +
 | 
| +      auto* decls = module_->scope()->declarations();
 | 
| +      for (int ii = 0; ii < decls->length(); ++ii) {
 | 
| +        Declaration* decl = decls->at(ii);
 | 
| +        if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) {
 | 
| +          fun_decl_ = fun_decl;
 | 
| +          break;
 | 
| +        }
 | 
| +      }
 | 
| +      CHECK_NOT_NULL(fun_decl_);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  struct VariableName {
 | 
| +    VariableName(const char* name, VariableMode mode)
 | 
| +        : name_(name), mode_(mode) {}
 | 
| +    VariableName(const VariableName&) = default;
 | 
| +    VariableName& operator=(const VariableName&) = default;
 | 
| +
 | 
| +    const char* name_;
 | 
| +    const VariableMode mode_;
 | 
| +  };
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithLocal(VariableName var_name, AsmType* type) {
 | 
| +    CHECK(validation_type_ == ValidateStatement ||
 | 
| +          validation_type_ == ValidateExpression);
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    auto* var_info = new (zone_) AsmTyper::VariableInfo(type);
 | 
| +    var_info->set_mutability(AsmTyper::VariableInfo::kLocal);
 | 
| +    CHECK(typer_->AddLocal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithGlobal(VariableName var_name, AsmType* type) {
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    auto* var_info = new (zone_) AsmTyper::VariableInfo(type);
 | 
| +    var_info->set_mutability(AsmTyper::VariableInfo::kMutableGlobal);
 | 
| +    CHECK(typer_->AddGlobal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithGlobal(
 | 
| +      VariableName var_name, std::function<AsmType*(Zone*)> type_creator) {
 | 
| +    return WithGlobal(var_name, type_creator(zone_));
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithUndefinedGlobal(
 | 
| +      VariableName var_name, std::function<AsmType*(Zone*)> type_creator) {
 | 
| +    auto* type = type_creator(zone_);
 | 
| +    CHECK(type->AsFunctionType() != nullptr ||
 | 
| +          type->AsFunctionTableType() != nullptr);
 | 
| +    WithGlobal(var_name, type);
 | 
| +    auto* var_info = typer_->Lookup(DeclareVariable(var_name));
 | 
| +    CHECK(var_info);
 | 
| +    var_info->FirstForwardUseIs(nullptr);
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithImport(VariableName var_name,
 | 
| +                                     AsmTyper::StandardMember standard_member) {
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    AsmTyper::VariableInfo* var_info = nullptr;
 | 
| +    auto* stdlib_map = &typer_->stdlib_math_types_;
 | 
| +    switch (standard_member) {
 | 
| +      case AsmTyper::kHeap:
 | 
| +      case AsmTyper::kStdlib:
 | 
| +      case AsmTyper::kModule:
 | 
| +      case AsmTyper::kNone:
 | 
| +        CHECK(false);
 | 
| +      case AsmTyper::kFFI:
 | 
| +        stdlib_map = nullptr;
 | 
| +        var_info = new (zone_) AsmTyper::VariableInfo(AsmType::FFIType(zone_));
 | 
| +        var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal);
 | 
| +        break;
 | 
| +      case AsmTyper::kInfinity:
 | 
| +      case AsmTyper::kNaN:
 | 
| +        stdlib_map = &typer_->stdlib_types_;
 | 
| +      default:
 | 
| +        break;
 | 
| +    }
 | 
| +
 | 
| +    if (var_info == nullptr) {
 | 
| +      for (auto iter : *stdlib_map) {
 | 
| +        if (iter.second->standard_member() == standard_member) {
 | 
| +          var_info = iter.second;
 | 
| +          break;
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      CHECK(var_info != nullptr);
 | 
| +      var_info = var_info->Clone(zone_);
 | 
| +    }
 | 
| +
 | 
| +    CHECK(typer_->AddGlobal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithReturnType(AsmType* type) {
 | 
| +    CHECK(type->IsReturnType());
 | 
| +    CHECK(typer_->return_type_ == AsmType::None());
 | 
| +    typer_->return_type_ = type;
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithStdlib(VariableName var_name) {
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    auto* var_info = new (zone_) AsmTyper::VariableInfo();
 | 
| +    var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal);
 | 
| +    var_info->set_standard_member(AsmTyper::kStdlib);
 | 
| +    CHECK(typer_->AddGlobal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithHeap(VariableName var_name) {
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    auto* var_info = new (zone_) AsmTyper::VariableInfo();
 | 
| +    var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal);
 | 
| +    var_info->set_standard_member(AsmTyper::kHeap);
 | 
| +    CHECK(typer_->AddGlobal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  AsmTyperHarnessBuilder* WithFFI(VariableName var_name) {
 | 
| +    auto* var = DeclareVariable(var_name);
 | 
| +    auto* var_info =
 | 
| +        new (zone_) AsmTyper::VariableInfo(AsmType::FFIType(zone_));
 | 
| +    var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal);
 | 
| +    var_info->set_standard_member(AsmTyper::kFFI);
 | 
| +    CHECK(typer_->AddGlobal(var, var_info));
 | 
| +    return this;
 | 
| +  }
 | 
| +
 | 
| +  bool Succeeds() {
 | 
| +    CHECK(validation_type_ == ValidateModule ||
 | 
| +          validation_type_ == ValidateGlobals ||
 | 
| +          validation_type_ == ValidateFunctionTables ||
 | 
| +          validation_type_ == ValidateExport ||
 | 
| +          validation_type_ == ValidateFunction ||
 | 
| +          validation_type_ == ValidateStatement);
 | 
| +
 | 
| +    if (validation_type_ == ValidateStatement) {
 | 
| +      CHECK(typer_->return_type_ != AsmType::None());
 | 
| +      if (ValidateAllStatements(fun_decl_)) {
 | 
| +        return true;
 | 
| +      }
 | 
| +    } else if (typer_->Validate()) {
 | 
| +      return true;
 | 
| +    }
 | 
| +
 | 
| +    std::cerr << "Asm validation failed: " << typer_->error_message() << "\n";
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  bool SucceedsWithExactType(AsmType* type) {
 | 
| +    CHECK(validation_type_ == ValidateExpression);
 | 
| +    auto* validated_as = ValidateExpressionStatment(fun_decl_);
 | 
| +    if (validated_as == AsmType::None()) {
 | 
| +      std::cerr << "Validation failure: " << typer_->error_message() << "\n";
 | 
| +      return false;
 | 
| +    } else if (validated_as != type) {
 | 
| +      std::cerr << "Validation succeeded with wrong type "
 | 
| +                << validated_as->Name() << " (vs. " << type->Name() << ").\n";
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  bool FailsWithMessage(const char* error_message) {
 | 
| +    CHECK(validation_type_ == ValidateModule ||
 | 
| +          validation_type_ == ValidateGlobals ||
 | 
| +          validation_type_ == ValidateFunctionTables ||
 | 
| +          validation_type_ == ValidateExport ||
 | 
| +          validation_type_ == ValidateFunction ||
 | 
| +          validation_type_ == ValidateStatement ||
 | 
| +          validation_type_ == ValidateExpression);
 | 
| +
 | 
| +    bool success;
 | 
| +    if (validation_type_ == ValidateStatement) {
 | 
| +      CHECK(typer_->return_type_ != AsmType::None());
 | 
| +      success = ValidateAllStatements(fun_decl_);
 | 
| +    } else if (validation_type_ == ValidateExpression) {
 | 
| +      success = ValidateExpressionStatment(fun_decl_) != AsmType::None();
 | 
| +    } else {
 | 
| +      success = typer_->Validate();
 | 
| +    }
 | 
| +
 | 
| +    if (success) {
 | 
| +      std::cerr << "Asm validation succeeded\n";
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    if (std::strstr(typer_->error_message(), error_message) == nullptr) {
 | 
| +      std::cerr << "Asm validation failed with the wrong error message:\n"
 | 
| +                   "Expected to contain '"
 | 
| +                << error_message << "'\n"
 | 
| +                                    "       Actually is  '"
 | 
| +                << typer_->error_message() << "'\n";
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  Variable* DeclareVariable(VariableName var_name) {
 | 
| +    auto* name_ast_string = ast_value_factory_.GetOneByteString(var_name.name_);
 | 
| +    return var_name.mode_ == DYNAMIC_GLOBAL
 | 
| +               ? outer_scope_->DeclareDynamicGlobal(name_ast_string)
 | 
| +               : module_->scope()->DeclareLocal(name_ast_string, VAR,
 | 
| +                                                kCreatedInitialized,
 | 
| +                                                Variable::NORMAL);
 | 
| +  }
 | 
| +
 | 
| +  bool ValidateAllStatements(FunctionDeclaration* fun_decl) {
 | 
| +    AsmTyper::FlattenedStatements iter(zone_, fun_decl->fun()->body());
 | 
| +    while (auto* curr = iter.Next()) {
 | 
| +      if (typer_->ValidateStatement(curr) == AsmType::None()) {
 | 
| +        return false;
 | 
| +      }
 | 
| +    }
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  AsmType* ValidateExpressionStatment(FunctionDeclaration* fun_decl) {
 | 
| +    AsmTyper::FlattenedStatements iter(zone_, fun_decl->fun()->body());
 | 
| +    AsmType* ret = AsmType::None();
 | 
| +    bool last_was_expression_statement = false;
 | 
| +    while (auto* curr = iter.Next()) {
 | 
| +      if (auto* expr_stmt = curr->AsExpressionStatement()) {
 | 
| +        last_was_expression_statement = true;
 | 
| +        if ((ret = typer_->ValidateExpression(expr_stmt->expression())) ==
 | 
| +            AsmType::None()) {
 | 
| +          break;
 | 
| +        }
 | 
| +      } else {
 | 
| +        ret = AsmType::None();
 | 
| +        last_was_expression_statement = true;
 | 
| +        if (typer_->ValidateStatement(curr) == AsmType::None()) {
 | 
| +          break;
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +    CHECK(last_was_expression_statement || ret == AsmType::None());
 | 
| +    return ret;
 | 
| +  }
 | 
| +
 | 
| +  std::string source_;
 | 
| +  ValidationType validation_type_;
 | 
| +  HandleAndZoneScope handles_;
 | 
| +  Zone* zone_;
 | 
| +  Isolate* isolate_;
 | 
| +  AstValueFactory ast_value_factory_;
 | 
| +  Factory* factory_;
 | 
| +  Handle<String> source_code_;
 | 
| +  Handle<Script> script_;
 | 
| +
 | 
| +  Scope* outer_scope_;
 | 
| +  FunctionLiteral* module_;
 | 
| +  FunctionDeclaration* fun_decl_;
 | 
| +  std::unique_ptr<AsmTyper> typer_;
 | 
| +  std::unique_ptr<AsmTyper::FunctionScope> fun_scope_;
 | 
| +};
 | 
| +
 | 
| +}  // namespace wasm
 | 
| +}  // namespace internal
 | 
| +}  // namespace v8
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +struct ValidationInput {
 | 
| +  ValidationInput(const std::string& source, iw::ValidationType type)
 | 
| +      : source_(source), type_(type) {}
 | 
| +
 | 
| +  const std::string source_;
 | 
| +  const iw::ValidationType type_;
 | 
| +};
 | 
| +
 | 
| +std::unique_ptr<iw::AsmTyperHarnessBuilder> ValidationOf(
 | 
| +    ValidationInput input) {
 | 
| +  return std::unique_ptr<iw::AsmTyperHarnessBuilder>(
 | 
| +      new iw::AsmTyperHarnessBuilder(input.source_.c_str(), input.type_));
 | 
| +}
 | 
| +
 | 
| +ValidationInput Module(const char* source) {
 | 
| +  return ValidationInput(source, iw::ValidateModule);
 | 
| +}
 | 
| +
 | 
| +std::string WrapInFunction(const char* source, bool needs_use_asm) {
 | 
| +  if (needs_use_asm) {
 | 
| +    return std::string(
 | 
| +               "function foo() {\n"
 | 
| +               "  'use asm';\n"
 | 
| +               "  ") +
 | 
| +           source +
 | 
| +           "\n"
 | 
| +           "}";
 | 
| +  }
 | 
| +
 | 
| +  return std::string(
 | 
| +             "function bar() {\n"
 | 
| +             "  ") +
 | 
| +         source +
 | 
| +         "\n"
 | 
| +         "}\n"
 | 
| +         "return {b: bar};\n";
 | 
| +}
 | 
| +
 | 
| +ValidationInput Globals(const char* source) {
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(WrapInFunction(source, kNeedsUseAsm),
 | 
| +                         iw::ValidateGlobals);
 | 
| +}
 | 
| +
 | 
| +ValidationInput FunctionTables(const char* source) {
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(WrapInFunction(source, kNeedsUseAsm),
 | 
| +                         iw::ValidateFunctionTables);
 | 
| +}
 | 
| +
 | 
| +ValidationInput Export(const char* source) {
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(WrapInFunction(source, kNeedsUseAsm),
 | 
| +                         iw::ValidateExport);
 | 
| +}
 | 
| +
 | 
| +ValidationInput Function(const char* source) {
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(WrapInFunction(source, kNeedsUseAsm),
 | 
| +                         iw::ValidateFunction);
 | 
| +}
 | 
| +
 | 
| +ValidationInput Statement(const char* source) {
 | 
| +  static const bool kDoesNotNeedUseAsm = false;
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(
 | 
| +      WrapInFunction(WrapInFunction(source, kDoesNotNeedUseAsm).c_str(),
 | 
| +                     kNeedsUseAsm),
 | 
| +      iw::ValidateStatement);
 | 
| +}
 | 
| +
 | 
| +ValidationInput Expression(const char* source) {
 | 
| +  static const bool kDoesNotNeedUseAsm = false;
 | 
| +  static const bool kNeedsUseAsm = true;
 | 
| +  return ValidationInput(
 | 
| +      WrapInFunction(WrapInFunction(source, kDoesNotNeedUseAsm).c_str(),
 | 
| +                     kNeedsUseAsm),
 | 
| +      iw::ValidateExpression);
 | 
| +}
 | 
| +
 | 
| +iw::AsmTyperHarnessBuilder::VariableName Var(const char* name) {
 | 
| +  return iw::AsmTyperHarnessBuilder::VariableName(name, VAR);
 | 
| +}
 | 
| +
 | 
| +iw::AsmTyperHarnessBuilder::VariableName DynamicGlobal(const char* name) {
 | 
| +  return iw::AsmTyperHarnessBuilder::VariableName(name, DYNAMIC_GLOBAL);
 | 
| +}
 | 
| +
 | 
| +TEST(MissingUseAsmDirective) {
 | 
| +  v8::V8::Initialize();
 | 
| +
 | 
| +  // We can't test the empty input ("") because the AsmTyperHarnessBuilder will
 | 
| +  // CHECK if there's no function in the top-level scope.
 | 
| +  const char* kTests[] = {"function module(){}",
 | 
| +                          "function module(){ use_asm; }",
 | 
| +                          "function module(){ \"use asm \"; }",
 | 
| +                          "function module(){ \" use asm \"; }",
 | 
| +                          "function module(){ \"use Asm\"; }"};
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const char* module = kTests[ii];
 | 
| +    if (!ValidationOf(Module(module))
 | 
| +             ->FailsWithMessage("Missing \"use asm\"")) {
 | 
| +      std::cerr << "Test:\n" << module;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(InvalidModuleSignature) {
 | 
| +  v8::V8::Initialize();
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* module;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"function eval(){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module name"},
 | 
| +      {"function arguments(){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module name"},
 | 
| +      {"function module(eval){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(arguments){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(stdlib, eval){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(stdlib, arguments){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, eval){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, arguments){ \"use asm\"; }",
 | 
| +       "Invalid asm.js identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, heap, eval){ \"use asm\"; }",
 | 
| +       "asm.js modules may not have more than three parameters"},
 | 
| +      {"function module(stdlib, foreign, heap, arguments){ \"use asm\"; }",
 | 
| +       "asm.js modules may not have more than three parameters"},
 | 
| +      {"function module(module){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +      {"function module(stdlib, module){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +      {"function module(stdlib, stdlib){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, module){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, stdlib){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +      {"function module(stdlib, foreign, foreign){ \"use asm\"; }",
 | 
| +       "Redeclared identifier in module parameter"},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Module(test->module))
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->module;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInGlobalVariableDefinition) {
 | 
| +  const struct {
 | 
| +    const char* decl;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"var v;", "Global variable missing initializer"},
 | 
| +      {"var v = uninitialized;", "Invalid global variable initializer"},
 | 
| +      {"var v = 'use asm';", "type annotation - forbidden literal"},
 | 
| +      {"var v = 4294967296;", " - forbidden literal"},
 | 
| +      {"var v = not_fround;", "Invalid global variable initializer"},
 | 
| +      {"var v = not_fround(1);", "expected call fround(literal)"},
 | 
| +      {"var v = __fround__(1.0);", "expected call fround(literal)"},
 | 
| +      {"var v = fround(1.0, 1.0);", "expected call fround(literal)"},
 | 
| +      {"var v = fround(not_fround);", "literal argument for call to fround"},
 | 
| +      {"var v = fround(1);", "literal argument to be a floating point"},
 | 
| +      {"var v = stdlib.nan", "Invalid import"},
 | 
| +      {"var v = stdlib.Math.nan", "Invalid import"},
 | 
| +      {"var v = stdlib.Mathh.E", "Invalid import"},
 | 
| +      {"var v = stdlib.Math", "Invalid import"},
 | 
| +      {"var v = Stdlib.Math.E", "Invalid import"},
 | 
| +      {"var v = stdlib.Math.E[0]", "Invalid import"},
 | 
| +      {"var v = stdlibb.NaN", "Invalid import"},
 | 
| +      {"var v = ffi.NaN[0]", "Invalid import"},
 | 
| +      {"var v = heap.NaN[0]", "Invalid import"},
 | 
| +      {"var v = ffi.foo * 2.0;", "unrecognized annotation"},
 | 
| +      {"var v = ffi.foo|1;", "unrecognized annotation"},
 | 
| +      {"var v = ffi()|0;", "must import member"},
 | 
| +      {"var v = +ffi();", "must import member"},
 | 
| +      {"var v = ffi().a|0;", "object lookup failed"},
 | 
| +      {"var v = +ffi().a;", "object lookup failed"},
 | 
| +      {"var v = sstdlib.a|0;", "object lookup failed"},
 | 
| +      {"var v = +sstdlib.a;", "object lookup failed"},
 | 
| +      {"var v = stdlib.NaN|0;", "object is not the ffi"},
 | 
| +      {"var v = +stdlib.NaN;", "object is not the ffi"},
 | 
| +      {"var v = new f()", "Invalid type after new"},
 | 
| +      {"var v = new stdli.Uint8Array(heap)", "Unknown stdlib member in heap"},
 | 
| +      {"var v = new stdlib.dd(heap)", "Unknown stdlib member in heap"},
 | 
| +      {"var v = new stdlib.Math.fround(heap)", "Type is not a heap view type"},
 | 
| +      {"var v = new stdlib.Uint8Array(a, b)", "Invalid number of arguments"},
 | 
| +      {"var v = new stdlib.Uint8Array(heap())", "should be the module's heap"},
 | 
| +      {"var v = new stdlib.Uint8Array(heap_)", "instead of heap parameter"},
 | 
| +      {"var v = new stdlib.Uint8Array(ffi)", "should be the module's heap"},
 | 
| +      {"var eval = 0;", "in global variable"},
 | 
| +      {"var eval = 0.0;", "in global variable"},
 | 
| +      {"var eval = fround(0.0);", "in global variable"},
 | 
| +      {"var eval = +ffi.a;", "in global variable"},
 | 
| +      {"var eval = ffi.a|0;", "in global variable"},
 | 
| +      {"var eval = ffi.a;", "in global variable"},
 | 
| +      {"var eval = new stdlib.Uint8Array(heap);", "in global variable"},
 | 
| +      {"var arguments = 0;", "in global variable"},
 | 
| +      {"var arguments = 0.0;", "in global variable"},
 | 
| +      {"var arguments = fround(0.0);", "in global variable"},
 | 
| +      {"var arguments = +ffi.a;", "in global variable"},
 | 
| +      {"var arguments = ffi.a|0;", "in global variable"},
 | 
| +      {"var arguments = ffi.a;", "in global variable"},
 | 
| +      {"var arguments = new stdlib.Uint8Array(heap);", "in global variable"},
 | 
| +      {"var a = 0, a = 0.0;", "Redefined global variable"},
 | 
| +      {"var a = 0; var a = 0;", "Redefined global variable"},
 | 
| +      {"var a = 0, b = 0; var a = 0;", "Redefined global variable"},
 | 
| +      {"var a = 0, b = 0; var b = 0, a = 0.0;", "Redefined global variable"},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Globals(test->decl))
 | 
| +             ->WithStdlib(DynamicGlobal("stdlib"))
 | 
| +             ->WithFFI(DynamicGlobal("ffi"))
 | 
| +             ->WithHeap(DynamicGlobal("heap"))
 | 
| +             ->WithGlobal(DynamicGlobal("not_fround"), iw::AsmType::Int())
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->decl;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInFunctionTableDefinition) {
 | 
| +  const struct {
 | 
| +    const char* tables;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"var a = [a, a, a];", "Invalid length for function pointer table"},
 | 
| +      {"var a = [d2s0()];", "must be a function name"},
 | 
| +      {"var a = [d2s44];", "Undefined identifier in function pointer"},
 | 
| +      {"var a = [fround];", "not be a member of the standard library"},
 | 
| +      {"var a = [imul];", "not be a member of the standard library"},
 | 
| +      {"var a = [ffi_import];", "must be an asm.js function"},
 | 
| +      {"var a = [dI];", "must be an asm.js function"},
 | 
| +      {"var a = [d2s0, d2s1, d2s0, f2s0];", "mismatch in function pointer"},
 | 
| +      {"var eval = [d2s0, d2s1];", "asm.js identifier in function table name"},
 | 
| +      {"var arguments = [d2s0, d2s1];", "asm.js identifier in function table"},
 | 
| +      {"var foo = [d2s0, d2s1];",
 | 
| +       "Identifier redefined as function pointer table"},
 | 
| +      {"var I = [d2s0, d2s1];",
 | 
| +       "Identifier redefined as function pointer table"},
 | 
| +      {"var d2s = [d2f0, d2f1];", "redefined as function pointer table"},
 | 
| +      {"var d2s_t = [d2s0];", "Function table size mismatch"},
 | 
| +      {"var d2s_t = [d2f0, d2f1];", "initializer does not match previous"},
 | 
| +  };
 | 
| +
 | 
| +  auto d2s = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  auto d2s_tbl = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* d2s = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    d2s->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +
 | 
| +    auto* ret = iw::AsmType::FunctionTableType(zone, 2, d2s);
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  auto f2s = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Float());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  auto d2f = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Float());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(FunctionTables(test->tables))
 | 
| +             ->WithImport(DynamicGlobal("ffi_import"), iw::AsmTyper::kFFI)
 | 
| +             ->WithImport(DynamicGlobal("imul"), iw::AsmTyper::kMathImul)
 | 
| +             ->WithImport(DynamicGlobal("E"), iw::AsmTyper::kMathE)
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithImport(DynamicGlobal("floor"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithGlobal(DynamicGlobal("d2s0"), d2s)
 | 
| +             ->WithGlobal(DynamicGlobal("d2s1"), d2s)
 | 
| +             ->WithGlobal(DynamicGlobal("f2s0"), f2s)
 | 
| +             ->WithGlobal(DynamicGlobal("f2s1"), f2s)
 | 
| +             ->WithGlobal(DynamicGlobal("d2f0"), d2f)
 | 
| +             ->WithGlobal(DynamicGlobal("d2f1"), d2f)
 | 
| +             ->WithGlobal(DynamicGlobal("dI"), iw::AsmType::Int())
 | 
| +             ->WithGlobal(Var("I"), iw::AsmType::Int())
 | 
| +             ->WithUndefinedGlobal(Var("d2s"), d2s)
 | 
| +             ->WithUndefinedGlobal(Var("d2s_t"), d2s_tbl)
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->tables;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInModuleExport) {
 | 
| +  const struct {
 | 
| +    const char* module_export;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"", "Missing asm.js module export"},
 | 
| +      {"return;", "Unrecognized expression in asm.js module export expression"},
 | 
| +      {"return f;", "Undefined identifier in asm.js module export"},
 | 
| +      {"return f();", "Unrecognized expression in asm.js module export"},
 | 
| +      {"return d2s_tbl;", "cannot export function tables"},
 | 
| +      {"return min;", "cannot export standard library functions"},
 | 
| +      {"return ffi;", "cannot export foreign functions"},
 | 
| +      {"return I;", "is not an asm.js function"},
 | 
| +      {"return {'a': d2s_tbl}", "cannot export function tables"},
 | 
| +      {"return {'a': min}", "cannot export standard library functions"},
 | 
| +      {"return {'a': ffi}", "cannot export foreign functions"},
 | 
| +      {"return {'a': f()}", "must be an asm.js function name"},
 | 
| +      {"return {'a': f}", "Undefined identifier in asm.js module export"},
 | 
| +      {"function v() { a(); } return {b: d2s}", "Missing definition for forw"},
 | 
| +      {"return {b: d2s, 'a': d2s_tbl}", "cannot export function tables"},
 | 
| +      {"return {b: d2s, 'a': min}", "cannot export standard library"},
 | 
| +      {"return {b: d2s, 'a': ffi}", "cannot export foreign functions"},
 | 
| +      {"return {b: d2s, 'a': f()}", "must be an asm.js function name"},
 | 
| +      {"return {b: d2s, 'a': f}", "Undefined identifier in asm.js module"},
 | 
| +  };
 | 
| +
 | 
| +  auto d2s_tbl = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* d2s = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    d2s->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +
 | 
| +    auto* ret = iw::AsmType::FunctionTableType(zone, 2, d2s);
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  auto d2s = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Export(test->module_export))
 | 
| +             ->WithGlobal(DynamicGlobal("d2s_tbl"), d2s_tbl)
 | 
| +             ->WithGlobal(DynamicGlobal("d2s"), d2s)
 | 
| +             ->WithImport(DynamicGlobal("min"), iw::AsmTyper::kMathMin)
 | 
| +             ->WithImport(DynamicGlobal("ffi"), iw::AsmTyper::kFFI)
 | 
| +             ->WithGlobal(DynamicGlobal("I"), iw::AsmType::Int())
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->module_export;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInFunction) {
 | 
| +  auto d2s = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* function;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"function f(eval) {"
 | 
| +       "  eval = eval|0;"
 | 
| +       "}\n",
 | 
| +       "Invalid asm.js identifier in parameter name"},
 | 
| +      {"function f(arguments) {"
 | 
| +       "  arguments = arguments|0;"
 | 
| +       "}\n",
 | 
| +       "Invalid asm.js identifier in parameter name"},
 | 
| +      // The following error should actually be a "redeclared local," but the
 | 
| +      // AST "hides" the first parameter from us, so the parameter type checking
 | 
| +      // will fail because the validator will think that the a = a|0 is
 | 
| +      // annotating the second parameter.
 | 
| +      {"function f(a, a) {\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  a = +a;\n"
 | 
| +       "}\n",
 | 
| +       "Incorrect parameter type annotations"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  if (0) return;\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "}\n",
 | 
| +       "Incorrect parameter type annotations"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  f();\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "}\n",
 | 
| +       "Incorrect parameter type annotations"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  f.a = 0;\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "}\n",
 | 
| +       "Incorrect parameter type annotations"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "}\n",
 | 
| +       "Incorrect parameter type annotations"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  var eval = 0;\n"
 | 
| +       "}\n",
 | 
| +       "Invalid asm.js identifier in local variable"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  var b = 0;\n"
 | 
| +       "}\n",
 | 
| +       "Redeclared local"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  var c = 0, c = 1.0;\n"
 | 
| +       "}\n",
 | 
| +       "Redeclared local"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  var c = 0; var c = 1.0;\n"
 | 
| +       "}\n",
 | 
| +       "Redeclared local"},
 | 
| +      {"function f(b, a) {\n"
 | 
| +       "  b = +b;\n"
 | 
| +       "  a = a|0;\n"
 | 
| +       "  f();\n"
 | 
| +       "  var c = 0;\n"
 | 
| +       "}\n",
 | 
| +       "Local variable missing initializer in asm.js module"},
 | 
| +      {"function f() {\n"
 | 
| +       "  function ff() {}\n"
 | 
| +       "}\n",
 | 
| +       "Functions may only define inner variables"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return a+1;\n"
 | 
| +       "}\n",
 | 
| +       "Invalid return type annotation"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return ~~x;\n"
 | 
| +       "}\n",
 | 
| +       "Invalid return type annotation"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return d();\n"
 | 
| +       "}\n",
 | 
| +       "Invalid function call in return statement"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return 'use asm';\n"
 | 
| +       "}\n",
 | 
| +       "Invalid literal in return statement"},
 | 
| +      {"function f() {\n"
 | 
| +       " return 2147483648;\n"
 | 
| +       "}\n",
 | 
| +       "Invalid literal in return statement"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return stdlib.Math.E;"
 | 
| +       "}\n",
 | 
| +       "Invalid return type expression"},
 | 
| +      {"function f() {\n"
 | 
| +       "  return E[0];"
 | 
| +       "}\n",
 | 
| +       "Invalid return type expression"},
 | 
| +      {"function I() {}\n", "Identifier redefined as function"},
 | 
| +      {"function foo() {}\n", "Identifier redefined as function"},
 | 
| +      {"function d2s() {}\n", "Identifier redefined (function name)"},
 | 
| +      {"function d2s(x) {\n"
 | 
| +       "  x = x|0;\n"
 | 
| +       "  return -1;\n"
 | 
| +       "}\n",
 | 
| +       "Identifier redefined (function name)"},
 | 
| +      {"function d2s(x) {\n"
 | 
| +       "  x = +x;\n"
 | 
| +       "  return -1.0;\n"
 | 
| +       "}\n",
 | 
| +       "Identifier redefined (function name)"},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Function(test->function))
 | 
| +             ->WithGlobal(Var("I"), iw::AsmType::Int())
 | 
| +             ->WithGlobal(Var("d2s"), d2s)
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->function;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInStatement) {
 | 
| +  const struct {
 | 
| +    const char* statement;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"if (fround(1));", "If condition must be type int"},
 | 
| +      {"return;", "Type mismatch in return statement"},
 | 
| +      {"return +1.0;", "Type mismatch in return statement"},
 | 
| +      {"return +d()", "Type mismatch in return statement"},
 | 
| +      {"while (fround(1));", "While condition must be type int"},
 | 
| +      {"do {} while (fround(1));", "Do {} While condition must be type int"},
 | 
| +      {"for (;fround(1););", "For condition must be type int"},
 | 
| +      {"switch(flocal){ case 0: return 0; }", "Switch tag must be signed"},
 | 
| +      {"switch(slocal){ case 1: case 1: return 0; }", "Duplicated case label"},
 | 
| +      {"switch(slocal){ case 1: case 0: break; case 1: return 0; }",
 | 
| +       "Duplicated case label"},
 | 
| +      {"switch(slocal){ case 1.0: return 0; }",
 | 
| +       "Case label must be a 32-bit signed integer"},
 | 
| +      {"switch(slocal){ case 1.0: return 0; }",
 | 
| +       "Case label must be a 32-bit signed integer"},
 | 
| +      {"switch(slocal){ case -100000: case 2147483647: return 0; }",
 | 
| +       "Out-of-bounds case"},
 | 
| +      {"switch(slocal){ case 2147483648: return 0; }",
 | 
| +       "Case label must be a 32-bit signed"},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Statement(test->statement))
 | 
| +             ->WithReturnType(iw::AsmType::Signed())
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithLocal(DynamicGlobal("flocal"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("slocal"), iw::AsmType::Signed())
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->statement;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ErrorsInExpression) {
 | 
| +  auto d2d = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Double());
 | 
| +    ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  auto d2s_tbl = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* d2s = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    d2s->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +
 | 
| +    auto* ret = iw::AsmType::FunctionTableType(zone, 2, d2s);
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    const char* error_message;
 | 
| +  } kTests[] = {
 | 
| +      {"noy_a_function();", "Unanotated call to a function must be a call to"},
 | 
| +      {"a = 0;", "Undeclared identifier"},
 | 
| +      {"ilocal = +1.0", "Type mismatch in assignment"},
 | 
| +      {"!dlocal", "Invalid type for !"},
 | 
| +      {"2 * dlocal", "Invalid types for intish *"},
 | 
| +      {"dlocal * 2", "Invalid types for intish *"},
 | 
| +      {"1048577 * ilocal", "Invalid operands for *"},
 | 
| +      {"1048577 / ilocal", "Invalid operands for /"},
 | 
| +      {"1048577 % dlocal", "Invalid operands for %"},
 | 
| +      {"1048577 * dlocal", "Invalid operands for *"},
 | 
| +      {"1048577 / dlocal", "Invalid operands for /"},
 | 
| +      {"1048577 % ilocal", "Invalid operands for %"},
 | 
| +      {"ilocal * dlocal", "Invalid operands for *"},
 | 
| +      {"ilocal / dlocal", "Invalid operands for /"},
 | 
| +      {"ilocal % dlocal", "Invalid operands for %"},
 | 
| +      {"1048577 + dlocal", "Invalid operands for additive expression"},
 | 
| +      {"1048577 - dlocal", "Invalid operands for additive expression"},
 | 
| +      {"ilocal + dlocal", "Invalid operands for additive expression"},
 | 
| +      {"ilocal - dlocal", "Invalid operands for additive expression"},
 | 
| +      {"1048577 << dlocal", "Invalid operands for <<"},
 | 
| +      {"1048577 >> dlocal", "Invalid operands for >>"},
 | 
| +      {"1048577 >>> dlocal", "Invalid operands for >>"},
 | 
| +      {"ilocal << dlocal", "Invalid operands for <<"},
 | 
| +      {"ilocal >> dlocal", "Invalid operands for >>"},
 | 
| +      {"ilocal >>> dlocal", "Invalid operands for >>>"},
 | 
| +      {"1048577 < dlocal", "Invalid operands for <"},
 | 
| +      {"ilocal < dlocal", "Invalid operands for <"},
 | 
| +      {"1048577 > dlocal", "Invalid operands for >"},
 | 
| +      {"ilocal > dlocal", "Invalid operands for >"},
 | 
| +      {"1048577 <= dlocal", "Invalid operands for <="},
 | 
| +      {"ilocal <= dlocal", "Invalid operands for <="},
 | 
| +      {"1048577 >= dlocal", "Invalid operands for >="},
 | 
| +      {"ilocal >= dlocal", "Invalid operands for >="},
 | 
| +      {"1048577 == dlocal", "Invalid operands for =="},
 | 
| +      {"ilocal == dlocal", "Invalid operands for =="},
 | 
| +      /* NOTE: the parser converts a == b to !(a == b). */
 | 
| +      {"1048577 != dlocal", "Invalid operands for =="},
 | 
| +      {"ilocal != dlocal", "Invalid operands for =="},
 | 
| +      {"dlocal & dlocal", "Invalid operands for &"},
 | 
| +      {"1048577 & dlocal", "Invalid operands for &"},
 | 
| +      {"ilocal & dlocal", "Invalid operands for &"},
 | 
| +      {"dlocal | dlocal2", "Invalid operands for |"},
 | 
| +      {"1048577 | dlocal", "Invalid operands for |"},
 | 
| +      {"ilocal | dlocal", "Invalid operands for |"},
 | 
| +      {"dlocal ^ dlocal2", "Invalid operands for ^"},
 | 
| +      {"1048577 ^ dlocal", "Invalid operands for ^"},
 | 
| +      {"ilocal ^ dlocal", "Invalid operands for ^"},
 | 
| +      {"dlocal ? 0 : 1", "Ternary operation condition should be int"},
 | 
| +      {"ilocal ? dlocal : 1", "Type mismatch for ternary operation result"},
 | 
| +      {"ilocal ? 1 : dlocal", "Type mismatch for ternary operation result"},
 | 
| +      {"eval(10)|0", "Invalid asm.js identifier in (forward) function"},
 | 
| +      {"arguments(10)|0", "Invalid asm.js identifier in (forward) function"},
 | 
| +      {"not_a_function(10)|0", "Calling something that's not a function"},
 | 
| +      {"fround(FFI())", "Foreign functions can't return float"},
 | 
| +      {"FFI(fround(0))|0", "Function invocation does not match function type"},
 | 
| +      {"FFI(2147483648)|0", "Function invocation does not match function type"},
 | 
| +      {"d2d(2.0)|0", "Function invocation does not match function type"},
 | 
| +      {"+d2d(2)", "Function invocation does not match function type"},
 | 
| +      {"eval[ilocal & 3]()|0", "Invalid asm.js identifier in (forward)"},
 | 
| +      {"arguments[ilocal & 3]()|0", "Invalid asm.js identifier in (forward)"},
 | 
| +      {"not_a_function[ilocal & 3]()|0", "Identifier does not name a function"},
 | 
| +      {"d2s_tbl[ilocal & 3](0.0)|0", "Function table size does not match"},
 | 
| +      {"+d2s_tbl[ilocal & 1](0.0)", "does not match previous signature"},
 | 
| +      {"d2s_tbl[ilocal & 1](0)|0", "does not match previous signature"},
 | 
| +      {"a.b()|0", "Indirect call index must be in the expr & mask form"},
 | 
| +      {"HEAP32[0][0] = 0", "Invalid heap access"},
 | 
| +      {"heap32[0] = 0", "Undeclared identifier in heap access"},
 | 
| +      {"not_a_function[0] = 0", "Identifier does not represent a heap view"},
 | 
| +      {"HEAP32[0.0] = 0", "Heap access index must be intish"},
 | 
| +      {"HEAP32[-1] = 0", "Heap access index must be a 32-bit unsigned integer"},
 | 
| +      {"HEAP32[ilocal >> 1] = 0", "Invalid heap access index"},
 | 
| +      // *VIOLATION* the following is invalid, but because of desugaring it is
 | 
| +      // accepted.
 | 
| +      // {"HEAP32[0 >> 1] = 0", "Invalid heap access index"},
 | 
| +      {"HEAP8[fround(0.0)] = 0", "Invalid heap access index for byte array"},
 | 
| +      {"HEAP8[iish] = 0", "Invalid heap access index for byte array"},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->WithLocal(DynamicGlobal("dlocal"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("dlocal2"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("not_a_function"), iw::AsmType::Int())
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithImport(DynamicGlobal("FFI"), iw::AsmTyper::kFFI)
 | 
| +             ->WithGlobal(DynamicGlobal("d2d"), d2d)
 | 
| +             ->WithGlobal(DynamicGlobal("d2s_tbl"), d2s_tbl)
 | 
| +             ->WithGlobal(DynamicGlobal("HEAP32"), iw::AsmType::Int32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("HEAP8"), iw::AsmType::Int8Array())
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateNumericLiteral) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* expected_type;
 | 
| +  } kTests[] = {
 | 
| +      {"0", iw::AsmType::FixNum()},
 | 
| +      {"-1", iw::AsmType::Signed()},
 | 
| +      {"2147483648", iw::AsmType::Unsigned()},
 | 
| +      {"0.0", iw::AsmType::Double()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->SucceedsWithExactType(test->expected_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateIdentifier) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* expected_type;
 | 
| +  } kTests[] = {{"afixnum", iw::AsmType::FixNum()},
 | 
| +                {"adouble", iw::AsmType::Double()},
 | 
| +                {"afloat", iw::AsmType::Float()},
 | 
| +                {"anextern", iw::AsmType::Extern()},
 | 
| +                {"avoid", iw::AsmType::Void()}};
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal(test->expression), test->expected_type)
 | 
| +             ->WithGlobal(DynamicGlobal(test->expression),
 | 
| +                          iw::AsmType::Floatish())
 | 
| +             ->SucceedsWithExactType(test->expected_type)) {
 | 
| +      std::cerr << "Test (local identifiers):\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithGlobal(DynamicGlobal(test->expression), test->expected_type)
 | 
| +             ->SucceedsWithExactType(test->expected_type)) {
 | 
| +      std::cerr << "Test (global identifiers):\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateCallExpression) {
 | 
| +  auto v2f = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Float());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +  } kTests[] = {
 | 
| +      {"a_float_function()"},
 | 
| +      {"fround(0)"},
 | 
| +      {"slocal"},
 | 
| +      {"ulocal"},
 | 
| +      {"dqlocal"},
 | 
| +      {"fishlocal"},
 | 
| +  };
 | 
| +
 | 
| +  char full_test[200];
 | 
| +  static const size_t kFullTestSize = arraysize(full_test);
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    CHECK(v8::base::OS::SNPrintF(full_test, kFullTestSize, "fround(%s)",
 | 
| +                                 test->expression) < kFullTestSize);
 | 
| +    if (!ValidationOf(Expression(full_test))
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithGlobal(DynamicGlobal("a_float_function"), v2f)
 | 
| +             ->WithLocal(DynamicGlobal("slocal"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("ulocal"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("dqlocal"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("fishlocal"), iw::AsmType::Floatish())
 | 
| +             ->SucceedsWithExactType(iw::AsmType::Float())) {
 | 
| +      std::cerr << "Test:\n" << full_test;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    const char* error_message;
 | 
| +  } kFailureTests[] = {
 | 
| +      {"vlocal", "Invalid argument type to fround"},
 | 
| +      {"ilocal", "Invalid argument type to fround"},
 | 
| +      {"a_double_function()", "Function invocation does not match"},
 | 
| +  };
 | 
| +
 | 
| +  auto v2d = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kFailureTests); ++ii) {
 | 
| +    const auto* test = kFailureTests + ii;
 | 
| +    CHECK(v8::base::OS::SNPrintF(full_test, kFullTestSize, "fround(%s)",
 | 
| +                                 test->expression) < kFullTestSize);
 | 
| +    if (!ValidationOf(Expression(full_test))
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->WithLocal(DynamicGlobal("vlocal"), iw::AsmType::Void())
 | 
| +             ->WithGlobal(DynamicGlobal("a_double_function"), v2d)
 | 
| +             ->FailsWithMessage(test->error_message)) {
 | 
| +      std::cerr << "Test:\n" << full_test;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateMemberExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"I8[i]", iw::AsmType::Intish()},  // Legacy: no shift for 8-bit view.
 | 
| +      {"I8[iish >> 0]", iw::AsmType::Intish()},
 | 
| +      {"I8[0]", iw::AsmType::Intish()},
 | 
| +      {"I8[2147483648]", iw::AsmType::Intish()},
 | 
| +      {"U8[iish >> 0]", iw::AsmType::Intish()},
 | 
| +      {"U8[i]", iw::AsmType::Intish()},  // Legacy: no shift for 8-bit view.
 | 
| +      {"U8[0]", iw::AsmType::Intish()},
 | 
| +      {"U8[2147483648]", iw::AsmType::Intish()},
 | 
| +      {"I16[iish >> 1]", iw::AsmType::Intish()},
 | 
| +      {"I16[0]", iw::AsmType::Intish()},
 | 
| +      {"I16[2147483648]", iw::AsmType::Intish()},  // bug: must be pre-shifted.
 | 
| +      {"U16[iish >> 1]", iw::AsmType::Intish()},
 | 
| +      {"U16[0]", iw::AsmType::Intish()},
 | 
| +      {"U16[2147483648]", iw::AsmType::Intish()},  // bug: must be pre-shifted.
 | 
| +      {"I32[iish >> 2]", iw::AsmType::Intish()},
 | 
| +      {"I32[0]", iw::AsmType::Intish()},
 | 
| +      {"I32[2147483648]", iw::AsmType::Intish()},  // bug: must be pre-shifted.
 | 
| +      {"U32[iish >> 2]", iw::AsmType::Intish()},
 | 
| +      {"U32[0]", iw::AsmType::Intish()},
 | 
| +      {"U32[2147483648]", iw::AsmType::Intish()},  // bug: must be pre-shifted.
 | 
| +      {"F32[iish >> 2]", iw::AsmType::FloatQ()},
 | 
| +      {"F32[0]", iw::AsmType::FloatQ()},
 | 
| +      {"F32[2147483648]", iw::AsmType::FloatQ()},  // bug: must be pre-shifted.
 | 
| +      {"F64[iish >> 3]", iw::AsmType::DoubleQ()},
 | 
| +      {"F64[0]", iw::AsmType::DoubleQ()},
 | 
| +      {"F64[2147483648]", iw::AsmType::DoubleQ()},  // bug: must be pre-shifted.
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithGlobal(DynamicGlobal("I8"), iw::AsmType::Int8Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U8"), iw::AsmType::Uint8Array())
 | 
| +             ->WithGlobal(DynamicGlobal("I16"), iw::AsmType::Int16Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U16"), iw::AsmType::Uint16Array())
 | 
| +             ->WithGlobal(DynamicGlobal("I32"), iw::AsmType::Int32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U32"), iw::AsmType::Uint32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("F32"), iw::AsmType::Float32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("F64"), iw::AsmType::Float64Array())
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithLocal(DynamicGlobal("i"), iw::AsmType::Int())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateAssignmentExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Array assignments.
 | 
| +      // Storing signed to int heap view.
 | 
| +      {"I8[1024] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I8[1024 >> 0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I8[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I8[2147483648] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U8[1024 >> 0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U8[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U8[2147483648] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I16[1024 >> 1] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I16[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I16[2147483648] = -1024", iw::AsmType::Signed()},  // not pre-shifted.
 | 
| +      {"U16[1024 >> 1] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U16[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U16[2147483648] = -1024", iw::AsmType::Signed()},  // not pre-shifted.
 | 
| +      {"I32[1024 >> 2] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I32[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"I32[2147483648] = -1024", iw::AsmType::Signed()},  // not pre-shifted.
 | 
| +      {"U32[1024 >> 2] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U32[0] = -1024", iw::AsmType::Signed()},
 | 
| +      {"U32[2147483648] = -1024", iw::AsmType::Signed()},  // not pre-shifted.
 | 
| +      // Sroting fixnum to int heap view.
 | 
| +      {"I8[1024] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I8[1024 >> 0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I8[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I8[2147483648] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U8[1024 >> 0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U8[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U8[2147483648] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I16[1024 >> 1] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I16[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I16[2147483648] = 1024", iw::AsmType::FixNum()},  // not pre-shifted.
 | 
| +      {"U16[1024 >> 1] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U16[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U16[2147483648] = 1024", iw::AsmType::FixNum()},  // not pre-shifted.
 | 
| +      {"I32[1024 >> 2] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I32[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"I32[2147483648] = 1024", iw::AsmType::FixNum()},  // not pre-shifted.
 | 
| +      {"U32[1024 >> 2] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U32[0] = 1024", iw::AsmType::FixNum()},
 | 
| +      {"U32[2147483648] = 1024", iw::AsmType::FixNum()},  // not pre-shifted.
 | 
| +      // Storing int to int heap view.
 | 
| +      {"I8[ilocal] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I8[ilocal >> 0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I8[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I8[2147483648] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U8[ilocal >> 0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U8[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U8[2147483648] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I16[ilocal >> 1] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I16[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I16[2147483648] = ilocal", iw::AsmType::Int()},  // not pre-shifted.
 | 
| +      {"U16[ilocal >> 1] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U16[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U16[2147483648] = ilocal", iw::AsmType::Int()},  // not pre-shifted.
 | 
| +      {"I32[ilocal >> 2] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I32[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"I32[2147483648] = ilocal", iw::AsmType::Int()},  // not pre-shifted.
 | 
| +      {"U32[ilocal >> 2] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U32[0] = ilocal", iw::AsmType::Int()},
 | 
| +      {"U32[2147483648] = ilocal", iw::AsmType::Int()},  // not pre-shifted.
 | 
| +      // Storing intish to int heap view.
 | 
| +      {"I8[ilocal] = iish", iw::AsmType::Intish()},
 | 
| +      {"I8[iish >> 0] = iish", iw::AsmType::Intish()},
 | 
| +      {"I8[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"I8[2147483648] = iish", iw::AsmType::Intish()},
 | 
| +      {"U8[iish >> 0] = iish", iw::AsmType::Intish()},
 | 
| +      {"U8[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"U8[2147483648] = iish", iw::AsmType::Intish()},
 | 
| +      {"I16[iish >> 1] = iish", iw::AsmType::Intish()},
 | 
| +      {"I16[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"I16[2147483648] = iish", iw::AsmType::Intish()},  // not pre-shifted.
 | 
| +      {"U16[iish >> 1] = iish", iw::AsmType::Intish()},
 | 
| +      {"U16[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"U16[2147483648] = iish", iw::AsmType::Intish()},  // not pre-shifted.
 | 
| +      {"I32[iish >> 2] = iish", iw::AsmType::Intish()},
 | 
| +      {"I32[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"I32[2147483648] = iish", iw::AsmType::Intish()},  // not pre-shifted.
 | 
| +      {"U32[iish >> 2] = iish", iw::AsmType::Intish()},
 | 
| +      {"U32[0] = iish", iw::AsmType::Intish()},
 | 
| +      {"U32[2147483648] = iish", iw::AsmType::Intish()},  // not pre-shifted.
 | 
| +      // Storing floatish to f32 heap view.
 | 
| +      {"F32[iish >> 2] = fish", iw::AsmType::Floatish()},
 | 
| +      {"F32[0] = fish", iw::AsmType::Floatish()},
 | 
| +      {"F32[2147483648] = fish ", iw::AsmType::Floatish()},  // not pre-shifted.
 | 
| +      // Storing double? to f32 heap view.
 | 
| +      {"F32[iish >> 2] = dq", iw::AsmType::DoubleQ()},
 | 
| +      {"F32[0] = dq", iw::AsmType::DoubleQ()},
 | 
| +      {"F32[2147483648] = dq", iw::AsmType::DoubleQ()},  // not pre-shifted.
 | 
| +      // Storing float? to f64 heap view.
 | 
| +      {"F64[iish >> 3] = fq", iw::AsmType::FloatQ()},
 | 
| +      {"F64[0] = fq", iw::AsmType::FloatQ()},
 | 
| +      {"F64[2147483648] = fq", iw::AsmType::FloatQ()},  // not pre-shifted.
 | 
| +      // Storing double? to f64 heap view.
 | 
| +      {"F64[iish >> 3] = dq", iw::AsmType::DoubleQ()},
 | 
| +      {"F64[0] = dq", iw::AsmType::DoubleQ()},
 | 
| +      {"F64[2147483648] = dq", iw::AsmType::DoubleQ()},  // not pre-shifted.
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Scalar assignments.
 | 
| +      {"ilocal = 1024", iw::AsmType::FixNum()},
 | 
| +      {"ilocal = -1024", iw::AsmType::Signed()},
 | 
| +      {"ilocal = 2147483648", iw::AsmType::Unsigned()},
 | 
| +      {"ilocal = iglobal", iw::AsmType::Int()},
 | 
| +      {"iglobal = 1024", iw::AsmType::FixNum()},
 | 
| +      {"iglobal = -1024", iw::AsmType::Signed()},
 | 
| +      {"iglobal = 2147483648", iw::AsmType::Unsigned()},
 | 
| +      {"iglobal = ilocal", iw::AsmType::Int()},
 | 
| +      {"dlocal = 0.0", iw::AsmType::Double()},
 | 
| +      {"dlocal = +make_double()", iw::AsmType::Double()},
 | 
| +      {"dglobal = 0.0", iw::AsmType::Double()},
 | 
| +      {"dglobal = +make_double()", iw::AsmType::Double()},
 | 
| +      {"flocal = fround(0)", iw::AsmType::Float()},
 | 
| +      {"flocal = fround(make_float())", iw::AsmType::Float()},
 | 
| +      {"fglobal = fround(0)", iw::AsmType::Float()},
 | 
| +      {"fglobal = fround(make_float())", iw::AsmType::Float()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithLocal(DynamicGlobal("fq"), iw::AsmType::FloatQ())
 | 
| +             ->WithLocal(DynamicGlobal("dq"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("fish"), iw::AsmType::Floatish())
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithGlobal(DynamicGlobal("iglobal"), iw::AsmType::Int())
 | 
| +             ->WithGlobal(DynamicGlobal("dglobal"), iw::AsmType::Double())
 | 
| +             ->WithGlobal(DynamicGlobal("fglobal"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->WithLocal(DynamicGlobal("dlocal"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("flocal"), iw::AsmType::Float())
 | 
| +             ->WithGlobal(DynamicGlobal("I8"), iw::AsmType::Int8Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U8"), iw::AsmType::Uint8Array())
 | 
| +             ->WithGlobal(DynamicGlobal("I16"), iw::AsmType::Int16Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U16"), iw::AsmType::Uint16Array())
 | 
| +             ->WithGlobal(DynamicGlobal("I32"), iw::AsmType::Int32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("U32"), iw::AsmType::Uint32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("F32"), iw::AsmType::Float32Array())
 | 
| +             ->WithGlobal(DynamicGlobal("F64"), iw::AsmType::Float64Array())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateUnaryExpression) {
 | 
| +  auto v2d = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Double());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"-2147483648", iw::AsmType::Signed()},
 | 
| +      {"-1024", iw::AsmType::Signed()},
 | 
| +      {"-1", iw::AsmType::Signed()},
 | 
| +      {"-2147483648.0", iw::AsmType::Double()},
 | 
| +      {"+make_double()", iw::AsmType::Double()},
 | 
| +      {"+dbl()", iw::AsmType::Double()},
 | 
| +      {"make_double() * 1.0", iw::AsmType::Double()},  // Violation.
 | 
| +      {"~~fq", iw::AsmType::Signed()},
 | 
| +      {"~~dglobal", iw::AsmType::Signed()},
 | 
| +      {"+slocal", iw::AsmType::Double()},
 | 
| +      {"slocal * 1.0", iw::AsmType::Double()},  // Violation.
 | 
| +      {"+ulocal", iw::AsmType::Double()},
 | 
| +      {"ulocal * 1.0", iw::AsmType::Double()},  // Violation.
 | 
| +      {"+dq", iw::AsmType::Double()},
 | 
| +      {"dq * 1.0", iw::AsmType::Double()},  // Violation.
 | 
| +      {"+fq", iw::AsmType::Double()},
 | 
| +      {"fq * 1.0", iw::AsmType::Double()},  // Violation.
 | 
| +      {"-ilocal", iw::AsmType::Intish()},
 | 
| +      {"ilocal * -1", iw::AsmType::Intish()},  // Violation.
 | 
| +      {"-dq", iw::AsmType::Double()},
 | 
| +      {"dq * -1", iw::AsmType::Double()},  // Violation.
 | 
| +      {"-fq", iw::AsmType::Floatish()},
 | 
| +      {"fq * -1", iw::AsmType::Floatish()},  // Violation.
 | 
| +      {"~iish", iw::AsmType::Signed()},
 | 
| +      {"iish ^ -1", iw::AsmType::Signed()},  // Violation, but OK.
 | 
| +      {"!ilocal", iw::AsmType::Int()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("fq"), iw::AsmType::FloatQ())
 | 
| +             ->WithLocal(DynamicGlobal("dq"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithLocal(DynamicGlobal("slocal"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("ulocal"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->WithGlobal(DynamicGlobal("dglobal"), iw::AsmType::Double())
 | 
| +             ->WithGlobal(DynamicGlobal("dbl"), v2d)
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateMultiplicativeExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"dq * dq", iw::AsmType::Double()},
 | 
| +      {"fq * fq", iw::AsmType::Floatish()},
 | 
| +      {"slocal / slocal", iw::AsmType::Intish()},
 | 
| +      {"ulocal / ulocal", iw::AsmType::Intish()},
 | 
| +      {"dq / dq", iw::AsmType::Double()},
 | 
| +      {"fq / fq", iw::AsmType::Floatish()},
 | 
| +      {"slocal % slocal", iw::AsmType::Intish()},
 | 
| +      {"ulocal % ulocal", iw::AsmType::Intish()},
 | 
| +      {"dq % dq", iw::AsmType::Double()},
 | 
| +      {"-1048575 * ilocal", iw::AsmType::Intish()},
 | 
| +      {"ilocal * -1048575", iw::AsmType::Intish()},
 | 
| +      {"1048575 * ilocal", iw::AsmType::Intish()},
 | 
| +      {"ilocal * 1048575", iw::AsmType::Intish()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("fq"), iw::AsmType::FloatQ())
 | 
| +             ->WithLocal(DynamicGlobal("dq"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("slocal"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("ulocal"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->WithGlobal(DynamicGlobal("dglobal"), iw::AsmType::Double())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateAdditiveExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"dlocal + dlocal", iw::AsmType::Double()},
 | 
| +      {"fq + fq", iw::AsmType::Floatish()},
 | 
| +      {"dq - dq", iw::AsmType::Double()},
 | 
| +      {"fq - fq", iw::AsmType::Floatish()},
 | 
| +      {"ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ilocal - 1", iw::AsmType::Intish()},
 | 
| +      {"slocal + ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"slocal - ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal + ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal - ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal + slocal + ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal + slocal - ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal - slocal + ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"ulocal - slocal - ilocal + 1", iw::AsmType::Intish()},
 | 
| +      {"1 + 1", iw::AsmType::FixNum()},  // Violation: intish.
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("fq"), iw::AsmType::FloatQ())
 | 
| +             ->WithLocal(DynamicGlobal("dq"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithLocal(DynamicGlobal("dlocal"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("slocal"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("ulocal"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("ilocal"), iw::AsmType::Int())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateShiftExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"iish << iish", iw::AsmType::Signed()},
 | 
| +      {"iish >> iish", iw::AsmType::Signed()},
 | 
| +      {"iish >>> iish", iw::AsmType::Unsigned()},
 | 
| +      {"1 << 0", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"1 >> 0", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"4294967295 >>> 0", iw::AsmType::Unsigned()},
 | 
| +      {"-1 >>> 0", iw::AsmType::Unsigned()},
 | 
| +      {"2147483647 >>> 0", iw::AsmType::FixNum()},  // Violation: unsigned.
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateComparisonExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Non const <op> Non const
 | 
| +      {"s0 == s1", iw::AsmType::Int()},
 | 
| +      {"u0 == u1", iw::AsmType::Int()},
 | 
| +      {"f0 == f1", iw::AsmType::Int()},
 | 
| +      {"d0 == d1", iw::AsmType::Int()},
 | 
| +      {"s0 != s1", iw::AsmType::Int()},
 | 
| +      {"u0 != u1", iw::AsmType::Int()},
 | 
| +      {"f0 != f1", iw::AsmType::Int()},
 | 
| +      {"d0 != d1", iw::AsmType::Int()},
 | 
| +      {"s0 < s1", iw::AsmType::Int()},
 | 
| +      {"u0 < u1", iw::AsmType::Int()},
 | 
| +      {"f0 < f1", iw::AsmType::Int()},
 | 
| +      {"d0 < d1", iw::AsmType::Int()},
 | 
| +      {"s0 <= s1", iw::AsmType::Int()},
 | 
| +      {"u0 <= u1", iw::AsmType::Int()},
 | 
| +      {"f0 <= f1", iw::AsmType::Int()},
 | 
| +      {"d0 <= d1", iw::AsmType::Int()},
 | 
| +      {"s0 > s1", iw::AsmType::Int()},
 | 
| +      {"u0 > u1", iw::AsmType::Int()},
 | 
| +      {"f0 > f1", iw::AsmType::Int()},
 | 
| +      {"d0 > d1", iw::AsmType::Int()},
 | 
| +      {"s0 >= s1", iw::AsmType::Int()},
 | 
| +      {"u0 >= u1", iw::AsmType::Int()},
 | 
| +      {"f0 >= f1", iw::AsmType::Int()},
 | 
| +      {"d0 >= d1", iw::AsmType::Int()},
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Non const <op> Const
 | 
| +      {"s0 == -1025", iw::AsmType::Int()},
 | 
| +      {"u0 == 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 == fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 == 9876543.201", iw::AsmType::Int()},
 | 
| +      {"s0 != -1025", iw::AsmType::Int()},
 | 
| +      {"u0 != 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 != fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 != 9876543.201", iw::AsmType::Int()},
 | 
| +      {"s0 < -1025", iw::AsmType::Int()},
 | 
| +      {"u0 < 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 < fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 < 9876543.201", iw::AsmType::Int()},
 | 
| +      {"s0 <= -1025", iw::AsmType::Int()},
 | 
| +      {"u0 <= 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 <= fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 <= 9876543.201", iw::AsmType::Int()},
 | 
| +      {"s0 > -1025", iw::AsmType::Int()},
 | 
| +      {"u0 > 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 > fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 > 9876543.201", iw::AsmType::Int()},
 | 
| +      {"s0 >= -1025", iw::AsmType::Int()},
 | 
| +      {"u0 >= 123456789", iw::AsmType::Int()},
 | 
| +      {"f0 >= fround(123456.78)", iw::AsmType::Int()},
 | 
| +      {"d0 >= 9876543.201", iw::AsmType::Int()},
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Const <op> Non const
 | 
| +      {"-1025 == s0", iw::AsmType::Int()},
 | 
| +      {"123456789 == u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) == f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 == d0", iw::AsmType::Int()},
 | 
| +      {"-1025 != s0", iw::AsmType::Int()},
 | 
| +      {"123456789 != u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) != f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 != d0", iw::AsmType::Int()},
 | 
| +      {"-1025 < s0", iw::AsmType::Int()},
 | 
| +      {"123456789 < u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) < f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 < d0", iw::AsmType::Int()},
 | 
| +      {"-1025 <= s0", iw::AsmType::Int()},
 | 
| +      {"123456789 <= u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) <= f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 <= d0", iw::AsmType::Int()},
 | 
| +      {"-1025 > s0", iw::AsmType::Int()},
 | 
| +      {"123456789 > u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) > f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 > d0", iw::AsmType::Int()},
 | 
| +      {"-1025 >= s0", iw::AsmType::Int()},
 | 
| +      {"123456789 >= u0", iw::AsmType::Int()},
 | 
| +      {"fround(123456.78) >= f0", iw::AsmType::Int()},
 | 
| +      {"9876543.201 >= d0", iw::AsmType::Int()},
 | 
| +      // TODO(jpp): maybe add Const <op> Const.
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithLocal(DynamicGlobal("u0"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("u1"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("s0"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("s1"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("f0"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("f1"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("d0"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("d1"), iw::AsmType::Double())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateBitwiseExpression) {
 | 
| +  auto v2s = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"iish0 & iish1", iw::AsmType::Signed()},
 | 
| +      {"iish0 | iish1", iw::AsmType::Signed()},
 | 
| +      {"iish0 ^ iish1", iw::AsmType::Signed()},
 | 
| +      {"iish0 & -1", iw::AsmType::Signed()},
 | 
| +      {"iish0 | -1", iw::AsmType::Signed()},
 | 
| +      {"iish0 ^ -1", iw::AsmType::Signed()},
 | 
| +      {"2147483648 & iish1", iw::AsmType::Signed()},
 | 
| +      {"2147483648 | iish1", iw::AsmType::Signed()},
 | 
| +      {"2147483648 ^ iish1", iw::AsmType::Signed()},
 | 
| +      {"2147483648 & 0", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"2147483648 | 0", iw::AsmType::Signed()},
 | 
| +      {"2147483648 ^ 0", iw::AsmType::Signed()},
 | 
| +      {"2134651 & 123", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"2134651 | 123", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"2134651 ^ 123", iw::AsmType::FixNum()},  // Violation: signed.
 | 
| +      {"make_signed()|0", iw::AsmType::Signed()},
 | 
| +      {"signed()|0", iw::AsmType::Signed()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("iish1"), iw::AsmType::Intish())
 | 
| +             ->WithLocal(DynamicGlobal("iish0"), iw::AsmType::Intish())
 | 
| +             ->WithGlobal(DynamicGlobal("signed"), v2s)
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateConditionalExpression) {
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      {"i0 ? i0 : i1", iw::AsmType::Int()},
 | 
| +      {"i0 ? f0 : f1", iw::AsmType::Float()},
 | 
| +      {"i0 ? d0 : d1", iw::AsmType::Double()},
 | 
| +      {"0 ? -1 : 2147483648", iw::AsmType::Int()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithLocal(DynamicGlobal("i0"), iw::AsmType::Int())
 | 
| +             ->WithLocal(DynamicGlobal("i1"), iw::AsmType::Int())
 | 
| +             ->WithLocal(DynamicGlobal("f0"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("f1"), iw::AsmType::Float())
 | 
| +             ->WithLocal(DynamicGlobal("d0"), iw::AsmType::Double())
 | 
| +             ->WithLocal(DynamicGlobal("d1"), iw::AsmType::Double())
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(ValidateCall) {
 | 
| +  auto v2f = [](Zone* zone) -> iw::AsmType* {
 | 
| +    auto* ret = iw::AsmType::Function(zone, iw::AsmType::Float());
 | 
| +    return ret;
 | 
| +  };
 | 
| +
 | 
| +  // ifd2_ is a helper function that returns a lambda for creating a function
 | 
| +  // type that accepts an int, a float, and a double. ret_type_factory is a
 | 
| +  // pointer to an AsmType*() function, and (*ret_type_factory)() returns the
 | 
| +  // desired return type. For example,
 | 
| +  //
 | 
| +  // ifd2_(&iw::AsmType::Float)
 | 
| +  //
 | 
| +  // returns an AsmType representing an asm.j function with the following
 | 
| +  // signature:
 | 
| +  //
 | 
| +  // float(int, float, double)
 | 
| +  auto ifd2_ = [](iw::AsmType* (
 | 
| +      *ret_type_factory)()) -> std::function<iw::AsmType*(Zone*)> {
 | 
| +    return [ret_type_factory](Zone* zone) -> iw::AsmType* {
 | 
| +      auto* ret = iw::AsmType::Function(zone, (*ret_type_factory)());
 | 
| +      ret->AsFunctionType()->AddArgument(iw::AsmType::Int());
 | 
| +      ret->AsFunctionType()->AddArgument(iw::AsmType::Float());
 | 
| +      ret->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +      return ret;
 | 
| +    };
 | 
| +  };
 | 
| +  auto ifd2f = ifd2_(&iw::AsmType::Float);
 | 
| +  auto ifd2d = ifd2_(&iw::AsmType::Double);
 | 
| +  auto ifd2i = ifd2_(&iw::AsmType::Signed);
 | 
| +
 | 
| +  // Just like ifd2_, but this one returns a type representing a function table.
 | 
| +  auto tbl_ifd2_ = [](size_t tbl_size, iw::AsmType* (*ret_type_factory)())
 | 
| +      -> std::function<iw::AsmType*(Zone*)> {
 | 
| +        return [tbl_size, ret_type_factory](Zone* zone) -> iw::AsmType* {
 | 
| +          auto* signature = iw::AsmType::Function(zone, (*ret_type_factory)());
 | 
| +          signature->AsFunctionType()->AddArgument(iw::AsmType::Int());
 | 
| +          signature->AsFunctionType()->AddArgument(iw::AsmType::Float());
 | 
| +          signature->AsFunctionType()->AddArgument(iw::AsmType::Double());
 | 
| +
 | 
| +          auto* ret = iw::AsmType::FunctionTableType(zone, tbl_size, signature);
 | 
| +          return ret;
 | 
| +        };
 | 
| +      };
 | 
| +  auto ifd2f_tbl = tbl_ifd2_(32, &iw::AsmType::Float);
 | 
| +  auto ifd2d_tbl = tbl_ifd2_(64, &iw::AsmType::Double);
 | 
| +  auto ifd2i_tbl = tbl_ifd2_(4096, &iw::AsmType::Signed);
 | 
| +
 | 
| +  const struct {
 | 
| +    const char* expression;
 | 
| +    iw::AsmType* load_type;
 | 
| +  } kTests[] = {
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Functions.
 | 
| +      {"fround(v2f())", iw::AsmType::Float()},
 | 
| +      {"fround(fish)", iw::AsmType::Float()},
 | 
| +      {"fround(dq)", iw::AsmType::Float()},
 | 
| +      {"fround(s)", iw::AsmType::Float()},
 | 
| +      {"fround(u)", iw::AsmType::Float()},
 | 
| +      {"ffi()|0", iw::AsmType::Signed()},
 | 
| +      {"ffi(1.0)|0", iw::AsmType::Signed()},
 | 
| +      {"ffi(1.0, 2.0)|0", iw::AsmType::Signed()},
 | 
| +      {"ffi(1.0, 2.0, 3)|0", iw::AsmType::Signed()},
 | 
| +      {"ffi(1.0, 2.0, 3, 4)|0", iw::AsmType::Signed()},
 | 
| +      {"+ffi()", iw::AsmType::Double()},
 | 
| +      {"+ffi(1.0)", iw::AsmType::Double()},
 | 
| +      {"+ffi(1.0, 2.0)", iw::AsmType::Double()},
 | 
| +      {"+ffi(1.0, 2.0, 3)", iw::AsmType::Double()},
 | 
| +      {"+ffi(1.0, 2.0, 3, 4)", iw::AsmType::Double()},
 | 
| +      {"fround(ifd2f(1, fround(1), 1.0))", iw::AsmType::Float()},
 | 
| +      {"+ifd2d(1, fround(1), 1.0)", iw::AsmType::Double()},
 | 
| +      {"ifd2i(1, fround(1), 1.0)|0", iw::AsmType::Signed()},
 | 
| +      // -----------------------------------------------------------------------
 | 
| +      // Function tables.
 | 
| +      {"fround(ifd2f_tbl[iish & 31](1, fround(1), 1.0))", iw::AsmType::Float()},
 | 
| +      {"+ifd2d_tbl[iish & 63](1, fround(1), 1.0)", iw::AsmType::Double()},
 | 
| +      {"ifd2i_tbl[iish & 4095](1, fround(1), 1.0)|0", iw::AsmType::Signed()},
 | 
| +  };
 | 
| +
 | 
| +  for (size_t ii = 0; ii < arraysize(kTests); ++ii) {
 | 
| +    const auto* test = kTests + ii;
 | 
| +    if (!ValidationOf(Expression(test->expression))
 | 
| +             ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround)
 | 
| +             ->WithImport(DynamicGlobal("ffi"), iw::AsmTyper::kFFI)
 | 
| +             ->WithLocal(DynamicGlobal("fish"), iw::AsmType::Floatish())
 | 
| +             ->WithLocal(DynamicGlobal("dq"), iw::AsmType::DoubleQ())
 | 
| +             ->WithLocal(DynamicGlobal("s"), iw::AsmType::Signed())
 | 
| +             ->WithLocal(DynamicGlobal("u"), iw::AsmType::Unsigned())
 | 
| +             ->WithLocal(DynamicGlobal("iish"), iw::AsmType::Intish())
 | 
| +             ->WithGlobal(DynamicGlobal("v2f"), v2f)
 | 
| +             ->WithGlobal(DynamicGlobal("ifd2f_tbl"), ifd2f_tbl)
 | 
| +             ->WithGlobal(DynamicGlobal("ifd2d_tbl"), ifd2d_tbl)
 | 
| +             ->WithGlobal(DynamicGlobal("ifd2i_tbl"), ifd2i_tbl)
 | 
| +             ->SucceedsWithExactType(test->load_type)) {
 | 
| +      std::cerr << "Test:\n" << test->expression;
 | 
| +      CHECK(false);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| 
 |