Commit 5331e3b6 authored by jpp's avatar jpp Committed by Commit bot

[V8][asm.js] Adds support to global const variables.

This CL adds support for:
https://discourse.wicg.io/t/allow-const-global-variables/684

It allows global const variables to be used as if they were numeric
literals. For example:

  const f0 = fround(0);
  ...
  function foo() {
    var v = f0;  // no type annotation.
    ...
    return f0;  // no return type annotation.
  }

BUG= https://bugs.chromium.org/p/v8/issues/detail?id=5540

Review-Url: https://chromiumcodereview.appspot.com/2435823002
Cr-Commit-Position: refs/heads/master@{#40477}
parent ef690ca3
......@@ -762,9 +762,15 @@ AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
bool global_variable = false;
if (value->IsLiteral() || value->IsCall()) {
AsmType* type = nullptr;
RECURSE(type = VariableTypeAnnotations(value, true));
VariableInfo::Mutability mutability;
if (target_variable->mode() == CONST) {
mutability = VariableInfo::kConstGlobal;
} else {
mutability = VariableInfo::kMutableGlobal;
}
RECURSE(type = VariableTypeAnnotations(value, mutability));
target_info = new (zone_) VariableInfo(type);
target_info->set_mutability(VariableInfo::kMutableGlobal);
target_info->set_mutability(mutability);
global_variable = true;
} else if (value->IsProperty()) {
target_info = ImportLookup(value->AsProperty());
......@@ -828,6 +834,23 @@ AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
RECURSE(type = NewHeapView(value->AsCallNew()));
target_info = new (zone_) VariableInfo(type);
target_info->set_mutability(VariableInfo::kImmutableGlobal);
} else if (auto* proxy = value->AsVariableProxy()) {
auto* var_info = Lookup(proxy->var());
if (var_info == nullptr) {
FAIL(value, "Undeclared identifier in global initializer");
}
if (var_info->mutability() != VariableInfo::kConstGlobal) {
FAIL(value, "Identifier used to initialize a global must be a const");
}
target_info = new (zone_) VariableInfo(var_info->type());
if (target_variable->mode() == CONST) {
target_info->set_mutability(VariableInfo::kConstGlobal);
} else {
target_info->set_mutability(VariableInfo::kMutableGlobal);
}
}
if (target_info == nullptr) {
......@@ -2684,13 +2707,27 @@ AsmType* AsmTyper::ReturnTypeAnnotations(ReturnStatement* statement) {
FAIL(statement, "Invalid literal in return statement.");
}
if (auto* proxy = ret_expr->AsVariableProxy()) {
auto* var_info = Lookup(proxy->var());
if (var_info == nullptr) {
FAIL(statement, "Undeclared identifier in return statement.");
}
if (var_info->mutability() != VariableInfo::kConstGlobal) {
FAIL(statement, "Identifier in return statement is not const.");
}
return var_info->type();
}
FAIL(statement, "Invalid return type expression.");
}
// 5.4 VariableTypeAnnotations
// Also used for 5.5 GlobalVariableTypeAnnotations
AsmType* AsmTyper::VariableTypeAnnotations(Expression* initializer,
bool global) {
AsmType* AsmTyper::VariableTypeAnnotations(
Expression* initializer, VariableInfo::Mutability mutability_type) {
if (auto* literal = initializer->AsLiteral()) {
if (literal->raw_value()->ContainsDot()) {
SetTypeOf(initializer, AsmType::Double());
......@@ -2698,24 +2735,50 @@ AsmType* AsmTyper::VariableTypeAnnotations(Expression* initializer,
}
int32_t i32;
uint32_t u32;
AsmType* initializer_type = nullptr;
if (literal->value()->ToUint32(&u32)) {
if (u32 > LargestFixNum) {
SetTypeOf(initializer, AsmType::Unsigned());
initializer_type = AsmType::Unsigned();
SetTypeOf(initializer, initializer_type);
} else {
SetTypeOf(initializer, AsmType::FixNum());
initializer_type = AsmType::FixNum();
SetTypeOf(initializer, initializer_type);
initializer_type = AsmType::Signed();
}
} else if (literal->value()->ToInt32(&i32)) {
SetTypeOf(initializer, AsmType::Signed());
initializer_type = AsmType::Signed();
SetTypeOf(initializer, initializer_type);
} else {
FAIL(initializer, "Invalid type annotation - forbidden literal.");
}
return AsmType::Int();
if (mutability_type != VariableInfo::kConstGlobal) {
return AsmType::Int();
}
return initializer_type;
}
if (auto* proxy = initializer->AsVariableProxy()) {
auto* var_info = Lookup(proxy->var());
if (var_info == nullptr) {
FAIL(initializer,
"Undeclared identifier in variable declaration initializer.");
}
if (var_info->mutability() != VariableInfo::kConstGlobal) {
FAIL(initializer,
"Identifier in variable declaration initializer must be const.");
}
SetTypeOf(initializer, var_info->type());
return var_info->type();
}
auto* call = initializer->AsCall();
if (call == nullptr) {
FAIL(initializer,
"Invalid variable initialization - it should be a literal, or "
"Invalid variable initialization - it should be a literal, const, or "
"fround(literal).");
}
......@@ -2732,7 +2795,7 @@ AsmType* AsmTyper::VariableTypeAnnotations(Expression* initializer,
}
// Float constants must contain dots in local, but not in globals.
if (!global) {
if (mutability_type == VariableInfo::kLocal) {
if (!src_expr->raw_value()->ContainsDot()) {
FAIL(initializer,
"Invalid float type annotation - expected literal argument to be a "
......
......@@ -102,6 +102,13 @@ class AsmTyper final {
kInvalidMutability,
kLocal,
kMutableGlobal,
// *VIOLATION* We support const variables in asm.js, as per the
//
// https://discourse.wicg.io/t/allow-const-global-variables/684
//
// Global const variables are treated as if they were numeric literals,
// and can be used anywhere a literal can be used.
kConstGlobal,
kImmutableGlobal,
};
......@@ -114,7 +121,8 @@ class AsmTyper final {
}
bool IsGlobal() const {
return mutability_ == kImmutableGlobal || mutability_ == kMutableGlobal;
return mutability_ == kImmutableGlobal || mutability_ == kConstGlobal ||
mutability_ == kMutableGlobal;
}
bool IsStdlib() const { return standard_member_ == kStdlib; }
......@@ -307,8 +315,9 @@ class AsmTyper final {
AsmType* ReturnTypeAnnotations(ReturnStatement* statement);
// 5.4 VariableTypeAnnotations
// 5.5 GlobalVariableTypeAnnotations
AsmType* VariableTypeAnnotations(Expression* initializer,
bool global = false);
AsmType* VariableTypeAnnotations(
Expression* initializer,
VariableInfo::Mutability global = VariableInfo::kLocal);
AsmType* ImportExpression(Property* import);
AsmType* NewHeapView(CallNew* new_heap_view);
......
......@@ -505,14 +505,15 @@ TEST(ErrorsInGlobalVariableDefinition) {
const char* error_message;
} kTests[] = {
{"var v;", "Global variable missing initializer"},
{"var v = uninitialized;", "Invalid global variable initializer"},
{"var v = uninitialized;", "Undeclared identifier in global"},
{"var v = 'use asm';", "type annotation - forbidden literal"},
{"var v = 4294967296;", " - forbidden literal"},
{"var v = not_fround;", "Invalid global variable initializer"},
{"var v = not_fround;", "initialize a global must be a const"},
{"var v = not_fround(1);", "expected call fround(literal)"},
{"var v = __fround__(1.0);", "expected call fround(literal)"},
{"var v = fround(1.0, 1.0);", "expected call fround(literal)"},
{"var v = fround(not_fround);", "literal argument for call to fround"},
{"var v = i?0:1;", "Invalid global variable initializer"},
{"var v = stdlib.nan", "Invalid import"},
{"var v = stdlib.Math.nan", "Invalid import"},
{"var v = stdlib.Mathh.E", "Invalid import"},
......@@ -788,6 +789,19 @@ TEST(ErrorsInFunction) {
" var c = 0;\n"
"}\n",
"Local variable missing initializer in asm.js module"},
{"function f(a) {\n"
" a = a|0;\n"
" var x = a;\n"
"}\n",
"variable declaration initializer must be const"},
{"function f() {\n"
" var x = 1+i;\n"
"}\n",
"should be a literal, const, or fround(literal"},
{"function f() {\n"
" var x = a;\n"
"}\n",
"Undeclared identifier in variable declaration initializer"},
{"function f() {\n"
" function ff() {}\n"
"}\n",
......@@ -812,6 +826,19 @@ TEST(ErrorsInFunction) {
" return 2147483648;\n"
"}\n",
"Invalid literal in return statement"},
{"function f(a) {\n"
" a = a|0;\n"
" return a;\n"
"}\n",
"in return statement is not const"},
{"function f() {\n"
" return a;\n"
"}\n",
"Undeclared identifier in return statement"},
{"function f() {\n"
" return i?0:1;\n"
"}\n",
"Invalid return type expression"},
{"function f() {\n"
" return stdlib.Math.E;"
"}\n",
......
......@@ -8,6 +8,76 @@ function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func));
}
(function TestConst() {
function Module(s) {
"use asm";
var fround = s.Math.fround;
// Global constants. These are treated just like numeric literals.
const fConst = fround(-3.0);
const dConst = -3.0;
const iConst = -3;
// consts can be used to initialize other consts.
const fPrime = fConst;
// The following methods verify that return statements with global constants
// do not need type annotations.
function f() {
return fPrime;
}
function d() {
return dConst;
}
function i() {
return iConst;
}
// The following methods verify that locals initialized with global
// constants do not need type annotations.
function fVar() {
var v = fPrime;
return fround(v);
}
function iVar() {
var v = iConst;
return v|0;
}
function dVar() {
var v = dConst;
return +v;
}
return {
f: f, d: d, i: i,
fVar: fVar, dVar: dVar, iVar: iVar,
};
}
function DisallowAssignToConstGlobal() {
const constant = 0;
function invalid(i) {
i = i|0;
constant = i;
return constant;
}
return invalid;
}
var m = Module(this);
assertValidAsm(Module);
assertEquals(-3, m.i());
assertEquals(-3.0, m.d());
assertEquals(Math.fround(-3.0), m.f());
assertEquals(-3, m.iVar());
assertEquals(-3.0, m.dVar());
assertEquals(Math.fround(-3.0), m.fVar());
var m = DisallowAssignToConstGlobal();
assertTrue(%IsNotAsmWasmCode(DisallowAssignToConstGlobal));
})();
(function TestModuleArgs() {
function Module1(stdlib) {
"use asm";
......
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