Skip to content

Commit 894aff7

Browse files
authored
deps: V8: cherry-pick bc831f8ba33b
Original commit message: [fastcall] Implement support for onebyte string arguments This CL adds one byte string specialization support for fast API call arguments. It introduces a kOneByteString variant to CTypeInfo. We see a ~6x improvement in Deno's TextEncoder#encode microbenchmark. Rendered results: https://divy-v8-patches.deno.dev/ Bug: chromium:1052746 Change-Id: I47c3a9e101cd18ddc6ad58f627db3a34231b60f7 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4036884 Reviewed-by: Toon Verwaest <[email protected]> Reviewed-by: Maya Lekova <[email protected]> Commit-Queue: Maya Lekova <[email protected]> Cr-Commit-Position: refs/heads/main@{#84552} Refs: v8/v8@bc831f8 PR-URL: #45788 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Jiawen Geng <[email protected]> Reviewed-By: Daeyeon Jeong <[email protected]>
1 parent 265ea1e commit 894aff7

File tree

8 files changed

+200
-2
lines changed

8 files changed

+200
-2
lines changed

common.gypi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
# Reset this number to 0 on major V8 upgrades.
3838
# Increment by one for each non-official patch applied to deps/v8.
39-
'v8_embedder_string': '-node.5',
39+
'v8_embedder_string': '-node.6',
4040

4141
##### V8 defaults for Node.js #####
4242

deps/v8/include/v8-fast-api-calls.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ class CTypeInfo {
248248
kFloat32,
249249
kFloat64,
250250
kV8Value,
251+
kSeqOneByteString,
251252
kApiObject, // This will be deprecated once all users have
252253
// migrated from v8::ApiObject to v8::Local<v8::Value>.
253254
kAny, // This is added to enable untyped representation of fast
@@ -379,6 +380,11 @@ struct FastApiArrayBuffer {
379380
size_t byte_length;
380381
};
381382

383+
struct FastOneByteString {
384+
const char* data;
385+
uint32_t length;
386+
};
387+
382388
class V8_EXPORT CFunctionInfo {
383389
public:
384390
// Construct a struct to hold a CFunction's type information.
@@ -438,6 +444,7 @@ struct AnyCType {
438444
const FastApiTypedArray<uint64_t>* uint64_ta_value;
439445
const FastApiTypedArray<float>* float_ta_value;
440446
const FastApiTypedArray<double>* double_ta_value;
447+
const FastOneByteString* string_value;
441448
FastApiCallbackOptions* options_value;
442449
};
443450
};
@@ -614,7 +621,7 @@ class CFunctionInfoImpl : public CFunctionInfo {
614621
kReturnType == CTypeInfo::Type::kFloat32 ||
615622
kReturnType == CTypeInfo::Type::kFloat64 ||
616623
kReturnType == CTypeInfo::Type::kAny,
617-
"64-bit int and api object values are not currently "
624+
"64-bit int, string and api object values are not currently "
618625
"supported return types.");
619626
}
620627

@@ -735,6 +742,18 @@ struct TypeInfoHelper<FastApiCallbackOptions&> {
735742
}
736743
};
737744

745+
template <>
746+
struct TypeInfoHelper<const FastOneByteString&> {
747+
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
748+
749+
static constexpr CTypeInfo::Type Type() {
750+
return CTypeInfo::Type::kSeqOneByteString;
751+
}
752+
static constexpr CTypeInfo::SequenceType SequenceType() {
753+
return CTypeInfo::SequenceType::kScalar;
754+
}
755+
};
756+
738757
#define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
739758
static_assert(((COND) == 0) || (ASSERTION), MSG)
740759

deps/v8/src/codegen/machine-type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ class MachineType {
315315
case CTypeInfo::Type::kFloat64:
316316
return MachineType::Float64();
317317
case CTypeInfo::Type::kV8Value:
318+
case CTypeInfo::Type::kSeqOneByteString:
318319
case CTypeInfo::Type::kApiObject:
319320
return MachineType::AnyTagged();
320321
}

