Commit d34178fd authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Introduce the select_with_type instruction

The instruction is the same as the existing {select} instruction with
type. Both inputs must be in a sub-type relationship with the type
specified in the type instruction.

R=clemensh@chromium.org

Bug: v8:7581
Change-Id: Ibead6cd0253210828c8114336ea0942e6cbd6126
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1631413
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61886}
parent f5ab7d38
......@@ -206,6 +206,68 @@ struct GlobalIndexImmediate {
}
};
namespace function_body_decoder {
// Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or the start of a type index.
inline bool decode_local_type(uint8_t val, ValueType* result) {
switch (static_cast<ValueTypeCode>(val)) {
case kLocalVoid:
*result = kWasmStmt;
return true;
case kLocalI32:
*result = kWasmI32;
return true;
case kLocalI64:
*result = kWasmI64;
return true;
case kLocalF32:
*result = kWasmF32;
return true;
case kLocalF64:
*result = kWasmF64;
return true;
case kLocalS128:
*result = kWasmS128;
return true;
case kLocalAnyFunc:
*result = kWasmAnyFunc;
return true;
case kLocalAnyRef:
*result = kWasmAnyRef;
return true;
case kLocalExceptRef:
*result = kWasmExceptRef;
return true;
default:
*result = kWasmVar;
return false;
}
}
} // namespace function_body_decoder
template <Decoder::ValidateFlag validate>
struct SelectTypeImmediate {
uint32_t length;
ValueType type;
inline SelectTypeImmediate(Decoder* decoder, const byte* pc) {
uint8_t num_types =
decoder->read_u32v<validate>(pc + 1, &length, "number of select types");
if (!VALIDATE(num_types == 1)) {
decoder->error(
pc + 1, "Invalid number of types. Select accepts exactly one type");
return;
}
uint8_t val = decoder->read_u8<validate>(pc + length + 1, "select type");
length++;
if (!function_body_decoder::decode_local_type(val, &type) ||
type == kWasmStmt) {
decoder->error(pc + 1, "invalid select type");
return;
}
}
};
template <Decoder::ValidateFlag validate>
struct BlockTypeImmediate {
uint32_t length = 1;
......@@ -216,7 +278,7 @@ struct BlockTypeImmediate {
inline BlockTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
const byte* pc) {
uint8_t val = decoder->read_u8<validate>(pc + 1, "block type");
if (!decode_local_type(val, &type)) {
if (!function_body_decoder::decode_local_type(val, &type)) {
// Handle multi-value blocks.
if (!VALIDATE(enabled.mv)) {
decoder->error(pc + 1, "invalid block type");
......@@ -233,43 +295,6 @@ struct BlockTypeImmediate {
}
}
// Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or the start of a type index.
inline bool decode_local_type(uint8_t val, ValueType* result) {
switch (static_cast<ValueTypeCode>(val)) {
case kLocalVoid:
*result = kWasmStmt;
return true;
case kLocalI32:
*result = kWasmI32;
return true;
case kLocalI64:
*result = kWasmI64;
return true;
case kLocalF32:
*result = kWasmF32;
return true;
case kLocalF64:
*result = kWasmF64;
return true;
case kLocalS128:
*result = kWasmS128;
return true;
case kLocalAnyFunc:
*result = kWasmAnyFunc;
return true;
case kLocalAnyRef:
*result = kWasmAnyRef;
return true;
case kLocalExceptRef:
*result = kWasmExceptRef;
return true;
default:
*result = kWasmVar;
return false;
}
}
uint32_t in_arity() const {
if (type != kWasmVar) return 0;
return static_cast<uint32_t>(sig->parameter_count());
......@@ -1253,6 +1278,10 @@ class WasmDecoder : public Decoder {
LocalIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprSelectWithType: {
SelectTypeImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprBrTable: {
BranchTableImmediate<validate> imm(decoder, pc);
BranchTableIterator<validate> iterator(decoder, imm);
......@@ -1397,6 +1426,7 @@ class WasmDecoder : public Decoder {
// clang-format off
switch (opcode) {
case kExprSelect:
case kExprSelectWithType:
return {3, 1};
case kExprSetTable:
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
......@@ -1895,10 +1925,28 @@ class WasmFullDecoder : public WasmDecoder<validate> {
auto cond = Pop(2, kWasmI32);
auto fval = Pop();
auto tval = Pop(0, fval.type);
auto* result = Push(tval.type == kWasmVar ? fval.type : tval.type);
ValueType type = tval.type == kWasmVar ? fval.type : tval.type;
if (ValueTypes::IsSubType(kWasmAnyRef, type)) {
this->error(
"select without type is only valid for value type inputs");
break;
}
auto* result = Push(type);
CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
break;
}
case kExprSelectWithType: {
CHECK_PROTOTYPE_OPCODE(anyref);
SelectTypeImmediate<validate> imm(this, this->pc_);
if (this->failed()) break;
auto cond = Pop(2, kWasmI32);
auto fval = Pop(1, imm.type);
auto tval = Pop(0, imm.type);
auto* result = Push(imm.type);
CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
len = 1 + imm.length;
break;
}
case kExprBr: {
BranchDepthImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm, control_.size())) break;
......
......@@ -2837,6 +2837,11 @@ class ThreadImpl {
}
break;
}
case kExprSelectWithType: {
SelectTypeImmediate<Decoder::kNoValidate> imm(&decoder, code->at(pc));
len = 1 + imm.length;
V8_FALLTHROUGH;
}
case kExprSelect: {
WasmValue cond = Pop();
WasmValue fval = Pop();
......
......@@ -142,6 +142,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ReturnCallIndirect, "return_call_indirect")
CASE_OP(Drop, "drop")
CASE_OP(Select, "select")
CASE_OP(SelectWithType, "select")
CASE_OP(GetLocal, "local.get")
CASE_OP(SetLocal, "local.set")
CASE_OP(TeeLocal, "local.tee")
......
......@@ -45,6 +45,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(ReturnCallIndirect, 0x13, _) \
V(Drop, 0x1a, _) \
V(Select, 0x1b, _) \
V(SelectWithType, 0x1c, _) \
V(GetLocal, 0x20, _) \
V(SetLocal, 0x21, _) \
V(TeeLocal, 0x22, _) \
......
......@@ -752,12 +752,19 @@ WASM_EXEC_TEST(Return_F64) {
WASM_EXEC_TEST(Select_float_parameters) {
WasmRunner<float, float, float, int32_t> r(execution_tier);
// return select(11, 22, a);
BUILD(r,
WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)));
CHECK_FLOAT_EQ(2.0f, r.Call(2.0f, 1.0f, 1));
}
WASM_EXEC_TEST(SelectWithType_float_parameters) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
WasmRunner<float, float, float, int32_t> r(execution_tier);
BUILD(r,
WASM_SELECT_F(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)));
CHECK_FLOAT_EQ(2.0f, r.Call(2.0f, 1.0f, 1));
}
WASM_EXEC_TEST(Select) {
WasmRunner<int32_t, int32_t> r(execution_tier);
// return select(11, 22, a);
......@@ -768,6 +775,17 @@ WASM_EXEC_TEST(Select) {
}
}
WASM_EXEC_TEST(SelectWithType) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
WasmRunner<int32_t, int32_t> r(execution_tier);
// return select(11, 22, a);
BUILD(r, WASM_SELECT_I(WASM_I32V_1(11), WASM_I32V_1(22), WASM_GET_LOCAL(0)));
FOR_INT32_INPUTS(i) {
int32_t expected = i ? 11 : 22;
CHECK_EQ(expected, r.Call(i));
}
}
WASM_EXEC_TEST(Select_strict1) {
WasmRunner<int32_t, int32_t> r(execution_tier);
// select(a=0, a=1, a=2); return a
......@@ -778,6 +796,18 @@ WASM_EXEC_TEST(Select_strict1) {
FOR_INT32_INPUTS(i) { CHECK_EQ(2, r.Call(i)); }
}
WASM_EXEC_TEST(SelectWithType_strict1) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
WasmRunner<int32_t, int32_t> r(execution_tier);
// select(a=0, a=1, a=2); return a
BUILD(r,
WASM_SELECT_I(WASM_TEE_LOCAL(0, WASM_ZERO),
WASM_TEE_LOCAL(0, WASM_I32V_1(1)),
WASM_TEE_LOCAL(0, WASM_I32V_1(2))),
WASM_DROP, WASM_GET_LOCAL(0));
FOR_INT32_INPUTS(i) { CHECK_EQ(2, r.Call(i)); }
}
WASM_EXEC_TEST(Select_strict2) {
WasmRunner<int32_t, int32_t> r(execution_tier);
r.AllocateLocal(kWasmI32);
......@@ -791,6 +821,20 @@ WASM_EXEC_TEST(Select_strict2) {
}
}
WASM_EXEC_TEST(SelectWithType_strict2) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
WasmRunner<int32_t, int32_t> r(execution_tier);
r.AllocateLocal(kWasmI32);
r.AllocateLocal(kWasmI32);
// select(b=5, c=6, a)
BUILD(r, WASM_SELECT_I(WASM_TEE_LOCAL(1, WASM_I32V_1(5)),
WASM_TEE_LOCAL(2, WASM_I32V_1(6)), WASM_GET_LOCAL(0)));
FOR_INT32_INPUTS(i) {
int32_t expected = i ? 5 : 6;
CHECK_EQ(expected, r.Call(i));
}
}
WASM_EXEC_TEST(Select_strict3) {
WasmRunner<int32_t, int32_t> r(execution_tier);
r.AllocateLocal(kWasmI32);
......@@ -805,6 +849,21 @@ WASM_EXEC_TEST(Select_strict3) {
}
}
WASM_EXEC_TEST(SelectWithType_strict3) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
WasmRunner<int32_t, int32_t> r(execution_tier);
r.AllocateLocal(kWasmI32);
r.AllocateLocal(kWasmI32);
// select(b=5, c=6, a=b)
BUILD(r, WASM_SELECT_I(WASM_TEE_LOCAL(1, WASM_I32V_1(5)),
WASM_TEE_LOCAL(2, WASM_I32V_1(6)),
WASM_TEE_LOCAL(0, WASM_GET_LOCAL(1))));
FOR_INT32_INPUTS(i) {
int32_t expected = 5;
CHECK_EQ(expected, r.Call(i));
}
}
WASM_EXEC_TEST(BrIf_strict) {
WasmRunner<int32_t, int32_t> r(execution_tier);
BUILD(r, WASM_BLOCK_I(WASM_BRV_IF(0, WASM_GET_LOCAL(0),
......
......@@ -38,6 +38,8 @@ class TestSignatures {
sig_d_dd(1, 2, kDoubleTypes4),
sig_r_v(1, 0, kRefTypes4),
sig_a_v(1, 0, kFuncTypes4),
sig_r_r(1, 1, kRefTypes4),
sig_a_a(1, 1, kFuncTypes4),
sig_v_v(0, 0, kIntTypes4),
sig_v_i(0, 1, kIntTypes4),
sig_v_ii(0, 2, kIntTypes4),
......@@ -93,6 +95,8 @@ class TestSignatures {
FunctionSig* r_v() { return &sig_r_v; }
FunctionSig* a_v() { return &sig_a_v; }
FunctionSig* r_r() { return &sig_r_r; }
FunctionSig* a_a() { return &sig_a_a; }
FunctionSig* v_v() { return &sig_v_v; }
FunctionSig* v_i() { return &sig_v_i; }
......@@ -153,6 +157,8 @@ class TestSignatures {
FunctionSig sig_r_v;
FunctionSig sig_a_v;
FunctionSig sig_r_r;
FunctionSig sig_a_a;
FunctionSig sig_v_v;
FunctionSig sig_v_i;
......
......@@ -140,6 +140,18 @@
kExprCatch, catchstmt, kExprEnd
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
#define WASM_SELECT_I(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalI32
#define WASM_SELECT_L(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalI64
#define WASM_SELECT_F(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalF32
#define WASM_SELECT_D(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalF64
#define WASM_SELECT_R(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalAnyRef
#define WASM_SELECT_A(tval, fval, cond) \
tval, fval, cond, kExprSelectWithType, U32V_1(1), kLocalAnyFunc
#define WASM_RETURN0 kExprReturn
#define WASM_RETURN1(val) val, kExprReturn
......
......@@ -389,6 +389,16 @@ class WasmGenerator {
global_op<wanted_type>(data);
}
template <ValueType select_type>
void select_with_type(DataRange& data) {
static_assert(select_type != kWasmStmt, "illegal type for select");
Generate<select_type, select_type, kWasmI32>(data);
// num_types is always 1.
uint8_t num_types = 1;
builder_->EmitWithU8U8(kExprSelectWithType, num_types,
ValueTypes::ValueTypeCodeFor(select_type));
}
void set_global(DataRange& data) { global_op<kWasmStmt>(data); }
template <ValueType... Types>
......@@ -603,6 +613,8 @@ void WasmGenerator::Generate<kWasmI32>(DataRange& data) {
&WasmGenerator::get_local<kWasmI32>,
&WasmGenerator::tee_local<kWasmI32>,
&WasmGenerator::get_global<kWasmI32>,
&WasmGenerator::op<kExprSelect, kWasmI32, kWasmI32, kWasmI32>,
&WasmGenerator::select_with_type<kWasmI32>,
&WasmGenerator::call<kWasmI32>};
......@@ -669,6 +681,8 @@ void WasmGenerator::Generate<kWasmI64>(DataRange& data) {
&WasmGenerator::get_local<kWasmI64>,
&WasmGenerator::tee_local<kWasmI64>,
&WasmGenerator::get_global<kWasmI64>,
&WasmGenerator::op<kExprSelect, kWasmI64, kWasmI64, kWasmI32>,
&WasmGenerator::select_with_type<kWasmI64>,
&WasmGenerator::call<kWasmI64>};
......@@ -702,6 +716,8 @@ void WasmGenerator::Generate<kWasmF32>(DataRange& data) {
&WasmGenerator::get_local<kWasmF32>,
&WasmGenerator::tee_local<kWasmF32>,
&WasmGenerator::get_global<kWasmF32>,
&WasmGenerator::op<kExprSelect, kWasmF32, kWasmF32, kWasmI32>,
&WasmGenerator::select_with_type<kWasmF32>,
&WasmGenerator::call<kWasmF32>};
......@@ -735,6 +751,8 @@ void WasmGenerator::Generate<kWasmF64>(DataRange& data) {
&WasmGenerator::get_local<kWasmF64>,
&WasmGenerator::tee_local<kWasmF64>,
&WasmGenerator::get_global<kWasmF64>,
&WasmGenerator::op<kExprSelect, kWasmF64, kWasmF64, kWasmI32>,
&WasmGenerator::select_with_type<kWasmF64>,
&WasmGenerator::call<kWasmF64>};
......
......@@ -2667,6 +2667,14 @@ TEST_F(FunctionBodyDecoderTest, Select) {
{WASM_SELECT(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, Select_needs_value_type) {
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.r_r(),
{WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_ZERO)});
ExpectFailure(sigs.a_a(),
{WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, Select_fail1) {
ExpectFailure(sigs.i_i(), {WASM_SELECT(WASM_F32(0.0), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0))});
......@@ -2680,6 +2688,8 @@ TEST_F(FunctionBodyDecoderTest, Select_fail2) {
for (size_t i = 0; i < arraysize(kValueTypes); i++) {
ValueType type = kValueTypes[i];
if (type == kWasmI32) continue;
// Select without specified type is only allowed for number types.
if (type == kWasmAnyRef) continue;
ValueType types[] = {type, kWasmI32, type};
FunctionSig sig(1, 2, types);
......@@ -2709,6 +2719,34 @@ TEST_F(FunctionBodyDecoderTest, Select_TypeCheck) {
WASM_I64V_1(0))});
}
TEST_F(FunctionBodyDecoderTest, SelectWithType) {
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.i_i(), {WASM_SELECT_I(WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0), WASM_ZERO)});
ExpectValidates(sigs.f_ff(),
{WASM_SELECT_F(WASM_F32(0.0), WASM_F32(0.0), WASM_ZERO)});
ExpectValidates(sigs.d_dd(),
{WASM_SELECT_D(WASM_F64(0.0), WASM_F64(0.0), WASM_ZERO)});
ExpectValidates(sigs.l_l(),
{WASM_SELECT_L(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)});
ExpectValidates(sigs.r_r(),
{WASM_SELECT_R(WASM_REF_NULL, WASM_REF_NULL, WASM_ZERO)});
ExpectValidates(sigs.a_a(),
{WASM_SELECT_A(WASM_REF_NULL, WASM_REF_NULL, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, SelectWithType_fail) {
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.i_i(), {WASM_SELECT_F(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_ZERO)});
ExpectFailure(sigs.f_ff(),
{WASM_SELECT_D(WASM_F32(0.0), WASM_F32(0.0), WASM_ZERO)});
ExpectFailure(sigs.d_dd(),
{WASM_SELECT_L(WASM_F64(0.0), WASM_F64(0.0), WASM_ZERO)});
ExpectFailure(sigs.l_l(),
{WASM_SELECT_I(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, Throw) {
WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder;
......
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