Skip to content

Commit

Permalink
deps: V8: cherry-pick bc831f8ba33b
Browse files Browse the repository at this point in the history
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 <verwaest@chromium.org>
    Reviewed-by: Maya Lekova <mslekova@chromium.org>
    Commit-Queue: Maya Lekova <mslekova@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#84552}

Refs: v8/v8@bc831f8
PR-URL: #45788
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
  • Loading branch information
anonrig authored and targos committed Jan 1, 2023
1 parent 7bd6a2c commit faee973
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 2 deletions.
2 changes: 1 addition & 1 deletion common.gypi
Expand Up @@ -36,7 +36,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.9',
'v8_embedder_string': '-node.10',

##### V8 defaults for Node.js #####

Expand Down
21 changes: 20 additions & 1 deletion deps/v8/include/v8-fast-api-calls.h
Expand Up @@ -248,6 +248,7 @@ class CTypeInfo {
kFloat32,
kFloat64,
kV8Value,
kSeqOneByteString,
kApiObject, // This will be deprecated once all users have
// migrated from v8::ApiObject to v8::Local<v8::Value>.
kAny, // This is added to enable untyped representation of fast
Expand Down Expand Up @@ -379,6 +380,11 @@ struct FastApiArrayBuffer {
size_t byte_length;
};

struct FastOneByteString {
const char* data;
uint32_t length;
};

class V8_EXPORT CFunctionInfo {
public:
// Construct a struct to hold a CFunction's type information.
Expand Down Expand Up @@ -438,6 +444,7 @@ struct AnyCType {
const FastApiTypedArray<uint64_t>* uint64_ta_value;
const FastApiTypedArray<float>* float_ta_value;
const FastApiTypedArray<double>* double_ta_value;
const FastOneByteString* string_value;
FastApiCallbackOptions* options_value;
};
};
Expand Down Expand Up @@ -614,7 +621,7 @@ class CFunctionInfoImpl : public CFunctionInfo {
kReturnType == CTypeInfo::Type::kFloat32 ||
kReturnType == CTypeInfo::Type::kFloat64 ||
kReturnType == CTypeInfo::Type::kAny,
"64-bit int and api object values are not currently "
"64-bit int, string and api object values are not currently "
"supported return types.");
}

Expand Down Expand Up @@ -735,6 +742,18 @@ struct TypeInfoHelper<FastApiCallbackOptions&> {
}
};

template <>
struct TypeInfoHelper<const FastOneByteString&> {
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }

static constexpr CTypeInfo::Type Type() {
return CTypeInfo::Type::kSeqOneByteString;
}
static constexpr CTypeInfo::SequenceType SequenceType() {
return CTypeInfo::SequenceType::kScalar;
}
};

#define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
static_assert(((COND) == 0) || (ASSERTION), MSG)

Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/codegen/machine-type.h
Expand Up @@ -315,6 +315,7 @@ class MachineType {
case CTypeInfo::Type::kFloat64:
return MachineType::Float64();
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kSeqOneByteString:
case CTypeInfo::Type::kApiObject:
return MachineType::AnyTagged();
}
Expand Down
45 changes: 45 additions & 0 deletions deps/v8/src/compiler/effect-control-linearizer.cc
Expand Up @@ -5246,6 +5246,50 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
case CTypeInfo::Type::kFloat32: {
return __ TruncateFloat64ToFloat32(node);
}
case CTypeInfo::Type::kSeqOneByteString: {
// Check that the value is a HeapObject.
Node* value_is_smi = ObjectIsSmi(node);
__ GotoIf(value_is_smi, if_error);

Node* map = __ LoadField(AccessBuilder::ForMap(), node);
Node* instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), map);

Node* encoding = __ Word32And(
instance_type,
__ Int32Constant(kStringRepresentationAndEncodingMask));

Node* is_onebytestring = __ Word32Equal(
encoding, __ Int32Constant(kSeqOneByteStringTag));
__ GotoIfNot(is_onebytestring, if_error);

Node* length_in_bytes =
__ LoadField(AccessBuilder::ForStringLength(), node);
Node* data_ptr = __ IntPtrAdd(
node, __ IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag));

constexpr int kAlign = alignof(FastOneByteString);
constexpr int kSize = sizeof(FastOneByteString);
static_assert(kSize == sizeof(uintptr_t) + sizeof(size_t),
"The size of "
"FastOneByteString isn't equal to the sum of its "
"expected members.");
Node* stack_slot = __ StackSlot(kSize, kAlign);

__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
stack_slot, 0, data_ptr);
__ Store(StoreRepresentation(MachineRepresentation::kWord32,
kNoWriteBarrier),
stack_slot, sizeof(size_t), length_in_bytes);

static_assert(sizeof(uintptr_t) == sizeof(size_t),
"The string length can't "
"fit the PointerRepresentation used to store it.");

