[wasm-stringrefs] Add string.hash instruction
Bug: v8:12868 Change-Id: I12ee551fbffc25c591d618f1957bbabbceff255e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4217413 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Matthias Liedtke <mliedtke@chromium.org> Cr-Commit-Position: refs/heads/main@{#85633}
This commit is contained in:
parent
31654be902
commit
14f1ec8cdd
@ -61,6 +61,7 @@ extern runtime WasmStringViewWtf8Slice(
|
||||
Context, ByteArray, Number, Number): String;
|
||||
extern runtime WasmStringCompare(NoContext, String, String): Smi;
|
||||
extern runtime WasmStringFromCodePoint(Context, Number): String;
|
||||
extern runtime WasmStringHash(NoContext, String): Smi;
|
||||
extern runtime WasmJSToWasmObject(Context, JSAny, Smi): JSAny;
|
||||
}
|
||||
|
||||
@ -1265,6 +1266,11 @@ builtin WasmStringFromCodePoint(codePoint: uint32): String {
|
||||
LoadContextFromFrame(), WasmUint32ToNumber(codePoint));
|
||||
}
|
||||
|
||||
builtin WasmStringHash(string: String): int32 {
|
||||
const result = runtime::WasmStringHash(kNoContext, string);
|
||||
return SmiToInt32(result);
|
||||
}
|
||||
|
||||
builtin WasmExternInternalize(externObject: JSAny): JSAny {
|
||||
const instance = LoadInstanceFromFrame();
|
||||
const context = LoadContextFromInstance(instance);
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/objects/heap-number.h"
|
||||
#include "src/objects/instance-type.h"
|
||||
#include "src/objects/name.h"
|
||||
#include "src/objects/string.h"
|
||||
#include "src/roots/roots.h"
|
||||
#include "src/tracing/trace-event.h"
|
||||
@ -6288,6 +6289,40 @@ Node* WasmGraphBuilder::StringFromCodePoint(Node* code_point) {
|
||||
Operator::kEliminatable, code_point);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringHash(Node* string, CheckForNull null_check,
|
||||
wasm::WasmCodePosition position) {
|
||||
if (null_check == kWithNullCheck) {
|
||||
string = AssertNotNull(string, position);
|
||||
}
|
||||
|
||||
auto runtime_label = gasm_->MakeLabel();
|
||||
auto end_label = gasm_->MakeLabel(MachineRepresentation::kWord32);
|
||||
|
||||
Node* raw_hash = gasm_->LoadFromObject(
|
||||
MachineType::Int32(), string,
|
||||
wasm::ObjectAccess::ToTagged(Name::kRawHashFieldOffset));
|
||||
Node* hash_not_computed_mask =
|
||||
gasm_->Int32Constant(static_cast<int32_t>(Name::kHashNotComputedMask));
|
||||
static_assert(Name::HashFieldTypeBits::kShift == 0);
|
||||
Node* hash_not_computed = gasm_->Word32And(raw_hash, hash_not_computed_mask);
|
||||
gasm_->GotoIf(hash_not_computed, &runtime_label);
|
||||
|
||||
// Fast path if hash is already computed: Decode raw hash value.
|
||||
static_assert(Name::HashBits::kLastUsedBit == kBitsPerInt - 1);
|
||||
Node* hash = gasm_->Word32Shr(
|
||||
raw_hash,
|
||||
gasm_->Int32Constant(static_cast<int32_t>(Name::HashBits::kShift)));
|
||||
gasm_->Goto(&end_label, hash);
|
||||
|
||||
gasm_->Bind(&runtime_label);
|
||||
Node* hash_runtime = gasm_->CallBuiltin(Builtin::kWasmStringHash,
|
||||
Operator::kEliminatable, string);
|
||||
gasm_->Goto(&end_label, hash_runtime);
|
||||
|
||||
gasm_->Bind(&end_label);
|
||||
return end_label.PhiAt(0);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::I31New(Node* input) {
|
||||
if constexpr (SmiValuesAre31Bits()) {
|
||||
return gasm_->Word32Shl(input, gasm_->BuildSmiShiftBitsConstant32());
|
||||
|
@ -598,6 +598,8 @@ class WasmGraphBuilder {
|
||||
CheckForNull null_check_rhs,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringFromCodePoint(Node* code_point);
|
||||
Node* StringHash(Node* string, CheckForNull null_check,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* IsNull(Node* object);
|
||||
Node* TypeGuard(Node* value, wasm::ValueType type);
|
||||
|
||||
|
@ -1400,5 +1400,13 @@ RUNTIME_FUNCTION(Runtime_WasmStringFromCodePoint) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmStringHash) {
|
||||
ClearThreadInWasmScope flag_scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
String string(String::cast(args[0]));
|
||||
uint32_t hash = string.EnsureHash();
|
||||
return Smi::FromInt(static_cast<int>(hash));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -654,7 +654,8 @@ namespace internal {
|
||||
F(WasmStringViewWtf8Encode, 6, 1) \
|
||||
F(WasmStringViewWtf8Slice, 3, 1) \
|
||||
F(WasmStringCompare, 2, 1) \
|
||||
F(WasmStringFromCodePoint, 1, 1)
|
||||
F(WasmStringFromCodePoint, 1, 1) \
|
||||
F(WasmStringHash, 1, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_WASM_TEST(F, I) \
|
||||
F(DeserializeWasmModule, 2, 1) \
|
||||
|
@ -7304,6 +7304,22 @@ class LiftoffCompiler {
|
||||
__ PushRegister(kRef, result_reg);
|
||||
}
|
||||
|
||||
void StringHash(FullDecoder* decoder, const Value& string, Value* result) {
|
||||
LiftoffRegList pinned;
|
||||
LiftoffRegister string_reg = pinned.set(
|
||||
__ LoadToRegister(__ cache_state()->stack_state.end()[-1], pinned));
|
||||
MaybeEmitNullCheck(decoder, string_reg.gp(), pinned, string.type);
|
||||
LiftoffAssembler::VarState string_var(kRef, string_reg, 0);
|
||||
|
||||
CallRuntimeStub(WasmCode::kWasmStringHash,
|
||||
MakeSig::Returns(kI32).Params(kRef), {string_var},
|
||||
decoder->position());
|
||||
|
||||
LiftoffRegister result_reg(kReturnRegister0);
|
||||
__ DropValues(1);
|
||||
__ PushRegister(kI32, result_reg);
|
||||
}
|
||||
|
||||
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
@ -1194,7 +1194,8 @@ struct ControlBase : public PcForErrors<ValidationTag::full_validation> {
|
||||
F(StringViewIterSlice, const Value& view, const Value& codepoints, \
|
||||
Value* result) \
|
||||
F(StringCompare, const Value& lhs, const Value& rhs, Value* result) \
|
||||
F(StringFromCodePoint, const Value& code_point, Value* result)
|
||||
F(StringFromCodePoint, const Value& code_point, Value* result) \
|
||||
F(StringHash, const Value& string, Value* result)
|
||||
|
||||
// This is a global constant invalid instruction trace, to be pointed at by
|
||||
// the current instruction trace pointer in the default case
|
||||
@ -2328,6 +2329,7 @@ class WasmDecoder : public Decoder {
|
||||
case kExprStringEncodeWtf16Array:
|
||||
case kExprStringCompare:
|
||||
case kExprStringFromCodePoint:
|
||||
case kExprStringHash:
|
||||
return length;
|
||||
default:
|
||||
// This is unreachable except for malformed modules.
|
||||
@ -2531,6 +2533,7 @@ class WasmDecoder : public Decoder {
|
||||
case kExprStringViewWtf16Length:
|
||||
case kExprStringViewIterNext:
|
||||
case kExprStringFromCodePoint:
|
||||
case kExprStringHash:
|
||||
return { 1, 1 };
|
||||
case kExprStringNewUtf8:
|
||||
case kExprStringNewUtf8Try:
|
||||
@ -6187,6 +6190,15 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> {
|
||||
Push(result);
|
||||
return opcode_length;
|
||||
}
|
||||
case kExprStringHash: {
|
||||
NON_CONST_ONLY
|
||||
Value string = Peek(0, 0, kWasmStringRef);
|
||||
Value result = CreateValue(kWasmI32);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(StringHash, string, &result);
|
||||
Drop(1);
|
||||
Push(result);
|
||||
return opcode_length;
|
||||
}
|
||||
default:
|
||||
this->DecodeError("invalid stringref opcode: %x", opcode);
|
||||
return 0;
|
||||
|
@ -1799,6 +1799,12 @@ class WasmGraphBuildingInterface {
|
||||
SetAndTypeNode(result, builder_->StringFromCodePoint(code_point.node));
|
||||
}
|
||||
|
||||
void StringHash(FullDecoder* decoder, const Value& string, Value* result) {
|
||||
SetAndTypeNode(result,
|
||||
builder_->StringHash(string.node, NullCheckFor(string.type),
|
||||
decoder->position()));
|
||||
}
|
||||
|
||||
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
||||
if (from.type == to->type) {
|
||||
to->node = from.node;
|
||||
|
@ -151,6 +151,7 @@ struct WasmModule;
|
||||
V(WasmStringViewIterSlice) \
|
||||
V(WasmStringCompare) \
|
||||
V(WasmStringFromCodePoint) \
|
||||
V(WasmStringHash) \
|
||||
V(WasmExternInternalize)
|
||||
|
||||
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
||||
|
@ -765,6 +765,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig);
|
||||
V(StringViewIterSlice, 0xfba4, _, "stringview_iter.slice") \
|
||||
V(StringCompare, 0xfba8, _, "string.compare") \
|
||||
V(StringFromCodePoint, 0xfba9, _, "string.from_code_point") \
|
||||
V(StringHash, 0xfbaa, _, "string.hash") \
|
||||
V(StringNewUtf8Array, 0xfbb0, _, "string.new_utf8_array") \
|
||||
V(StringNewWtf16Array, 0xfbb1, _, "string.new_wtf16_array") \
|
||||
V(StringEncodeUtf8Array, 0xfbb2, _, "string.encode_utf8_array") \
|
||||
|
@ -1296,3 +1296,34 @@ function makeWtf16TestDataSegment() {
|
||||
WebAssembly.RuntimeError, /Invalid code point [0-9]+/);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestStringHash() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addFunction("hash", kSig_i_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
...GCInstr(kExprStringHash),
|
||||
]);
|
||||
|
||||
let hash = builder.instantiate().exports.hash;
|
||||
assertEquals(hash(""), hash(""));
|
||||
assertEquals(hash("foo"), hash("foo"));
|
||||
assertEquals(hash("bar"), hash("bar"));
|
||||
assertEquals(hash("123"), hash("123"));
|
||||
// Assuming that hash collisions are very rare.
|
||||
assertNotEquals(hash("foo"), hash("bar"));
|
||||
// Test with cons strings.
|
||||
assertEquals(hash("f" + "o" + "o"), hash("foo"));
|
||||
assertEquals(hash("f" + 1), hash("f1"));
|
||||
|
||||
assertEquals(hash(new String(" foo ").trim()), hash("foo"));
|
||||
assertEquals(hash(new String("xfoox").substring(1, 4)), hash("foo"));
|
||||
|
||||
// Test integer index hash.
|
||||
let dummy_obj = {123: 456};
|
||||
let index_string = "123";
|
||||
assertEquals(456, dummy_obj[index_string]);
|
||||
assertEquals(hash("1" + "23"), hash(index_string));
|
||||
})();
|
||||
|
@ -257,6 +257,12 @@ let kSig_w_zi = makeSig([kWasmStringViewIter, kWasmI32],
|
||||
...GCInstr(kExprStringFromCodePoint)
|
||||
]);
|
||||
|
||||
builder.addFunction("string.hash", kSig_i_w)
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
...GCInstr(kExprStringHash)
|
||||
]);
|
||||
|
||||
let i8_array = builder.addArray(kWasmI8, true);
|
||||
let i16_array = builder.addArray(kWasmI16, true);
|
||||
|
||||
|
@ -569,6 +569,7 @@ let kExprStringViewIterRewind = 0xa3
|
||||
let kExprStringViewIterSlice = 0xa4;
|
||||
let kExprStringCompare = 0xa8;
|
||||
let kExprStringFromCodePoint = 0xa9;
|
||||
let kExprStringHash = 0xaa;
|
||||
let kExprStringNewUtf8Array = 0xb0;
|
||||
let kExprStringNewWtf16Array = 0xb1;
|
||||
let kExprStringEncodeUtf8Array = 0xb2;
|
||||
|
Loading…
Reference in New Issue
Block a user