blob: b364ac8dd199d6dc20f944374c30751143318a53 [file] [log] [blame]
// Copyright 2021 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.
#ifndef V8_MAGLEV_MAGLEV_IR_INL_H_
#define V8_MAGLEV_MAGLEV_IR_INL_H_
#include <type_traits>
#include "src/maglev/maglev-interpreter-frame-state.h"
#include "src/maglev/maglev-ir.h"
namespace v8 {
namespace internal {
namespace maglev {
namespace detail {
// A little bit of template magic to allow DeepForEachInput to either take a
// `const DeoptInfo*` or a `DeoptInfo*`, depending on whether the Function
// processes read-only `ValueNode*` or read-write `ValueNode*&`.
template <typename F, typename Ret, typename A, typename... Rest>
A first_argument_helper(Ret (F::*)(A, Rest...));
template <typename F, typename Ret, typename A, typename... Rest>
A first_argument_helper(Ret (F::*)(A, Rest...) const);
template <typename Function>
using first_argument = decltype(first_argument_helper(
&std::remove_reference_t<Function>::operator()));
template <typename T, typename Function>
using const_if_function_first_arg_not_reference =
std::conditional_t<std::is_reference_v<first_argument<Function>>, T,
const T>;
template <typename Function>
void DeepForEachInputImpl(
const_if_function_first_arg_not_reference<DeoptFrame, Function>& frame,
InputLocation* input_locations, int& index, Function&& f) {
if (frame.parent()) {
DeepForEachInputImpl(*frame.parent(), input_locations, index, f);
}
switch (frame.type()) {
case DeoptFrame::FrameType::kInterpretedFrame:
f(frame.as_interpreted().closure(), &input_locations[index++]);
frame.as_interpreted().frame_state()->ForEachValue(
frame.as_interpreted().unit(),
[&](first_argument<Function> node, interpreter::Register reg) {
f(node, &input_locations[index++]);
});
break;
case DeoptFrame::FrameType::kInlinedArgumentsFrame: {
f(frame.as_inlined_arguments().closure(), &input_locations[index++]);
for (first_argument<Function> node :
frame.as_inlined_arguments().arguments()) {
f(node, &input_locations[index++]);
}
break;
}
case DeoptFrame::FrameType::kConstructStubFrame: {
f(frame.as_construct_stub().closure(), &input_locations[index++]);
f(frame.as_construct_stub().receiver(), &input_locations[index++]);
for (first_argument<Function> node :
frame.as_construct_stub().arguments_without_receiver()) {
f(node, &input_locations[index++]);
}
f(frame.as_construct_stub().context(), &input_locations[index++]);
break;
}
case DeoptFrame::FrameType::kBuiltinContinuationFrame:
for (first_argument<Function> node :
frame.as_builtin_continuation().parameters()) {
f(node, &input_locations[index++]);
}
f(frame.as_builtin_continuation().context(), &input_locations[index++]);
break;
}
}
template <typename Function>
void DeepForEachInput(const_if_function_first_arg_not_reference<
EagerDeoptInfo, Function>* deopt_info,
Function&& f) {
int index = 0;
DeepForEachInputImpl(deopt_info->top_frame(), deopt_info->input_locations(),
index, std::forward<Function>(f));
}
template <typename Function>
void DeepForEachInput(const_if_function_first_arg_not_reference<
LazyDeoptInfo, Function>* deopt_info,
Function&& f) {
int index = 0;
InputLocation* input_locations = deopt_info->input_locations();
auto& top_frame = deopt_info->top_frame();
if (top_frame.parent()) {
DeepForEachInputImpl(*top_frame.parent(), input_locations, index, f);
}
// Handle the top-of-frame info separately, since we have to skip the result
// location.
switch (top_frame.type()) {
case DeoptFrame::FrameType::kInterpretedFrame:
f(top_frame.as_interpreted().closure(), &input_locations[index++]);
top_frame.as_interpreted().frame_state()->ForEachValue(
top_frame.as_interpreted().unit(),
[&](first_argument<Function> node, interpreter::Register reg) {
// Skip over the result location since it is irrelevant for lazy
// deopts (unoptimized code will recreate the result).
if (deopt_info->IsResultRegister(reg)) return;
f(node, &input_locations[index++]);
});
break;
case DeoptFrame::FrameType::kConstructStubFrame: {
f(top_frame.as_construct_stub().closure(), &input_locations[index++]);
f(top_frame.as_construct_stub().receiver(), &input_locations[index++]);
for (first_argument<Function> node :
top_frame.as_construct_stub().arguments_without_receiver()) {
f(node, &input_locations[index++]);
}
f(top_frame.as_construct_stub().context(), &input_locations[index++]);
break;
}
case DeoptFrame::FrameType::kInlinedArgumentsFrame:
// The inlined arguments frame can never be the top frame.
UNREACHABLE();
case DeoptFrame::FrameType::kBuiltinContinuationFrame:
for (first_argument<Function> node :
top_frame.as_builtin_continuation().parameters()) {
f(node, &input_locations[index++]);
}
f(top_frame.as_builtin_continuation().context(),
&input_locations[index++]);
break;
}
}
} // namespace detail
inline void AddDeoptRegistersToSnapshot(RegisterSnapshot* snapshot,
const EagerDeoptInfo* deopt_info) {
detail::DeepForEachInput(deopt_info, [&](ValueNode* node,
InputLocation* input) {
if (!input->IsAnyRegister()) return;
if (input->IsDoubleRegister()) {
snapshot->live_double_registers.set(input->AssignedDoubleRegister());
} else {
snapshot->live_registers.set(input->AssignedGeneralRegister());
if (node->is_tagged()) {
snapshot->live_tagged_registers.set(input->AssignedGeneralRegister());
}
}
});
}
#ifdef DEBUG
inline RegList GetGeneralRegistersUsedAsInputs(
const EagerDeoptInfo* deopt_info) {
RegList regs;
detail::DeepForEachInput(deopt_info,
[&regs](ValueNode* value, InputLocation* input) {
if (input->IsGeneralRegister()) {
regs.set(input->AssignedGeneralRegister());
}
});
return regs;
}
#endif // DEBUG
// Helper macro for checking that a reglist is empty which prints the contents
// when non-empty.
#define DCHECK_REGLIST_EMPTY(...) DCHECK_EQ((__VA_ARGS__), RegList{})
// ---
// Value location constraint setting helpers.
// ---
static constexpr int kNoVreg = -1;
inline void DefineAsRegister(Node* node) {
node->result().SetUnallocated(
compiler::UnallocatedOperand::MUST_HAVE_REGISTER, kNoVreg);
}
inline void DefineAsConstant(Node* node) {
node->result().SetUnallocated(compiler::UnallocatedOperand::NONE, kNoVreg);
}
inline void DefineAsFixed(Node* node, Register reg) {
node->result().SetUnallocated(compiler::UnallocatedOperand::FIXED_REGISTER,
reg.code(), kNoVreg);
}
inline void DefineSameAsFirst(Node* node) {
node->result().SetUnallocated(kNoVreg, 0);
}
inline void UseRegister(Input& input) {
input.SetUnallocated(compiler::UnallocatedOperand::MUST_HAVE_REGISTER,
compiler::UnallocatedOperand::USED_AT_END, kNoVreg);
}
inline void UseAndClobberRegister(Input& input) {
input.SetUnallocated(compiler::UnallocatedOperand::MUST_HAVE_REGISTER,
compiler::UnallocatedOperand::USED_AT_START, kNoVreg);
}
inline void UseAny(Input& input) {
input.SetUnallocated(
compiler::UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT,
compiler::UnallocatedOperand::USED_AT_END, kNoVreg);
}
inline void UseFixed(Input& input, Register reg) {
input.SetUnallocated(compiler::UnallocatedOperand::FIXED_REGISTER, reg.code(),
kNoVreg);
input.node()->SetHint(input.operand());
}
inline void UseFixed(Input& input, DoubleRegister reg) {
input.SetUnallocated(compiler::UnallocatedOperand::FIXED_FP_REGISTER,
reg.code(), kNoVreg);
input.node()->SetHint(input.operand());
}
} // namespace maglev
} // namespace internal
} // namespace v8
#endif // V8_MAGLEV_MAGLEV_IR_INL_H_