Commit bf1565d7 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm] Implement the Extended Constants proposal

This proposal adds i32 and i64 addition, subtraction, and multiplication
to the list of constant expressions.
See https://github.com/WebAssembly/extended-const.

Bug: v8:12089
Change-Id: I23a27a54a15fd37ee1d553992ab3b355eb9d317c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3497665Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79362}
parent 6b565a04
......@@ -910,6 +910,8 @@ struct ControlBase : public PcForErrors<validate> {
F(F32Const, Value* result, float value) \
F(F64Const, Value* result, double value) \
F(S128Const, Simd128Immediate<validate>& imm, Value* result) \
F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
Value* result) \
F(RefNull, ValueType type, Value* result) \
F(RefFunc, uint32_t function_index, Value* result) \
F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
......@@ -935,8 +937,6 @@ struct ControlBase : public PcForErrors<validate> {
F(PopControl, Control* block) \
/* Instructions: */ \
F(UnOp, WasmOpcode opcode, const Value& value, Value* result) \
F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
Value* result) \
F(RefAsNonNull, const Value& arg, Value* result) \
F(Drop) \
F(LocalGet, Value* result, const IndexImmediate<validate>& imm) \
......@@ -2560,7 +2560,20 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
#define BUILD_SIMPLE_OPCODE(op, _, sig) \
DECODE(op) { return BuildSimpleOperator_##sig(kExpr##op); }
FOREACH_SIMPLE_OPCODE(BUILD_SIMPLE_OPCODE)
FOREACH_SIMPLE_NON_CONST_OPCODE(BUILD_SIMPLE_OPCODE)
#undef BUILD_SIMPLE_OPCODE
#define BUILD_SIMPLE_OPCODE(op, _, sig) \
DECODE(op) { \
if (decoding_mode == kInitExpression) { \
if (!VALIDATE(this->enabled_.has_extended_const())) { \
NonConstError(this, kExpr##op); \
return 0; \
} \
} \
return BuildSimpleOperator_##sig(kExpr##op); \
}
FOREACH_SIMPLE_EXTENDED_CONST_OPCODE(BUILD_SIMPLE_OPCODE)
#undef BUILD_SIMPLE_OPCODE
DECODE(Block) {
......@@ -3501,8 +3514,11 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
static constexpr OpcodeHandler GetOpcodeHandlerTableEntry(size_t idx) {
DECODE_IMPL(Nop);
#define BUILD_SIMPLE_OPCODE(op, _, sig) DECODE_IMPL(op);
FOREACH_SIMPLE_OPCODE(BUILD_SIMPLE_OPCODE)
FOREACH_SIMPLE_NON_CONST_OPCODE(BUILD_SIMPLE_OPCODE)
#undef BUILD_SIMPLE_OPCODE
#define BUILD_SIMPLE_EXTENDED_CONST_OPCODE(op, _, sig) DECODE_IMPL_CONST(op);
FOREACH_SIMPLE_EXTENDED_CONST_OPCODE(BUILD_SIMPLE_EXTENDED_CONST_OPCODE)
#undef BUILD_SIMPLE_EXTENDED_CONST_OPCODE
DECODE_IMPL(Block);
DECODE_IMPL(Rethrow);
DECODE_IMPL(Throw);
......
......@@ -44,6 +44,40 @@ void InitExprInterface::S128Const(FullDecoder* decoder,
result->runtime_value = WasmValue(imm.value, kWasmS128);
}
void InitExprInterface::BinOp(FullDecoder* decoder, WasmOpcode opcode,
const Value& lhs, const Value& rhs,
Value* result) {
if (!generate_result()) return;
switch (opcode) {
case kExprI32Add:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i32() + rhs.runtime_value.to_i32());
break;
case kExprI32Sub:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i32() - rhs.runtime_value.to_i32());
break;
case kExprI32Mul:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i32() * rhs.runtime_value.to_i32());
break;
case kExprI64Add:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i64() + rhs.runtime_value.to_i64());
break;
case kExprI64Sub:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i64() - rhs.runtime_value.to_i64());
break;
case kExprI64Mul:
result->runtime_value =
WasmValue(lhs.runtime_value.to_i64() * rhs.runtime_value.to_i64());
break;
default:
UNREACHABLE();
}
}
void InitExprInterface::RefNull(FullDecoder* decoder, ValueType type,
Value* result) {
if (!generate_result()) return;
......
......@@ -64,7 +64,12 @@
/* Stack Switching proposal. */ \
/* https://github.com/WebAssembly/stack-switching */ \
/* V8 side owner: thibaudm, fgm */ \
V(stack_switching, "stack switching", false)
V(stack_switching, "stack switching", false) \
\
/* Extended Constant Expressions Proposal. */ \
/* https://github.com/WebAssembly/extended-const */ \
/* V8 side owner: manoskouk */ \
V(extended_const, "extended constant expressions", false)
// #############################################################################
// Staged features (disabled by default, but enabled via --wasm-staging (also
......
This diff is collapsed.
......@@ -339,6 +339,12 @@ class InitExprInterface {
result->init_expr = WasmInitExpr(imm.value);
}
void BinOp(FullDecoder* decoder, WasmOpcode opcode, const Value& lhs,
const Value& rhs, Value* result) {
// TODO(12089): Implement.
UNIMPLEMENTED();
}
void RefNull(FullDecoder* decoder, ValueType type, Value* result) {
result->init_expr = WasmInitExpr::RefNullConst(type.heap_representation());
}
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-extended-const
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function ExtendedConstantsTestI32() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let imported_global_0 = builder.addImportedGlobal("m", "g0", kWasmI32, false);
let imported_global_1 = builder.addImportedGlobal("m", "g1", kWasmI32, false);
let defined_global = builder.addGlobal(
kWasmI32, false,
WasmInitExpr.I32Add(
WasmInitExpr.GlobalGet(imported_global_0),
WasmInitExpr.I32Mul(
WasmInitExpr.GlobalGet(imported_global_1),
WasmInitExpr.I32Sub(
WasmInitExpr.GlobalGet(imported_global_0),
WasmInitExpr.I32Const(1)))));
builder.addExportOfKind("global", kExternalGlobal, defined_global.index);
let value0 = 123;
let value1 = -450;
let global_obj0 = new WebAssembly.Global({value: "i32", mutable: false},
value0);
let global_obj1 = new WebAssembly.Global({value: "i32", mutable: false},
value1);
let instance = builder.instantiate({m : {g0: global_obj0, g1: global_obj1}});
assertEquals(value0 + (value1 * (value0 - 1)), instance.exports.global.value);
})();
(function ExtendedConstantsTestI64() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let imported_global_0 = builder.addImportedGlobal("m", "g0", kWasmI64, false);
let imported_global_1 = builder.addImportedGlobal("m", "g1", kWasmI64, false);
let defined_global = builder.addGlobal(
kWasmI64, false,
WasmInitExpr.I64Add(
WasmInitExpr.GlobalGet(imported_global_0),
WasmInitExpr.I64Mul(
WasmInitExpr.GlobalGet(imported_global_1),
WasmInitExpr.I64Sub(
WasmInitExpr.GlobalGet(imported_global_0),
WasmInitExpr.I64Const(1)))));
builder.addExportOfKind("global", kExternalGlobal, defined_global.index);
let value0 = 123n;
let value1 = -450n;
let global_obj0 = new WebAssembly.Global({value: "i64", mutable: false},
value0);
let global_obj1 = new WebAssembly.Global({value: "i64", mutable: false},
value1);
let instance = builder.instantiate({m : {g0: global_obj0, g1: global_obj1}});
assertEquals(value0 + (value1 * (value0 - 1n)),
instance.exports.global.value);
})();
......@@ -1016,6 +1016,16 @@ class Binary {
case kSimdPrefix:
this.emit_bytes(wasmS128Const(expr.value));
break;
case kExprI32Add:
case kExprI32Sub:
case kExprI32Mul:
case kExprI64Add:
case kExprI64Sub:
case kExprI64Mul:
this.emit_init_expr_recursive(expr.operands[0]);
this.emit_init_expr_recursive(expr.operands[1]);
this.emit_u8(expr.kind);
break;
case kExprRefFunc:
this.emit_u8(kExprRefFunc);
this.emit_u32v(expr.value);
......@@ -1192,6 +1202,24 @@ class WasmInitExpr {
static S128Const(value) {
return {kind: kSimdPrefix, value: value};
}
static I32Add(lhs, rhs) {
return {kind: kExprI32Add, operands: [lhs, rhs]};
}
static I32Sub(lhs, rhs) {
return {kind: kExprI32Sub, operands: [lhs, rhs]};
}
static I32Mul(lhs, rhs) {
return {kind: kExprI32Mul, operands: [lhs, rhs]};
}
static I64Add(lhs, rhs) {
return {kind: kExprI64Add, operands: [lhs, rhs]};
}
static I64Sub(lhs, rhs) {
return {kind: kExprI64Sub, operands: [lhs, rhs]};
}
static I64Mul(lhs, rhs) {
return {kind: kExprI64Mul, operands: [lhs, rhs]};
}
static GlobalGet(index) {
return {kind: kExprGlobalGet, value: index};
}
......
......@@ -2048,6 +2048,65 @@ TEST_F(WasmModuleVerifyTest, ElementSectionGlobalGetOutOfBounds) {
EXPECT_FAILURE_WITH_MSG(data, "Invalid global index: 0");
}
// Make sure extended constants do not work without the experimental feature.
TEST_F(WasmModuleVerifyTest, ExtendedConstantsFail) {
static const byte data[] = {
SECTION(Import, ENTRY_COUNT(1), // one import
0x01, 'm', 0x01, 'g', // module, name
kExternalGlobal, kI32Code, 0), // type, mutability
SECTION(Global, ENTRY_COUNT(1), // one defined global
kI32Code, 0, // type, mutability
// initializer
kExprGlobalGet, 0x00, kExprGlobalGet, 0x00, kExprI32Add,
kExprEnd)};
EXPECT_FAILURE_WITH_MSG(data,
"opcode i32.add is not allowed in init. expressions");
}
TEST_F(WasmModuleVerifyTest, ExtendedConstantsI32) {
WASM_FEATURE_SCOPE(extended_const);
static const byte data[] = {
SECTION(Import, ENTRY_COUNT(1), // one import
0x01, 'm', 0x01, 'g', // module, name
kExternalGlobal, kI32Code, 0), // type, mutability
SECTION(Global, ENTRY_COUNT(1), // one defined global
kI32Code, 0, // type, mutability
// initializer
kExprGlobalGet, 0x00, kExprGlobalGet, 0x00, kExprI32Add,
kExprGlobalGet, 0x00, kExprI32Sub, kExprGlobalGet, 0x00,
kExprI32Mul, kExprEnd)};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExtendedConstantsI64) {
WASM_FEATURE_SCOPE(extended_const);
static const byte data[] = {
SECTION(Import, ENTRY_COUNT(1), // one import
0x01, 'm', 0x01, 'g', // module, name
kExternalGlobal, kI64Code, 0), // type, mutability
SECTION(Global, ENTRY_COUNT(1), // one defined global
kI64Code, 0, // type, mutability
// initializer
kExprGlobalGet, 0x00, kExprGlobalGet, 0x00, kExprI64Add,
kExprGlobalGet, 0x00, kExprI64Sub, kExprGlobalGet, 0x00,
kExprI64Mul, kExprEnd)};
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ExtendedConstantsTypeError) {
WASM_FEATURE_SCOPE(extended_const);
static const byte data[] = {
SECTION(Import, ENTRY_COUNT(1), // one import
0x01, 'm', 0x01, 'g', // module, name
kExternalGlobal, kI32Code, 0), // type, mutability
SECTION(Global, ENTRY_COUNT(1), // one defined global
kI32Code, 0, // type, mutability
// initializer
kExprGlobalGet, 0x00, kExprI64Const, 1, kExprI32Add, kExprEnd)};
EXPECT_FAILURE_WITH_MSG(
data, "i32.add[1] expected type i32, found i64.const of type i64");
}
TEST_F(WasmModuleVerifyTest, IndirectFunctionNoFunctions) {
static const byte data[] = {
// sig#0 -------------------------------------------------------
......
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