Commit 8e5026b5 authored by Gus Caplan's avatar Gus Caplan Committed by Commit Bot

[fastcall] support more return types

This adds support for kBool, kInt32, and kUint32 types.

Bug: chromium:1052746
Change-Id: I54641eb036eea30113c44eab2c08626176ecc40a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2629463
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72574}
parent 8c99b253
......@@ -148,7 +148,11 @@
* receiver := the {embedder_object} from above
* param := 42
*
* Currently only void return types are supported.
* Currently supported return types:
* - void
* - bool
* - int32_t
* - uint32_t
* Currently supported argument types:
* - pointer to an embedder type
* - bool
......@@ -378,9 +382,12 @@ class CFunctionInfoImpl : public CFunctionInfo {
"Only one options parameter is supported.");
static_assert(sizeof...(Args) >= kOptionsArgCount + kReceiverCount,
"The receiver or the fallback argument is missing.");
constexpr CTypeInfo::Type type = internal::GetCType<R>::Get().GetType();
static_assert(
internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
"Only void return types are currently supported.");
type == CTypeInfo::Type::kVoid || type == CTypeInfo::Type::kBool ||
type == CTypeInfo::Type::kInt32 || type == CTypeInfo::Type::kUint32,
"floating point, 64-bit, and api object values are not currently "
"supported.");
}
const CTypeInfo& ReturnInfo() const override { return return_info_; }
......
......@@ -250,6 +250,9 @@ CallDescriptor* Linkage::GetSimplifiedCDescriptor(Zone* zone,
CHECK_GE(2, locations.return_count_);
if (locations.return_count_ > 0) {
// TODO(chromium:1052746): Use the correctly sized register here (e.g. "al"
// if the return type is kBit), so we don't have to use a hacky bitwise AND
// elsewhere.
locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister0.code(),
msig->GetReturn(0)));
}
......
......@@ -250,6 +250,7 @@ class EffectControlLinearizer {
Node* CallBuiltin(Builtins::Name builtin, Operator::Properties properties,
Args...);
Node* ChangeBitToTagged(Node* value);
Node* ChangeInt32ToSmi(Node* value);
// In pointer compression, we smi-corrupt. This means the upper bits of a Smi
// are not important. ChangeTaggedInt32ToSmi has a known tagged int32 as input
......@@ -258,11 +259,13 @@ class EffectControlLinearizer {
// In non pointer compression, it behaves like ChangeInt32ToSmi.
Node* ChangeTaggedInt32ToSmi(Node* value);
Node* ChangeInt32ToIntPtr(Node* value);
Node* ChangeInt32ToTagged(Node* value);
Node* ChangeInt64ToSmi(Node* value);
Node* ChangeIntPtrToInt32(Node* value);
Node* ChangeIntPtrToSmi(Node* value);
Node* ChangeUint32ToUintPtr(Node* value);
Node* ChangeUint32ToSmi(Node* value);
Node* ChangeUint32ToTagged(Node* value);
Node* ChangeSmiToIntPtr(Node* value);
Node* ChangeSmiToInt32(Node* value);
Node* ChangeSmiToInt64(Node* value);
......@@ -1434,7 +1437,10 @@ Node* EffectControlLinearizer::LowerChangeFloat64ToTaggedPointer(Node* node) {
Node* EffectControlLinearizer::LowerChangeBitToTagged(Node* node) {
Node* value = node->InputAt(0);
return ChangeBitToTagged(value);
}
Node* EffectControlLinearizer::ChangeBitToTagged(Node* value) {
auto if_true = __ MakeLabel();
auto done = __ MakeLabel(MachineRepresentation::kTagged);
......@@ -1455,7 +1461,10 @@ Node* EffectControlLinearizer::LowerChangeInt31ToTaggedSigned(Node* node) {
Node* EffectControlLinearizer::LowerChangeInt32ToTagged(Node* node) {
Node* value = node->InputAt(0);
return ChangeInt32ToTagged(value);
}
Node* EffectControlLinearizer::ChangeInt32ToTagged(Node* value) {
if (SmiValuesAre32Bits()) {
return ChangeInt32ToSmi(value);
}
......@@ -1501,7 +1510,10 @@ Node* EffectControlLinearizer::LowerChangeInt64ToTagged(Node* node) {
Node* EffectControlLinearizer::LowerChangeUint32ToTagged(Node* node) {
Node* value = node->InputAt(0);
return ChangeUint32ToTagged(value);
}
Node* EffectControlLinearizer::ChangeUint32ToTagged(Node* value) {
auto if_not_in_smi_range = __ MakeDeferredLabel();
auto done = __ MakeLabel(MachineRepresentation::kTagged);
......@@ -4960,7 +4972,6 @@ void EffectControlLinearizer::LowerStoreMessage(Node* node) {
__ StoreField(AccessBuilder::ForExternalIntPtr(), offset, object_pattern);
}
// TODO(mslekova): Avoid code duplication with simplified lowering.
static MachineType MachineTypeFor(CTypeInfo::Type type) {
switch (type) {
case CTypeInfo::Type::kVoid:
......@@ -5022,7 +5033,10 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
}
MachineSignature::Builder builder(
graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasOptionsInputCount);
graph()->zone(), 1,
c_arg_count + (c_signature->HasOptions()
? FastApiCallNode::kHasOptionsInputCount
: 0));
MachineType return_type = MachineTypeFor(c_signature->ReturnInfo().GetType());
builder.AddReturn(return_type);
for (int i = 0; i < c_arg_count; ++i) {
......@@ -5030,7 +5044,9 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
MachineTypeFor(c_signature->ArgumentInfo(i).GetType());
builder.AddParam(machine_type);
}
builder.AddParam(MachineType::Pointer()); // fast_api_call_stack_slot_
if (c_signature->HasOptions()) {
builder.AddParam(MachineType::Pointer()); // fast_api_call_stack_slot_
}
CallDescriptor* call_descriptor =
Linkage::GetSimplifiedCDescriptor(graph()->zone(), builder.Build());
......@@ -5045,7 +5061,9 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
target_address, 0, n.target());
Node** const inputs = graph()->zone()->NewArray<Node*>(
c_arg_count + FastApiCallNode::kFastCallExtraInputCount);
c_arg_count + (c_signature->HasOptions()
? FastApiCallNode::kFastCallExtraInputCountWithOptions
: FastApiCallNode::kFastCallExtraInputCount));
inputs[0] = n.target();
for (int i = FastApiCallNode::kFastTargetInputCount;
i < c_arg_count + FastApiCallNode::kFastTargetInputCount; ++i) {
......@@ -5057,18 +5075,46 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
inputs[i] = NodeProperties::GetValueInput(node, i);
}
}
inputs[c_arg_count + 1] = fast_api_call_stack_slot_;
inputs[c_arg_count + 2] = __ effect();
inputs[c_arg_count + 3] = __ control();
if (c_signature->HasOptions()) {
inputs[c_arg_count + 1] = fast_api_call_stack_slot_;
inputs[c_arg_count + 2] = __ effect();
inputs[c_arg_count + 3] = __ control();
} else {
inputs[c_arg_count + 1] = __ effect();
inputs[c_arg_count + 2] = __ control();
}
__ Call(call_descriptor,
c_arg_count + FastApiCallNode::kFastCallExtraInputCount, inputs);
Node* c_call_result = __ Call(
call_descriptor,
c_arg_count + (c_signature->HasOptions()
? FastApiCallNode::kFastCallExtraInputCountWithOptions
: FastApiCallNode::kFastCallExtraInputCount),
inputs);
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
target_address, 0, __ IntPtrConstant(0));
auto gen_c_call_return = [&]() -> Node* {
switch (c_signature->ReturnInfo().GetType()) {
case CTypeInfo::Type::kVoid:
return __ UndefinedConstant();
case CTypeInfo::Type::kBool:
return ChangeBitToTagged(
__ Word32And(c_call_result, __ Int32Constant(0xFF)));
case CTypeInfo::Type::kInt32:
return ChangeInt32ToTagged(c_call_result);
case CTypeInfo::Type::kUint32:
return ChangeUint32ToTagged(c_call_result);
case CTypeInfo::Type::kInt64:
case CTypeInfo::Type::kUint64:
case CTypeInfo::Type::kFloat32:
case CTypeInfo::Type::kFloat64:
case CTypeInfo::Type::kV8Value:
UNREACHABLE();
}
};
if (c_signature->HasOptions()) {
// Generate the load from `fast_api_call_stack_slot_`.
Node* load = __ Load(
......@@ -5084,7 +5130,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
// Generate fast call.
__ Bind(&if_success);
Node* then_result = [&]() { return __ UndefinedConstant(); }();
Node* then_result = gen_c_call_return();
__ Goto(&merge, then_result);
// Generate direct slow call.
......@@ -5115,7 +5161,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
return merge.PhiAt(0);
}
return __ UndefinedConstant();
return gen_c_call_return();
}
Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
......
......@@ -3508,6 +3508,10 @@ Reduction JSCallReducer::ReduceCallWasmFunction(
#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
namespace {
bool HasFPParamsInSignature(const CFunctionInfo* c_signature) {
if (c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat32 ||
c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat64) {
return true;
}
for (unsigned int i = 0; i < c_signature->ArgumentCount(); ++i) {
if (c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat32 ||
c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat64) {
......@@ -3522,6 +3526,10 @@ bool HasFPParamsInSignature(const CFunctionInfo* c_signature) {
#ifndef V8_TARGET_ARCH_64_BIT
namespace {
bool Has64BitIntegerParamsInSignature(const CFunctionInfo* c_signature) {
if (c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kInt64 ||
c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kUint64) {
return true;
}
for (unsigned int i = 0; i < c_signature->ArgumentCount(); ++i) {
if (c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kInt64 ||
c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kUint64) {
......
......@@ -1737,29 +1737,6 @@ class RepresentationSelector {
}
}
static MachineType MachineTypeFor(CTypeInfo::Type type) {
switch (type) {
case CTypeInfo::Type::kVoid:
return MachineType::AnyTagged();
case CTypeInfo::Type::kBool:
return MachineType::Bool();
case CTypeInfo::Type::kInt32:
return MachineType::Int32();
case CTypeInfo::Type::kUint32:
return MachineType::Uint32();
case CTypeInfo::Type::kInt64:
return MachineType::Int64();
case CTypeInfo::Type::kUint64:
return MachineType::Uint64();
case CTypeInfo::Type::kFloat32:
return MachineType::Float32();
case CTypeInfo::Type::kFloat64:
return MachineType::Float64();
case CTypeInfo::Type::kV8Value:
return MachineType::AnyTagged();
}
}
UseInfo UseInfoForFastApiCallArgument(CTypeInfo::Type type,
FeedbackSource const& feedback) {
switch (type) {
......@@ -1824,10 +1801,7 @@ class RepresentationSelector {
ProcessInput<T>(node, i, UseInfo::AnyTagged());
}
ProcessRemainingInputs<T>(node, value_input_count);
MachineType return_type =
MachineTypeFor(c_signature->ReturnInfo().GetType());
SetOutput<T>(node, return_type.representation());
SetOutput<T>(node, MachineRepresentation::kTagged);
}
static MachineType MachineTypeForWasmReturnType(wasm::ValueType type) {
......
......@@ -1139,9 +1139,10 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
static constexpr int kHolderInputCount = 1;
static constexpr int kContextAndFrameStateInputCount = 2;
static constexpr int kEffectAndControlInputCount = 2;
static constexpr int kFastCallExtraInputCount = kFastTargetInputCount +
kHasOptionsInputCount +
kEffectAndControlInputCount;
static constexpr int kFastCallExtraInputCount =
kFastTargetInputCount + kEffectAndControlInputCount;
static constexpr int kFastCallExtraInputCountWithOptions =
kFastCallExtraInputCount + kHasOptionsInputCount;
static constexpr int kSlowCallExtraInputCount =
kSlowTargetInputCount + kArityInputCount + kNewTargetInputCount +
kSlowReceiverInputCount + kHolderInputCount +
......
......@@ -27589,18 +27589,19 @@ UNINITIALIZED_TEST(NestedIsolates) {
#ifndef V8_LITE_MODE
namespace {
template <typename Value, typename Impl>
template <typename Value, typename Impl, typename Ret>
struct BasicApiChecker {
static void FastCallback(v8::ApiObject receiver, Value argument,
v8::FastApiCallbackOptions& options) {
static Ret FastCallback(v8::ApiObject receiver, Value argument,
v8::FastApiCallbackOptions& options) {
const v8::Value* data = reinterpret_cast<const v8::Value*>(&options.data);
CHECK(data->IsNumber());
CHECK_EQ(reinterpret_cast<const v8::Number*>(data)->Value(), 42.0);
Impl::FastCallback(receiver, argument, options);
return Impl::FastCallback(receiver, argument, options);
}
static void FastCallbackNoFallback(v8::ApiObject receiver, Value argument) {
static Ret FastCallbackNoFallback(v8::ApiObject receiver, Value argument) {
v8::FastApiCallbackOptions options = {false, {0}};
Impl::FastCallback(receiver, argument, options);
return Impl::FastCallback(receiver, argument, options);
}
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
Impl::SlowCallback(info);
......@@ -27624,7 +27625,7 @@ enum class FallbackPolicy {
};
template <typename T>
struct ApiNumberChecker : BasicApiChecker<T, ApiNumberChecker<T>> {
struct ApiNumberChecker : BasicApiChecker<T, ApiNumberChecker<T>, void> {
explicit ApiNumberChecker(
T value, Behavior raise_exception = Behavior::kNoException,
FallbackPolicy write_to_fallback = FallbackPolicy::kDontRequestFallback,
......@@ -27684,7 +27685,7 @@ struct ApiNumberChecker : BasicApiChecker<T, ApiNumberChecker<T>> {
};
struct UnexpectedObjectChecker
: BasicApiChecker<v8::ApiObject, UnexpectedObjectChecker> {
: BasicApiChecker<v8::ApiObject, UnexpectedObjectChecker, void> {
static void FastCallback(v8::ApiObject receiver, v8::ApiObject argument,
v8::FastApiCallbackOptions& options) {
v8::Object* receiver_obj = reinterpret_cast<v8::Object*>(&receiver);
......@@ -27712,24 +27713,26 @@ struct UnexpectedObjectChecker
}
};
template <typename Value, typename Impl>
template <typename Value, typename Impl, typename Ret>
bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
BasicApiChecker<Value, Impl>* checker, const char* source_code,
bool supports_fallback = true, bool accept_any_receiver = true) {
BasicApiChecker<Value, Impl, Ret>* checker,
const char* source_code, bool supports_fallback = true,
bool accept_any_receiver = true) {
v8::Isolate* isolate = CcTest::isolate();
v8::TryCatch try_catch(isolate);
v8::CFunction c_func;
if (supports_fallback) {
c_func = v8::CFunction::Make(BasicApiChecker<Value, Impl>::FastCallback);
c_func =
v8::CFunction::Make(BasicApiChecker<Value, Impl, Ret>::FastCallback);
} else {
c_func = v8::CFunction::Make(
BasicApiChecker<Value, Impl>::FastCallbackNoFallback);
BasicApiChecker<Value, Impl, Ret>::FastCallbackNoFallback);
}
CHECK_EQ(c_func.ArgumentInfo(0).GetType(), v8::CTypeInfo::Type::kV8Value);
Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New(
isolate, BasicApiChecker<Value, Impl>::SlowCallback,
isolate, BasicApiChecker<Value, Impl, Ret>::SlowCallback,
v8::Number::New(isolate, 42), v8::Local<v8::Signature>(), 1,
v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasSideEffect,
&c_func);
......@@ -27755,10 +27758,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
->Global()
->Set(env->local(), v8_str("value"), initial_value)
.FromJust());
v8::Local<v8::Value> result = CompileRun(source_code);
if (!try_catch.HasCaught()) {
CHECK(result->IsUndefined());
}
USE(CompileRun(source_code));
return try_catch.HasCaught();
}
......@@ -27799,7 +27799,7 @@ void CallAndCheck(
ApiNumberChecker<T> checker(expected_value, raise_exception,
write_to_fallback);
bool has_caught = SetupTest<T, ApiNumberChecker<T>>(
bool has_caught = SetupTest<T, ApiNumberChecker<T>, void>(
initial_value, &env, &checker,
"function func(arg) { return receiver.api_func(arg); }"
"%PrepareFunctionForOptimization(func);"
......@@ -27808,9 +27808,12 @@ void CallAndCheck(
v8::Isolate* isolate = CcTest::isolate();
v8::TryCatch try_catch(isolate);
CompileRun(
v8::Local<v8::Value> result = CompileRun(
"%OptimizeFunctionOnNextCall(func);"
"func(value);");
if (!try_catch.HasCaught()) {
CHECK(result->IsUndefined());
}
CHECK_EQ(expected_behavior == Behavior::kException, has_caught);
CHECK_EQ(expected_path == ApiCheckerResult::kSlowCalled,
......@@ -27819,6 +27822,7 @@ void CallAndCheck(
!checker.DidCallSlow());
if (expected_path & ApiCheckerResult::kSlowCalled) {
CHECK(checker.DidCallSlow());
if (expected_behavior != Behavior::kException) {
CheckEqual(checker.slow_value_.ToChecked(), expected_value);
}
......@@ -27829,6 +27833,56 @@ void CallAndCheck(
}
}
template <typename T>
struct ReturnValueChecker : BasicApiChecker<T, ReturnValueChecker<T>, T> {
static T FastCallback(v8::ApiObject receiver, T arg,
v8::FastApiCallbackOptions& options) {
v8::Object* receiver_obj = reinterpret_cast<v8::Object*>(&receiver);
ReturnValueChecker<T>* receiver_ptr =
GetInternalField<ReturnValueChecker<T>, kV8WrapperObjectIndex>(
receiver_obj);
receiver_ptr->result_ |= ApiCheckerResult::kFastCalled;
return arg;
}
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Object* receiver_obj = v8::Object::Cast(*info.Holder());
ReturnValueChecker<T>* receiver_ptr =
GetInternalField<ReturnValueChecker<T>, kV8WrapperObjectIndex>(
receiver_obj);
receiver_ptr->result_ |= ApiCheckerResult::kSlowCalled;
info.GetReturnValue().Set(info[0]);
}
};
template <typename T>
void CheckFastReturnValue(v8::Local<v8::Value> expected_value,
ApiCheckerResultFlags expected_path) {
LocalContext env;
ReturnValueChecker<T> checker{};
bool has_caught = SetupTest<T, ReturnValueChecker<T>, T>(
expected_value, &env, &checker,
"function func(arg) { return receiver.api_func(arg); }"
"%PrepareFunctionForOptimization(func);"
"func(value);");
CHECK(!has_caught);
checker.result_ = ApiCheckerResult::kNotCalled;
v8::Isolate* isolate = CcTest::isolate();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> result = CompileRun(
"%OptimizeFunctionOnNextCall(func);"
"func(value);");
CHECK_EQ(expected_path == ApiCheckerResult::kSlowCalled,
!checker.DidCallFast());
CHECK_EQ(expected_path == ApiCheckerResult::kFastCalled,
!checker.DidCallSlow());
CHECK(result->StrictEquals(expected_value));
}
void CallAndDeopt() {
LocalContext env;
v8::Local<v8::Value> initial_value(v8_num(42));
......@@ -28415,6 +28469,24 @@ TEST(FastApiCalls) {
CallAndCheck<bool>(true, Behavior::kNoException,
ApiCheckerResult::kFastCalled, v8::Object::New(isolate));
// Test return values
CheckFastReturnValue<bool>(v8::Boolean::New(isolate, true),
ApiCheckerResult::kFastCalled);
CheckFastReturnValue<bool>(v8::Boolean::New(isolate, false),
ApiCheckerResult::kFastCalled);
CheckFastReturnValue<int32_t>(v8_num(0), ApiCheckerResult::kFastCalled);
CheckFastReturnValue<int32_t>(v8_num(std::numeric_limits<int32_t>::min()),
ApiCheckerResult::kFastCalled);
CheckFastReturnValue<int32_t>(v8_num(std::numeric_limits<int32_t>::max()),
ApiCheckerResult::kFastCalled);
CheckFastReturnValue<uint32_t>(v8_num(0), ApiCheckerResult::kFastCalled);
CheckFastReturnValue<uint32_t>(v8_num(std::numeric_limits<uint32_t>::min()),
ApiCheckerResult::kFastCalled);
CheckFastReturnValue<uint32_t>(v8_num(std::numeric_limits<uint32_t>::max()),
ApiCheckerResult::kFastCalled);
// Check for the deopt loop protection
CallAndDeopt();
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