deps/v8/src/compiler/effect-control-linearizer.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5395,6 +5395,50 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
53955395
case CTypeInfo::Type::kFloat32: {
53965396
return __ TruncateFloat64ToFloat32(node);
53975397
}
5398+
case CTypeInfo::Type::kSeqOneByteString: {
5399+
// Check that the value is a HeapObject.
5400+
Node* value_is_smi = ObjectIsSmi(node);
5401+
__ GotoIf(value_is_smi, if_error);
5402+
5403+
Node* map = __ LoadField(AccessBuilder::ForMap(), node);
5404+
Node* instance_type =
5405+
__ LoadField(AccessBuilder::ForMapInstanceType(), map);
5406+
5407+
Node* encoding = __ Word32And(
5408+
instance_type,
5409+
__ Int32Constant(kStringRepresentationAndEncodingMask));
5410+
5411+
Node* is_onebytestring = __ Word32Equal(
5412+
encoding, __ Int32Constant(kSeqOneByteStringTag));
5413+
__ GotoIfNot(is_onebytestring, if_error);
5414+
5415+
Node* length_in_bytes =
5416+
__ LoadField(AccessBuilder::ForStringLength(), node);
5417+
Node* data_ptr = __ IntPtrAdd(
5418+
node, __ IntPtrConstant(SeqOneByteString::kHeaderSize -
5419+
kHeapObjectTag));
5420+
5421+
constexpr int kAlign = alignof(FastOneByteString);
5422+
constexpr int kSize = sizeof(FastOneByteString);
5423+
static_assert(kSize == sizeof(uintptr_t) + sizeof(size_t),
5424+
"The size of "
5425+
"FastOneByteString isn't equal to the sum of its "
5426+
"expected members.");
5427+
Node* stack_slot = __ StackSlot(kSize, kAlign);
5428+
5429+
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
5430+
kNoWriteBarrier),
5431+
stack_slot, 0, data_ptr);
5432+
__ Store(StoreRepresentation(MachineRepresentation::kWord32,
5433+
kNoWriteBarrier),
5434+
stack_slot, sizeof(size_t), length_in_bytes);
5435+
5436+
static_assert(sizeof(uintptr_t) == sizeof(size_t),
5437+
"The string length can't "
5438+
"fit the PointerRepresentation used to store it.");
5439+
5440+
return stack_slot;
5441+
}
53985442
default: {
53995443
return node;
54005444
}
@@ -5600,6 +5644,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
56005644
case CTypeInfo::Type::kFloat64:
56015645
return ChangeFloat64ToTagged(
56025646
c_call_result, CheckForMinusZeroMode::kCheckForMinusZero);
5647+
case CTypeInfo::Type::kSeqOneByteString:
56035648
case CTypeInfo::Type::kV8Value:
56045649
case CTypeInfo::Type::kApiObject:
56055650
case CTypeInfo::Type::kUint8:

