Bug 1706124 - wasm: Implement the extended-const proposal. r=lth

This commit implements the extended-constants proposal.
 * A new feature flag and pref are added.
 * Basic tests are added.

Differential Revision: https://phabricator.services.mozilla.com/D112661
This commit is contained in:
Ryan Hunt 2021-05-04 15:58:26 +00:00
Родитель 17eab55693
Коммит 6374503adf
9 изменённых файлов: 308 добавлений и 4 удалений

Просмотреть файл

@ -726,6 +726,31 @@ def enable_shared_memory(value):
set_config("ENABLE_SHARED_MEMORY", enable_shared_memory)
set_define("ENABLE_SHARED_MEMORY", enable_shared_memory)
# Support for WebAssembly extended constant expressions
# =====================================================
@depends(milestone.is_nightly)
def default_wasm_extended_const(is_nightly):
if is_nightly:
return True
option(
"--enable-wasm-extended-const",
default=default_wasm_extended_const,
help="{Enable|Disable} WebAssembly extended constant expressions",
)
@depends("--enable-wasm-extended-const")
def wasm_extended_const(value):
if value:
return True
set_config("ENABLE_WASM_EXTENDED_CONST", wasm_extended_const)
set_define("ENABLE_WASM_EXTENDED_CONST", wasm_extended_const)
# Support for WebAssembly SIMD
# =====================================================

Просмотреть файл

