Commit f245717d authored by bradnelson's avatar bradnelson Committed by Commit bot

Retain information on which standard objects are used in asm typer.

Reconstructing which stdlib object is used within an asm.js
module seems wasteful, given the typer has already checked this.
Preserving this information in a form that can be queried in
the asm-wasm-builder which generating code.

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

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

Cr-Commit-Position: refs/heads/master@{#32687}
parent 6fbd4240
......@@ -99,7 +99,6 @@ void AsmTyper::VisitAsmModule(FunctionLiteral* fun) {
if (decl != NULL) {
RECURSE(VisitFunctionAnnotation(decl->fun()));
Variable* var = decl->proxy()->var();
DCHECK(GetType(var) == NULL);
if (property_info_ != NULL) {
SetVariableInfo(var, property_info_);
property_info_ = NULL;
......@@ -151,6 +150,10 @@ void AsmTyper::VisitFunctionDeclaration(FunctionDeclaration* decl) {
if (in_function_) {
FAIL(decl, "function declared inside another");
}
// Set function type so global references to functions have some type
// (so they can give a more useful error).
Variable* var = decl->proxy()->var();
SetType(var, Type::Function(zone()));
}
......@@ -535,7 +538,7 @@ void AsmTyper::VisitVariableProxy(VariableProxy* expr) {
if (type->Is(cache_.kAsmInt)) {
type = cache_.kAsmInt;
}
SetType(var, type);
info->type = type;
intish_ = 0;
IntersectResult(expr, type);
}
......@@ -776,14 +779,14 @@ bool AsmTyper::IsStdlibObject(Expression* expr) {
Variable* var = proxy->var();
VariableInfo* info = GetVariableInfo(var, false);
if (info) {
if (info->is_stdlib_object) return info->is_stdlib_object;
if (info->standard_member == kStdlib) return true;
}
if (var->location() != VariableLocation::PARAMETER || var->index() != 0) {
return false;
}
info = GetVariableInfo(var, true);
info->type = Type::Object();
info->is_stdlib_object = true;
info->standard_member = kStdlib;
return true;
}
......@@ -874,8 +877,20 @@ void AsmTyper::VisitProperty(Property* expr) {
void AsmTyper::VisitCall(Call* expr) {
RECURSE(VisitWithExpectation(expr->expression(), Type::Any(),
"callee expected to be any"));
StandardMember standard_member = kNone;
VariableProxy* proxy = expr->expression()->AsVariableProxy();
if (proxy) {
standard_member = VariableAsStandardMember(proxy->var());
}
if (!in_function_ && (proxy == NULL || standard_member != kMathFround)) {
FAIL(expr, "calls forbidden outside function bodies");
}
if (proxy == NULL && !expr->expression()->IsProperty()) {
FAIL(expr, "calls must be to bound variables or function tables");
}
if (computed_type_->IsFunction()) {
Type::FunctionType* fun_type = computed_type_->AsFunction();
Type* result_type = fun_type->Result();
ZoneList<Expression*>* args = expr->arguments();
if (fun_type->Arity() != args->length()) {
FAIL(expr, "call with wrong arity");
......@@ -885,9 +900,36 @@ void AsmTyper::VisitCall(Call* expr) {
RECURSE(VisitWithExpectation(
arg, fun_type->Parameter(i),
"call argument expected to match callee parameter"));
if (standard_member != kNone && standard_member != kMathFround &&
i == 0) {
result_type = computed_type_;
}
}
// Handle polymorphic stdlib functions specially.
if (standard_member == kMathCeil || standard_member == kMathFloor ||
standard_member == kMathSqrt) {
if (!args->at(0)->bounds().upper->Is(cache_.kAsmFloat) &&
!args->at(0)->bounds().upper->Is(cache_.kAsmDouble)) {
FAIL(expr, "illegal function argument type");
}
} else if (standard_member == kMathAbs || standard_member == kMathMin ||
standard_member == kMathMax) {
if (!args->at(0)->bounds().upper->Is(cache_.kAsmFloat) &&
!args->at(0)->bounds().upper->Is(cache_.kAsmDouble) &&
!args->at(0)->bounds().upper->Is(cache_.kAsmSigned)) {
FAIL(expr, "illegal function argument type");
}
if (args->length() > 1) {
Type* other = Type::Intersect(args->at(0)->bounds().upper,
args->at(1)->bounds().upper, zone());
if (!other->Is(cache_.kAsmFloat) && !other->Is(cache_.kAsmDouble) &&
!other->Is(cache_.kAsmSigned)) {
FAIL(expr, "function arguments types don't match");
}
}
}
intish_ = 0;
IntersectResult(expr, fun_type->Result());
IntersectResult(expr, result_type);
} else if (computed_type_->Is(Type::Any())) {
// For foreign calls.
ZoneList<Expression*>* args = expr->arguments();
......@@ -1230,34 +1272,52 @@ void AsmTyper::InitializeStdlib() {
Type::Function(cache_.kAsmSigned, cache_.kAsmInt, cache_.kAsmInt, zone());
// TODO(bradnelson): currently only approximating the proper intersection type
// (which we cannot currently represent).
Type* abs_type = Type::Function(number_type, number_type, zone());
Type* number_fn1_type = Type::Function(number_type, number_type, zone());
Type* number_fn2_type =
Type::Function(number_type, number_type, number_type, zone());
struct Assignment {
const char* name;
StandardMember standard_member;
Type* type;
};
const Assignment math[] = {
{"PI", double_type}, {"E", double_type},
{"LN2", double_type}, {"LN10", double_type},
{"LOG2E", double_type}, {"LOG10E", double_type},
{"SQRT2", double_type}, {"SQRT1_2", double_type},
{"imul", imul_type}, {"abs", abs_type},
{"ceil", double_fn1_type}, {"floor", double_fn1_type},
{"fround", fround_type}, {"pow", double_fn2_type},
{"exp", double_fn1_type}, {"log", double_fn1_type},
{"min", double_fn2_type}, {"max", double_fn2_type},
{"sqrt", double_fn1_type}, {"cos", double_fn1_type},
{"sin", double_fn1_type}, {"tan", double_fn1_type},
{"acos", double_fn1_type}, {"asin", double_fn1_type},
{"atan", double_fn1_type}, {"atan2", double_fn2_type}};
const Assignment math[] = {{"PI", kMathPI, double_type},
{"E", kMathE, double_type},
{"LN2", kMathLN2, double_type},
{"LN10", kMathLN10, double_type},
{"LOG2E", kMathLOG2E, double_type},
{"LOG10E", kMathLOG10E, double_type},
{"SQRT2", kMathSQRT2, double_type},
{"SQRT1_2", kMathSQRT1_2, double_type},
{"imul", kMathImul, imul_type},
{"abs", kMathAbs, number_fn1_type},
{"ceil", kMathCeil, number_fn1_type},
{"floor", kMathFloor, number_fn1_type},
{"fround", kMathFround, fround_type},
{"pow", kMathPow, double_fn2_type},
{"exp", kMathExp, double_fn1_type},
{"log", kMathLog, double_fn1_type},
{"min", kMathMin, number_fn2_type},
{"max", kMathMax, number_fn2_type},
{"sqrt", kMathSqrt, number_fn1_type},
{"cos", kMathCos, double_fn1_type},
{"sin", kMathSin, double_fn1_type},
{"tan", kMathTan, double_fn1_type},
{"acos", kMathAcos, double_fn1_type},
{"asin", kMathAsin, double_fn1_type},
{"atan", kMathAtan, double_fn1_type},
{"atan2", kMathAtan2, double_fn2_type}};
for (unsigned i = 0; i < arraysize(math); ++i) {
stdlib_math_types_[math[i].name] = new (zone()) VariableInfo(math[i].type);
stdlib_math_types_[math[i].name]->standard_member = math[i].standard_member;
}
stdlib_math_types_["fround"]->is_check_function = true;
stdlib_types_["Infinity"] = new (zone()) VariableInfo(double_type);
stdlib_types_["Infinity"]->standard_member = kInfinity;
stdlib_types_["NaN"] = new (zone()) VariableInfo(double_type);
stdlib_types_["NaN"]->standard_member = kNaN;
Type* buffer_type = Type::Any(zone());
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
stdlib_types_[#TypeName "Array"] = new (zone()) VariableInfo( \
......@@ -1326,6 +1386,8 @@ AsmTyper::VariableInfo* AsmTyper::GetVariableInfo(Variable* variable,
if (!entry && in_function_) {
entry =
global_variable_type_.Lookup(variable, ComputePointerHash(variable));
if (entry && entry->value) {
}
}
}
if (!entry) return NULL;
......@@ -1340,9 +1402,17 @@ AsmTyper::VariableInfo* AsmTyper::GetVariableInfo(Variable* variable,
void AsmTyper::SetVariableInfo(Variable* variable, const VariableInfo* info) {
VariableInfo* dest = GetVariableInfo(variable, true);
dest->type = info->type;
dest->is_stdlib_object = info->is_stdlib_object;
dest->is_check_function = info->is_check_function;
dest->is_constructor_function = info->is_constructor_function;
dest->standard_member = info->standard_member;
}
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(
Variable* variable) {
VariableInfo* info = GetVariableInfo(variable, false);
if (!info) return kNone;
return info->standard_member;
}
......
......@@ -25,6 +25,41 @@ class AsmTyper : public AstVisitor {
void set_allow_simd(bool simd);
const char* error_message() { return error_message_; }
enum StandardMember {
kNone = 0,
kStdlib,
kInfinity,
kNaN,
kMathAcos,
kMathAsin,
kMathAtan,
kMathCos,
kMathSin,
kMathTan,
kMathExp,
kMathLog,
kMathCeil,
kMathFloor,
kMathSqrt,
kMathAbs,
kMathMin,
kMathMax,
kMathAtan2,
kMathPow,
kMathImul,
kMathFround,
kMathE,
kMathLN10,
kMathLN2,
kMathLOG2E,
kMathLOG10E,
kMathPI,
kMathSQRT1_2,
kMathSQRT2,
};
StandardMember VariableAsStandardMember(Variable* variable);
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
private:
......@@ -37,17 +72,20 @@ class AsmTyper : public AstVisitor {
struct VariableInfo : public ZoneObject {
Type* type;
bool is_stdlib_object;
bool is_check_function;
bool is_constructor_function;
StandardMember standard_member;
VariableInfo()
: type(NULL),
is_stdlib_object(false),
is_check_function(false),
is_constructor_function(false) {}
is_constructor_function(false),
standard_member(kNone) {}
explicit VariableInfo(Type* t)
: type(t), is_check_function(false), is_constructor_function(false) {}
: type(t),
is_check_function(false),
is_constructor_function(false),
standard_member(kNone) {}
};
// Information for bi-directional typing with a cap on nesting depth.
......
......@@ -36,6 +36,9 @@
#define FUNC_DD2D_TYPE \
Bounds(Type::Function(cache.kAsmDouble, cache.kAsmDouble, cache.kAsmDouble, \
zone))
#define FUNC_NN2N_TYPE \
Bounds(Type::Function(Type::Number(zone), Type::Number(zone), \
Type::Number(zone), zone))
#define FUNC_N2N_TYPE \
Bounds(Type::Function(Type::Number(zone), Type::Number(zone), zone))
......@@ -402,10 +405,10 @@ namespace {
void CheckStdlibShortcuts1(Zone* zone, ZoneVector<ExpressionTypeEntry>& types,
size_t& index, int& depth, TypeCache& cache) {
// var exp = stdlib.*; (D * 12)
// var exp = stdlib.*;
CHECK_VAR_SHORTCUT(Infinity, Bounds(cache.kAsmDouble));
CHECK_VAR_SHORTCUT(NaN, Bounds(cache.kAsmDouble));
// var x = stdlib.Math.x; D2D
// var x = stdlib.Math.x;
CHECK_VAR_MATH_SHORTCUT(acos, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(asin, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(atan, FUNC_D2D_TYPE);
......@@ -414,15 +417,17 @@ void CheckStdlibShortcuts1(Zone* zone, ZoneVector<ExpressionTypeEntry>& types,
CHECK_VAR_MATH_SHORTCUT(tan, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(exp, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(log, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(ceil, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(floor, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(sqrt, FUNC_D2D_TYPE);
// var exp = stdlib.Math.*; (DD2D * 12)
CHECK_VAR_MATH_SHORTCUT(min, FUNC_DD2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(max, FUNC_DD2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(ceil, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(floor, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(sqrt, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(min, FUNC_NN2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(max, FUNC_NN2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(atan2, FUNC_DD2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(pow, FUNC_DD2D_TYPE);
// Special ones.
CHECK_VAR_MATH_SHORTCUT(abs, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(imul, FUNC_II2I_TYPE);
CHECK_VAR_MATH_SHORTCUT(fround, FUNC_N2F_TYPE);
......@@ -1733,6 +1738,24 @@ TEST(BadArrayAssignment) {
}
TEST(BadStandardFunctionCallOutside) {
CHECK_FUNC_ERROR(
"var s0 = sin(0);\n"
"function bar() { }\n"
"function foo() { bar(); }",
"asm: line 39: calls forbidden outside function bodies\n");
}
TEST(BadFunctionCallOutside) {
CHECK_FUNC_ERROR(
"function bar() { return 0.0; }\n"
"var s0 = bar(0);\n"
"function foo() { bar(); }",
"asm: line 40: calls forbidden outside function bodies\n");
}
TEST(NestedVariableAssignment) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; x = x = 4; }\n"
......@@ -1817,6 +1840,55 @@ TEST(NegativeInteger) {
}
TEST(AbsFunction) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = -123.0; x = abs(x); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(abs, FUNC_N2N_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmDouble));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(CeilFloat) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = fround(3.1); x = ceil(x); }\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_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(ceil, FUNC_N2N_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmFloat));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(TypeConsistency) {
v8::V8::Initialize();
TypeCache cache;
......
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