1e994192d6
Reason for revert:
speculative revert: https://codereview.chromium.org/2596013002/
Original issue's description:
> [regexp] Remove IsRegExp intrinsic
>
> The two remaining uses of this intrinsic in debug.js and mirrors.js now
> simply rely on the runtime function.
>
> BUG=v8:5339
>
> Review-Url: https://codereview.chromium.org/2591923003
> Cr-Commit-Position: refs/heads/master@{#41892}
> Committed: c9cb94a06f
TBR=bmeurer@chromium.org,jgruber@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5339
Review-Url: https://codereview.chromium.org/2592383002
Cr-Commit-Position: refs/heads/master@{#41915}
407 lines
14 KiB
C++
407 lines
14 KiB
C++
// Copyright 2015 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.
|
|
|
|
#include "src/interpreter/interpreter-intrinsics.h"
|
|
|
|
#include "src/code-factory.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace interpreter {
|
|
|
|
using compiler::Node;
|
|
|
|
#define __ assembler_->
|
|
|
|
IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
|
|
: isolate_(assembler->isolate()),
|
|
zone_(assembler->zone()),
|
|
assembler_(assembler) {}
|
|
|
|
// static
|
|
bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) {
|
|
switch (function_id) {
|
|
#define SUPPORTED(name, lower_case, count) case Runtime::kInline##name:
|
|
INTRINSICS_LIST(SUPPORTED)
|
|
return true;
|
|
#undef SUPPORTED
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// static
|
|
IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId(
|
|
Runtime::FunctionId function_id) {
|
|
switch (function_id) {
|
|
#define TO_RUNTIME_ID(name, lower_case, count) \
|
|
case Runtime::kInline##name: \
|
|
return IntrinsicId::k##name;
|
|
INTRINSICS_LIST(TO_RUNTIME_ID)
|
|
#undef TO_RUNTIME_ID
|
|
default:
|
|
UNREACHABLE();
|
|
return static_cast<IntrinsicsHelper::IntrinsicId>(-1);
|
|
}
|
|
}
|
|
|
|
// static
|
|
Runtime::FunctionId IntrinsicsHelper::ToRuntimeId(
|
|
IntrinsicsHelper::IntrinsicId intrinsic_id) {
|
|
switch (intrinsic_id) {
|
|
#define TO_INTRINSIC_ID(name, lower_case, count) \
|
|
case IntrinsicId::k##name: \
|
|
return Runtime::kInline##name;
|
|
INTRINSICS_LIST(TO_INTRINSIC_ID)
|
|
#undef TO_INTRINSIC_ID
|
|
default:
|
|
UNREACHABLE();
|
|
return static_cast<Runtime::FunctionId>(-1);
|
|
}
|
|
}
|
|
|
|
Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context,
|
|
Node* first_arg_reg, Node* arg_count) {
|
|
InterpreterAssembler::Label abort(assembler_), end(assembler_);
|
|
InterpreterAssembler::Variable result(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
|
|
#define MAKE_LABEL(name, lower_case, count) \
|
|
InterpreterAssembler::Label lower_case(assembler_);
|
|
INTRINSICS_LIST(MAKE_LABEL)
|
|
#undef MAKE_LABEL
|
|
|
|
#define LABEL_POINTER(name, lower_case, count) &lower_case,
|
|
InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)};
|
|
#undef LABEL_POINTER
|
|
|
|
#define CASE(name, lower_case, count) \
|
|
static_cast<int32_t>(IntrinsicId::k##name),
|
|
int32_t cases[] = {INTRINSICS_LIST(CASE)};
|
|
#undef CASE
|
|
|
|
__ Switch(function_id, &abort, cases, labels, arraysize(cases));
|
|
#define HANDLE_CASE(name, lower_case, expected_arg_count) \
|
|
__ Bind(&lower_case); \
|
|
if (FLAG_debug_code && expected_arg_count >= 0) { \
|
|
AbortIfArgCountMismatch(expected_arg_count, arg_count); \
|
|
} \
|
|
result.Bind(name(first_arg_reg, arg_count, context)); \
|
|
__ Goto(&end);
|
|
INTRINSICS_LIST(HANDLE_CASE)
|
|
#undef HANDLE_CASE
|
|
|
|
__ Bind(&abort);
|
|
{
|
|
__ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic);
|
|
result.Bind(__ UndefinedConstant());
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&end);
|
|
return result.value();
|
|
}
|
|
|
|
Node* IntrinsicsHelper::CompareInstanceType(Node* object, int type,
|
|
InstanceTypeCompareMode mode) {
|
|
Node* instance_type = __ LoadInstanceType(object);
|
|
|
|
if (mode == kInstanceTypeEqual) {
|
|
return __ Word32Equal(instance_type, __ Int32Constant(type));
|
|
} else {
|
|
DCHECK(mode == kInstanceTypeGreaterThanOrEqual);
|
|
return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type));
|
|
}
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsInstanceType(Node* input, int type) {
|
|
InterpreterAssembler::Variable return_value(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
// TODO(ishell): Use Select here.
|
|
InterpreterAssembler::Label if_not_smi(assembler_), return_true(assembler_),
|
|
return_false(assembler_), end(assembler_);
|
|
Node* arg = __ LoadRegister(input);
|
|
__ GotoIf(__ TaggedIsSmi(arg), &return_false);
|
|
|
|
Node* condition = CompareInstanceType(arg, type, kInstanceTypeEqual);
|
|
__ Branch(condition, &return_true, &return_false);
|
|
|
|
__ Bind(&return_true);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(true));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&return_false);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(false));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&end);
|
|
return return_value.value();
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsJSReceiver(Node* input, Node* arg_count,
|
|
Node* context) {
|
|
// TODO(ishell): Use Select here.
|
|
// TODO(ishell): Use CSA::IsJSReceiverInstanceType here.
|
|
InterpreterAssembler::Variable return_value(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
InterpreterAssembler::Label return_true(assembler_), return_false(assembler_),
|
|
end(assembler_);
|
|
|
|
Node* arg = __ LoadRegister(input);
|
|
__ GotoIf(__ TaggedIsSmi(arg), &return_false);
|
|
|
|
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
|
|
Node* condition = CompareInstanceType(arg, FIRST_JS_RECEIVER_TYPE,
|
|
kInstanceTypeGreaterThanOrEqual);
|
|
__ Branch(condition, &return_true, &return_false);
|
|
|
|
__ Bind(&return_true);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(true));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&return_false);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(false));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&end);
|
|
return return_value.value();
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) {
|
|
return IsInstanceType(input, JS_ARRAY_TYPE);
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) {
|
|
return IsInstanceType(input, JS_PROXY_TYPE);
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) {
|
|
return IsInstanceType(input, JS_REGEXP_TYPE);
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count,
|
|
Node* context) {
|
|
return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IsSmi(Node* input, Node* arg_count, Node* context) {
|
|
// TODO(ishell): Use SelectBooleanConstant here.
|
|
InterpreterAssembler::Variable return_value(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_),
|
|
end(assembler_);
|
|
|
|
Node* arg = __ LoadRegister(input);
|
|
|
|
__ Branch(__ TaggedIsSmi(arg), &if_smi, &if_not_smi);
|
|
__ Bind(&if_smi);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(true));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&if_not_smi);
|
|
{
|
|
return_value.Bind(__ BooleanConstant(false));
|
|
__ Goto(&end);
|
|
}
|
|
|
|
__ Bind(&end);
|
|
return return_value.value();
|
|
}
|
|
|
|
Node* IntrinsicsHelper::IntrinsicAsStubCall(Node* args_reg, Node* context,
|
|
Callable const& callable) {
|
|
int param_count = callable.descriptor().GetParameterCount();
|
|
int input_count = param_count + 2; // +2 for target and context
|
|
Node** args = zone()->NewArray<Node*>(input_count);
|
|
int index = 0;
|
|
args[index++] = __ HeapConstant(callable.code());
|
|
for (int i = 0; i < param_count; i++) {
|
|
args[index++] = __ LoadRegister(args_reg);
|
|
args_reg = __ NextRegister(args_reg);
|
|
}
|
|
args[index++] = context;
|
|
return __ CallStubN(callable.descriptor(), 1, input_count, args);
|
|
}
|
|
|
|
Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count,
|
|
Node* context) {
|
|
return IntrinsicAsStubCall(input, context,
|
|
CodeFactory::HasProperty(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context,
|
|
CodeFactory::FastNewObject(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count,
|
|
Node* context) {
|
|
return IntrinsicAsStubCall(input, context,
|
|
CodeFactory::NumberToString(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count,
|
|
Node* context) {
|
|
return IntrinsicAsStubCall(input, context,
|
|
CodeFactory::RegExpExec(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) {
|
|
return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate()));
|
|
}
|
|
|
|
Node* IntrinsicsHelper::Call(Node* args_reg, Node* arg_count, Node* context) {
|
|
// First argument register contains the function target.
|
|
Node* function = __ LoadRegister(args_reg);
|
|
|
|
// Receiver is the second runtime call argument.
|
|
Node* receiver_reg = __ NextRegister(args_reg);
|
|
Node* receiver_arg = __ RegisterLocation(receiver_reg);
|
|
|
|
// Subtract function and receiver from arg count.
|
|
Node* function_and_receiver_count = __ Int32Constant(2);
|
|
Node* target_args_count = __ Int32Sub(arg_count, function_and_receiver_count);
|
|
|
|
if (FLAG_debug_code) {
|
|
InterpreterAssembler::Label arg_count_positive(assembler_);
|
|
Node* comparison = __ Int32LessThan(target_args_count, __ Int32Constant(0));
|
|
__ GotoUnless(comparison, &arg_count_positive);
|
|
__ Abort(kWrongArgumentCountForInvokeIntrinsic);
|
|
__ Goto(&arg_count_positive);
|
|
__ Bind(&arg_count_positive);
|
|
}
|
|
|
|
Node* result = __ CallJS(function, context, receiver_arg, target_args_count,
|
|
TailCallMode::kDisallow);
|
|
return result;
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ValueOf(Node* args_reg, Node* arg_count,
|
|
Node* context) {
|
|
InterpreterAssembler::Variable return_value(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
InterpreterAssembler::Label done(assembler_);
|
|
|
|
Node* object = __ LoadRegister(args_reg);
|
|
return_value.Bind(object);
|
|
|
|
// If the object is a smi return the object.
|
|
__ GotoIf(__ TaggedIsSmi(object), &done);
|
|
|
|
// If the object is not a value type, return the object.
|
|
Node* condition =
|
|
CompareInstanceType(object, JS_VALUE_TYPE, kInstanceTypeEqual);
|
|
__ GotoUnless(condition, &done);
|
|
|
|
// If the object is a value type, return the value field.
|
|
return_value.Bind(__ LoadObjectField(object, JSValue::kValueOffset));
|
|
__ Goto(&done);
|
|
|
|
__ Bind(&done);
|
|
return return_value.value();
|
|
}
|
|
|
|
Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count,
|
|
Node* context) {
|
|
InterpreterAssembler::Variable return_value(assembler_,
|
|
MachineRepresentation::kTagged);
|
|
InterpreterAssembler::Label done(assembler_), null(assembler_),
|
|
function(assembler_), non_function_constructor(assembler_);
|
|
|
|
Node* object = __ LoadRegister(args_reg);
|
|
|
|
// If the object is not a JSReceiver, we return null.
|
|
__ GotoIf(__ TaggedIsSmi(object), &null);
|
|
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
|
Node* is_js_receiver = CompareInstanceType(object, FIRST_JS_RECEIVER_TYPE,
|
|
kInstanceTypeGreaterThanOrEqual);
|
|
__ GotoUnless(is_js_receiver, &null);
|
|
|
|
// Return 'Function' for JSFunction and JSBoundFunction objects.
|
|
Node* is_function = CompareInstanceType(object, FIRST_FUNCTION_TYPE,
|
|
kInstanceTypeGreaterThanOrEqual);
|
|
STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE);
|
|
__ GotoIf(is_function, &function);
|
|
|
|
// Check if the constructor in the map is a JS function.
|
|
Node* constructor = __ LoadMapConstructor(__ LoadMap(object));
|
|
Node* constructor_is_js_function =
|
|
CompareInstanceType(constructor, JS_FUNCTION_TYPE, kInstanceTypeEqual);
|
|
__ GotoUnless(constructor_is_js_function, &non_function_constructor);
|
|
|
|
// Grab the instance class name from the constructor function.
|
|
Node* shared =
|
|
__ LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset);
|
|
return_value.Bind(
|
|
__ LoadObjectField(shared, SharedFunctionInfo::kInstanceClassNameOffset));
|
|
__ Goto(&done);
|
|
|
|
// Non-JS objects have class null.
|
|
__ Bind(&null);
|
|
{
|
|
return_value.Bind(__ LoadRoot(Heap::kNullValueRootIndex));
|
|
__ Goto(&done);
|
|
}
|
|
|
|
// Functions have class 'Function'.
|
|
__ Bind(&function);
|
|
{
|
|
return_value.Bind(__ LoadRoot(Heap::kFunction_stringRootIndex));
|
|
__ Goto(&done);
|
|
}
|
|
|
|
// Objects with a non-function constructor have class 'Object'.
|
|
__ Bind(&non_function_constructor);
|
|
{
|
|
return_value.Bind(__ LoadRoot(Heap::kObject_stringRootIndex));
|
|
__ Goto(&done);
|
|
}
|
|
|
|
__ Bind(&done);
|
|
return return_value.value();
|
|
}
|
|
|
|
void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) {
|
|
InterpreterAssembler::Label match(assembler_);
|
|
Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected));
|
|
__ GotoIf(comparison, &match);
|
|
__ Abort(kWrongArgumentCountForInvokeIntrinsic);
|
|
__ Goto(&match);
|
|
__ Bind(&match);
|
|
}
|
|
|
|
} // namespace interpreter
|
|
} // namespace internal
|
|
} // namespace v8
|