blob: b9975e66eaa3a0df7c1fca93838fd95c1dc02017 [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/builtins/builtins.h"
#include "src/codegen/code-factory.h"
#include "src/execution/frames.h"
#include "src/heap/factory-inl.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-assembler.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/objects/js-generator.h"
#include "src/objects/objects-inl.h"
#include "src/objects/source-text-module.h"
#include "src/utils/allocation.h"
namespace v8 {
namespace internal {
namespace interpreter {
class IntrinsicsGenerator {
public:
explicit IntrinsicsGenerator(InterpreterAssembler* assembler)
: isolate_(assembler->isolate()),
zone_(assembler->zone()),
assembler_(assembler) {}
IntrinsicsGenerator(const IntrinsicsGenerator&) = delete;
IntrinsicsGenerator& operator=(const IntrinsicsGenerator&) = delete;
TNode<Object> InvokeIntrinsic(
TNode<Uint32T> function_id, TNode<Context> context,
const InterpreterAssembler::RegListNodePair& args);
private:
enum InstanceTypeCompareMode {
kInstanceTypeEqual,
kInstanceTypeGreaterThanOrEqual
};
TNode<Oddball> IsInstanceType(TNode<Object> input, int type);
TNode<BoolT> CompareInstanceType(TNode<HeapObject> map, int type,
InstanceTypeCompareMode mode);
TNode<Object> IntrinsicAsBuiltinCall(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
Builtins::Name name, int arg_count);
void AbortIfArgCountMismatch(int expected, TNode<Word32T> actual);
#define DECLARE_INTRINSIC_HELPER(name, lower_case, count) \
TNode<Object> name(const InterpreterAssembler::RegListNodePair& args, \
TNode<Context> context, int arg_count);
INTRINSICS_LIST(DECLARE_INTRINSIC_HELPER)
#undef DECLARE_INTRINSIC_HELPER
Isolate* isolate() { return isolate_; }
Zone* zone() { return zone_; }
Factory* factory() { return isolate()->factory(); }
Isolate* isolate_;
Zone* zone_;
InterpreterAssembler* assembler_;
};
TNode<Object> GenerateInvokeIntrinsic(
InterpreterAssembler* assembler, TNode<Uint32T> function_id,
TNode<Context> context, const InterpreterAssembler::RegListNodePair& args) {
IntrinsicsGenerator generator(assembler);
return generator.InvokeIntrinsic(function_id, context, args);
}
#define __ assembler_->
TNode<Object> IntrinsicsGenerator::InvokeIntrinsic(
TNode<Uint32T> function_id, TNode<Context> context,
const InterpreterAssembler::RegListNodePair& args) {
InterpreterAssembler::Label abort(assembler_), end(assembler_);
InterpreterAssembler::TVariable<Object> result(assembler_);
#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, args.reg_count()); \
} \
TNode<Object> value = name(args, context, expected_arg_count); \
if (value) { \
result = value; \
__ Goto(&end); \
} \
}
INTRINSICS_LIST(HANDLE_CASE)
#undef HANDLE_CASE
__ BIND(&abort);
{
__ Abort(AbortReason::kUnexpectedFunctionIDForInvokeIntrinsic);
result = __ UndefinedConstant();
__ Goto(&end);
}
__ BIND(&end);
return result.value();
}
TNode<BoolT> IntrinsicsGenerator::CompareInstanceType(
TNode<HeapObject> object, int type, InstanceTypeCompareMode mode) {
TNode<Uint16T> instance_type = __ LoadInstanceType(object);
if (mode == kInstanceTypeEqual) {
return __ Word32Equal(instance_type, __ Int32Constant(type));
} else {
DCHECK_EQ(mode, kInstanceTypeGreaterThanOrEqual);
return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type));
}
}
TNode<Oddball> IntrinsicsGenerator::IsInstanceType(TNode<Object> input,
int type) {
TNode<Oddball> result = __ Select<Oddball>(
__ TaggedIsSmi(input), [=] { return __ FalseConstant(); },
[=] {
return __ SelectBooleanConstant(
CompareInstanceType(__ CAST(input), type, kInstanceTypeEqual));
});
return result;
}
TNode<Object> IntrinsicsGenerator::IntrinsicAsBuiltinCall(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
Builtins::Name name, int arg_count) {
Callable callable = Builtins::CallableFor(isolate_, name);
switch (arg_count) {
case 1:
return __ CallStub(callable, context,
__ LoadRegisterFromRegisterList(args, 0));
break;
case 2:
return __ CallStub(callable, context,
__ LoadRegisterFromRegisterList(args, 0),
__ LoadRegisterFromRegisterList(args, 1));
break;
case 3:
return __ CallStub(callable, context,
__ LoadRegisterFromRegisterList(args, 0),
__ LoadRegisterFromRegisterList(args, 1),
__ LoadRegisterFromRegisterList(args, 2));
break;
default:
UNREACHABLE();
}
}
TNode<Object> IntrinsicsGenerator::IsJSReceiver(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<Object> input = __ LoadRegisterFromRegisterList(args, 0);
TNode<Oddball> result = __ Select<Oddball>(
__ TaggedIsSmi(input), [=] { return __ FalseConstant(); },
[=] {
return __ SelectBooleanConstant(__ IsJSReceiver(__ CAST(input)));
});
return result;
}
TNode<Object> IntrinsicsGenerator::IsArray(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<Object> input = __ LoadRegisterFromRegisterList(args, 0);
return IsInstanceType(input, JS_ARRAY_TYPE);
}
TNode<Object> IntrinsicsGenerator::IsSmi(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<Object> input = __ LoadRegisterFromRegisterList(args, 0);
return __ SelectBooleanConstant(__ TaggedIsSmi(input));
}
TNode<Object> IntrinsicsGenerator::CopyDataProperties(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kCopyDataProperties,
arg_count);
}
TNode<Object> IntrinsicsGenerator::CreateIterResultObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context,
Builtins::kCreateIterResultObject, arg_count);
}
TNode<Object> IntrinsicsGenerator::HasProperty(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kHasProperty,
arg_count);
}
TNode<Object> IntrinsicsGenerator::ToString(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kToString, arg_count);
}
TNode<Object> IntrinsicsGenerator::ToLength(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kToLength, arg_count);
}
TNode<Object> IntrinsicsGenerator::ToObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kToObject, arg_count);
}
TNode<Object> IntrinsicsGenerator::Call(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
// First argument register contains the function target.
TNode<Object> function = __ LoadRegisterFromRegisterList(args, 0);
// The arguments for the target function are from the second runtime call
// argument.
InterpreterAssembler::RegListNodePair target_args(
__ RegisterLocationInRegisterList(args, 1),
__ Int32Sub(args.reg_count(), __ Int32Constant(1)));
if (FLAG_debug_code) {
InterpreterAssembler::Label arg_count_positive(assembler_);
TNode<BoolT> comparison =
__ Int32LessThan(target_args.reg_count(), __ Int32Constant(0));
__ GotoIfNot(comparison, &arg_count_positive);
__ Abort(AbortReason::kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&arg_count_positive);
__ BIND(&arg_count_positive);
}
__ CallJSAndDispatch(function, context, target_args,
ConvertReceiverMode::kAny);
return TNode<Object>(); // We never return from the CallJSAndDispatch above.
}
TNode<Object> IntrinsicsGenerator::CreateAsyncFromSyncIterator(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
InterpreterAssembler::Label not_receiver(
assembler_, InterpreterAssembler::Label::kDeferred);
InterpreterAssembler::Label done(assembler_);
InterpreterAssembler::TVariable<Object> return_value(assembler_);
TNode<Object> sync_iterator = __ LoadRegisterFromRegisterList(args, 0);
__ GotoIf(__ TaggedIsSmi(sync_iterator), &not_receiver);
__ GotoIfNot(__ IsJSReceiver(__ CAST(sync_iterator)), &not_receiver);
const TNode<Object> next =
__ GetProperty(context, sync_iterator, factory()->next_string());
const TNode<NativeContext> native_context = __ LoadNativeContext(context);
const TNode<Map> map = __ CAST(__ LoadContextElement(
native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX));
const TNode<JSObject> iterator = __ AllocateJSObjectFromMap(map);
__ StoreObjectFieldNoWriteBarrier(
iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset, sync_iterator);
__ StoreObjectFieldNoWriteBarrier(iterator,
JSAsyncFromSyncIterator::kNextOffset, next);
return_value = iterator;
__ Goto(&done);
__ BIND(&not_receiver);
{
return_value =
__ CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context);
// Unreachable due to the Throw in runtime call.
__ Goto(&done);
}
__ BIND(&done);
return return_value.value();
}
TNode<Object> IntrinsicsGenerator::CreateJSGeneratorObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kCreateGeneratorObject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::GeneratorGetResumeMode(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<JSGeneratorObject> generator =
__ CAST(__ LoadRegisterFromRegisterList(args, 0));
const TNode<Object> value =
__ LoadObjectField(generator, JSGeneratorObject::kResumeModeOffset);
return value;
}
TNode<Object> IntrinsicsGenerator::GeneratorClose(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<JSGeneratorObject> generator =
__ CAST(__ LoadRegisterFromRegisterList(args, 0));
__ StoreObjectFieldNoWriteBarrier(
generator, JSGeneratorObject::kContinuationOffset,
__ SmiConstant(JSGeneratorObject::kGeneratorClosed));
return __ UndefinedConstant();
}
TNode<Object> IntrinsicsGenerator::GetImportMetaObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
const TNode<Context> module_context = __ LoadModuleContext(context);
const TNode<HeapObject> module =
__ CAST(__ LoadContextElement(module_context, Context::EXTENSION_INDEX));
const TNode<Object> import_meta =
__ LoadObjectField(module, SourceTextModule::kImportMetaOffset);
InterpreterAssembler::TVariable<Object> return_value(assembler_);
return_value = import_meta;
InterpreterAssembler::Label end(assembler_);
__ GotoIfNot(__ IsTheHole(import_meta), &end);
return_value = __ CallRuntime(Runtime::kGetImportMetaObject, context);
__ Goto(&end);
__ BIND(&end);
return return_value.value();
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionAwaitCaught(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context,
Builtins::kAsyncFunctionAwaitCaught, arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionAwaitUncaught(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(
args, context, Builtins::kAsyncFunctionAwaitUncaught, arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionEnter(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncFunctionEnter,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionReject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncFunctionReject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionResolve(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncFunctionResolve,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorAwaitCaught(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(
args, context, Builtins::kAsyncGeneratorAwaitCaught, arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorAwaitUncaught(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(
args, context, Builtins::kAsyncGeneratorAwaitUncaught, arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorReject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorReject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorResolve(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorResolve,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorYield(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorYield,
arg_count);
}
void IntrinsicsGenerator::AbortIfArgCountMismatch(int expected,
TNode<Word32T> actual) {
InterpreterAssembler::Label match(assembler_);
TNode<BoolT> comparison = __ Word32Equal(actual, __ Int32Constant(expected));
__ GotoIf(comparison, &match);
__ Abort(AbortReason::kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&match);
__ BIND(&match);
}
#undef __
} // namespace interpreter
} // namespace internal
} // namespace v8