Commit f128a1c5 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement classification functions in liftoff

Bug: v8:7748
Change-Id: I56a5e0a3e3141ef781a816656dc50d0d0a1573cc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2659258
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72452}
parent d19dcaac
...@@ -4468,7 +4468,7 @@ class LiftoffCompiler { ...@@ -4468,7 +4468,7 @@ class LiftoffCompiler {
} }
void RefTest(FullDecoder* decoder, const Value& obj, const Value& rtt, void RefTest(FullDecoder* decoder, const Value& obj, const Value& rtt,
Value* result_val) { Value* /* result_val */) {
Label return_false, done; Label return_false, done;
LiftoffRegList pinned; LiftoffRegList pinned;
LiftoffRegister result = pinned.set(__ GetUnusedRegister(kGpReg, {})); LiftoffRegister result = pinned.set(__ GetUnusedRegister(kGpReg, {}));
...@@ -4522,43 +4522,180 @@ class LiftoffCompiler { ...@@ -4522,43 +4522,180 @@ class LiftoffCompiler {
__ PushRegister(obj.type, obj_reg); __ PushRegister(obj.type, obj_reg);
} }
void RefIsData(FullDecoder* decoder, const Value& object, Value* result) { // Abstract type checkers. They all return the object register and fall
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_data"); // through to match.
LiftoffRegister DataCheck(const Value& obj, Label* no_match,
LiftoffRegList pinned, Register opt_scratch) {
LiftoffRegister obj_reg = pinned.set(__ PopToRegister(pinned));
// Reserve all temporary registers up front, so that the cache state
// tracking doesn't get confused by the following conditional jumps.
LiftoffRegister tmp1 =
opt_scratch != no_reg
? LiftoffRegister(opt_scratch)
: pinned.set(__ GetUnusedRegister(kGpReg, pinned));
LiftoffRegister tmp2 = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
if (obj.type.is_nullable()) {
LoadNullValue(tmp1.gp(), pinned);
__ emit_cond_jump(kEqual, no_match, obj.type, obj_reg.gp(), tmp1.gp());
}
__ emit_smi_check(obj_reg.gp(), no_match, LiftoffAssembler::kJumpOnSmi);
// Load the object's map and check if it is a struct/array map.
__ LoadMap(tmp1.gp(), obj_reg.gp());
EmitDataRefCheck(tmp1.gp(), no_match, tmp2, pinned);
return obj_reg;
} }
void RefAsData(FullDecoder* decoder, const Value& object, Value* result) { LiftoffRegister FuncCheck(const Value& obj, Label* no_match,
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_data"); LiftoffRegList pinned, Register opt_scratch) {
LiftoffRegister obj_reg = pinned.set(__ PopToRegister(pinned));
// Reserve all temporary registers up front, so that the cache state
// tracking doesn't get confused by the following conditional jumps.
LiftoffRegister tmp1 =
opt_scratch != no_reg
? LiftoffRegister(opt_scratch)
: pinned.set(__ GetUnusedRegister(kGpReg, pinned));
if (obj.type.is_nullable()) {
LoadNullValue(tmp1.gp(), pinned);
__ emit_cond_jump(kEqual, no_match, obj.type, obj_reg.gp(), tmp1.gp());
}
__ emit_smi_check(obj_reg.gp(), no_match, LiftoffAssembler::kJumpOnSmi);
// Load the object's map and check if its InstaceType field is that of a
// function.
__ LoadMap(tmp1.gp(), obj_reg.gp());
__ Load(tmp1, tmp1.gp(), no_reg,
wasm::ObjectAccess::ToTagged(Map::kInstanceTypeOffset),
LoadType::kI32Load16U, pinned);
__ emit_i32_cond_jumpi(kUnequal, no_match, tmp1.gp(), JS_FUNCTION_TYPE);
return obj_reg;
} }
void BrOnData(FullDecoder* decoder, const Value& object, LiftoffRegister I31Check(const Value& object, Label* no_match,
Value* value_on_branch, uint32_t br_depth) { LiftoffRegList pinned, Register opt_scratch) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_data"); LiftoffRegister obj_reg = pinned.set(__ PopToRegister(pinned));
__ emit_smi_check(obj_reg.gp(), no_match, LiftoffAssembler::kJumpOnNotSmi);
return obj_reg;
}
using TypeChecker = LiftoffRegister (LiftoffCompiler::*)(
const Value& obj, Label* no_match, LiftoffRegList pinned,
Register opt_scratch);
template <TypeChecker type_checker>
void AbstractTypeCheck(const Value& object) {
Label match, no_match, done;
LiftoffRegList pinned;
LiftoffRegister result = pinned.set(__ GetUnusedRegister(kGpReg, {}));
(this->*type_checker)(object, &no_match, pinned, result.gp());
__ bind(&match);
__ LoadConstant(result, WasmValue(1));
// TODO(jkummerow): Emit near jumps on platforms where it's more efficient.
__ emit_jump(&done);
__ bind(&no_match);
__ LoadConstant(result, WasmValue(0));
__ bind(&done);
__ PushRegister(kWasmI32, result);
} }
void RefIsFunc(FullDecoder* decoder, const Value& object, Value* result) { void RefIsData(FullDecoder* /* decoder */, const Value& object,
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_func"); Value* /* result_val */) {
return AbstractTypeCheck<&LiftoffCompiler::DataCheck>(object);
} }
void RefAsFunc(FullDecoder* decoder, const Value& object, Value* result) { void RefIsFunc(FullDecoder* /* decoder */, const Value& object,
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_func"); Value* /* result_val */) {
return AbstractTypeCheck<&LiftoffCompiler::FuncCheck>(object);
} }
void BrOnFunc(FullDecoder* decoder, const Value& object, void RefIsI31(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) { Value* /* result */) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_func"); return AbstractTypeCheck<&LiftoffCompiler::I31Check>(object);
}
template <TypeChecker type_checker>
void AbstractTypeCast(const Value& object, FullDecoder* decoder,
ValueType result_type) {
Label* trap_label = AddOutOfLineTrap(decoder->position(),
WasmCode::kThrowWasmTrapIllegalCast);
Label match;
LiftoffRegister obj_reg =
(this->*type_checker)(object, trap_label, {}, no_reg);
__ bind(&match);
__ PushRegister(result_type, obj_reg);
} }
void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) { void RefAsData(FullDecoder* decoder, const Value& object,
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_i31"); Value* /* result */) {
return AbstractTypeCast<&LiftoffCompiler::DataCheck>(object, decoder,
kWasmDataRef);
}
void RefAsFunc(FullDecoder* decoder, const Value& object,
Value* /* result */) {
return AbstractTypeCast<&LiftoffCompiler::FuncCheck>(
object, decoder, ValueType::Ref(HeapType::kFunc, kNonNullable));
} }
void RefAsI31(FullDecoder* decoder, const Value& object, Value* result) { void RefAsI31(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_i31"); return AbstractTypeCast<&LiftoffCompiler::I31Check>(object, decoder,
kWasmI31Ref);
}
template <TypeChecker type_checker>
void BrOnAbstractType(const Value& object, FullDecoder* decoder,
uint32_t br_depth, ValueType result_type) {
// Before branching, materialize all constants. This avoids repeatedly
// materializing them for each conditional branch.
if (br_depth != decoder->control_depth() - 1) {
__ MaterializeMergedConstants(
decoder->control_at(br_depth)->br_merge()->arity);
}
Label match, no_match;
LiftoffRegister obj_reg =
(this->*type_checker)(object, &no_match, {}, no_reg);
__ bind(&match);
__ PushRegister(result_type, obj_reg);
BrOrRet(decoder, br_depth);
__ bind(&no_match);
// Drop the branch's value, restore original value.
Drop(decoder);
__ PushRegister(object.type, obj_reg);
}
void BrOnData(FullDecoder* decoder, const Value& object,
Value* /* value_on_branch */, uint32_t br_depth) {
return BrOnAbstractType<&LiftoffCompiler::DataCheck>(
object, decoder, br_depth, kWasmDataRef);
}
void BrOnFunc(FullDecoder* decoder, const Value& object,
Value* /* value_on_branch */, uint32_t br_depth) {
return BrOnAbstractType<&LiftoffCompiler::FuncCheck>(
object, decoder, br_depth,
ValueType::Ref(HeapType::kFunc, kNonNullable));
} }
void BrOnI31(FullDecoder* decoder, const Value& object, void BrOnI31(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) { Value* /* value_on_branch */, uint32_t br_depth) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_i31"); return BrOnAbstractType<&LiftoffCompiler::I31Check>(object, decoder,
br_depth, kWasmI31Ref);
} }
void Forward(FullDecoder* decoder, const Value& from, Value* to) { void Forward(FullDecoder* decoder, const Value& from, Value* to) {
......
...@@ -1084,8 +1084,8 @@ WASM_COMPILED_EXEC_TEST(RefTestCastNull) { ...@@ -1084,8 +1084,8 @@ WASM_COMPILED_EXEC_TEST(RefTestCastNull) {
tester.CheckResult(kRefCastNull, 1); tester.CheckResult(kRefCastNull, 1);
} }
TEST(AbstractTypeChecks) { WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
WasmGCTester tester; WasmGCTester tester(execution_tier);
byte array_index = tester.DefineArray(kWasmI32, true); byte array_index = tester.DefineArray(kWasmI32, true);
byte function_index = byte function_index =
......
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