@ -57,6 +57,11 @@
#else
# define WASM_RELAXED_SIMD_ENABLED 0
#endif
#ifdef ENABLE_WASM_EXTENDED_CONST
# define WASM_EXTENDED_CONST_ENABLED 1
#else
# define WASM_EXTENDED_CONST_ENABLED 0
#endif
#ifdef ENABLE_WASM_EXCEPTIONS
# define WASM_EXCEPTIONS_ENABLED 1
#else
@ -83,6 +88,13 @@
js::jit::JitSupportsWasmSimd(), \
/* shell flag */ "simd", \
/* preference name */ "simd") \
EXPERIMENTAL(/* capitalized name */ ExtendedConst, \
/* lower case name */ extendedConst, \
/* compile predicate */ WASM_EXTENDED_CONST_ENABLED, \
/* compiler predicate */ true, \
/* flag predicate */ true, \
/* shell flag */ "extended-const", \
/* preference name */ "extended_const") \
EXPERIMENTAL( \
/* capitalized name */ Exceptions, \
/* lower case name */ exceptions, \

Просмотреть файл

@ -0,0 +1,95 @@
// |jit-test| skip-if: !wasmExtendedConstEnabled()
function testPrivateGlobal(valtype, expr, result) {
// Immutable private globals have a single cell for wasm.
let { get } = wasmEvalText(`(module
(global $global ${valtype} ${expr})
(func (export "get") (result ${valtype})
global.get $global
)
)`).exports;
assertEq(get(), result);
}
function testExportedGlobal(valtype, expr, result) {
// Immutable exported globals have a separate cell for wasm and the exported
// global object.
let { global, get } = wasmEvalText(`(module
(global $global (export "global") ${valtype} ${expr})
(func (export "get") (result ${valtype})
global.get $global
)
)`).exports;
assertEq(get(), result);
assertEq(global.value, result);
}
function testIndirectGlobal(valtype, expr, result) {
// Mutable exported globals share an indirect cell for wasm and the exported
// global object.
let { global } = wasmEvalText(`(module
(global (export "global") (mut ${valtype}) ${expr})
)`).exports;
assertEq(global.value, result);
}
// i32 tests
const I32_SQ_OVERFLOW = 0xFFFF + 1;
const MAX_I32 = 0xFFFF_FFFF;
function testI32(expr, result) {
testPrivateGlobal('i32', expr, result);
testExportedGlobal('i32', expr, result);
testIndirectGlobal('i32', expr, result);
}
testI32('i32.const 1', 1);
testI32('i32.const 1 i32.const 2 i32.add', 3);
testI32(`i32.const ${MAX_I32} i32.const 1 i32.add`, 0);
testI32('i32.const 1 i32.const 2 i32.sub', 1);
testI32(`i32.const 1 i32.const 0 i32.sub`, -1);
testI32('i32.const 1 i32.const 2 i32.mul', 2);
testI32(`i32.const ${I32_SQ_OVERFLOW} i32.const ${I32_SQ_OVERFLOW} i32.mul`, 0);
// i64 tests
const I64_SQ_OVERFLOW = 0xFFFF_FFFFn + 1n;
const MAX_I64 = 0xFFFF_FFFF_FFFF_FFFFn;
function testI64(expr, result) {
testPrivateGlobal('i64', expr, result);
testExportedGlobal('i64', expr, result);
testIndirectGlobal('i64', expr, result);
}
testI64('i64.const 1', 1n);
testI64('i64.const 1 i64.const 2 i64.add', 3n);
testI64(`i64.const ${MAX_I64} i64.const 1 i64.add`, 0n);
testI64('i64.const 1 i64.const 2 i64.sub', 1n);
testI64(`i64.const 1 i64.const 0 i64.sub`, -1n);
testI64('i64.const 1 i64.const 2 i64.mul', 2n);
testI64(`i64.const ${I64_SQ_OVERFLOW} i64.const ${I64_SQ_OVERFLOW} i64.mul`, 0n);
// test global.get
function testGlobalGet(valtype, aExpr, bExpr, cExpr, cResult) {
let { a, b } = wasmEvalText(`(module
(global (export "a") ${valtype} ${aExpr})
(global (export "b") ${valtype} ${bExpr})
)`).exports;
let { c } = wasmEvalText(`(module
(global $a (import "" "a") ${valtype})
(global $b (import "" "b") ${valtype})
(global (export "c") ${valtype} ${cExpr})
)`, {"": {a, b}}).exports;
assertEq(c.value, cResult);
}
testGlobalGet('i32', 'i32.const 2', 'i32.const 3', 'global.get $a global.get $b i32.add', 5);
testGlobalGet('i32', 'i32.const 2', 'i32.const 3', 'global.get $a global.get $b i32.sub', 1);
testGlobalGet('i32', 'i32.const 2', 'i32.const 3', 'global.get $a global.get $b i32.mul', 6);
testGlobalGet('i64', 'i64.const 2', 'i64.const 3', 'global.get $a global.get $b i64.add', 5n);
testGlobalGet('i64', 'i64.const 2', 'i64.const 3', 'global.get $a global.get $b i64.sub', 1n);
testGlobalGet('i64', 'i64.const 2', 'i64.const 3', 'global.get $a global.get $b i64.mul', 6n);

Просмотреть файл

@ -0,0 +1 @@
|jit-test| --wasm-extended-const; test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js

Просмотреть файл

@ -0,0 +1,22 @@
// |jit-test| skip-if: wasmExtendedConstEnabled()
const { CompileError, validate } = WebAssembly;
const DISABLED = /extended constant expressions not enabled|unexpected initializer opcode/;
let tests = [
"(module (global i32 i32.const 0 i32.const 0 i32.add))",
"(module (global i32 i32.const 0 i32.const 0 i32.sub))",
"(module (global i32 i32.const 0 i32.const 0 i32.mul))",
"(module (global i64 i64.const 0 i64.const 0 i64.add))",
"(module (global i64 i64.const 0 i64.const 0 i64.sub))",
"(module (global i64 i64.const 0 i64.const 0 i64.mul))",
];
// Test that use of extended constants fails when disabled.
for (let src of tests) {
let bin = wasmTextToBinary(src);
assertEq(validate(bin), false);
wasmCompilationShouldFail(bin, DISABLED);
}

Просмотреть файл

@ -1,4 +1,4 @@
// |jit-test| test-also=--wasm-exceptions; test-also=--wasm-function-references; test-also=--wasm-gc
// |jit-test| test-also=--wasm-extended-const; test-also=--wasm-exceptions; test-also=--wasm-function-references; test-also=--wasm-gc
// Test that if a feature is 'experimental' then we must be in a nightly build,
// and if a feature is 'released' then it must be enabled on release and beta.
@ -24,9 +24,32 @@ let { release_or_beta } = getBuildConfiguration();
let nightly = !release_or_beta;
let nightlyOnlyFeatures = [
['exceptions', wasmExceptionsEnabled(), `(module (type (func)) (event (type 0)))`],
['function-references', wasmFunctionReferencesEnabled(), `(module (func (param (ref extern))))`],
['gc', wasmGcEnabled(), `(module (type $s (struct)) (func (param (ref null $s))))`],
[
'extended-const',
wasmExtendedConstEnabled(),
`(module
(global i32
i32.const 0
i32.const 0
i32.add
)
)`
],
[
'exceptions',
wasmExceptionsEnabled(),
`(module (type (func)) (event (type 0)))`
],
[
'function-references',
wasmFunctionReferencesEnabled(),
`(module (func (param (ref extern))))`
],
[
'gc',
wasmGcEnabled(),
`(module (type $s (struct)) (func (param (ref null $s))))`
],
];
for (let [name, enabled, test] of nightlyOnlyFeatures) {

Просмотреть файл

@ -455,6 +455,7 @@ class Decoder {
// Instruction immediates for constant instructions
[[nodiscard]] bool readBinary() { return true; }
[[nodiscard]] bool readGetGlobal(uint32_t* id);
[[nodiscard]] bool readI32Const(int32_t* i32);
[[nodiscard]] bool readI64Const(int64_t* i64);

Просмотреть файл

@ -52,6 +52,9 @@ static bool ValidateInitExpr(Decoder& d, ModuleEnvironment* env,
return false;
}
#ifdef ENABLE_WASM_EXTENDED_CONST
Nothing nothing;
#endif
NothingVector nothings{};
ResultType unusedType;
@ -142,6 +145,32 @@ static bool ValidateInitExpr(Decoder& d, ModuleEnvironment* env,
*literal = Some(LitVal(ValType(type)));
break;
}
#ifdef ENABLE_WASM_EXTENDED_CONST
case uint16_t(Op::I32Add):
case uint16_t(Op::I32Sub):
case uint16_t(Op::I32Mul): {
if (!env->extendedConstEnabled()) {
return d.fail("unexpected initializer opcode");
}
if (!iter.readBinary(ValType::I32, &nothing, &nothing)) {
return false;
}
*literal = Nothing();
break;
}
case uint16_t(Op::I64Add):
case uint16_t(Op::I64Sub):
case uint16_t(Op::I64Mul): {
if (!env->extendedConstEnabled()) {
return d.fail("unexpected initializer opcode");
}
if (!iter.readBinary(ValType::I64, &nothing, &nothing)) {
return false;
}
*literal = Nothing();
break;
}
#endif
default: {
return d.fail("unexpected initializer opcode");
}
@ -184,6 +213,19 @@ class MOZ_STACK_CLASS InitExprInterpreter {
return stack.append(Val(RefType::func(), ref));
}
#ifdef ENABLE_WASM_EXTENDED_CONST
int32_t popI32() {
uint32_t result = stack.back().i32();
stack.popBack();
return int32_t(result);
}
int64_t popI64() {
uint64_t result = stack.back().i64();
stack.popBack();
return int64_t(result);
}
#endif
bool evalGetGlobal(uint32_t index) {
return stack.append(globalImportValues[index]);
}
@ -200,6 +242,44 @@ class MOZ_STACK_CLASS InitExprInterpreter {
return pushFuncRef(FuncRef::fromCompiledCode(fnref));
}
bool evalRefNull(RefType type) { return pushRef(type, AnyRef::null()); }
#ifdef ENABLE_WASM_EXTENDED_CONST
bool evalI32Add() {
uint32_t a = popI32();
uint32_t b = popI32();
pushI32(a + b);
return true;
}
bool evalI32Sub() {
uint32_t a = popI32();
uint32_t b = popI32();
pushI32(a - b);
return true;
}
bool evalI32Mul() {
uint32_t a = popI32();
uint32_t b = popI32();
pushI32(a * b);
return true;
}
bool evalI64Add() {
uint64_t a = popI64();
uint64_t b = popI64();
pushI64(a + b);
return true;
}
bool evalI64Sub() {
uint64_t a = popI64();
uint64_t b = popI64();
pushI64(a - b);
return true;
}
bool evalI64Mul() {
uint64_t a = popI64();
uint64_t b = popI64();
pushI64(a * b);
return true;
}
#endif
};
bool InitExprInterpreter::evaluate(Decoder& d) {
@ -276,6 +356,44 @@ bool InitExprInterpreter::evaluate(Decoder& d) {
}
CHECK(evalRefNull(type));
}
#ifdef ENABLE_WASM_EXTENDED_CONST
case uint16_t(Op::I32Add): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI32Add());
}
case uint16_t(Op::I32Sub): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI32Sub());
}
case uint16_t(Op::I32Mul): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI32Mul());
}
case uint16_t(Op::I64Add): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI64Add());
}
case uint16_t(Op::I64Sub): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI64Sub());
}
case uint16_t(Op::I64Mul): {
if (!d.readBinary()) {
return false;
}
CHECK(evalI64Mul());
}
#endif
default: {
MOZ_CRASH();
}

Просмотреть файл

@ -5792,6 +5792,13 @@
mirror: always
#endif // defined(ENABLE_WASM_RELAXED_SIMD)
#if defined(ENABLE_WASM_EXTENDED_CONST)
- name: javascript.options.wasm_extended_const
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
#endif // defined(ENABLE_WASM_EXTENDED_CONST)
#if defined(ENABLE_WASM_EXCEPTIONS)
- name: javascript.options.wasm_exceptions
type: bool