return stack_slot;
}
default: {
return node;
}
Expand Down Expand Up @@ -5451,6 +5495,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
case CTypeInfo::Type::kFloat64:
return ChangeFloat64ToTagged(
c_call_result, CheckForMinusZeroMode::kCheckForMinusZero);
case CTypeInfo::Type::kSeqOneByteString:
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kApiObject:
case CTypeInfo::Type::kUint8:
Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/compiler/fast-api-calls.cc
Expand Up @@ -28,6 +28,7 @@ ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type) {
case CTypeInfo::Type::kFloat64:
return FLOAT64_ELEMENTS;
case CTypeInfo::Type::kVoid:
case CTypeInfo::Type::kSeqOneByteString:
case CTypeInfo::Type::kBool:
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kApiObject:
Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/compiler/simplified-lowering.cc
Expand Up @@ -1924,6 +1924,7 @@ class RepresentationSelector {
case CTypeInfo::Type::kFloat64:
return UseInfo::CheckedNumberAsFloat64(kDistinguishZeros, feedback);
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kSeqOneByteString:
case CTypeInfo::Type::kApiObject:
return UseInfo::AnyTagged();
}
Expand Down
47 changes: 47 additions & 0 deletions deps/v8/src/d8/d8-test.cc
Expand Up @@ -42,6 +42,43 @@ class FastCApiObject {
public:
static FastCApiObject& instance();

#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
static AnyCType CopyStringFastCallbackPatch(AnyCType receiver,
AnyCType should_fallback,
AnyCType source, AnyCType out,
AnyCType options) {
AnyCType ret;
CopyStringFastCallback(receiver.object_value, should_fallback.bool_value,
*source.string_value, *out.uint8_ta_value,
*options.options_value);
return ret;
}

#endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
static void CopyStringFastCallback(Local<Object> receiver,
bool should_fallback,
const FastOneByteString& source,
const FastApiTypedArray<uint8_t>& out,
FastApiCallbackOptions& options) {
FastCApiObject* self = UnwrapObject(receiver);
self->fast_call_count_++;

if (should_fallback) {
options.fallback = true;
} else {
options.fallback = false;
}

uint8_t* memory = nullptr;
CHECK(out.getStorageIfAligned(&memory));
memcpy(memory, source.data, source.length);
}

static void CopyStringSlowCallback(const FunctionCallbackInfo<Value>& args) {
FastCApiObject* self = UnwrapObject(args.This());
CHECK_SELF_OR_THROW();
self->slow_call_count_++;
}
#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
static AnyCType AddAllFastCallbackPatch(AnyCType receiver,
AnyCType should_fallback,
Expand Down Expand Up @@ -1072,6 +1109,16 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
PerIsolateData::Get(isolate)->SetTestApiObjectCtor(api_obj_ctor);
Local<Signature> signature = Signature::New(isolate, api_obj_ctor);
{
CFunction copy_str_func = CFunction::Make(
FastCApiObject::CopyStringFastCallback V8_IF_USE_SIMULATOR(
FastCApiObject::CopyStringFastCallbackPatch));
api_obj_ctor->PrototypeTemplate()->Set(
isolate, "copy_string",
FunctionTemplate::New(isolate, FastCApiObject::CopyStringSlowCallback,
Local<Value>(), signature, 1,
ConstructorBehavior::kThrow,
SideEffectType::kHasSideEffect, &copy_str_func));

CFunction add_all_c_func =
CFunction::Make(FastCApiObject::AddAllFastCallback V8_IF_USE_SIMULATOR(
FastCApiObject::AddAllFastCallbackPatch));
Expand Down
84 changes: 84 additions & 0 deletions deps/v8/test/mjsunit/compiler/fast-api-calls-string.js
@@ -0,0 +1,84 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file excercises one byte string support for fast API calls.

// Flags: --turbo-fast-api-calls --expose-fast-api --allow-natives-syntax --turbofan
// --always-turbofan is disabled because we rely on particular feedback for
// optimizing to the fastest path.
// Flags: --no-always-turbofan
// The test relies on optimizing/deoptimizing at predictable moments, so
// it's not suitable for deoptimization fuzzing.
// Flags: --deopt-every-n-times=0

assertThrows(() => d8.test.FastCAPI());
const fast_c_api = new d8.test.FastCAPI();

function assertSlowCall(input) {
assertEquals(new Uint8Array(input.length), copy_string(false, input));
}

function assertFastCall(input) {
const bytes = Uint8Array.from(input, c => c.charCodeAt(0));
assertEquals(bytes, copy_string(false, input));
}

function copy_string(should_fallback = false, input) {
const buffer = new Uint8Array(input.length);
fast_c_api.copy_string(should_fallback, input, buffer);
return buffer;
}

%PrepareFunctionForOptimization(copy_string);
assertSlowCall('Hello');
%OptimizeFunctionOnNextCall(copy_string);

fast_c_api.reset_counts();
assertFastCall('Hello');
assertFastCall('');
assertFastCall(['Hello', 'World'].join(''));
assertOptimized(copy_string);
assertEquals(3, fast_c_api.fast_call_count());
assertEquals(0, fast_c_api.slow_call_count());

// Fall back for twobyte strings.
fast_c_api.reset_counts();
assertSlowCall('Hello\u{10000}');
assertSlowCall('नमस्ते');
assertSlowCall(['नमस्ते', 'World'].join(''));
assertOptimized(copy_string);
assertEquals(0, fast_c_api.fast_call_count());
assertEquals(3, fast_c_api.slow_call_count());

// Fall back for cons strings.
function getTwoByteString() {
return '\u1234t';
}
function getCons() {
return 'hello' + getTwoByteString()
}

fast_c_api.reset_counts();
assertSlowCall(getCons());
assertOptimized(copy_string);
assertEquals(0, fast_c_api.fast_call_count());
assertEquals(1, fast_c_api.slow_call_count());

// Fall back for sliced strings.
fast_c_api.reset_counts();
function getSliced() {
return getCons().slice(1);
}
assertSlowCall(getSliced());
assertOptimized(copy_string);
assertEquals(0, fast_c_api.fast_call_count());
assertEquals(1, fast_c_api.slow_call_count());

// Fall back for SMI and non-string inputs.
fast_c_api.reset_counts();
assertSlowCall(1);
assertSlowCall({});
assertSlowCall(new Uint8Array(1));
assertEquals(0, fast_c_api.fast_call_count());
assertEquals(3, fast_c_api.slow_call_count());

0 comments on commit faee973

Please sign in to comment.