Commit 6a6c151d authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement br_on_null

Add br_on_null opcode, encoding, decoding, and elementary tests.

Bug: v8:7748
Change-Id: Id771ea7f57694e1c1bffc83c4232132bf9ad9dbd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2190424
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67712}
parent 1a6fe2a7
......@@ -2956,6 +2956,15 @@ Node* WasmGraphBuilder::ReturnCallIndirect(uint32_t table_index,
kReturnCall);
}
Node* WasmGraphBuilder::BrOnNull(Node* ref_object, Node** null_node,
Node** non_null_node) {
BranchExpectFalse(gasm_->WordEqual(ref_object, RefNull()), null_node,
non_null_node);
// Return value is not used, but we need it for compatibility
// with graph-builder-interface.
return nullptr;
}
Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) {
// Implement Rol by Ror since TurboFan does not have Rol opcode.
// TODO(weiliang): support Word32Rol opcode in TurboFan.
......
......@@ -262,6 +262,9 @@ class WasmGraphBuilder {
wasm::WasmCodePosition position);
Node* ReturnCallIndirect(uint32_t table_index, uint32_t sig_index,
Vector<Node*> args, wasm::WasmCodePosition position);
// Return value is not expected to be used,
// but we need it for compatibility with graph-builder-interface.
Node* BrOnNull(Node* ref_object, Node** non_null_node, Node** null_node);
Node* Invert(Node* node);
......
......@@ -2335,6 +2335,10 @@ class LiftoffCompiler {
unsupported(decoder, kTailCall, "return_call_indirect");
}
void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
unsupported(decoder, kAnyRef, "br_on_null");
}
template <ValueType::Kind src_type, ValueType::Kind result_type,
typename EmitFn>
void EmitTerOp(EmitFn fn) {
......
......@@ -881,6 +881,7 @@ enum class LoadTransformationKind : uint8_t {
const Value args[]) \
F(ReturnCallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[]) \
F(BrOnNull, const Value& ref_object, uint32_t depth) \
F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \
......@@ -1576,6 +1577,7 @@ class WasmDecoder : public Decoder {
case kExprLocalTee:
case kExprMemoryGrow:
case kExprRefAsNonNull:
case kExprBrOnNull:
return {1, 1};
case kExprLocalSet:
case kExprGlobalSet:
......@@ -1976,6 +1978,51 @@ class WasmFullDecoder : public WasmDecoder<validate> {
*pexception = exception;
break;
}
case kExprBrOnNull: {
CHECK_PROTOTYPE_OPCODE(gc);
BranchDepthImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm, control_.size())) break;
len = 1 + imm.length;
Value ref_object = Pop();
if (this->failed()) break;
Control* c = control_at(imm.depth);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
if (V8_LIKELY(check_result == kReachableBranch)) {
switch (ref_object.type.kind()) {
case ValueType::kRef: {
auto* result = Push(
ValueType(ValueType::kRef, ref_object.type.ref_index()));
CALL_INTERFACE(PassThrough, ref_object, result);
break;
}
case ValueType::kOptRef: {
// We need to Push the result value after calling BrOnNull on
// the interface. Therefore we must sync the ref_object and
// result nodes afterwards (in PassThrough).
CALL_INTERFACE(BrOnNull, ref_object, imm.depth);
auto* result = Push(
ValueType(ValueType::kRef, ref_object.type.ref_index()));
CALL_INTERFACE(PassThrough, ref_object, result);
c->br_merge()->reached = true;
break;
}
case ValueType::kNullRef:
if (imm.depth == control_.size() - 1) {
DoReturn();
} else {
CALL_INTERFACE(Br, c);
c->br_merge()->reached = true;
}
EndControl();
break;
default:
this->error(this->pc_,
"invalid agrument type to ref.as_non_null");
break;
}
}
break;
}
case kExprLoop: {
BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(imm)) break;
......
......@@ -450,6 +450,18 @@ class WasmGraphBuildingInterface {
args);
}
void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
SsaEnv* non_null_env = ssa_env_;
SsaEnv* null_env = Split(decoder, non_null_env);
non_null_env->SetNotMerged();
BUILD(BrOnNull, ref_object.node, &null_env->control,
&non_null_env->control);
builder_->SetControl(non_null_env->control);
SetEnv(null_env);
BrOrRet(decoder, depth);
SetEnv(non_null_env);
}
void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
Value* result) {
base::SmallVector<TFNode*, 8> inputs(args.size());
......
......@@ -149,6 +149,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(CallIndirect, "call_indirect")
CASE_OP(ReturnCall, "return_call")
CASE_OP(ReturnCallIndirect, "return_call_indirect")
CASE_OP(BrOnNull, "br_on_null")
CASE_OP(Drop, "drop")
CASE_OP(Select, "select")
CASE_OP(SelectWithType, "select")
......
......@@ -37,7 +37,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \
V(BrTable, 0x0e, _) \
V(Return, 0x0f, _)
V(Return, 0x0f, _) \
V(BrOnNull, 0xd4, _) /* gc prototype */
// Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \
......
......@@ -99,6 +99,36 @@ WASM_EXEC_TEST(BasicStruct) {
kExprEnd};
k->EmitCode(k_code, sizeof(k_code));
// Test br_on_null 1.
WasmFunctionBuilder* l = builder->AddFunction(sigs.i_v());
uint32_t l_local_index = l->AddLocal(kOptRefType);
l->builder()->AddExport(CStrVector("l"), l);
byte l_code[] = {
WASM_BLOCK_I(WASM_I32V(42),
// Branch will be taken.
// 42 left on stack outside the block (not 52).
WASM_BR_ON_NULL(0, WASM_GET_LOCAL(l_local_index)),
WASM_I32V(52), WASM_BR(0)),
kExprEnd};
l->EmitCode(l_code, sizeof(l_code));
// Test br_on_null 2.
WasmFunctionBuilder* m = builder->AddFunction(sigs.i_v());
uint32_t m_field_index = 0;
m->builder()->AddExport(CStrVector("m"), m);
byte m_code[] = {
WASM_BLOCK_I(
WASM_I32V(42),
WASM_STRUCT_GET(
type_index, m_field_index,
// Branch will not be taken.
// 52 left on stack outside the block (not 42).
WASM_BR_ON_NULL(0, WASM_STRUCT_NEW(type_index, WASM_I32V(52),
WASM_I32V(62)))),
WASM_BR(0)),
kExprEnd};
m->EmitCode(m_code, sizeof(m_code));
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
......@@ -132,6 +162,12 @@ WASM_EXEC_TEST(BasicStruct) {
CHECK_EQ(55, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"k", 0, nullptr));
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"l", 0, nullptr));
CHECK_EQ(52, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"m", 0, nullptr));
}
WASM_EXEC_TEST(BasicArray) {
......
......@@ -440,6 +440,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_NEW(index, default_value, length) \
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
#define WASM_BR_ON_NULL(depth, ref_object) \
ref_object, kExprBrOnNull, static_cast<byte>(depth)
// Pass: sig_index, ...args, func_index
#define WASM_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
......
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