Commit aa912252 authored by jpp's avatar jpp Committed by Commit bot

V8. ASM-2-WASM. Migrates asm-wasm-builder to the new asm-typer.

BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203
TEST=mjsunit/wasm/*
LOG=N

Review-Url: https://codereview.chromium.org/2134333003
Cr-Commit-Position: refs/heads/master@{#37729}
parent 819fe046
......@@ -6,8 +6,8 @@
#include "src/api-natives.h"
#include "src/api.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/asmjs/typing-asm.h"
#include "src/assert-scope.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
......@@ -59,12 +59,8 @@ i::MaybeHandle<i::FixedArray> CompileModule(
MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
wasm::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
info->literal());
typer.set_fixed_signature(true);
if (i::FLAG_enable_simd_asmjs) {
typer.set_allow_simd(true);
}
if (!typer.Validate()) {
DCHECK(!info->isolate()->has_pending_exception());
PrintF("Validation of asm.js module failed: %s", typer.error_message());
......
......@@ -48,6 +48,9 @@
namespace v8 {
namespace internal {
namespace wasm {
namespace {
static const uint32_t LargestFixNum = std::numeric_limits<int32_t>::max();
} // namespace
using v8::internal::AstNode;
using v8::internal::GetCurrentStackPosition;
......@@ -122,7 +125,8 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
stack_limit_(isolate->stack_guard()->real_climit()),
node_types_(zone_) {
node_types_(zone_),
fround_type_(AsmType::FroundType(zone_)) {
InitializeStdlib();
module_info_.set_standard_member(kModule);
}
......@@ -165,12 +169,16 @@ void AsmTyper::InitializeStdlib() {
ii2s->AsFunctionType()->AddArgument(i);
auto* minmax_d = AsmType::MinMaxType(zone_, d, d);
// *VIOLATION* The float variant is not part of the spec, but firefox accepts
// it.
auto* minmax_f = AsmType::MinMaxType(zone_, f, f);
auto* minmax_i = AsmType::MinMaxType(zone_, s, i);
auto* minmax = AsmType::OverloadedFunction(zone_);
minmax->AsOverloadedFunctionType()->AddOverload(minmax_i);
minmax->AsOverloadedFunctionType()->AddOverload(minmax_f);
minmax->AsOverloadedFunctionType()->AddOverload(minmax_d);
auto* fround = AsmType::FroundType(zone_);
auto* fround = fround_type_;
auto* abs = AsmType::OverloadedFunction(zone_);
abs->AsOverloadedFunctionType()->AddOverload(s2s);
......@@ -362,17 +370,36 @@ bool AsmTyper::AddLocal(Variable* variable, VariableInfo* info) {
void AsmTyper::SetTypeOf(AstNode* node, AsmType* type) {
DCHECK_NE(type, AsmType::None());
auto** node_type = &node_types_[node];
DCHECK(*node_type == nullptr);
*node_type = type;
DCHECK(node_types_.find(node) == node_types_.end());
node_types_.insert(std::make_pair(node, type));
}
AsmType* AsmTyper::TypeOf(AstNode* node) const {
auto node_type_iter = node_types_.find(node);
if (node_type_iter == node_types_.end()) {
return AsmType::None();
}
if (node_type_iter != node_types_.end()) {
return node_type_iter->second;
}
// Sometimes literal nodes are not added to the node_type_ map simply because
// their are not visited with ValidateExpression().
if (auto* literal = node->AsLiteral()) {
if (literal->raw_value()->ContainsDot()) {
return AsmType::Double();
}
uint32_t u;
if (literal->value()->ToUint32(&u)) {
if (u > LargestFixNum) {
return AsmType::Unsigned();
}
return AsmType::FixNum();
}
int32_t i;
if (literal->value()->ToInt32(&i)) {
return AsmType::Signed();
}
}
return AsmType::None();
}
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
......@@ -607,11 +634,13 @@ AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
auto* value = assign->value();
// Not all types of assignment are allowed by asm.js. See
// 5.5 Global Variable Type Annotations.
bool global_variable = false;
if (value->IsLiteral() || value->IsCall()) {
AsmType* type = nullptr;
RECURSE(type = VariableTypeAnnotations(value));
target_info = new (zone_) VariableInfo(type);
target_info->set_mutability(VariableInfo::kMutableGlobal);
global_variable = true;
} else if (value->IsProperty()) {
target_info = ImportLookup(value->AsProperty());
if (target_info == nullptr) {
......@@ -689,6 +718,12 @@ AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
}
DCHECK(target_info->type() != AsmType::None());
if (!global_variable) {
// Global variables have their types set in VariableTypeAnnotations.
SetTypeOf(value, target_info->type());
}
SetTypeOf(assign, target_info->type());
SetTypeOf(target, target_info->type());
return target_info->type();
}
......@@ -830,6 +865,7 @@ AsmType* AsmTyper::ValidateFunctionTable(Assignment* assign) {
DCHECK(false);
FAIL(assign, "Redeclared global identifier in function table name.");
}
SetTypeOf(value, target_info->type());
return target_info->type();
}
......@@ -854,7 +890,7 @@ AsmType* AsmTyper::ValidateFunctionTable(Assignment* assign) {
}
target_info->MarkDefined();
DCHECK(target_info->type() == AsmType::None());
DCHECK(target_info->type() != AsmType::None());
SetTypeOf(value, target_info->type());
return target_info->type();
......@@ -916,6 +952,8 @@ AsmType* AsmTyper::ValidateFunction(FunctionDeclaration* fun_decl) {
FAIL(proxy, "Redeclared parameter.");
}
parameter_types.push_back(type);
SetTypeOf(proxy, type);
SetTypeOf(expr, type);
}
if (annotated_parameters != fun->parameter_count()) {
......@@ -952,6 +990,9 @@ AsmType* AsmTyper::ValidateFunction(FunctionDeclaration* fun_decl) {
if (!AddLocal(local->var(), local_info)) {
FAIL(initializer, "Redeclared local.");
}
SetTypeOf(local, type);
SetTypeOf(initializer, type);
}
// 5.2 Return Type Annotations
......@@ -1018,6 +1059,8 @@ AsmType* AsmTyper::ValidateFunction(FunctionDeclaration* fun_decl) {
DCHECK(false);
FAIL(fun_decl, "Redeclared global identifier.");
}
SetTypeOf(fun, fun_type);
return fun_type;
}
......@@ -1221,6 +1264,7 @@ AsmType* AsmTyper::ValidateSwitchStatement(SwitchStatement* stmt) {
CHECK(!has_default);
RECURSE(ValidateDefault(a_case));
has_default = true;
continue;
}
int32_t case_lbl;
......@@ -1364,7 +1408,7 @@ bool IsUnaryMinus(BinaryOperation* binop) {
return false;
}
return right_as_literal->raw_value()->ContainsDot() &&
return !right_as_literal->raw_value()->ContainsDot() &&
right_as_literal->raw_value()->AsNumber() == -1.0;
}
} // namespace
......@@ -1391,6 +1435,7 @@ AsmType* AsmTyper::ValidateBinaryOperation(BinaryOperation* expr) {
}
AsmType* left_type;
RECURSE(left_type = ValidateExpression(expr->left()));
SetTypeOf(expr->right(), AsmType::Double());
UNOP_OVERLOAD(Signed, Double);
UNOP_OVERLOAD(Unsigned, Double);
UNOP_OVERLOAD(DoubleQ, Double);
......@@ -1402,6 +1447,7 @@ AsmType* AsmTyper::ValidateBinaryOperation(BinaryOperation* expr) {
// *VIOLATION* the parser converts -x to x * -1.0.
AsmType* left_type;
RECURSE(left_type = ValidateExpression(expr->left()));
SetTypeOf(expr->right(), left_type);
UNOP_OVERLOAD(Int, Intish);
UNOP_OVERLOAD(DoubleQ, Double);
UNOP_OVERLOAD(FloatQ, Floatish);
......@@ -1431,6 +1477,9 @@ AsmType* AsmTyper::ValidateBinaryOperation(BinaryOperation* expr) {
// This is the special ~~ operator.
AsmType* left_type;
RECURSE(left_type = ValidateExpression(left_as_binop->left()));
SetTypeOf(left_as_binop->right(), AsmType::FixNum());
SetTypeOf(left_as_binop, AsmType::Signed());
SetTypeOf(expr->right(), AsmType::FixNum());
UNOP_OVERLOAD(Double, Signed);
UNOP_OVERLOAD(FloatQ, Signed);
FAIL(left_as_binop, "Invalid type for conversion to signed.");
......@@ -1456,24 +1505,21 @@ AsmType* AsmTyper::ValidateCommaExpression(BinaryOperation* comma) {
// (expr COMMA (expr COMMA (expr COMMA (... ))))
auto* left = comma->left();
auto* left_as_binop = left->AsBinaryOperation();
if (left_as_binop && left_as_binop->op() == Token::COMMA) {
ValidateCommaExpression(left_as_binop);
} else if (auto* left_as_call = left->AsCall()) {
ValidateCall(AsmType::Void(), left_as_call);
if (auto* left_as_call = left->AsCall()) {
RECURSE(ValidateCall(AsmType::Void(), left_as_call));
} else {
ValidateExpression(left);
RECURSE(ValidateExpression(left));
}
auto* right = comma->right();
auto* right_as_binop = right->AsBinaryOperation();
if (right_as_binop && right_as_binop->op() == Token::COMMA) {
return ValidateCommaExpression(right_as_binop);
AsmType* right_type = nullptr;
if (auto* right_as_call = right->AsCall()) {
RECURSE(right_type = ValidateCall(AsmType::Void(), right_as_call));
} else {
return ValidateExpression(right);
RECURSE(right_type = ValidateExpression(right));
}
UNREACHABLE();
return right_type;
}
// 6.8.2 NumericLiteral
......@@ -1500,7 +1546,6 @@ AsmType* AsmTyper::ValidateNumericLiteral(Literal* literal) {
return AsmType::Signed();
}
static const uint32_t LargestFixNum = std::numeric_limits<int32_t>::max();
if (value <= LargestFixNum) {
return AsmType::FixNum();
}
......@@ -1587,13 +1632,15 @@ AsmType* AsmTyper::ValidateAssignmentExpression(Assignment* assignment) {
ValidateHeapAccess(target_as_property, StoreToHeap));
// TODO(jpp): Change FloatishDoubleQ and FloatQDoubleQ so that they are base
// classes for Floatish, DoubleQ, and FloatQ, and then invert this if so
// that it reads more naturally as
//
// if (!value_type->IsA(allowed_store_types))
if (allowed_store_types == AsmType::FloatishDoubleQ() ||
allowed_store_types == AsmType::FloatQDoubleQ()) {
if (!allowed_store_types->IsA(value_type)) {
// classes for Floatish, DoubleQ, and FloatQ.
if (allowed_store_types == AsmType::FloatishDoubleQ()) {
if (!value_type->IsA(AsmType::Floatish()) &&
!value_type->IsA(AsmType::DoubleQ())) {
FAIL(assignment, "Type mismatch in heap assignment.");
}
} else if (allowed_store_types == AsmType::FloatQDoubleQ()) {
if (!value_type->IsA(AsmType::FloatQ()) &&
!value_type->IsA(AsmType::DoubleQ())) {
FAIL(assignment, "Type mismatch in heap assignment.");
}
} else {
......@@ -1778,6 +1825,7 @@ AsmType* AsmTyper::ValidateAdditiveExpression(BinaryOperation* binop,
left_as_binop->op() == Token::SUB)) {
RECURSE(left_type =
ValidateAdditiveExpression(left_as_binop, intish_count + 1));
SetTypeOf(left_as_binop, left_type);
} else {
RECURSE(left_type = ValidateExpression(left));
}
......@@ -1790,6 +1838,7 @@ AsmType* AsmTyper::ValidateAdditiveExpression(BinaryOperation* binop,
right_as_binop->op() == Token::SUB)) {
RECURSE(right_type =
ValidateAdditiveExpression(right_as_binop, intish_count + 1));
SetTypeOf(right_as_binop, right_type);
} else {
RECURSE(right_type = ValidateExpression(right));
}
......@@ -2084,32 +2133,14 @@ bool ExtractIndirectCallMask(Expression* expr, uint32_t* value) {
return base::bits::IsPowerOfTwo32(1 + *value);
}
// TODO(jpp): Add a AsmType::ValidateCall is poorly designed. It can only handle
// function declarations, not invocations. CheckInvocationOf temporarily works
// around this limitation by converting each actual in actuals to a parameter
// type before invoking prototype->ValidateCall. This is the wrong behavior for
// FFIs (we need to pass Signed integers to FFIs, not Ints), so that case is
// handled separately.
bool CheckInvocationOf(AsmCallableType* prototype, AsmType* return_type,
ZoneVector<AsmType*>* actuals) {
if (auto* ffi = prototype->AsFFIType()) {
return ffi->ValidateCall(return_type, *actuals) != AsmType::None();
}
for (size_t ii = 0; ii < actuals->size(); ++ii) {
(*actuals)[ii] = (*actuals)[ii]->ToParameterType();
}
return prototype->ValidateCall(return_type, *actuals) != AsmType::None();
}
} // namespace
AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
AsmType* float_coercion_type;
RECURSE(float_coercion_type = ValidateFloatCoercion(call));
if (float_coercion_type == AsmType::Float()) {
return AsmType::Float();
SetTypeOf(call, AsmType::Float());
return return_type;
}
// TODO(jpp): we should be able to reuse the args vector's storage space.
......@@ -2148,6 +2179,8 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
DCHECK(false);
FAIL(call, "Redeclared global identifier.");
}
SetTypeOf(call_var_proxy, reinterpret_cast<AsmType*>(call_type));
SetTypeOf(call, return_type);
return return_type;
}
......@@ -2161,10 +2194,12 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
FAIL(call, "Foreign functions can't return float.");
}
if (!CheckInvocationOf(callee_type, return_type, &args)) {
if (!callee_type->CanBeInvokedWith(return_type, args)) {
FAIL(call, "Function invocation does not match function type.");
}
SetTypeOf(call_var_proxy, call_var_info->type());
SetTypeOf(call, return_type);
return return_type;
}
......@@ -2221,6 +2256,8 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
DCHECK(false);
FAIL(call, "Redeclared global identifier.");
}
SetTypeOf(call_property, reinterpret_cast<AsmType*>(call_type));
SetTypeOf(call, return_type);
return return_type;
}
......@@ -2236,12 +2273,15 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
auto* previous_type_signature =
previous_type->signature()->AsFunctionType();
DCHECK(previous_type_signature != nullptr);
if (!CheckInvocationOf(previous_type_signature, return_type, &args)) {
if (!previous_type_signature->CanBeInvokedWith(return_type, args)) {
// TODO(jpp): better error messages.
FAIL(call,
"Function pointer table signature does not match previous "
"signature.");
}
SetTypeOf(call_property, previous_type->signature());
SetTypeOf(call, return_type);
return return_type;
}
......@@ -2280,6 +2320,7 @@ AsmType* AsmTyper::ValidateHeapAccess(Property* heap,
if (!obj_type->IsA(AsmType::Heap())) {
FAIL(heap, "Identifier does not represent a heap view.");
}
SetTypeOf(obj, obj_type);
if (auto* key_as_literal = heap->key()->AsLiteral()) {
if (key_as_literal->raw_value()->ContainsDot()) {
......@@ -2371,6 +2412,7 @@ AsmType* AsmTyper::ValidateFloatCoercion(Call* call) {
RECURSE(arg_type = ValidateExpression(arg));
if (arg_type->IsA(AsmType::Floatish()) || arg_type->IsA(AsmType::DoubleQ()) ||
arg_type->IsA(AsmType::Signed()) || arg_type->IsA(AsmType::Unsigned())) {
SetTypeOf(call->expression(), fround_type_);
return AsmType::Float();
}
......@@ -2395,9 +2437,11 @@ AsmType* AsmTyper::ParameterTypeAnnotations(Variable* parameter,
"Invalid parameter type annotation - should annotate a parameter.");
}
if (IsDoubleAnnotation(binop)) {
SetTypeOf(left, AsmType::Double());
return AsmType::Double();
}
if (IsIntAnnotation(binop)) {
SetTypeOf(left, AsmType::Int());
return AsmType::Int();
}
FAIL(binop, "Invalid parameter type annotation.");
......@@ -2428,6 +2472,7 @@ AsmType* AsmTyper::ParameterTypeAnnotations(Variable* parameter,
"a parameter.");
}
SetTypeOf(src_expr, AsmType::Float());
return AsmType::Float();
}
......@@ -2485,18 +2530,26 @@ AsmType* AsmTyper::ReturnTypeAnnotations(ReturnStatement* statement) {
AsmType* AsmTyper::VariableTypeAnnotations(Expression* initializer) {
if (auto* literal = initializer->AsLiteral()) {
if (literal->raw_value()->ContainsDot()) {
SetTypeOf(initializer, AsmType::Double());
return AsmType::Double();
}
int32_t i32;
uint32_t u32;
if (literal->value()->ToInt32(&i32) || literal->value()->ToUint32(&u32)) {
return AsmType::Int();
if (literal->value()->ToUint32(&u32)) {
if (u32 > LargestFixNum) {
SetTypeOf(initializer, AsmType::Unsigned());
} else {
SetTypeOf(initializer, AsmType::FixNum());
}
} else if (literal->value()->ToInt32(&i32)) {
SetTypeOf(initializer, AsmType::Signed());
} else {
FAIL(initializer, "Invalid type annotation - forbidden literal.");
}
return AsmType::Int();
}
auto* call = initializer->AsCall();
DCHECK(call != nullptr);
if (call == nullptr) {
FAIL(initializer,
"Invalid variable initialization - it should be a literal, or "
......
......@@ -22,6 +22,7 @@ namespace v8 {
namespace internal {
namespace wasm {
class AsmType;
class AsmTyperHarnessBuilder;
class AsmTyper final {
......@@ -313,6 +314,7 @@ class AsmTyper final {
bool stack_overflow_ = false;
ZoneMap<AstNode*, AsmType*> node_types_;
static const int kErrorMessageLimit = 100;
AsmType* fround_type_;
char error_message_[kErrorMessageLimit];
DISALLOW_IMPLICIT_CONSTRUCTORS(AsmTyper);
......
......@@ -173,6 +173,8 @@ class AsmFroundType final : public AsmFunctionType {
AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
};
} // namespace
......@@ -181,6 +183,7 @@ AsmType* AsmType::FroundType(Zone* zone) {
return reinterpret_cast<AsmType*>(Fround);
}
// TODO(jpp): Remove this method.
AsmType* AsmFroundType::ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (args.size() != 1) {
......@@ -196,6 +199,21 @@ AsmType* AsmFroundType::ValidateCall(AsmType* return_type,
return AsmType::Float();
}
bool AsmFroundType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (args.size() != 1) {
return false;
}
auto* arg = args[0];
if (!arg->IsA(AsmType::Floatish()) && !arg->IsA(AsmType::DoubleQ()) &&
!arg->IsA(AsmType::Signed()) && !arg->IsA(AsmType::Unsigned())) {
return false;
}
return true;
}
namespace {
class AsmMinMaxType final : public AsmFunctionType {
public:
......@@ -228,6 +246,26 @@ class AsmMinMaxType final : public AsmFunctionType {
return ReturnType();
}
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override {
if (!ReturnType()->IsExactly(return_type)) {
return false;
}
if (args.size() < 2) {
return false;
}
auto* arg_type = Arguments()[0];
for (size_t ii = 0; ii < Arguments().size(); ++ii) {
if (!args[ii]->IsA(arg_type)) {
return false;
}
}
return true;
}
};
} // namespace
......@@ -249,6 +287,21 @@ AsmType* AsmFFIType::ValidateCall(AsmType* return_type,
return return_type;
}
bool AsmFFIType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (return_type->IsExactly(AsmType::Float())) {
return false;
}
for (size_t ii = 0; ii < args.size(); ++ii) {
if (!args[ii]->IsA(AsmType::Extern())) {
return false;
}
}
return true;
}
AsmType* AsmFunctionType::ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (!return_type_->IsExactly(return_type)) {
......@@ -268,6 +321,25 @@ AsmType* AsmFunctionType::ValidateCall(AsmType* return_type,
return return_type_;
}
bool AsmFunctionType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (!return_type_->IsExactly(return_type)) {
return false;
}
if (args_.size() != args.size()) {
return false;
}
for (size_t ii = 0; ii < args_.size(); ++ii) {
if (!args[ii]->IsA(args_[ii])) {
return false;
}
}
return true;
}
std::string AsmOverloadedFunctionType::Name() {
std::string ret;
......@@ -294,6 +366,17 @@ AsmType* AsmOverloadedFunctionType::ValidateCall(
return AsmType::None();
}
bool AsmOverloadedFunctionType::CanBeInvokedWith(
AsmType* return_type, const ZoneVector<AsmType*>& args) {
for (size_t ii = 0; ii < overloads_.size(); ++ii) {
if (overloads_[ii]->AsCallableType()->CanBeInvokedWith(return_type, args)) {
return true;
}
}
return false;
}
void AsmOverloadedFunctionType::AddOverload(AsmType* overload) {
DCHECK(overload->AsFunctionType() != nullptr);
overloads_.push_back(overload);
......@@ -314,6 +397,11 @@ AsmType* AsmFunctionTableType::ValidateCall(AsmType* return_type,
return signature_->AsCallableType()->ValidateCall(return_type, args);
}
bool AsmFunctionTableType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
return signature_->AsCallableType()->CanBeInvokedWith(return_type, args);
}
} // namespace wasm
} // namespace internal
} // namespace v8
......@@ -110,6 +110,9 @@ class AsmCallableType : public ZoneObject {
virtual AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) = 0;
virtual bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) = 0;
#define DECLARE_CAST(CamelName) \
virtual Asm##CamelName* As##CamelName() { return nullptr; }
FOR_EACH_ASM_CALLABLE_TYPE_LIST(DECLARE_CAST)
......@@ -136,6 +139,8 @@ class AsmFunctionType : public AsmCallableType {
AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
protected:
AsmFunctionType(Zone* zone, AsmType* return_type)
......@@ -168,6 +173,8 @@ class AsmOverloadedFunctionType final : public AsmCallableType {
std::string Name() override;
AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
ZoneVector<AsmType*> overloads_;
......@@ -181,6 +188,8 @@ class AsmFFIType final : public AsmCallableType {
std::string Name() override { return "Function"; }
AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
private:
friend AsmType;
......@@ -198,6 +207,8 @@ class AsmFunctionTableType : public AsmCallableType {
AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
size_t length() const { return length_; }
AsmType* signature() { return signature_; }
......
......@@ -10,6 +10,7 @@
#endif
#include <math.h>
#include "src/asmjs/asm-types.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/wasm/switch-logic.h"
#include "src/wasm/wasm-macro-gen.h"
......@@ -18,7 +19,6 @@
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/codegen.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
......@@ -59,7 +59,6 @@ class AsmWasmBuilderImpl : public AstVisitor {
isolate_(isolate),
zone_(zone),
typer_(typer),
cache_(TypeCache::Get()),
breakable_blocks_(zone),
foreign_variables_(zone),
init_function_index_(0),
......@@ -68,8 +67,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
function_tables_(base::HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
imported_function_table_(this),
bounds_(typer->bounds()) {
imported_function_table_(this) {
InitializeAstVisitor(isolate);
}
......@@ -359,12 +357,10 @@ class AsmWasmBuilderImpl : public AstVisitor {
if (!clause->is_default()) {
Literal* label = clause->label()->AsLiteral();
Handle<Object> value = label->value();
DCHECK(value->IsNumber() &&
bounds_->get(label).upper->Is(cache_.kAsmSigned));
int32_t label_value;
if (!value->ToInt32(&label_value)) {
UNREACHABLE();
}
bool label_is_i32 = value->ToInt32(&label_value);
DCHECK(value->IsNumber() && label_is_i32);
(void)label_is_i32;
case_to_block[label_value] = i;
cases.push_back(label_value);
} else {
......@@ -459,15 +455,15 @@ class AsmWasmBuilderImpl : public AstVisitor {
void VisitFunctionLiteral(FunctionLiteral* expr) override {
Scope* scope = expr->scope();
if (scope_ == kFuncScope) {
if (bounds_->get(expr).lower->IsFunction()) {
if (auto* func_type = typer_->TypeOf(expr)->AsFunctionType()) {
// Build the signature for the function.
FunctionType* func_type = bounds_->get(expr).lower->AsFunction();
LocalType return_type = TypeFrom(func_type->Result());
LocalType return_type = TypeFrom(func_type->ReturnType());
const auto& arguments = func_type->Arguments();
FunctionSig::Builder b(zone(), return_type == kAstStmt ? 0 : 1,
func_type->Arity());
arguments.size());
if (return_type != kAstStmt) b.AddReturn(return_type);
for (int i = 0; i < expr->parameter_count(); ++i) {
LocalType type = TypeFrom(func_type->Parameter(i));
LocalType type = TypeFrom(arguments[i]);
DCHECK_NE(kAstStmt, type);
b.AddParam(type);
InsertParameter(scope->parameter(i), type, i);
......@@ -573,15 +569,17 @@ class AsmWasmBuilderImpl : public AstVisitor {
if (!value->IsNumber() || (scope_ != kFuncScope && scope_ != kInitScope)) {
return;
}
Type* type = bounds_->get(expr).upper;
if (type->Is(cache_.kAsmSigned)) {
AsmType* type = typer_->TypeOf(expr);
DCHECK_NE(type, AsmType::None());
if (type->IsA(AsmType::Signed())) {
int32_t i = 0;
if (!value->ToInt32(&i)) {
UNREACHABLE();
}
byte code[] = {WASM_I32V(i)};
current_function_builder_->EmitCode(code, sizeof(code));
} else if (type->Is(cache_.kAsmUnsigned) || type->Is(cache_.kAsmFixnum)) {
} else if (type->IsA(AsmType::Unsigned()) || type->IsA(AsmType::FixNum())) {
uint32_t u = 0;
if (!value->ToUint32(&u)) {
UNREACHABLE();
......@@ -589,7 +587,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
int32_t i = static_cast<int32_t>(u);
byte code[] = {WASM_I32V(i)};
current_function_builder_->EmitCode(code, sizeof(code));
} else if (type->Is(cache_.kAsmDouble)) {
} else if (type->IsA(AsmType::Double())) {
double val = expr->raw_value()->AsNumber();
byte code[] = {WASM_F64(val)};
current_function_builder_->EmitCode(code, sizeof(code));
......@@ -635,16 +633,18 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
void AddFunctionTable(VariableProxy* table, ArrayLiteral* funcs) {
FunctionType* func_type =
bounds_->get(funcs).lower->AsArray()->Element()->AsFunction();
LocalType return_type = TypeFrom(func_type->Result());
auto* func_tbl_type = typer_->TypeOf(funcs)->AsFunctionTableType();
DCHECK_NOT_NULL(func_tbl_type);
auto* func_type = func_tbl_type->signature()->AsFunctionType();
const auto& arguments = func_type->Arguments();
LocalType return_type = TypeFrom(func_type->ReturnType());
FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1,
func_type->Arity());
arguments.size());
if (return_type != kAstStmt) {
sig.AddReturn(static_cast<LocalType>(return_type));
sig.AddReturn(return_type);
}
for (int i = 0; i < func_type->Arity(); ++i) {
sig.AddParam(TypeFrom(func_type->Parameter(i)));
for (auto* arg : arguments) {
sig.AddParam(TypeFrom(arg));
}
uint32_t signature_index = builder_->AddSignature(sig.Build());
InsertFunctionTable(table->var(), next_table_index_, signature_index);
......@@ -805,8 +805,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
if (target_prop != nullptr) {
// Left hand side is a property access, i.e. the asm.js heap.
if (TypeOf(expr->value()) == kAstF64 && expr->target()->IsProperty() &&
bounds_->get(expr->target()->AsProperty()->obj())
.lower->Is(cache_.kFloat32Array)) {
typer_->TypeOf(expr->target()->AsProperty()->obj())
->IsA(AsmType::Float32Array())) {
current_function_builder_->Emit(kExprF32ConvertF64);
}
WasmOpcode opcode;
......@@ -846,7 +846,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
if (vp != nullptr && vp->var()->IsParameter() &&
vp->var()->index() == 1) {
VariableProxy* target = expr->target()->AsVariableProxy();
if (bounds_->get(target).lower->Is(Type::Function())) {
if (typer_->TypeOf(target)->AsFFIType() != nullptr) {
const AstRawString* name =
prop->key()->AsLiteral()->AsRawPropertyName();
imported_function_table_.AddImport(
......@@ -859,7 +859,10 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
if (funcs != nullptr &&
bounds_->get(funcs).lower->AsArray()->Element()->IsFunction()) {
typer_->TypeOf(funcs)
->AsFunctionTableType()
->signature()
->AsFunctionType()) {
VariableProxy* target = expr->target()->AsVariableProxy();
DCHECK_NOT_NULL(target);
AddFunctionTable(target, funcs);
......@@ -905,34 +908,33 @@ class AsmWasmBuilderImpl : public AstVisitor {
void VisitPropertyAndEmitIndex(Property* expr, MachineType* mtype) {
Expression* obj = expr->obj();
DCHECK_EQ(bounds_->get(obj).lower, bounds_->get(obj).upper);
Type* type = bounds_->get(obj).lower;
AsmType* type = typer_->TypeOf(obj);
int size;
if (type->Is(cache_.kUint8Array)) {
if (type->IsA(AsmType::Uint8Array())) {
*mtype = MachineType::Uint8();
size = 1;
} else if (type->Is(cache_.kInt8Array)) {
} else if (type->IsA(AsmType::Int8Array())) {
*mtype = MachineType::Int8();
size = 1;
} else if (type->Is(cache_.kUint16Array)) {
} else if (type->IsA(AsmType::Uint16Array())) {
*mtype = MachineType::Uint16();
size = 2;
} else if (type->Is(cache_.kInt16Array)) {
} else if (type->IsA(AsmType::Int16Array())) {
*mtype = MachineType::Int16();
size = 2;
} else if (type->Is(cache_.kUint32Array)) {
} else if (type->IsA(AsmType::Uint32Array())) {
*mtype = MachineType::Uint32();
size = 4;
} else if (type->Is(cache_.kInt32Array)) {
} else if (type->IsA(AsmType::Int32Array())) {
*mtype = MachineType::Int32();
size = 4;
} else if (type->Is(cache_.kUint32Array)) {
} else if (type->IsA(AsmType::Uint32Array())) {
*mtype = MachineType::Uint32();
size = 4;
} else if (type->Is(cache_.kFloat32Array)) {
} else if (type->IsA(AsmType::Float32Array())) {
*mtype = MachineType::Float32();
size = 4;
} else if (type->Is(cache_.kFloat64Array)) {
} else if (type->IsA(AsmType::Float64Array())) {
*mtype = MachineType::Float64();
size = 8;
} else {
......@@ -1228,7 +1230,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
}
VisitCallArgs(call);
switch (TypeIndexOf(args->at(0))) {
static const bool kDontIgnoreSign = false;
switch (TypeIndexOf(args->at(0), kDontIgnoreSign)) {
case kInt32:
case kFixnum:
current_function_builder_->Emit(kExprF32SConvertI32);
......@@ -1275,8 +1278,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
uint32_t index;
VariableProxy* vp = expr->expression()->AsVariableProxy();
if (vp != nullptr &&
Type::Any()->Is(bounds_->get(vp).lower->AsFunction()->Result())) {
DCHECK_NOT_NULL(vp);
if (typer_->TypeOf(vp)->AsFFIType() != nullptr) {
LocalType return_type = TypeOf(expr);
ZoneList<Expression*>* args = expr->arguments();
FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1,
......@@ -1479,9 +1482,10 @@ class AsmWasmBuilderImpl : public AstVisitor {
void VisitBinaryOperation(BinaryOperation* expr) override {
ConvertOperation convertOperation = MatchBinaryOperation(expr);
static const bool kDontIgnoreSign = false;
if (convertOperation == kToDouble) {
RECURSE(Visit(expr->left()));
TypeIndex type = TypeIndexOf(expr->left());
TypeIndex type = TypeIndexOf(expr->left(), kDontIgnoreSign);
if (type == kInt32 || type == kFixnum) {
current_function_builder_->Emit(kExprF64SConvertI32);
} else if (type == kUint32) {
......@@ -1493,7 +1497,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
} else if (convertOperation == kToInt) {
RECURSE(Visit(GetLeft(expr)));
TypeIndex type = TypeIndexOf(GetLeft(expr));
TypeIndex type = TypeIndexOf(GetLeft(expr), kDontIgnoreSign);
if (type == kFloat32) {
current_function_builder_->Emit(kExprI32AsmjsSConvertF32);
} else if (type == kFloat64) {
......@@ -1583,8 +1587,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
};
TypeIndex TypeIndexOf(Expression* left, Expression* right, bool ignore_sign) {
TypeIndex left_index = TypeIndexOf(left);
TypeIndex right_index = TypeIndexOf(right);
TypeIndex left_index = TypeIndexOf(left, ignore_sign);
TypeIndex right_index = TypeIndexOf(right, ignore_sign);
if (left_index == kFixnum) {
left_index = right_index;
}
......@@ -1595,31 +1599,44 @@ class AsmWasmBuilderImpl : public AstVisitor {
left_index = kInt32;
right_index = kInt32;
}
DCHECK((left_index == right_index) ||
(ignore_sign && (left_index <= 1) && (right_index <= 1)));
if (left_index != right_index) {
DCHECK(ignore_sign && (left_index <= 1) && (right_index <= 1));
}
return left_index;
}
TypeIndex TypeIndexOf(Expression* expr) {
DCHECK_EQ(bounds_->get(expr).lower, bounds_->get(expr).upper);
Type* type = bounds_->get(expr).lower;
if (type->Is(cache_.kAsmFixnum)) {
TypeIndex TypeIndexOf(Expression* expr, bool ignore_sign) {
AsmType* type = typer_->TypeOf(expr);
if (type->IsA(AsmType::FixNum())) {
return kFixnum;
} else if (type->Is(cache_.kAsmSigned)) {
}
if (type->IsA(AsmType::Signed())) {
return kInt32;
} else if (type->Is(cache_.kAsmUnsigned)) {
}
if (type->IsA(AsmType::Unsigned())) {
return kUint32;
} else if (type->Is(cache_.kAsmInt)) {
}
if (type->IsA(AsmType::Intish())) {
if (!ignore_sign) {
// TODO(jpp): log a warning and move on.
}
return kInt32;
} else if (type->Is(cache_.kAsmFloat)) {
}
if (type->IsA(AsmType::Floatish())) {
return kFloat32;
} else if (type->Is(cache_.kAsmDouble)) {
}
if (type->IsA(AsmType::DoubleQ())) {
return kFloat64;
} else {
}
UNREACHABLE();
return kInt32;
}
}
#undef CASE
#undef NON_SIGNED_INT
......@@ -1721,21 +1738,22 @@ class AsmWasmBuilderImpl : public AstVisitor {
return (reinterpret_cast<IndexContainer*>(entry->value))->index;
}
LocalType TypeOf(Expression* expr) {
DCHECK_EQ(bounds_->get(expr).lower, bounds_->get(expr).upper);
return TypeFrom(bounds_->get(expr).lower);
}
LocalType TypeOf(Expression* expr) { return TypeFrom(typer_->TypeOf(expr)); }
LocalType TypeFrom(Type* type) {
if (type->Is(cache_.kAsmInt)) {
LocalType TypeFrom(AsmType* type) {
if (type->IsA(AsmType::Intish())) {
return kAstI32;
} else if (type->Is(cache_.kAsmFloat)) {
}
if (type->IsA(AsmType::Floatish())) {
return kAstF32;
} else if (type->Is(cache_.kAsmDouble)) {
}
if (type->IsA(AsmType::DoubleQ())) {
return kAstF64;
} else {
return kAstStmt;
}
return kAstStmt;
}
Zone* zone() { return zone_; }
......@@ -1750,7 +1768,6 @@ class AsmWasmBuilderImpl : public AstVisitor {
Isolate* isolate_;
Zone* zone_;
AsmTyper* typer_;
TypeCache const& cache_;
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
ZoneVector<ForeignVariable> foreign_variables_;
uint32_t init_function_index_;
......@@ -1758,7 +1775,6 @@ class AsmWasmBuilderImpl : public AstVisitor {
uint32_t next_table_index_;
ZoneHashMap function_tables_;
ImportedFunctionTable imported_function_table_;
const AstTypeBounds* bounds_;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
......
......@@ -6,7 +6,7 @@
#define V8_ASMJS_ASM_WASM_BUILDER_H_
#include "src/allocation.h"
#include "src/asmjs/typing-asm.h"
#include "src/asmjs/asm-typer.h"
#include "src/objects.h"
#include "src/wasm/encoder.h"
#include "src/zone.h"
......
......@@ -504,7 +504,6 @@ DEFINE_BOOL(wasm_loop_assignment_analysis, true,
"perform loop assignment analysis for WASM")
DEFINE_BOOL(validate_asm, false, "validate asm.js modules before compiling")
DEFINE_BOOL(enable_simd_asmjs, false, "enable SIMD.js in asm.js stdlib")
DEFINE_BOOL(dump_wasm_module, false, "dump WASM module bytes")
DEFINE_STRING(dump_wasm_module_path, NULL, "directory to dump wasm modules to")
......
......@@ -149,11 +149,8 @@ v8::internal::wasm::ZoneBuffer* TranslateAsmModule(
info->set_literal(
info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun());
v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
info->literal());
if (i::FLAG_enable_simd_asmjs) {
typer.set_allow_simd(true);
}
v8::internal::wasm::AsmTyper typer(info->isolate(), info->zone(),
*(info->script()), info->literal());
if (!typer.Validate()) {
thrower->Error("Asm.js validation failed: %s", typer.error_message());
return nullptr;
......
......@@ -4,7 +4,7 @@
// Flags: --expose-wasm
function __f_61(stdlib, buffer) {
function __f_61(stdlib, foreign, buffer) {
"use asm";
var __v_14 = new stdlib.Float64Array(buffer);
function __f_74() {
......
......@@ -83,33 +83,34 @@
}
function caller() {
if (!deltaEqual(StdlibMathSqrt(123.0), 11.090536506409418)) return 0;
if (StdlibMathSqrt(fround(256.0)) != fround(16.0)) return 0;
if (StdlibMathCeil(123.7) != 124.0) return 0;
if (StdlibMathCeil(fround(123.7)) != fround(124.0)) return 0;
if (StdlibMathFloor(123.7) != 123.0) return 0;
if (StdlibMathFloor(fround(123.7)) != fround(123.0)) return 0;
if (StdlibMathAbs(-123.0) != 123.0) return 0;
if (StdlibMathAbs(fround(-123.0)) != fround(123.0)) return 0;
if (StdlibMathMin(123.4, 1236.4) != 123.4) return 0;
if (StdlibMathMin(fround(123.4),
fround(1236.4)) != fround(123.4)) return 0;
if (StdlibMathMax(123.4, 1236.4) != 1236.4) return 0;
if (StdlibMathMax(fround(123.4), fround(1236.4))
if (!(deltaEqual(+StdlibMathSqrt(123.0), 11.090536506409418)|0)) return 0;
if (fround(StdlibMathSqrt(fround(256.0))) != fround(16.0)) return 0;
if (+StdlibMathCeil(123.7) != 124.0) return 0;
if (fround(StdlibMathCeil(fround(123.7))) != fround(124.0)) return 0;
if (+StdlibMathFloor(123.7) != 123.0) return 0;
if (fround(StdlibMathFloor(fround(123.7))) != fround(123.0)) return 0;
if (+StdlibMathAbs(-123.0) != 123.0) return 0;
if (fround(StdlibMathAbs(fround(-123.0))) != fround(123.0)) return 0;
if (+StdlibMathMin(123.4, 1236.4) != 123.4) return 0;
if (fround(StdlibMathMin(fround(123.4),
fround(1236.4))) != fround(123.4)) return 0;
if (+StdlibMathMax(123.4, 1236.4) != 1236.4) return 0;
if (fround(StdlibMathMax(fround(123.4), fround(1236.4)))
!= fround(1236.4)) return 0;
if (!deltaEqual(StdlibMathAcos(0.1), 1.4706289056333368)) return 0;
if (!deltaEqual(StdlibMathAsin(0.2), 0.2013579207903308)) return 0;
if (!deltaEqual(StdlibMathAtan(0.2), 0.19739555984988078)) return 0;
if (!deltaEqual(StdlibMathCos(0.2), 0.9800665778412416)) return 0;
if (!deltaEqual(StdlibMathSin(0.2), 0.19866933079506122)) return 0;
if (!deltaEqual(StdlibMathTan(0.2), 0.20271003550867250)) return 0;
if (!deltaEqual(StdlibMathExp(0.2), 1.2214027581601699)) return 0;
if (!deltaEqual(StdlibMathLog(0.2), -1.6094379124341003)) return 0;
if (StdlibMathImul(6, 7) != 42) return 0;
if (!deltaEqual(StdlibMathAtan2(6.0, 7.0), 0.7086262721276703)) return 0;
if (StdlibMathPow(6.0, 7.0) != 279936.0) return 0;
if (!(deltaEqual(+StdlibMathAcos(0.1), 1.4706289056333368)|0)) return 0;
if (!(deltaEqual(+StdlibMathAsin(0.2), 0.2013579207903308)|0)) return 0;
if (!(deltaEqual(+StdlibMathAtan(0.2), 0.19739555984988078)|0)) return 0;
if (!(deltaEqual(+StdlibMathCos(0.2), 0.9800665778412416)|0)) return 0;
if (!(deltaEqual(+StdlibMathSin(0.2), 0.19866933079506122)|0)) return 0;
if (!(deltaEqual(+StdlibMathTan(0.2), 0.20271003550867250)|0)) return 0;
if (!(deltaEqual(+StdlibMathExp(0.2), 1.2214027581601699)|0)) return 0;
if (!(deltaEqual(+StdlibMathLog(0.2), -1.6094379124341003)|0)) return 0;
if ((StdlibMathImul(6, 7)|0) != 42) return 0;
if (!(deltaEqual(+StdlibMathAtan2(6.0, 7.0), 0.7086262721276703)|0))
return 0;
if (+StdlibMathPow(6.0, 7.0) != 279936.0) return 0;
return 1;
}
......
......@@ -11,7 +11,7 @@
function caller() {
var ret = 0;
var x = 7;
switch (x) {
switch (x|0) {
case 1: {
return 0;
}
......@@ -37,7 +37,7 @@
function caller() {
var ret = 0;
var x = 7;
switch (x) {
switch (x|0) {
case 1: return 0;
case 7: {
ret = 12;
......@@ -45,7 +45,7 @@
}
default: return 0;
}
switch (x) {
switch (x|0) {
case 1: return 0;
case 8: return 0;
default: ret = (ret + 11)|0;
......@@ -66,7 +66,7 @@
function caller() {
var x = 17;
var ret = 0;
switch (x) {
switch (x|0) {
case 17:
case 14: ret = 39;
case 1: ret = (ret + 3)|0;
......@@ -89,10 +89,10 @@
function caller() {
var x = 3;
var y = -13;
switch (x) {
switch (x|0) {
case 1: return 0;
case 3: {
switch (y) {
switch (y|0) {
case 2: return 0;
case -13: return 43;
default: return 0;
......
......@@ -43,9 +43,11 @@ function IntTest() {
function sum(a, b) {
a = a|0;
b = b|0;
var c = (b + 1)|0
var c = 0;
var d = 3.0;
var e = ~~d; // double conversion
var e = 0;
e = ~~d; // double conversion
c = (b + 1)|0
return (a + c + 1)|0;
}
......@@ -68,8 +70,9 @@ function Float64Test() {
}
function caller() {
var a = +sum(70.1,10.2);
var a = 0.0;
var ret = 0|0;
a = +sum(70.1,10.2);
if (a == 80.3) {
ret = 1|0;
} else {
......@@ -89,7 +92,8 @@ function BadModule() {
function caller(a, b) {
a = a|0;
b = b+0;
var c = (b + 1)|0
var c = 0;
c = (b + 1)|0
return (a + c + 1)|0;
}
......@@ -293,12 +297,12 @@ function TestBreakInNestedWhile() {
function caller() {
var x = 1.0;
var ret = 0;
while(x < 1.5) {
while(1)
break;
x = +(x + 0.25);
}
var ret = 0;
if (x == 1.5) {
ret = 9;
}
......@@ -405,7 +409,8 @@ function TestNot() {
"use asm";
function caller() {
var a = !(2 > 3);
var a = 0;
a = !(2 > 3);
return a | 0;
}
......@@ -886,7 +891,9 @@ function TestFunctionTableSingleFunction() {
}
function caller() {
return function_table[0&0]() | 0;
// TODO(jpp): the parser optimizes function_table[0&0] to function table[0].
var v = 0;
return function_table[v&0]() | 0;
}
var function_table = [dummy]
......@@ -911,8 +918,9 @@ function TestFunctionTableMultipleFunctions() {
}
function caller() {
if ((function_table[0&1](50)|0) == 51) {
if ((function_table[1&1](60)|0) == 62) {
var i = 0, j = 1;
if ((function_table[i&1](50)|0) == 51) {
if ((function_table[j&1](60)|0) == 62) {
return 73;
}
}
......@@ -1350,7 +1358,7 @@ assertWasm(1, TestXor);
"use asm";
function func() {
var a = 1;
return ((a * 3) + (4 * a)) | 0;
return (((a * 3)|0) + ((4 * a)|0)) | 0;
}
return {func: func};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment