Commit 493fba88 authored by bradnelson's avatar bradnelson Committed by Commit bot

Refactor VisitProperty, add starting point for SIMD.js support.

SIMD.js potentially adds to the standard library passed into
asm.js modules. Splitting off the point where the SIMD object
would be referenced to allow work on SIMD typing to occur orthogonally.

Adding VariableInfo to allow tracking of simd constructors / check functions. Using this for fround.

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

Looking at simd.js

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

Cr-Commit-Position: refs/heads/master@{#32431}
parent 219794da
...@@ -38,16 +38,22 @@ namespace internal { ...@@ -38,16 +38,22 @@ namespace internal {
AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script, AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
FunctionLiteral* root) FunctionLiteral* root)
: zone_(zone), : zone_(zone),
isolate_(isolate),
script_(script), script_(script),
root_(root), root_(root),
valid_(true), valid_(true),
allow_simd_(false),
intish_(0), intish_(0),
stdlib_types_(zone), stdlib_types_(zone),
stdlib_heap_types_(zone), stdlib_heap_types_(zone),
stdlib_math_types_(zone), stdlib_math_types_(zone),
global_variable_type_(HashMap::PointersMatch, #define V(NAME, Name, name, lane_count, lane_type) \
ZoneHashMap::kDefaultHashMapCapacity, stdlib_simd_##name##_types_(zone),
ZoneAllocationPolicy(zone)), SIMD128_TYPES(V)
#undef V
global_variable_type_(HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
local_variable_type_(HashMap::PointersMatch, local_variable_type_(HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity, ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)), ZoneAllocationPolicy(zone)),
...@@ -93,6 +99,10 @@ void AsmTyper::VisitAsmModule(FunctionLiteral* fun) { ...@@ -93,6 +99,10 @@ void AsmTyper::VisitAsmModule(FunctionLiteral* fun) {
RECURSE(VisitFunctionAnnotation(decl->fun())); RECURSE(VisitFunctionAnnotation(decl->fun()));
Variable* var = decl->proxy()->var(); Variable* var = decl->proxy()->var();
DCHECK(GetType(var) == NULL); DCHECK(GetType(var) == NULL);
if (property_info_ != NULL) {
SetVariableInfo(var, property_info_);
property_info_ = NULL;
}
SetType(var, computed_type_); SetType(var, computed_type_);
DCHECK(GetType(var) != NULL); DCHECK(GetType(var) != NULL);
} }
...@@ -181,6 +191,10 @@ void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) { ...@@ -181,6 +191,10 @@ void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
if (var->location() != VariableLocation::PARAMETER || var->index() != i) if (var->location() != VariableLocation::PARAMETER || var->index() != i)
break; break;
RECURSE(VisitExpressionAnnotation(expr->value(), var, false)); RECURSE(VisitExpressionAnnotation(expr->value(), var, false));
if (property_info_ != NULL) {
SetVariableInfo(var, property_info_);
property_info_ = NULL;
}
SetType(var, computed_type_); SetType(var, computed_type_);
type->InitParameter(i, computed_type_); type->InitParameter(i, computed_type_);
good = true; good = true;
...@@ -197,12 +211,12 @@ void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var, ...@@ -197,12 +211,12 @@ void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var,
BinaryOperation* bin = expr->AsBinaryOperation(); BinaryOperation* bin = expr->AsBinaryOperation();
if (bin != NULL) { if (bin != NULL) {
if (var != NULL) { if (var != NULL) {
VariableProxy* left = bin->left()->AsVariableProxy(); VariableProxy* proxy = bin->left()->AsVariableProxy();
if (!left) { if (proxy == NULL) {
FAIL(expr, "variable name expected in type annotation"); FAIL(bin->left(), "expected variable for type annotation");
} }
if (left->var() != var) { if (proxy->var() != var) {
FAIL(left, "variable type annotation references other variable"); FAIL(proxy, "annotation source doesn't match destination");
} }
} }
Literal* right = bin->right()->AsLiteral(); Literal* right = bin->right()->AsLiteral();
...@@ -241,19 +255,28 @@ void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var, ...@@ -241,19 +255,28 @@ void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var,
Call* call = expr->AsCall(); Call* call = expr->AsCall();
if (call != NULL) { if (call != NULL) {
if (call->expression()->IsVariableProxy()) { VariableProxy* proxy = call->expression()->AsVariableProxy();
RECURSE(VisitWithExpectation( if (proxy != NULL) {
call->expression(), Type::Any(zone()), VariableInfo* info = GetVariableInfo(proxy->var(), false);
"only fround allowed on expression annotations")); if (!info ||
if (!computed_type_->Is( (!info->is_check_function && !info->is_constructor_function)) {
Type::Function(cache_.kAsmFloat, Type::Number(zone()), zone()))) { if (allow_simd_) {
FAIL(call->expression(), FAIL(call->expression(),
"only fround allowed on expression annotations"); "only fround/SIMD.checks allowed on expression annotations");
} else {
FAIL(call->expression(),
"only fround allowed on expression annotations");
}
} }
if (call->arguments()->length() != 1) { Type* type = info->type;
FAIL(call, "invalid argument count calling fround"); DCHECK(type->IsFunction());
if (info->is_check_function) {
DCHECK(type->AsFunction()->Arity() == 1);
} }
SetResult(expr, cache_.kAsmFloat); if (call->arguments()->length() != type->AsFunction()->Arity()) {
FAIL(call, "invalid argument count calling function");
}
SetResult(expr, type->AsFunction()->Result());
return; return;
} }
} }
...@@ -499,10 +522,15 @@ void AsmTyper::VisitConditional(Conditional* expr) { ...@@ -499,10 +522,15 @@ void AsmTyper::VisitConditional(Conditional* expr) {
void AsmTyper::VisitVariableProxy(VariableProxy* expr) { void AsmTyper::VisitVariableProxy(VariableProxy* expr) {
Variable* var = expr->var(); Variable* var = expr->var();
if (GetType(var) == NULL) { VariableInfo* info = GetVariableInfo(var, false);
if (info == NULL || info->type == NULL) {
FAIL(expr, "unbound variable"); FAIL(expr, "unbound variable");
} }
Type* type = Type::Intersect(GetType(var), expected_type_, zone()); if (property_info_ != NULL) {
SetVariableInfo(var, property_info_);
property_info_ = NULL;
}
Type* type = Type::Intersect(info->type, expected_type_, zone());
if (type->Is(cache_.kAsmInt)) { if (type->Is(cache_.kAsmInt)) {
type = cache_.kAsmInt; type = cache_.kAsmInt;
} }
...@@ -739,35 +767,83 @@ void AsmTyper::VisitHeapAccess(Property* expr, bool assigning, ...@@ -739,35 +767,83 @@ void AsmTyper::VisitHeapAccess(Property* expr, bool assigning,
} }
void AsmTyper::VisitProperty(Property* expr) { bool AsmTyper::IsStdlibObject(Expression* expr) {
// stdlib.Math.x VariableProxy* proxy = expr->AsVariableProxy();
Property* inner_prop = expr->obj()->AsProperty(); if (proxy == NULL) {
if (inner_prop != NULL) { return false;
// Get property name. }
Literal* key = expr->key()->AsLiteral(); Variable* var = proxy->var();
if (key == NULL || !key->IsPropertyName()) VariableInfo* info = GetVariableInfo(var, false);
FAIL(expr, "invalid type annotation on property 2"); if (info) {
Handle<String> name = key->AsPropertyName(); if (info->is_stdlib_object) return info->is_stdlib_object;
}
// Check that inner property name is "Math". if (var->location() != VariableLocation::PARAMETER || var->index() != 0) {
Literal* math_key = inner_prop->key()->AsLiteral(); return false;
if (math_key == NULL || !math_key->IsPropertyName() || }
!math_key->AsPropertyName()->IsUtf8EqualTo(CStrVector("Math"))) info = GetVariableInfo(var, true);
FAIL(expr, "invalid type annotation on stdlib (a1)"); info->type = Type::Object();
info->is_stdlib_object = true;
// Check that object is stdlib. return true;
VariableProxy* proxy = inner_prop->obj()->AsVariableProxy(); }
if (proxy == NULL) FAIL(expr, "invalid type annotation on stdlib (a2)");
Variable* var = proxy->var();
if (var->location() != VariableLocation::PARAMETER || var->index() != 0) Expression* AsmTyper::GetReceiverOfPropertyAccess(Expression* expr,
FAIL(expr, "invalid type annotation on stdlib (a3)"); const char* name) {
Property* property = expr->AsProperty();
if (property == NULL) {
return NULL;
}
Literal* key = property->key()->AsLiteral();
if (key == NULL || !key->IsPropertyName() ||
!key->AsPropertyName()->IsUtf8EqualTo(CStrVector(name))) {
return NULL;
}
return property->obj();
}
bool AsmTyper::IsMathObject(Expression* expr) {
Expression* obj = GetReceiverOfPropertyAccess(expr, "Math");
return obj && IsStdlibObject(obj);
}
bool AsmTyper::IsSIMDObject(Expression* expr) {
Expression* obj = GetReceiverOfPropertyAccess(expr, "SIMD");
return obj && IsStdlibObject(obj);
}
// Look up library type. bool AsmTyper::IsSIMDTypeObject(Expression* expr, const char* name) {
Type* type = LibType(stdlib_math_types_, name); Expression* obj = GetReceiverOfPropertyAccess(expr, name);
if (type == NULL) FAIL(expr, "unknown standard function 3 "); return obj && IsSIMDObject(obj);
SetResult(expr, type); }
void AsmTyper::VisitProperty(Property* expr) {
if (IsMathObject(expr->obj())) {
VisitLibraryAccess(&stdlib_math_types_, expr);
return; return;
} }
#define V(NAME, Name, name, lane_count, lane_type) \
if (IsSIMDTypeObject(expr->obj(), #Name)) { \
VisitLibraryAccess(&stdlib_simd_##name##_types_, expr); \
return; \
} \
if (IsSIMDTypeObject(expr, #Name)) { \
VariableInfo* info = stdlib_simd_##name##_constructor_type_; \
SetResult(expr, info->type); \
property_info_ = info; \
return; \
}
SIMD128_TYPES(V)
#undef V
if (IsStdlibObject(expr->obj())) {
VisitLibraryAccess(&stdlib_types_, expr);
return;
}
property_info_ = NULL;
// Only recurse at this point so that we avoid needing // Only recurse at this point so that we avoid needing
// stdlib.Math to have a real type. // stdlib.Math to have a real type.
...@@ -779,35 +855,14 @@ void AsmTyper::VisitProperty(Property* expr) { ...@@ -779,35 +855,14 @@ void AsmTyper::VisitProperty(Property* expr) {
return; return;
} }
// Get property name.
Literal* key = expr->key()->AsLiteral();
if (key == NULL || !key->IsPropertyName())
FAIL(expr, "invalid type annotation on property 3");
Handle<String> name = key->AsPropertyName();
// stdlib.x or foreign.x // stdlib.x or foreign.x
VariableProxy* proxy = expr->obj()->AsVariableProxy(); VariableProxy* proxy = expr->obj()->AsVariableProxy();
if (proxy != NULL) { if (proxy != NULL) {
Variable* var = proxy->var(); Variable* var = proxy->var();
if (var->location() != VariableLocation::PARAMETER) { if (var->location() == VariableLocation::PARAMETER && var->index() == 1) {
FAIL(expr, "invalid type annotation on variable"); // foreign.x is ok.
} SetResult(expr, expected_type_);
switch (var->index()) { return;
case 0: {
// Object is stdlib, look up library type.
Type* type = LibType(stdlib_types_, name);
if (type == NULL) {
FAIL(expr, "unknown standard function 4");
}
SetResult(expr, type);
return;
}
case 1:
// Object is foreign lib.
SetResult(expr, expected_type_);
return;
default:
FAIL(expr, "invalid type annotation on parameter");
} }
} }
...@@ -1143,7 +1198,26 @@ void AsmTyper::VisitSuperCallReference(SuperCallReference* expr) { ...@@ -1143,7 +1198,26 @@ void AsmTyper::VisitSuperCallReference(SuperCallReference* expr) {
} }
void AsmTyper::InitializeStdlibSIMD() {
#define V(NAME, Name, name, lane_count, lane_type) \
{ \
Type* type = Type::Function(Type::Name(isolate_, zone()), Type::Any(), \
lane_count, zone()); \
for (int i = 0; i < lane_count; ++i) { \
type->AsFunction()->InitParameter(i, Type::Number()); \
} \
stdlib_simd_##name##_constructor_type_ = new (zone()) VariableInfo(type); \
stdlib_simd_##name##_constructor_type_->is_constructor_function = true; \
}
SIMD128_TYPES(V)
#undef V
}
void AsmTyper::InitializeStdlib() { void AsmTyper::InitializeStdlib() {
if (allow_simd_) {
InitializeStdlibSIMD();
}
Type* number_type = Type::Number(zone()); Type* number_type = Type::Number(zone());
Type* double_type = cache_.kAsmDouble; Type* double_type = cache_.kAsmDouble;
Type* double_fn1_type = Type::Function(double_type, double_type, zone()); Type* double_fn1_type = Type::Function(double_type, double_type, zone());
...@@ -1177,30 +1251,44 @@ void AsmTyper::InitializeStdlib() { ...@@ -1177,30 +1251,44 @@ void AsmTyper::InitializeStdlib() {
{"acos", double_fn1_type}, {"asin", double_fn1_type}, {"acos", double_fn1_type}, {"asin", double_fn1_type},
{"atan", double_fn1_type}, {"atan2", double_fn2_type}}; {"atan", double_fn1_type}, {"atan2", double_fn2_type}};
for (unsigned i = 0; i < arraysize(math); ++i) { for (unsigned i = 0; i < arraysize(math); ++i) {
stdlib_math_types_[math[i].name] = math[i].type; stdlib_math_types_[math[i].name] = new (zone()) VariableInfo(math[i].type);
} }
stdlib_math_types_["fround"]->is_check_function = true;
stdlib_types_["Infinity"] = double_type; stdlib_types_["Infinity"] = new (zone()) VariableInfo(double_type);
stdlib_types_["NaN"] = double_type; stdlib_types_["NaN"] = new (zone()) VariableInfo(double_type);
Type* buffer_type = Type::Any(zone()); Type* buffer_type = Type::Any(zone());
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \ #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
stdlib_types_[#TypeName "Array"] = \ stdlib_types_[#TypeName "Array"] = new (zone()) VariableInfo( \
Type::Function(cache_.k##TypeName##Array, buffer_type, zone()); Type::Function(cache_.k##TypeName##Array, buffer_type, zone()));
TYPED_ARRAYS(TYPED_ARRAY) TYPED_ARRAYS(TYPED_ARRAY)
#undef TYPED_ARRAY #undef TYPED_ARRAY
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \ #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
stdlib_heap_types_[#TypeName "Array"] = \ stdlib_heap_types_[#TypeName "Array"] = new (zone()) VariableInfo( \
Type::Function(cache_.k##TypeName##Array, buffer_type, zone()); Type::Function(cache_.k##TypeName##Array, buffer_type, zone()));
TYPED_ARRAYS(TYPED_ARRAY) TYPED_ARRAYS(TYPED_ARRAY)
#undef TYPED_ARRAY #undef TYPED_ARRAY
} }
Type* AsmTyper::LibType(ObjectTypeMap map, Handle<String> name) { void AsmTyper::VisitLibraryAccess(ObjectTypeMap* map, Property* expr) {
Literal* key = expr->key()->AsLiteral();
if (key == NULL || !key->IsPropertyName())
FAIL(expr, "invalid key used on stdlib member");
Handle<String> name = key->AsPropertyName();
VariableInfo* info = LibType(map, name);
if (info == NULL || info->type == NULL) FAIL(expr, "unknown stdlib function");
SetResult(expr, info->type);
property_info_ = info;
}
AsmTyper::VariableInfo* AsmTyper::LibType(ObjectTypeMap* map,
Handle<String> name) {
base::SmartArrayPointer<char> aname = name->ToCString(); base::SmartArrayPointer<char> aname = name->ToCString();
ObjectTypeMap::iterator i = map.find(std::string(aname.get())); ObjectTypeMap::iterator i = map->find(std::string(aname.get()));
if (i == map.end()) { if (i == map->end()) {
return NULL; return NULL;
} }
return i->second; return i->second;
...@@ -1208,32 +1296,52 @@ Type* AsmTyper::LibType(ObjectTypeMap map, Handle<String> name) { ...@@ -1208,32 +1296,52 @@ Type* AsmTyper::LibType(ObjectTypeMap map, Handle<String> name) {
void AsmTyper::SetType(Variable* variable, Type* type) { void AsmTyper::SetType(Variable* variable, Type* type) {
ZoneHashMap::Entry* entry; VariableInfo* info = GetVariableInfo(variable, true);
if (in_function_) { info->type = type;
entry = local_variable_type_.LookupOrInsert(
variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone()));
} else {
entry = global_variable_type_.LookupOrInsert(
variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone()));
}
entry->value = reinterpret_cast<void*>(type);
} }
Type* AsmTyper::GetType(Variable* variable) { Type* AsmTyper::GetType(Variable* variable) {
i::ZoneHashMap::Entry* entry = NULL; VariableInfo* info = GetVariableInfo(variable, false);
if (!info) return NULL;
return info->type;
}
AsmTyper::VariableInfo* AsmTyper::GetVariableInfo(Variable* variable,
bool setting) {
ZoneHashMap::Entry* entry;
ZoneHashMap* map;
if (in_function_) { if (in_function_) {
entry = local_variable_type_.Lookup(variable, ComputePointerHash(variable)); map = &local_variable_type_;
} } else {
if (entry == NULL) { map = &global_variable_type_;
entry =
global_variable_type_.Lookup(variable, ComputePointerHash(variable));
} }
if (entry == NULL) { if (setting) {
return NULL; entry = map->LookupOrInsert(variable, ComputePointerHash(variable),
ZoneAllocationPolicy(zone()));
} else { } else {
return reinterpret_cast<Type*>(entry->value); entry = map->Lookup(variable, ComputePointerHash(variable));
if (!entry && in_function_) {
entry =
global_variable_type_.Lookup(variable, ComputePointerHash(variable));
}
} }
if (!entry) return NULL;
if (!entry->value) {
if (!setting) return NULL;
entry->value = new (zone()) VariableInfo;
}
return reinterpret_cast<VariableInfo*>(entry->value);
}
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;
} }
......
...@@ -22,28 +22,52 @@ class AsmTyper : public AstVisitor { ...@@ -22,28 +22,52 @@ class AsmTyper : public AstVisitor {
explicit AsmTyper(Isolate* isolate, Zone* zone, Script* script, explicit AsmTyper(Isolate* isolate, Zone* zone, Script* script,
FunctionLiteral* root); FunctionLiteral* root);
bool Validate(); bool Validate();
void set_allow_simd(bool simd);
const char* error_message() { return error_message_; } const char* error_message() { return error_message_; }
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
private: private:
Zone* zone_; Zone* zone_;
Isolate* isolate_;
Script* script_; Script* script_;
FunctionLiteral* root_; FunctionLiteral* root_;
bool valid_; bool valid_;
bool allow_simd_;
struct VariableInfo : public ZoneObject {
Type* type;
bool is_stdlib_object;
bool is_check_function;
bool is_constructor_function;
VariableInfo()
: type(NULL),
is_stdlib_object(false),
is_check_function(false),
is_constructor_function(false) {}
explicit VariableInfo(Type* t)
: type(t), is_check_function(false), is_constructor_function(false) {}
};
// Information for bi-directional typing with a cap on nesting depth. // Information for bi-directional typing with a cap on nesting depth.
Type* expected_type_; Type* expected_type_;
Type* computed_type_; Type* computed_type_;
VariableInfo* property_info_;
int intish_; // How many ops we've gone without a x|0. int intish_; // How many ops we've gone without a x|0.
Type* return_type_; // Return type of last function. Type* return_type_; // Return type of last function.
size_t array_size_; // Array size of last ArrayLiteral. size_t array_size_; // Array size of last ArrayLiteral.
typedef ZoneMap<std::string, Type*> ObjectTypeMap; typedef ZoneMap<std::string, VariableInfo*> ObjectTypeMap;
ObjectTypeMap stdlib_types_; ObjectTypeMap stdlib_types_;
ObjectTypeMap stdlib_heap_types_; ObjectTypeMap stdlib_heap_types_;
ObjectTypeMap stdlib_math_types_; ObjectTypeMap stdlib_math_types_;
#define V(NAME, Name, name, lane_count, lane_type) \
ObjectTypeMap stdlib_simd_##name##_types_; \
VariableInfo* stdlib_simd_##name##_constructor_type_;
SIMD128_TYPES(V)
#undef V
// Map from Variable* to global/local variable Type*. // Map from Variable* to global/local variable Type*.
ZoneHashMap global_variable_type_; ZoneHashMap global_variable_type_;
...@@ -61,6 +85,7 @@ class AsmTyper : public AstVisitor { ...@@ -61,6 +85,7 @@ class AsmTyper : public AstVisitor {
static const int kMaxUncombinedMultiplicativeSteps = 1; static const int kMaxUncombinedMultiplicativeSteps = 1;
void InitializeStdlib(); void InitializeStdlib();
void InitializeStdlibSIMD();
void VisitDeclarations(ZoneList<Declaration*>* d) override; void VisitDeclarations(ZoneList<Declaration*>* d) override;
void VisitStatements(ZoneList<Statement*>* s) override; void VisitStatements(ZoneList<Statement*>* s) override;
...@@ -71,13 +96,24 @@ class AsmTyper : public AstVisitor { ...@@ -71,13 +96,24 @@ class AsmTyper : public AstVisitor {
void VisitHeapAccess(Property* expr, bool assigning, Type* assignment_type); void VisitHeapAccess(Property* expr, bool assigning, Type* assignment_type);
Expression* GetReceiverOfPropertyAccess(Expression* expr, const char* name);
bool IsMathObject(Expression* expr);
bool IsSIMDObject(Expression* expr);
bool IsSIMDTypeObject(Expression* expr, const char* name);
bool IsStdlibObject(Expression* expr);
void VisitSIMDProperty(Property* expr);
int ElementShiftSize(Type* type); int ElementShiftSize(Type* type);
Type* StorageType(Type* type); Type* StorageType(Type* type);
void SetType(Variable* variable, Type* type); void SetType(Variable* variable, Type* type);
Type* GetType(Variable* variable); Type* GetType(Variable* variable);
VariableInfo* GetVariableInfo(Variable* variable, bool setting);
void SetVariableInfo(Variable* variable, const VariableInfo* info);
Type* LibType(ObjectTypeMap map, Handle<String> name); VariableInfo* LibType(ObjectTypeMap* map, Handle<String> name);
void VisitLibraryAccess(ObjectTypeMap* map, Property* expr);
void SetResult(Expression* expr, Type* type); void SetResult(Expression* expr, Type* type);
void IntersectResult(Expression* expr, Type* type); void IntersectResult(Expression* expr, Type* type);
......
...@@ -1530,7 +1530,7 @@ TEST(InvalidArgumentCount) { ...@@ -1530,7 +1530,7 @@ TEST(InvalidArgumentCount) {
CHECK_FUNC_ERROR( CHECK_FUNC_ERROR(
"function bar(x) { return fround(4, 5); }\n" "function bar(x) { return fround(4, 5); }\n"
"function foo() { bar(); }", "function foo() { bar(); }",
"asm: line 39: invalid argument count calling fround\n"); "asm: line 39: invalid argument count calling function\n");
} }
......
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