Make typing-asm match spec more closely around load/store, add more tests.

Shifts of integer values are in some contexts collapsed by the parser into single literal AST nodes, rather than a direct representation of the parse tree. Confirming this behavior in tests.

Integer TypedArrays are assumed to load and store "intish" values rather than more fine-grained type information. Reducing the precision of the typing information to match the spec and simplify the wasm generator.

The asm spec requires load and store values of various "float?", "floatish", "double?" and "intish" types to ensure undefined values are not visible and that float32 rounding occurs at the right time. More closely matching this.

Adding additional testing around unsigned / signed comparisons, loads and stores.

Adding addition debug mode printing when asserting about types fail.

BUG= https://code.google.com/p/v8/issues/detail?id=4203
TEST=test-asm-validator, wasm side tests
R=titzer@chromium.org,aseemgarg@chromium.org
LOG=N

Review URL: https://codereview.chromium.org/1471073003

Cr-Commit-Position: refs/heads/master@{#32419}
This commit is contained in:
bradnelson 2015-11-30 13:11:35 -08:00 committed by Commit bot
parent 000baddfd3
commit cf5546baad
5 changed files with 408 additions and 24 deletions

View File

@ -58,6 +58,9 @@ class TypeCache final {
Type* const kSafeInteger = CreateRange(-kMaxSafeInteger, kMaxSafeInteger);
Type* const kPositiveSafeInteger = CreateRange(0.0, kMaxSafeInteger);
Type* const kUntaggedUndefined =
Type::Intersect(Type::Undefined(), Type::Untagged(), zone());
// Asm.js related types.
Type* const kAsmSigned = kInt32;
Type* const kAsmUnsigned = kUint32;
@ -65,6 +68,12 @@ class TypeCache final {
Type* const kAsmFixnum = Type::Intersect(kAsmSigned, kAsmUnsigned, zone());
Type* const kAsmFloat = kFloat32;
Type* const kAsmDouble = kFloat64;
Type* const kAsmFloatQ = Type::Union(kAsmFloat, kUntaggedUndefined, zone());
Type* const kAsmDoubleQ = Type::Union(kAsmDouble, kUntaggedUndefined, zone());
// Not part of the Asm.js type hierarchy, but represents a part of what
// intish encompasses.
Type* const kAsmIntQ = Type::Union(kAsmInt, kUntaggedUndefined, zone());
Type* const kAsmFloatDoubleQ = Type::Union(kAsmFloatQ, kAsmDoubleQ, zone());
// Asm.js size unions.
Type* const kAsmSize8 = Type::Union(kInt8, kUint8, zone());
Type* const kAsmSize16 = Type::Union(kInt16, kUint16, zone());
@ -77,6 +86,11 @@ class TypeCache final {
Type::Union(kAsmUnsigned, Type::Union(kAsmDouble, kAsmFloat, zone()),
zone()),
zone());
Type* const kAsmIntArrayElement =
Type::Union(Type::Union(kInt8, kUint8, zone()),
Type::Union(Type::Union(kInt16, kUint16, zone()),
Type::Union(kInt32, kUint32, zone()), zone()),
zone());
// The FixedArray::length property always containts a smi in the range
// [0, FixedArray::kMaxLength].

View File

@ -41,6 +41,7 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
script_(script),
root_(root),
valid_(true),
intish_(0),
stdlib_types_(zone),
stdlib_heap_types_(zone),
stdlib_math_types_(zone),
@ -155,7 +156,7 @@ void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
if (literal) {
RECURSE(VisitLiteral(literal, true));
} else {
RECURSE(VisitExpressionAnnotation(stmt->expression(), true));
RECURSE(VisitExpressionAnnotation(stmt->expression(), NULL, true));
}
expected_type_ = old_expected;
result_type = computed_type_;
@ -179,7 +180,7 @@ void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
Variable* var = proxy->var();
if (var->location() != VariableLocation::PARAMETER || var->index() != i)
break;
RECURSE(VisitExpressionAnnotation(expr->value(), false));
RECURSE(VisitExpressionAnnotation(expr->value(), var, false));
SetType(var, computed_type_);
type->InitParameter(i, computed_type_);
good = true;
@ -190,10 +191,20 @@ void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
}
void AsmTyper::VisitExpressionAnnotation(Expression* expr, bool is_return) {
void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var,
bool is_return) {
// Normal +x or x|0 annotations.
BinaryOperation* bin = expr->AsBinaryOperation();
if (bin != NULL) {
if (var != NULL) {
VariableProxy* left = bin->left()->AsVariableProxy();
if (!left) {
FAIL(expr, "variable name expected in type annotation");
}
if (left->var() != var) {
FAIL(left, "variable type annotation references other variable");
}
}
Literal* right = bin->right()->AsLiteral();
if (right != NULL) {
switch (bin->op()) {
@ -593,19 +604,23 @@ void AsmTyper::VisitAssignment(Assignment* expr) {
Type* type = expected_type_;
RECURSE(VisitWithExpectation(
expr->value(), type, "assignment value expected to match surrounding"));
Type* target_type = StorageType(computed_type_);
if (intish_ != 0) {
FAIL(expr, "value still an intish");
FAIL(expr, "intish or floatish assignment");
}
Type* target_type = computed_type_;
if (target_type->Is(cache_.kAsmInt)) {
target_type = cache_.kAsmInt;
if (expr->target()->IsVariableProxy()) {
RECURSE(VisitWithExpectation(expr->target(), target_type,
"assignment target expected to match value"));
} else if (expr->target()->IsProperty()) {
Property* property = expr->target()->AsProperty();
RECURSE(VisitWithExpectation(property->obj(), Type::Any(),
"bad propety object"));
if (!computed_type_->IsArray()) {
FAIL(property->obj(), "array expected");
}
VisitHeapAccess(property, true, target_type);
}
RECURSE(VisitWithExpectation(expr->target(), target_type,
"assignment target expected to match value"));
if (intish_ != 0) {
FAIL(expr, "value still an intish");
}
IntersectResult(expr, computed_type_);
IntersectResult(expr, target_type);
}
@ -637,11 +652,15 @@ Type* AsmTyper::StorageType(Type* type) {
}
void AsmTyper::VisitHeapAccess(Property* expr) {
void AsmTyper::VisitHeapAccess(Property* expr, bool assigning,
Type* assignment_type) {
Type::ArrayType* array_type = computed_type_->AsArray();
size_t size = array_size_;
Type* type = array_type->AsArray()->Element();
if (type->IsFunction()) {
if (assigning) {
FAIL(expr, "assigning to function table is illegal");
}
BinaryOperation* bin = expr->key()->AsBinaryOperation();
if (bin == NULL || bin->op() != Token::BIT_AND) {
FAIL(expr->key(), "expected & in call");
@ -658,6 +677,7 @@ void AsmTyper::VisitHeapAccess(Property* expr) {
FAIL(right, "call mask must match function table");
}
bin->set_bounds(Bounds(cache_.kAsmSigned));
IntersectResult(expr, type);
} else {
Literal* literal = expr->key()->AsLiteral();
if (literal) {
@ -683,8 +703,39 @@ void AsmTyper::VisitHeapAccess(Property* expr) {
}
bin->set_bounds(Bounds(cache_.kAsmSigned));
}
Type* result_type;
if (type->Is(cache_.kAsmIntArrayElement)) {
result_type = cache_.kAsmIntQ;
intish_ = kMaxUncombinedAdditiveSteps;
} else if (type->Is(cache_.kAsmFloat)) {
if (assigning) {
result_type = cache_.kAsmFloatDoubleQ;
} else {
result_type = cache_.kAsmFloatQ;
}
intish_ = 0;
} else if (type->Is(cache_.kAsmDouble)) {
if (assigning) {
result_type = cache_.kAsmFloatDoubleQ;
if (intish_ != 0) {
FAIL(expr, "Assignment of floatish to Float64Array");
}
} else {
result_type = cache_.kAsmDoubleQ;
}
intish_ = 0;
} else {
UNREACHABLE();
}
if (assigning) {
if (!assignment_type->Is(result_type)) {
FAIL(expr, "illegal type in assignment");
}
} else {
IntersectResult(expr, expected_type_);
IntersectResult(expr, result_type);
}
}
IntersectResult(expr, type);
}
@ -720,12 +771,11 @@ void AsmTyper::VisitProperty(Property* expr) {
// Only recurse at this point so that we avoid needing
// stdlib.Math to have a real type.
RECURSE(VisitWithExpectation(expr->obj(), Type::Any(),
"property holder expected to be object"));
RECURSE(VisitWithExpectation(expr->obj(), Type::Any(), "bad propety object"));
// For heap view or function table access.
if (computed_type_->IsArray()) {
VisitHeapAccess(expr);
VisitHeapAccess(expr, false, NULL);
return;
}
@ -780,6 +830,7 @@ void AsmTyper::VisitCall(Call* expr) {
arg, fun_type->Parameter(i),
"call argument expected to match callee parameter"));
}
intish_ = 0;
IntersectResult(expr, fun_type->Result());
} else if (computed_type_->Is(Type::Any())) {
// For foreign calls.
@ -789,6 +840,7 @@ void AsmTyper::VisitCall(Call* expr) {
RECURSE(VisitWithExpectation(arg, Type::Any(),
"foreign call argument expected to be any"));
}
intish_ = kMaxUncombinedAdditiveSteps;
IntersectResult(expr, Type::Number());
} else {
FAIL(expr, "invalid callee");
@ -994,13 +1046,17 @@ void AsmTyper::VisitBinaryOperation(BinaryOperation* expr) {
IntersectResult(expr, cache_.kAsmInt);
return;
}
} else if (expr->op() == Token::MUL && left_type->Is(cache_.kAsmInt) &&
} else if (expr->op() == Token::MUL && expr->right()->IsLiteral() &&
right_type->Is(cache_.kAsmDouble)) {
// For unary +, expressed as x * 1.0
IntersectResult(expr, cache_.kAsmDouble);
return;
} else if (type->Is(cache_.kAsmFloat) && expr->op() != Token::MOD) {
if (left_intish != 0 || right_intish != 0) {
FAIL(expr, "float operation before required fround");
}
IntersectResult(expr, cache_.kAsmFloat);
intish_ = 1;
return;
} else if (type->Is(cache_.kAsmDouble)) {
IntersectResult(expr, cache_.kAsmDouble);

View File

@ -65,11 +65,11 @@ class AsmTyper : public AstVisitor {
void VisitDeclarations(ZoneList<Declaration*>* d) override;
void VisitStatements(ZoneList<Statement*>* s) override;
void VisitExpressionAnnotation(Expression* e, bool is_return);
void VisitExpressionAnnotation(Expression* e, Variable* var, bool is_return);
void VisitFunctionAnnotation(FunctionLiteral* f);
void VisitAsmModule(FunctionLiteral* f);
void VisitHeapAccess(Property* expr);
void VisitHeapAccess(Property* expr, bool assigning, Type* assignment_type);
int ElementShiftSize(Type* type);
Type* StorageType(Type* type);

View File

@ -14,11 +14,30 @@
CHECK_EQ(index, types.size()); \
}
#ifdef DEBUG
#define CHECK_TYPE(type) \
if (!types[index].bounds.Narrows(type)) { \
fprintf(stderr, "Expected:\n"); \
fprintf(stderr, " lower: "); \
type.lower->Print(); \
fprintf(stderr, " upper: "); \
type.upper->Print(); \
fprintf(stderr, "Actual:\n"); \
fprintf(stderr, " lower: "); \
types[index].bounds.lower->Print(); \
fprintf(stderr, " upper: "); \
types[index].bounds.upper->Print(); \
} \
CHECK(types[index].bounds.Narrows(type));
#else
#define CHECK_TYPE(type) CHECK(types[index].bounds.Narrows(type));
#endif
#define CHECK_EXPR(ekind, type) \
CHECK_LT(index, types.size()); \
CHECK(strcmp(#ekind, types[index].kind) == 0); \
CHECK_EQ(depth, types[index].depth); \
CHECK(types[index].bounds.Narrows(type)); \
CHECK_TYPE(type); \
for (int j = (++depth, ++index, 0); j < 1 ? 1 : (--depth, 0); ++j)
#define CHECK_VAR(vname, type) \

View File

@ -841,6 +841,69 @@ TEST_INT_BIN_OP(OrOperator, "|")
TEST_INT_BIN_OP(XorOperator, "^")
TEST(SignedCompare) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x|0) < (y|0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(SignedCompareConst) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x|0) < (1<<31))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedCompare) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < (y>>>0))|0; }\n"
@ -874,6 +937,67 @@ TEST(UnsignedCompare) {
}
TEST(UnsignedCompareConst0) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < (0>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedCompareConst1) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < "
"(0xffffffff>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmUnsigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedDivide) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) / (y>>>0))|0; }\n"
@ -1146,7 +1270,7 @@ TEST(Load1) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, Bounds(cache.kInt8)) {
CHECK_EXPR(Property, Bounds(cache.kAsmInt)) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmSigned));
@ -1163,6 +1287,101 @@ TEST(Load1) {
}
TEST(LoadDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 0.0; y = +f64[x>>3]; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Property, Bounds(cache.kAsmDouble)) {
CHECK_VAR(f64, Bounds(cache.kFloat64Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmSigned));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Store1) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; i8[x>>0] = 0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmSigned));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(StoreFloat) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = fround(1.0); "
"f32[0] = fround(x + fround(1.0)); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(f32, Bounds(cache.kFloat32Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Load1Constant) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = i8[5]|0; }\n"
@ -1175,7 +1394,7 @@ TEST(Load1Constant) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, Bounds(cache.kInt8)) {
CHECK_EXPR(Property, Bounds(cache.kAsmInt)) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
@ -1436,6 +1655,14 @@ TEST(MismatchedReturnTypeExpression) {
}
TEST(AssignToFloatishToF64) {
CHECK_FUNC_ERROR(
"function bar() { var v = fround(1.0); f32[0] = v + fround(1.0); }\n"
"function foo() { bar(); }",
"asm: line 39: intish or floatish assignment\n");
}
TEST(ForeignFunction) {
CHECK_FUNC_TYPES_BEGIN(
"var baz = foreign.baz;\n"
@ -1484,6 +1711,74 @@ TEST(BadExports) {
}
TEST(NestedHeapAssignment) {
CHECK_FUNC_ERROR(
"function bar() { var x = 0; i8[x = 1] = 2; }\n"
"function foo() { bar(); }",
"asm: line 39: expected >> in heap access\n");
}
TEST(BadArrayAssignment) {
CHECK_FUNC_ERROR(
"function bar() { i8[0] = 0.0; }\n"
"function foo() { bar(); }",
"asm: line 39: illegal type in assignment\n");
}
TEST(NestedVariableAssignment) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; x = x = 4; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NestedAssignmentInHeap) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; i8[(x = 1) >> 0] = 2; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(TypeConsistency) {
v8::V8::Initialize();
TypeCache cache;