deps/v8/src/compiler/fast-api-calls.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type) {
2929
case CTypeInfo::Type::kFloat64:
3030
return FLOAT64_ELEMENTS;
3131
case CTypeInfo::Type::kVoid:
32+
case CTypeInfo::Type::kSeqOneByteString:
3233
case CTypeInfo::Type::kBool:
3334
case CTypeInfo::Type::kV8Value:
3435
case CTypeInfo::Type::kApiObject:

deps/v8/src/compiler/simplified-lowering.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,7 @@ class RepresentationSelector {
19611961
case CTypeInfo::Type::kFloat64:
19621962
return UseInfo::CheckedNumberAsFloat64(kDistinguishZeros, feedback);
19631963
case CTypeInfo::Type::kV8Value:
1964+
case CTypeInfo::Type::kSeqOneByteString:
19641965
case CTypeInfo::Type::kApiObject:
19651966
return UseInfo::AnyTagged();
19661967
}

deps/v8/src/d8/d8-test.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,43 @@ class FastCApiObject {
4242
public:
4343
static FastCApiObject& instance();
4444

45+
#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
46+
static AnyCType CopyStringFastCallbackPatch(AnyCType receiver,
47+
AnyCType should_fallback,
48+
AnyCType source, AnyCType out,
49+
AnyCType options) {
50+
AnyCType ret;
51+
CopyStringFastCallback(receiver.object_value, should_fallback.bool_value,
52+
*source.string_value, *out.uint8_ta_value,
53+
*options.options_value);
54+
return ret;
55+
}
56+
57+
#endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
58+
static void CopyStringFastCallback(Local<Object> receiver,
59+
bool should_fallback,
60+
const FastOneByteString& source,
61+
const FastApiTypedArray<uint8_t>& out,
62+
FastApiCallbackOptions& options) {
63+
FastCApiObject* self = UnwrapObject(receiver);
64+
self->fast_call_count_++;
65+
66+
if (should_fallback) {
67+
options.fallback = true;
68+
} else {
69+
options.fallback = false;
70+
}
71+
72+
uint8_t* memory = nullptr;
73+
CHECK(out.getStorageIfAligned(&memory));
74+
memcpy(memory, source.data, source.length);
75+
}
76+
77+
static void CopyStringSlowCallback(const FunctionCallbackInfo<Value>& args) {
78+
FastCApiObject* self = UnwrapObject(args.This());
79+
CHECK_SELF_OR_THROW();
80+
self->slow_call_count_++;
81+
}
4582
#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
4683
static AnyCType AddAllFastCallbackPatch(AnyCType receiver,
4784
AnyCType should_fallback,
@@ -1084,6 +1121,16 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
10841121
PerIsolateData::Get(isolate)->SetTestApiObjectCtor(api_obj_ctor);
10851122
Local<Signature> signature = Signature::New(isolate, api_obj_ctor);
10861123
{
1124+
CFunction copy_str_func = CFunction::Make(
1125+
FastCApiObject::CopyStringFastCallback V8_IF_USE_SIMULATOR(
1126+
FastCApiObject::CopyStringFastCallbackPatch));
1127+
api_obj_ctor->PrototypeTemplate()->Set(
1128+
isolate, "copy_string",
1129+
FunctionTemplate::New(isolate, FastCApiObject::CopyStringSlowCallback,
1130+
Local<Value>(), signature, 1,
1131+
ConstructorBehavior::kThrow,
1132+
SideEffectType::kHasSideEffect, &copy_str_func));
1133+
10871134
CFunction add_all_c_func =
10881135
CFunction::Make(FastCApiObject::AddAllFastCallback V8_IF_USE_SIMULATOR(
10891136
FastCApiObject::AddAllFastCallbackPatch));
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2022 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// This file excercises one byte string support for fast API calls.
6+
7+
// Flags: --turbo-fast-api-calls --expose-fast-api --allow-natives-syntax --turbofan
8+
// --always-turbofan is disabled because we rely on particular feedback for
9+
// optimizing to the fastest path.
10+
// Flags: --no-always-turbofan
11+
// The test relies on optimizing/deoptimizing at predictable moments, so
12+
// it's not suitable for deoptimization fuzzing.
13+
// Flags: --deopt-every-n-times=0
14+
15+
assertThrows(() => d8.test.FastCAPI());
16+
const fast_c_api = new d8.test.FastCAPI();
17+
18+
function assertSlowCall(input) {
19+
assertEquals(new Uint8Array(input.length), copy_string(false, input));
20+
}
21+
22+
function assertFastCall(input) {
23+
const bytes = Uint8Array.from(input, c => c.charCodeAt(0));
24+
assertEquals(bytes, copy_string(false, input));
25+
}
26+
27+
function copy_string(should_fallback = false, input) {
28+
const buffer = new Uint8Array(input.length);
29+
fast_c_api.copy_string(should_fallback, input, buffer);
30+
return buffer;
31+
}
32+
33+
%PrepareFunctionForOptimization(copy_string);
34+
assertSlowCall('Hello');
35+
%OptimizeFunctionOnNextCall(copy_string);
36+
37+
fast_c_api.reset_counts();
38+
assertFastCall('Hello');
39+
assertFastCall('');
40+
assertFastCall(['Hello', 'World'].join(''));
41+
assertOptimized(copy_string);
42+
assertEquals(3, fast_c_api.fast_call_count());
43+
assertEquals(0, fast_c_api.slow_call_count());
44+
45+
// Fall back for twobyte strings.
46+
fast_c_api.reset_counts();
47+
assertSlowCall('Hello\u{10000}');
48+
assertSlowCall('नमस्ते');
49+
assertSlowCall(['नमस्ते', 'World'].join(''));
50+
assertOptimized(copy_string);
51+
assertEquals(0, fast_c_api.fast_call_count());
52+
assertEquals(3, fast_c_api.slow_call_count());
53+
54+
// Fall back for cons strings.
55+
function getTwoByteString() {
56+
return '\u1234t';
57+
}
58+
function getCons() {
59+
return 'hello' + getTwoByteString()
60+
}
61+
62+
fast_c_api.reset_counts();
63+
assertSlowCall(getCons());
64+
assertOptimized(copy_string);
65+
assertEquals(0, fast_c_api.fast_call_count());
66+
assertEquals(1, fast_c_api.slow_call_count());
67+
68+
// Fall back for sliced strings.
69+
fast_c_api.reset_counts();
70+
function getSliced() {
71+
return getCons().slice(1);
72+
}
73+
assertSlowCall(getSliced());
74+
assertOptimized(copy_string);
75+
assertEquals(0, fast_c_api.fast_call_count());
76+
assertEquals(1, fast_c_api.slow_call_count());
77+
78+
// Fall back for SMI and non-string inputs.
79+
fast_c_api.reset_counts();
80+
assertSlowCall(1);
81+
assertSlowCall({});
82+
assertSlowCall(new Uint8Array(1));
83+
assertEquals(0, fast_c_api.fast_call_count());
84+
assertEquals(3, fast_c_api.slow_call_count());

0 commit comments

Comments
 (0)