blob: 029cf125949c32f4a65748c2a651f75b5630c94d [file] [log] [blame]
// Copyright 2017 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-generator.h"
#include "src/allocation.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/frames.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-assembler.h"
#include "src/interpreter/interpreter-intrinsics.h"
namespace v8 {
namespace internal {
namespace interpreter {
using compiler::Node;
class IntrinsicsGenerator {
public:
explicit IntrinsicsGenerator(InterpreterAssembler* assembler)
: isolate_(assembler->isolate()),
zone_(assembler->zone()),
assembler_(assembler) {}
Node* InvokeIntrinsic(Node* function_id, Node* context, Node* first_arg_reg,
Node* arg_count);
private:
enum InstanceTypeCompareMode {
kInstanceTypeEqual,
kInstanceTypeGreaterThanOrEqual
};
Node* IsInstanceType(Node* input, int type);
Node* CompareInstanceType(Node* map, int type, InstanceTypeCompareMode mode);
Node* IntrinsicAsStubCall(Node* input, Node* context,
Callable const& callable);
Node* IntrinsicAsBuiltinCall(Node* input, Node* context, Builtins::Name name);
void AbortIfArgCountMismatch(int expected, compiler::Node* actual);
#define DECLARE_INTRINSIC_HELPER(name, lower_case, count) \
Node* name(Node* input, Node* arg_count, Node* context);
INTRINSICS_LIST(DECLARE_INTRINSIC_HELPER)
#undef DECLARE_INTRINSIC_HELPER
Isolate* isolate() { return isolate_; }
Zone* zone() { return zone_; }
Isolate* isolate_;
Zone* zone_;
InterpreterAssembler* assembler_;
DISALLOW_COPY_AND_ASSIGN(IntrinsicsGenerator);
};
Node* GenerateInvokeIntrinsic(InterpreterAssembler* assembler,
Node* function_id, Node* context,
Node* first_arg_reg, Node* arg_count) {
IntrinsicsGenerator generator(assembler);
return generator.InvokeIntrinsic(function_id, context, first_arg_reg,
arg_count);
}
#define __ assembler_->
Node* IntrinsicsGenerator::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>(IntrinsicsHelper::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); \
} \
Node* value = name(first_arg_reg, arg_count, context); \
if (value) { \
result.Bind(value); \
__ 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* IntrinsicsGenerator::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* IntrinsicsGenerator::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* IntrinsicsGenerator::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* IntrinsicsGenerator::IsArray(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_ARRAY_TYPE);
}
Node* IntrinsicsGenerator::IsJSProxy(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_PROXY_TYPE);
}
Node* IntrinsicsGenerator::IsTypedArray(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
}
Node* IntrinsicsGenerator::IsJSMap(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_MAP_TYPE);
}
Node* IntrinsicsGenerator::IsJSSet(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_SET_TYPE);
}
Node* IntrinsicsGenerator::IsJSWeakMap(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_WEAK_MAP_TYPE);
}
Node* IntrinsicsGenerator::IsJSWeakSet(Node* input, Node* arg_count,
Node* context) {
return IsInstanceType(input, JS_WEAK_SET_TYPE);
}
Node* IntrinsicsGenerator::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* IntrinsicsGenerator::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* IntrinsicsGenerator::IntrinsicAsBuiltinCall(Node* input, Node* context,
Builtins::Name name) {
Callable callable = Builtins::CallableFor(isolate_, name);
return IntrinsicAsStubCall(input, context, callable);
}
Node* IntrinsicsGenerator::CreateIterResultObject(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context,
Builtins::CallableFor(isolate(), Builtins::kCreateIterResultObject));
}
Node* IntrinsicsGenerator::HasProperty(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kHasProperty));
}
Node* IntrinsicsGenerator::SubString(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
}
Node* IntrinsicsGenerator::ToString(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kToString));
}
Node* IntrinsicsGenerator::ToLength(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kToLength));
}
Node* IntrinsicsGenerator::ToInteger(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kToInteger));
}
Node* IntrinsicsGenerator::ToNumber(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kToNumber));
}
Node* IntrinsicsGenerator::ToObject(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsStubCall(
input, context, Builtins::CallableFor(isolate(), Builtins::kToObject));
}
Node* IntrinsicsGenerator::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));
__ GotoIfNot(comparison, &arg_count_positive);
__ Abort(kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&arg_count_positive);
__ BIND(&arg_count_positive);
}
__ CallJSAndDispatch(function, context, receiver_arg, target_args_count,
ConvertReceiverMode::kAny);
return nullptr; // We never return from the CallJSAndDispatch above.
}
Node* IntrinsicsGenerator::ClassOf(Node* args_reg, Node* arg_count,
Node* context) {
Node* value = __ LoadRegister(args_reg);
return __ ClassOf(value);
}
Node* IntrinsicsGenerator::CreateAsyncFromSyncIterator(Node* args_reg,
Node* arg_count,
Node* context) {
InterpreterAssembler::Label not_receiver(
assembler_, InterpreterAssembler::Label::kDeferred);
InterpreterAssembler::Label done(assembler_);
InterpreterAssembler::Variable return_value(assembler_,
MachineRepresentation::kTagged);
Node* sync_iterator = __ LoadRegister(args_reg);
__ GotoIf(__ TaggedIsSmi(sync_iterator), &not_receiver);
__ GotoIfNot(__ IsJSReceiver(sync_iterator), &not_receiver);
Node* const native_context = __ LoadNativeContext(context);
Node* const map = __ LoadContextElement(
native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX);
Node* const iterator = __ AllocateJSObjectFromMap(map);
__ StoreObjectFieldNoWriteBarrier(
iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset, sync_iterator);
return_value.Bind(iterator);
__ Goto(&done);
__ BIND(&not_receiver);
{
return_value.Bind(
__ CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context));
// Unreachable due to the Throw in runtime call.
__ Goto(&done);
}
__ BIND(&done);
return return_value.value();
}
Node* IntrinsicsGenerator::CreateJSGeneratorObject(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsBuiltinCall(input, context,
Builtins::kCreateGeneratorObject);
}
Node* IntrinsicsGenerator::GeneratorGetContext(Node* args_reg, Node* arg_count,
Node* context) {
Node* generator = __ LoadRegister(args_reg);
Node* const value =
__ LoadObjectField(generator, JSGeneratorObject::kContextOffset);
return value;
}
Node* IntrinsicsGenerator::GeneratorGetInputOrDebugPos(Node* args_reg,
Node* arg_count,
Node* context) {
Node* generator = __ LoadRegister(args_reg);
Node* const value =
__ LoadObjectField(generator, JSGeneratorObject::kInputOrDebugPosOffset);
return value;
}
Node* IntrinsicsGenerator::GeneratorGetResumeMode(Node* args_reg,
Node* arg_count,
Node* context) {
Node* generator = __ LoadRegister(args_reg);
Node* const value =
__ LoadObjectField(generator, JSGeneratorObject::kResumeModeOffset);
return value;
}
Node* IntrinsicsGenerator::GeneratorClose(Node* args_reg, Node* arg_count,
Node* context) {
Node* generator = __ LoadRegister(args_reg);
__ StoreObjectFieldNoWriteBarrier(
generator, JSGeneratorObject::kContinuationOffset,
__ SmiConstant(JSGeneratorObject::kGeneratorClosed));
return __ UndefinedConstant();
}
Node* IntrinsicsGenerator::AsyncGeneratorReject(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsBuiltinCall(input, context,
Builtins::kAsyncGeneratorReject);
}
Node* IntrinsicsGenerator::AsyncGeneratorResolve(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsBuiltinCall(input, context,
Builtins::kAsyncGeneratorResolve);
}
Node* IntrinsicsGenerator::AsyncGeneratorYield(Node* input, Node* arg_count,
Node* context) {
return IntrinsicAsBuiltinCall(input, context, Builtins::kAsyncGeneratorYield);
}
void IntrinsicsGenerator::AbortIfArgCountMismatch(int expected, Node* actual) {
InterpreterAssembler::Label match(assembler_);
Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected));
__ GotoIf(comparison, &match);
__ Abort(kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&match);
__ BIND(&match);
}
#undef __
} // namespace interpreter
} // namespace internal
} // namespace v8