Reland "[turbofan] Optimize rab/gsab-backed TypedArrays and DataViews"
This reverts commit 4b28d53011
.
Original change's description:
> [turbofan] Optimize rab/gsab-backed TypedArrays and DataViews
>
> This CL adds TurboFan optimizations for length and element access
> of TypedArrays and DataViews that are rab/gsab-backed.
>
> To enable this optimization, this CL builds the necessary machinery
> required to allow machine operators at the front of the pipeline
> (before simplified lowering). Some key changes to allow this are:
> - Introduce Type::Machine() to allow the typer and the verifier to
> provide a type to those machine operators in parts of the pipeline
> that require nodes to be typed.
> - Add EnterMachineGraph and ExitMachineGraph operators that define
> the boundary between early machine graphs and the normal graph with
> JS semantics.
> - Give Branch operators a BranchSemantics parameter to distinguish
> between machine branches (condition is a machine level value) and
> JS branches (condition is a JS boolean value) and have phases that
> handle branches decide on the branch's semantics based on this
> parameter instead of the position in the pipeline.
> - Extend SimplifiedLowering and SimplifiedLoweringVerifier to handle
> machine graphs. In particular, constants required special handling,
> because they are cached in the graph but they may have uses in both
> a machine and the JS graph, which prevents consistent typing of
> them.
> - Moved lots of logic from JSCallReducerAssembler into
> [JS]GraphAssembler such that functionality can be shared between
> different phases (e.g. JSNativeContextSpecialization and
> JSCallReducer need to generate logic to compute a TypedArray's
> byte length). Extended assembler interface in general with
> additional TNode<> overloads.
>
>
> Bug: v8:11111, chromium:1358505
> Change-Id: Ife006b8c38a83045cd3b8558acbfdcb66408891f
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3898690
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
> Reviewed-by: Clemens Backes <clemensb@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#83881}
Bug: v8:11111, chromium:1358505, v8:13412
Change-Id: I61664e18a9dba1741bcb70ec22ba6342521f500a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3976512
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83904}
This commit is contained in:
parent
f44d43de74
commit
05bd7d9cd6
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace v8::base {
|
namespace v8::base {
|
||||||
|
|
||||||
@ -77,6 +78,18 @@ inline size_t count_if(const C& container, const P& predicate) {
|
|||||||
return std::count_if(begin(container), end(container), predicate);
|
return std::count_if(begin(container), end(container), predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for std::all_of.
|
||||||
|
template <typename C, typename P>
|
||||||
|
inline bool all_of(const C& container, const P& predicate) {
|
||||||
|
return std::all_of(begin(container), end(container), predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for std::none_of.
|
||||||
|
template <typename C, typename P>
|
||||||
|
inline bool none_of(const C& container, const P& predicate) {
|
||||||
|
return std::none_of(begin(container), end(container), predicate);
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true iff all elements of {container} compare equal using operator==.
|
// Returns true iff all elements of {container} compare equal using operator==.
|
||||||
template <typename C>
|
template <typename C>
|
||||||
inline bool all_equal(const C& container) {
|
inline bool all_equal(const C& container) {
|
||||||
@ -87,6 +100,21 @@ inline bool all_equal(const C& container) {
|
|||||||
[&](const auto& v) { return v == value; });
|
[&](const auto& v) { return v == value; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true iff all elements of {container} compare equal to {value} using
|
||||||
|
// operator==.
|
||||||
|
template <typename C, typename T>
|
||||||
|
inline bool all_equal(const C& container, const T& value) {
|
||||||
|
return std::all_of(begin(container), end(container),
|
||||||
|
[&](const auto& v) { return v == value; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends to vector {v} all the elements in the range {begin(container)} and
|
||||||
|
// {end(container)}.
|
||||||
|
template <typename T, typename A, typename C>
|
||||||
|
inline void vector_append(std::vector<T, A>& v, const C& container) {
|
||||||
|
v.insert(end(v), begin(container), end(container));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace v8::base
|
} // namespace v8::base
|
||||||
|
|
||||||
#endif // V8_BASE_CONTAINER_UTILS_H_
|
#endif // V8_BASE_CONTAINER_UTILS_H_
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "src/logging/log.h"
|
#include "src/logging/log.h"
|
||||||
#include "src/numbers/hash-seed-inl.h"
|
#include "src/numbers/hash-seed-inl.h"
|
||||||
#include "src/numbers/math-random.h"
|
#include "src/numbers/math-random.h"
|
||||||
|
#include "src/objects/elements-kind.h"
|
||||||
#include "src/objects/elements.h"
|
#include "src/objects/elements.h"
|
||||||
#include "src/objects/object-type.h"
|
#include "src/objects/object-type.h"
|
||||||
#include "src/objects/objects-inl.h"
|
#include "src/objects/objects-inl.h"
|
||||||
@ -950,6 +951,20 @@ ExternalReference ExternalReference::search_string_raw_two_two() {
|
|||||||
return search_string_raw<const base::uc16, const base::uc16>();
|
return search_string_raw<const base::uc16, const base::uc16>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExternalReference
|
||||||
|
ExternalReference::typed_array_and_rab_gsab_typed_array_elements_kind_shifts() {
|
||||||
|
uint8_t* ptr =
|
||||||
|
const_cast<uint8_t*>(TypedArrayAndRabGsabTypedArrayElementsKindShifts());
|
||||||
|
return ExternalReference(reinterpret_cast<Address>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalReference
|
||||||
|
ExternalReference::typed_array_and_rab_gsab_typed_array_elements_kind_sizes() {
|
||||||
|
uint8_t* ptr =
|
||||||
|
const_cast<uint8_t*>(TypedArrayAndRabGsabTypedArrayElementsKindSizes());
|
||||||
|
return ExternalReference(reinterpret_cast<Address>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void StringWriteToFlatOneByte(Address source, uint8_t* sink, int32_t start,
|
void StringWriteToFlatOneByte(Address source, uint8_t* sink, int32_t start,
|
||||||
|
@ -338,6 +338,10 @@ class StatsCounter;
|
|||||||
V(re_match_for_call_from_js, "IrregexpInterpreter::MatchForCallFromJs") \
|
V(re_match_for_call_from_js, "IrregexpInterpreter::MatchForCallFromJs") \
|
||||||
V(re_experimental_match_for_call_from_js, \
|
V(re_experimental_match_for_call_from_js, \
|
||||||
"ExperimentalRegExp::MatchForCallFromJs") \
|
"ExperimentalRegExp::MatchForCallFromJs") \
|
||||||
|
V(typed_array_and_rab_gsab_typed_array_elements_kind_shifts, \
|
||||||
|
"TypedArrayAndRabGsabTypedArrayElementsKindShifts") \
|
||||||
|
V(typed_array_and_rab_gsab_typed_array_elements_kind_sizes, \
|
||||||
|
"TypedArrayAndRabGsabTypedArrayElementsKindSizes") \
|
||||||
EXTERNAL_REFERENCE_LIST_INTL(V) \
|
EXTERNAL_REFERENCE_LIST_INTL(V) \
|
||||||
EXTERNAL_REFERENCE_LIST_SANDBOX(V)
|
EXTERNAL_REFERENCE_LIST_SANDBOX(V)
|
||||||
#ifdef V8_INTL_SUPPORT
|
#ifdef V8_INTL_SUPPORT
|
||||||
|
@ -359,10 +359,10 @@ class TNode {
|
|||||||
public:
|
public:
|
||||||
template <class U,
|
template <class U,
|
||||||
typename std::enable_if<is_subtype<U, T>::value, int>::type = 0>
|
typename std::enable_if<is_subtype<U, T>::value, int>::type = 0>
|
||||||
TNode(const TNode<U>& other) : node_(other) {
|
TNode(const TNode<U>& other) V8_NOEXCEPT : node_(other) {
|
||||||
LazyTemplateChecks();
|
LazyTemplateChecks();
|
||||||
}
|
}
|
||||||
TNode(const TNode& other) : node_(other) { LazyTemplateChecks(); }
|
TNode(const TNode& other) V8_NOEXCEPT : node_(other) { LazyTemplateChecks(); }
|
||||||
TNode() : TNode(nullptr) {}
|
TNode() : TNode(nullptr) {}
|
||||||
|
|
||||||
TNode operator=(TNode other) {
|
TNode operator=(TNode other) {
|
||||||
@ -375,7 +375,7 @@ class TNode {
|
|||||||
|
|
||||||
static TNode UncheckedCast(compiler::Node* node) { return TNode(node); }
|
static TNode UncheckedCast(compiler::Node* node) { return TNode(node); }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
explicit TNode(compiler::Node* node) : node_(node) { LazyTemplateChecks(); }
|
explicit TNode(compiler::Node* node) : node_(node) { LazyTemplateChecks(); }
|
||||||
// These checks shouldn't be checked before TNode is actually used.
|
// These checks shouldn't be checked before TNode is actually used.
|
||||||
void LazyTemplateChecks() {
|
void LazyTemplateChecks() {
|
||||||
@ -385,6 +385,21 @@ class TNode {
|
|||||||
compiler::Node* node_;
|
compiler::Node* node_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SloppyTNode<T> is a variant of TNode<T> and allows implicit casts from
|
||||||
|
// Node*. It is intended for function arguments as long as some call sites
|
||||||
|
// still use untyped Node* arguments.
|
||||||
|
// TODO(turbofan): Delete this class once transition is finished.
|
||||||
|
template <class T>
|
||||||
|
class SloppyTNode : public TNode<T> {
|
||||||
|
public:
|
||||||
|
SloppyTNode(compiler::Node* node) // NOLINT(runtime/explicit)
|
||||||
|
: TNode<T>(node) {}
|
||||||
|
template <class U, typename std::enable_if<is_subtype<U, T>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
SloppyTNode(const TNode<U>& other) V8_NOEXCEPT // NOLINT(runtime/explicit)
|
||||||
|
: TNode<T>(other) {}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
@ -364,6 +364,22 @@ FieldAccess AccessBuilder::ForJSArrayBufferBitField() {
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForJSArrayBufferByteLength() {
|
||||||
|
FieldAccess access = {kTaggedBase,
|
||||||
|
JSArrayBuffer::kRawByteLengthOffset,
|
||||||
|
MaybeHandle<Name>(),
|
||||||
|
MaybeHandle<Map>(),
|
||||||
|
TypeCache::Get()->kJSArrayBufferByteLengthType,
|
||||||
|
MachineType::UintPtr(),
|
||||||
|
kNoWriteBarrier,
|
||||||
|
"JSArrayBufferByteLength"};
|
||||||
|
#ifdef V8_ENABLE_SANDBOX
|
||||||
|
access.is_bounded_size_access = true;
|
||||||
|
#endif
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForJSArrayBufferViewBuffer() {
|
FieldAccess AccessBuilder::ForJSArrayBufferViewBuffer() {
|
||||||
FieldAccess access = {kTaggedBase, JSArrayBufferView::kBufferOffset,
|
FieldAccess access = {kTaggedBase, JSArrayBufferView::kBufferOffset,
|
||||||
@ -405,6 +421,19 @@ FieldAccess AccessBuilder::ForJSArrayBufferViewByteOffset() {
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForJSArrayBufferViewBitField() {
|
||||||
|
FieldAccess access = {kTaggedBase,
|
||||||
|
JSArrayBufferView::kBitFieldOffset,
|
||||||
|
MaybeHandle<Name>(),
|
||||||
|
MaybeHandle<Map>(),
|
||||||
|
TypeCache::Get()->kUint32,
|
||||||
|
MachineType::Uint32(),
|
||||||
|
kNoWriteBarrier,
|
||||||
|
"JSArrayBufferViewBitField"};
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForJSTypedArrayLength() {
|
FieldAccess AccessBuilder::ForJSTypedArrayLength() {
|
||||||
FieldAccess access = {kTaggedBase,
|
FieldAccess access = {kTaggedBase,
|
||||||
|
@ -134,6 +134,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
|
|||||||
// Provides access to JSArrayBuffer::bit_field() field.
|
// Provides access to JSArrayBuffer::bit_field() field.
|
||||||
static FieldAccess ForJSArrayBufferBitField();
|
static FieldAccess ForJSArrayBufferBitField();
|
||||||
|
|
||||||
|
// Provides access to JSArrayBuffer::byteLength() field.
|
||||||
|
static FieldAccess ForJSArrayBufferByteLength();
|
||||||
|
|
||||||
// Provides access to JSArrayBufferView::buffer() field.
|
// Provides access to JSArrayBufferView::buffer() field.
|
||||||
static FieldAccess ForJSArrayBufferViewBuffer();
|
static FieldAccess ForJSArrayBufferViewBuffer();
|
||||||
|
|
||||||
@ -143,6 +146,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
|
|||||||
// Provides access to JSArrayBufferView::byteOffset() field.
|
// Provides access to JSArrayBufferView::byteOffset() field.
|
||||||
static FieldAccess ForJSArrayBufferViewByteOffset();
|
static FieldAccess ForJSArrayBufferViewByteOffset();
|
||||||
|
|
||||||
|
// Provides access to JSArrayBufferView::bitfield() field
|
||||||
|
static FieldAccess ForJSArrayBufferViewBitField();
|
||||||
|
|
||||||
// Provides access to JSTypedArray::length() field.
|
// Provides access to JSTypedArray::length() field.
|
||||||
static FieldAccess ForJSTypedArrayLength();
|
static FieldAccess ForJSTypedArrayLength();
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "src/codegen/tick-counter.h"
|
#include "src/codegen/tick-counter.h"
|
||||||
#include "src/common/globals.h"
|
#include "src/common/globals.h"
|
||||||
#include "src/compiler/backend/instruction-selector-impl.h"
|
#include "src/compiler/backend/instruction-selector-impl.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/compiler-source-position-table.h"
|
#include "src/compiler/compiler-source-position-table.h"
|
||||||
#include "src/compiler/js-heap-broker.h"
|
#include "src/compiler/js-heap-broker.h"
|
||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
@ -1288,6 +1289,10 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
|
|||||||
}
|
}
|
||||||
case BasicBlock::kBranch: {
|
case BasicBlock::kBranch: {
|
||||||
DCHECK_EQ(IrOpcode::kBranch, input->opcode());
|
DCHECK_EQ(IrOpcode::kBranch, input->opcode());
|
||||||
|
// TODO(nicohartmann@): Once all branches have explicitly specified
|
||||||
|
// semantics, we should allow only BranchSemantics::kMachine here.
|
||||||
|
DCHECK_NE(BranchSemantics::kJS,
|
||||||
|
BranchParametersOf(input->op()).semantics());
|
||||||
BasicBlock* tbranch = block->SuccessorAt(0);
|
BasicBlock* tbranch = block->SuccessorAt(0);
|
||||||
BasicBlock* fbranch = block->SuccessorAt(1);
|
BasicBlock* fbranch = block->SuccessorAt(1);
|
||||||
if (tbranch == fbranch) {
|
if (tbranch == fbranch) {
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
#include "src/compiler/branch-elimination.h"
|
#include "src/compiler/branch-elimination.h"
|
||||||
|
|
||||||
#include "src/base/small-vector.h"
|
#include "src/base/small-vector.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/js-graph.h"
|
#include "src/compiler/js-graph.h"
|
||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
|
#include "src/compiler/opcodes.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -81,11 +83,24 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
|
|||||||
// second_true second_false
|
// second_true second_false
|
||||||
//
|
//
|
||||||
|
|
||||||
|
auto SemanticsOf = [phase = this->phase_](Node* branch) {
|
||||||
|
BranchSemantics semantics = BranchSemantics::kUnspecified;
|
||||||
|
if (branch->opcode() == IrOpcode::kBranch) {
|
||||||
|
semantics = BranchParametersOf(branch->op()).semantics();
|
||||||
|
}
|
||||||
|
if (semantics == BranchSemantics::kUnspecified) {
|
||||||
|
semantics =
|
||||||
|
(phase == kEARLY ? BranchSemantics::kJS : BranchSemantics::kMachine);
|
||||||
|
}
|
||||||
|
return semantics;
|
||||||
|
};
|
||||||
|
|
||||||
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
|
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
|
||||||
Node* merge = NodeProperties::GetControlInput(branch);
|
Node* merge = NodeProperties::GetControlInput(branch);
|
||||||
if (merge->opcode() != IrOpcode::kMerge) return;
|
if (merge->opcode() != IrOpcode::kMerge) return;
|
||||||
|
|
||||||
Node* condition = branch->InputAt(0);
|
Node* condition = branch->InputAt(0);
|
||||||
|
BranchSemantics semantics = SemanticsOf(branch);
|
||||||
Graph* graph = jsgraph()->graph();
|
Graph* graph = jsgraph()->graph();
|
||||||
base::SmallVector<Node*, 2> phi_inputs;
|
base::SmallVector<Node*, 2> phi_inputs;
|
||||||
|
|
||||||
@ -97,12 +112,14 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
|
|||||||
|
|
||||||
BranchCondition branch_condition = from_input.LookupState(condition);
|
BranchCondition branch_condition = from_input.LookupState(condition);
|
||||||
if (!branch_condition.IsSet()) return;
|
if (!branch_condition.IsSet()) return;
|
||||||
|
if (SemanticsOf(branch_condition.branch) != semantics) return;
|
||||||
bool condition_value = branch_condition.is_true;
|
bool condition_value = branch_condition.is_true;
|
||||||
|
|
||||||
if (phase_ == kEARLY) {
|
if (semantics == BranchSemantics::kJS) {
|
||||||
phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
|
phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
|
||||||
: jsgraph()->FalseConstant());
|
: jsgraph()->FalseConstant());
|
||||||
} else {
|
} else {
|
||||||
|
DCHECK_EQ(semantics, BranchSemantics::kMachine);
|
||||||
phi_inputs.emplace_back(
|
phi_inputs.emplace_back(
|
||||||
condition_value
|
condition_value
|
||||||
? graph->NewNode(jsgraph()->common()->Int32Constant(1))
|
? graph->NewNode(jsgraph()->common()->Int32Constant(1))
|
||||||
@ -110,8 +127,9 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
phi_inputs.emplace_back(merge);
|
phi_inputs.emplace_back(merge);
|
||||||
Node* new_phi = graph->NewNode(
|
Node* new_phi =
|
||||||
common()->Phi(phase_ == kEARLY ? MachineRepresentation::kTagged
|
graph->NewNode(common()->Phi(semantics == BranchSemantics::kJS
|
||||||
|
? MachineRepresentation::kTagged
|
||||||
: MachineRepresentation::kWord32,
|
: MachineRepresentation::kWord32,
|
||||||
input_count),
|
input_count),
|
||||||
input_count + 1, &phi_inputs.at(0));
|
input_count + 1, &phi_inputs.at(0));
|
||||||
|
@ -43,6 +43,8 @@ class V8_EXPORT_PRIVATE BranchElimination final
|
|||||||
: public NON_EXPORTED_BASE(AdvancedReducerWithControlPathState)<
|
: public NON_EXPORTED_BASE(AdvancedReducerWithControlPathState)<
|
||||||
BranchCondition, kUniqueInstance> {
|
BranchCondition, kUniqueInstance> {
|
||||||
public:
|
public:
|
||||||
|
// TODO(nicohartmann@): Remove {Phase} once all Branch operators have
|
||||||
|
// specified semantics.
|
||||||
enum Phase {
|
enum Phase {
|
||||||
kEARLY,
|
kEARLY,
|
||||||
kLATE,
|
kLATE,
|
||||||
|
@ -29,6 +29,18 @@ std::ostream& operator<<(std::ostream& os, BranchHint hint) {
|
|||||||
|
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, BranchSemantics semantics) {
|
||||||
|
switch (semantics) {
|
||||||
|
case BranchSemantics::kJS:
|
||||||
|
return os << "JS";
|
||||||
|
case BranchSemantics::kMachine:
|
||||||
|
return os << "Machine";
|
||||||
|
case BranchSemantics::kUnspecified:
|
||||||
|
return os << "Unspecified";
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, TrapId trap_id) {
|
std::ostream& operator<<(std::ostream& os, TrapId trap_id) {
|
||||||
switch (trap_id) {
|
switch (trap_id) {
|
||||||
#define TRAP_CASE(Name) \
|
#define TRAP_CASE(Name) \
|
||||||
@ -48,13 +60,33 @@ TrapId TrapIdOf(const Operator* const op) {
|
|||||||
return OpParameter<TrapId>(op);
|
return OpParameter<TrapId>(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(const BranchParameters& lhs, const BranchParameters& rhs) {
|
||||||
|
return lhs.semantics() == rhs.semantics() && lhs.hint() == rhs.hint();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value(const BranchParameters& p) {
|
||||||
|
return base::hash_combine(p.semantics(), p.hint());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const BranchParameters& p) {
|
||||||
|
return os << p.semantics() << ", " << p.hint();
|
||||||
|
}
|
||||||
|
|
||||||
|
const BranchParameters& BranchParametersOf(const Operator* const op) {
|
||||||
|
DCHECK_EQ(op->opcode(), IrOpcode::kBranch);
|
||||||
|
return OpParameter<BranchParameters>(op);
|
||||||
|
}
|
||||||
|
|
||||||
BranchHint BranchHintOf(const Operator* const op) {
|
BranchHint BranchHintOf(const Operator* const op) {
|
||||||
switch (op->opcode()) {
|
switch (op->opcode()) {
|
||||||
case IrOpcode::kIfValue:
|
case IrOpcode::kIfValue:
|
||||||
return IfValueParametersOf(op).hint();
|
return IfValueParametersOf(op).hint();
|
||||||
case IrOpcode::kIfDefault:
|
case IrOpcode::kIfDefault:
|
||||||
case IrOpcode::kBranch:
|
|
||||||
return OpParameter<BranchHint>(op);
|
return OpParameter<BranchHint>(op);
|
||||||
|
// TODO(nicohartmann@): Should remove all uses of BranchHintOf for branches
|
||||||
|
// and replace with BranchParametersOf.
|
||||||
|
case IrOpcode::kBranch:
|
||||||
|
return BranchParametersOf(op).hint();
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -434,6 +466,27 @@ const SLVerifierHintParameters& SLVerifierHintParametersOf(const Operator* op) {
|
|||||||
return OpParameter<SLVerifierHintParameters>(op);
|
return OpParameter<SLVerifierHintParameters>(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE bool operator==(const ExitMachineGraphParameters& lhs,
|
||||||
|
const ExitMachineGraphParameters& rhs) {
|
||||||
|
return lhs.output_representation() == rhs.output_representation() &&
|
||||||
|
lhs.output_type().Equals(rhs.output_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value(const ExitMachineGraphParameters& p) {
|
||||||
|
return base::hash_combine(p.output_representation(), p.output_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE std::ostream& operator<<(
|
||||||
|
std::ostream& os, const ExitMachineGraphParameters& p) {
|
||||||
|
return os << p.output_representation() << ", " << p.output_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExitMachineGraphParameters& ExitMachineGraphParametersOf(
|
||||||
|
const Operator* op) {
|
||||||
|
DCHECK_EQ(op->opcode(), IrOpcode::kExitMachineGraph);
|
||||||
|
return OpParameter<ExitMachineGraphParameters>(op);
|
||||||
|
}
|
||||||
|
|
||||||
#define COMMON_CACHED_OP_LIST(V) \
|
#define COMMON_CACHED_OP_LIST(V) \
|
||||||
V(Plug, Operator::kNoProperties, 0, 0, 0, 1, 0, 0) \
|
V(Plug, Operator::kNoProperties, 0, 0, 0, 1, 0, 0) \
|
||||||
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
|
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
|
||||||
@ -453,9 +506,15 @@ const SLVerifierHintParameters& SLVerifierHintParametersOf(const Operator* op) {
|
|||||||
#define CACHED_LOOP_EXIT_VALUE_LIST(V) V(kTagged)
|
#define CACHED_LOOP_EXIT_VALUE_LIST(V) V(kTagged)
|
||||||
|
|
||||||
#define CACHED_BRANCH_LIST(V) \
|
#define CACHED_BRANCH_LIST(V) \
|
||||||
V(None) \
|
V(JS, None) \
|
||||||
V(True) \
|
V(JS, True) \
|
||||||
V(False)
|
V(JS, False) \
|
||||||
|
V(Machine, None) \
|
||||||
|
V(Machine, True) \
|
||||||
|
V(Machine, False) \
|
||||||
|
V(Unspecified, None) \
|
||||||
|
V(Unspecified, True) \
|
||||||
|
V(Unspecified, False)
|
||||||
|
|
||||||
#define CACHED_RETURN_LIST(V) \
|
#define CACHED_RETURN_LIST(V) \
|
||||||
V(1) \
|
V(1) \
|
||||||
@ -626,17 +685,18 @@ struct CommonOperatorGlobalCache final {
|
|||||||
CACHED_RETURN_LIST(CACHED_RETURN)
|
CACHED_RETURN_LIST(CACHED_RETURN)
|
||||||
#undef CACHED_RETURN
|
#undef CACHED_RETURN
|
||||||
|
|
||||||
template <BranchHint hint>
|
template <BranchSemantics semantics, BranchHint hint>
|
||||||
struct BranchOperator final : public Operator1<BranchHint> {
|
struct BranchOperator final : public Operator1<BranchParameters> {
|
||||||
BranchOperator()
|
BranchOperator()
|
||||||
: Operator1<BranchHint>( // --
|
: Operator1<BranchParameters>( // --
|
||||||
IrOpcode::kBranch, Operator::kKontrol, // opcode
|
IrOpcode::kBranch, Operator::kKontrol, // opcode
|
||||||
"Branch", // name
|
"Branch", // name
|
||||||
1, 0, 1, 0, 0, 2, // counts
|
1, 0, 1, 0, 0, 2, // counts
|
||||||
hint) {} // parameter
|
{semantics, hint}) {} // parameter
|
||||||
};
|
};
|
||||||
#define CACHED_BRANCH(Hint) \
|
#define CACHED_BRANCH(Semantics, Hint) \
|
||||||
BranchOperator<BranchHint::k##Hint> kBranch##Hint##Operator;
|
BranchOperator<BranchSemantics::k##Semantics, BranchHint::k##Hint> \
|
||||||
|
kBranch##Semantics##Hint##Operator;
|
||||||
CACHED_BRANCH_LIST(CACHED_BRANCH)
|
CACHED_BRANCH_LIST(CACHED_BRANCH)
|
||||||
#undef CACHED_BRANCH
|
#undef CACHED_BRANCH
|
||||||
|
|
||||||
@ -924,10 +984,12 @@ const Operator* CommonOperatorBuilder::SLVerifierHint(
|
|||||||
0, 0, 1, 0, 0, SLVerifierHintParameters(semantics, override_output_type));
|
0, 0, 1, 0, 0, SLVerifierHintParameters(semantics, override_output_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
const Operator* CommonOperatorBuilder::Branch(BranchHint hint) {
|
const Operator* CommonOperatorBuilder::Branch(BranchHint hint,
|
||||||
#define CACHED_BRANCH(Hint) \
|
BranchSemantics semantics) {
|
||||||
if (hint == BranchHint::k##Hint) { \
|
#define CACHED_BRANCH(Semantics, Hint) \
|
||||||
return &cache_.kBranch##Hint##Operator; \
|
if (semantics == BranchSemantics::k##Semantics && \
|
||||||
|
hint == BranchHint::k##Hint) { \
|
||||||
|
return &cache_.kBranch##Semantics##Hint##Operator; \
|
||||||
}
|
}
|
||||||
CACHED_BRANCH_LIST(CACHED_BRANCH)
|
CACHED_BRANCH_LIST(CACHED_BRANCH)
|
||||||
#undef CACHED_BRANCH
|
#undef CACHED_BRANCH
|
||||||
@ -1309,6 +1371,19 @@ const Operator* CommonOperatorBuilder::FoldConstant() {
|
|||||||
2, 0, 0, 1, 0, 0); // counts
|
2, 0, 0, 1, 0, 0); // counts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Operator* CommonOperatorBuilder::EnterMachineGraph(UseInfo use_info) {
|
||||||
|
return zone()->New<Operator1<UseInfo>>(IrOpcode::kEnterMachineGraph,
|
||||||
|
Operator::kPure, "EnterMachineGraph",
|
||||||
|
1, 0, 0, 1, 0, 0, use_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Operator* CommonOperatorBuilder::ExitMachineGraph(
|
||||||
|
MachineRepresentation output_representation, Type output_type) {
|
||||||
|
return zone()->New<Operator1<ExitMachineGraphParameters>>(
|
||||||
|
IrOpcode::kExitMachineGraph, Operator::kPure, "ExitMachineGraph", 1, 0, 0,
|
||||||
|
1, 0, 0, ExitMachineGraphParameters{output_representation, output_type});
|
||||||
|
}
|
||||||
|
|
||||||
const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
|
const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
|
||||||
DCHECK_LT(0, effect_input_count); // Disallow empty effect phis.
|
DCHECK_LT(0, effect_input_count); // Disallow empty effect phis.
|
||||||
switch (effect_input_count) {
|
switch (effect_input_count) {
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/compiler/frame-states.h"
|
#include "src/compiler/frame-states.h"
|
||||||
#include "src/compiler/linkage.h"
|
#include "src/compiler/linkage.h"
|
||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
|
#include "src/compiler/use-info.h"
|
||||||
#include "src/deoptimizer/deoptimize-reason.h"
|
#include "src/deoptimizer/deoptimize-reason.h"
|
||||||
#include "src/zone/zone-containers.h"
|
#include "src/zone/zone-containers.h"
|
||||||
|
|
||||||
@ -35,7 +36,11 @@ class Node;
|
|||||||
// (machine branch semantics). Some passes are applied both before and after
|
// (machine branch semantics). Some passes are applied both before and after
|
||||||
// SimplifiedLowering, and use the BranchSemantics enum to know how branches
|
// SimplifiedLowering, and use the BranchSemantics enum to know how branches
|
||||||
// should be treated.
|
// should be treated.
|
||||||
enum class BranchSemantics { kJS, kMachine };
|
// TODO(nicohartmann@): Need to remove BranchSemantics::kUnspecified once all
|
||||||
|
// branch uses have been updated.
|
||||||
|
enum class BranchSemantics { kJS, kMachine, kUnspecified };
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, BranchSemantics);
|
||||||
|
|
||||||
inline BranchHint NegateBranchHint(BranchHint hint) {
|
inline BranchHint NegateBranchHint(BranchHint hint) {
|
||||||
switch (hint) {
|
switch (hint) {
|
||||||
@ -62,6 +67,32 @@ std::ostream& operator<<(std::ostream&, TrapId trap_id);
|
|||||||
|
|
||||||
TrapId TrapIdOf(const Operator* const op);
|
TrapId TrapIdOf(const Operator* const op);
|
||||||
|
|
||||||
|
class BranchParameters final {
|
||||||
|
public:
|
||||||
|
BranchParameters(BranchSemantics semantics, BranchHint hint)
|
||||||
|
: semantics_(semantics), hint_(hint) {}
|
||||||
|
|
||||||
|
BranchSemantics semantics() const { return semantics_; }
|
||||||
|
BranchHint hint() const { return hint_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BranchSemantics semantics_;
|
||||||
|
const BranchHint hint_;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const BranchParameters& lhs, const BranchParameters& rhs);
|
||||||
|
inline bool operator!=(const BranchParameters& lhs,
|
||||||
|
const BranchParameters& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value(const BranchParameters& p);
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream&, const BranchParameters& p);
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE const BranchParameters& BranchParametersOf(
|
||||||
|
const Operator* const) V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
V8_EXPORT_PRIVATE BranchHint BranchHintOf(const Operator* const)
|
V8_EXPORT_PRIVATE BranchHint BranchHintOf(const Operator* const)
|
||||||
V8_WARN_UNUSED_RESULT;
|
V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
@ -439,6 +470,35 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
|
|||||||
V8_EXPORT_PRIVATE const SLVerifierHintParameters& SLVerifierHintParametersOf(
|
V8_EXPORT_PRIVATE const SLVerifierHintParameters& SLVerifierHintParametersOf(
|
||||||
const Operator* op) V8_WARN_UNUSED_RESULT;
|
const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
class ExitMachineGraphParameters final {
|
||||||
|
public:
|
||||||
|
ExitMachineGraphParameters(MachineRepresentation output_representation,
|
||||||
|
Type output_type)
|
||||||
|
: output_representation_(output_representation),
|
||||||
|
output_type_(output_type) {}
|
||||||
|
|
||||||
|
MachineRepresentation output_representation() const {
|
||||||
|
return output_representation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type& output_type() const { return output_type_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const MachineRepresentation output_representation_;
|
||||||
|
const Type output_type_;
|
||||||
|
};
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE bool operator==(const ExitMachineGraphParameters& lhs,
|
||||||
|
const ExitMachineGraphParameters& rhs);
|
||||||
|
|
||||||
|
size_t hash_value(const ExitMachineGraphParameters& p);
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
|
||||||
|
const ExitMachineGraphParameters& p);
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE const ExitMachineGraphParameters&
|
||||||
|
ExitMachineGraphParametersOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Interface for building common operators that can be used at any level of IR,
|
// Interface for building common operators that can be used at any level of IR,
|
||||||
// including JavaScript, mid-level, and low-level.
|
// including JavaScript, mid-level, and low-level.
|
||||||
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
||||||
@ -464,7 +524,11 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
|||||||
const Operator* semantics,
|
const Operator* semantics,
|
||||||
const base::Optional<Type>& override_output_type);
|
const base::Optional<Type>& override_output_type);
|
||||||
const Operator* End(size_t control_input_count);
|
const Operator* End(size_t control_input_count);
|
||||||
const Operator* Branch(BranchHint = BranchHint::kNone);
|
// TODO(nicohartmann@): Remove the default argument for {semantics} once all
|
||||||
|
// uses are updated.
|
||||||
|
const Operator* Branch(
|
||||||
|
BranchHint = BranchHint::kNone,
|
||||||
|
BranchSemantics semantics = BranchSemantics::kUnspecified);
|
||||||
const Operator* IfTrue();
|
const Operator* IfTrue();
|
||||||
const Operator* IfFalse();
|
const Operator* IfFalse();
|
||||||
const Operator* IfSuccess();
|
const Operator* IfSuccess();
|
||||||
@ -537,6 +601,9 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
|||||||
const Operator* Retain();
|
const Operator* Retain();
|
||||||
const Operator* TypeGuard(Type type);
|
const Operator* TypeGuard(Type type);
|
||||||
const Operator* FoldConstant();
|
const Operator* FoldConstant();
|
||||||
|
const Operator* EnterMachineGraph(UseInfo use_info);
|
||||||
|
const Operator* ExitMachineGraph(MachineRepresentation output_representation,
|
||||||
|
Type output_type);
|
||||||
|
|
||||||
// Constructs a new merge or phi operator with the same opcode as {op}, but
|
// Constructs a new merge or phi operator with the same opcode as {op}, but
|
||||||
// with {size} inputs.
|
// with {size} inputs.
|
||||||
|
@ -23,10 +23,12 @@ struct Diamond {
|
|||||||
Node* merge;
|
Node* merge;
|
||||||
|
|
||||||
Diamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
|
Diamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
|
||||||
BranchHint hint = BranchHint::kNone) {
|
BranchHint hint = BranchHint::kNone,
|
||||||
|
BranchSemantics semantics = BranchSemantics::kUnspecified) {
|
||||||
graph = g;
|
graph = g;
|
||||||
common = b;
|
common = b;
|
||||||
branch = graph->NewNode(common->Branch(hint), cond, graph->start());
|
branch =
|
||||||
|
graph->NewNode(common->Branch(hint, semantics), cond, graph->start());
|
||||||
if_true = graph->NewNode(common->IfTrue(), branch);
|
if_true = graph->NewNode(common->IfTrue(), branch);
|
||||||
if_false = graph->NewNode(common->IfFalse(), branch);
|
if_false = graph->NewNode(common->IfFalse(), branch);
|
||||||
merge = graph->NewNode(common->Merge(2), if_true, if_false);
|
merge = graph->NewNode(common->Merge(2), if_true, if_false);
|
||||||
|
@ -5113,14 +5113,14 @@ Node* EffectControlLinearizer::AdaptFastCallTypedArrayArgument(
|
|||||||
Node* buffer_is_not_detached = __ Word32Equal(
|
Node* buffer_is_not_detached = __ Word32Equal(
|
||||||
__ Word32And(buffer_bit_field,
|
__ Word32And(buffer_bit_field,
|
||||||
__ Int32Constant(JSArrayBuffer::WasDetachedBit::kMask)),
|
__ Int32Constant(JSArrayBuffer::WasDetachedBit::kMask)),
|
||||||
__ ZeroConstant());
|
__ Int32Constant(0));
|
||||||
__ GotoIfNot(buffer_is_not_detached, bailout);
|
__ GotoIfNot(buffer_is_not_detached, bailout);
|
||||||
|
|
||||||
// Go to the slow path if the {buffer} is shared.
|
// Go to the slow path if the {buffer} is shared.
|
||||||
Node* buffer_is_not_shared = __ Word32Equal(
|
Node* buffer_is_not_shared = __ Word32Equal(
|
||||||
__ Word32And(buffer_bit_field,
|
__ Word32And(buffer_bit_field,
|
||||||
__ Int32Constant(JSArrayBuffer::IsSharedBit::kMask)),
|
__ Int32Constant(JSArrayBuffer::IsSharedBit::kMask)),
|
||||||
__ ZeroConstant());
|
__ Int32Constant(0));
|
||||||
__ GotoIfNot(buffer_is_not_shared, bailout);
|
__ GotoIfNot(buffer_is_not_shared, bailout);
|
||||||
|
|
||||||
// Unpack the store and length, and store them to a struct
|
// Unpack the store and length, and store them to a struct
|
||||||
@ -6941,7 +6941,8 @@ void LinearizeEffectControl(JSGraph* graph, Schedule* schedule, Zone* temp_zone,
|
|||||||
SourcePositionTable* source_positions,
|
SourcePositionTable* source_positions,
|
||||||
NodeOriginTable* node_origins,
|
NodeOriginTable* node_origins,
|
||||||
JSHeapBroker* broker) {
|
JSHeapBroker* broker) {
|
||||||
JSGraphAssembler graph_assembler_(graph, temp_zone);
|
JSGraphAssembler graph_assembler_(graph, temp_zone,
|
||||||
|
BranchSemantics::kMachine);
|
||||||
EffectControlLinearizer linearizer(graph, schedule, &graph_assembler_,
|
EffectControlLinearizer linearizer(graph, schedule, &graph_assembler_,
|
||||||
temp_zone, source_positions, node_origins,
|
temp_zone, source_positions, node_origins,
|
||||||
MaintainSchedule::kDiscard, broker);
|
MaintainSchedule::kDiscard, broker);
|
||||||
|
@ -4,12 +4,21 @@
|
|||||||
|
|
||||||
#include "src/compiler/graph-assembler.h"
|
#include "src/compiler/graph-assembler.h"
|
||||||
|
|
||||||
|
#include "src/base/container-utils.h"
|
||||||
#include "src/codegen/callable.h"
|
#include "src/codegen/callable.h"
|
||||||
|
#include "src/codegen/machine-type.h"
|
||||||
|
#include "src/codegen/tnode.h"
|
||||||
|
#include "src/common/globals.h"
|
||||||
#include "src/compiler/access-builder.h"
|
#include "src/compiler/access-builder.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/graph-reducer.h"
|
#include "src/compiler/graph-reducer.h"
|
||||||
#include "src/compiler/linkage.h"
|
#include "src/compiler/linkage.h"
|
||||||
|
#include "src/compiler/type-cache.h"
|
||||||
// For TNode types.
|
// For TNode types.
|
||||||
|
#include "src/objects/elements-kind.h"
|
||||||
#include "src/objects/heap-number.h"
|
#include "src/objects/heap-number.h"
|
||||||
|
#include "src/objects/instance-type.h"
|
||||||
|
#include "src/objects/js-array-buffer.h"
|
||||||
#include "src/objects/oddball.h"
|
#include "src/objects/oddball.h"
|
||||||
#include "src/objects/string.h"
|
#include "src/objects/string.h"
|
||||||
|
|
||||||
@ -33,18 +42,21 @@ class V8_NODISCARD GraphAssembler::BlockInlineReduction {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GraphAssembler::GraphAssembler(
|
GraphAssembler::GraphAssembler(
|
||||||
MachineGraph* mcgraph, Zone* zone,
|
MachineGraph* mcgraph, Zone* zone, BranchSemantics default_branch_semantics,
|
||||||
base::Optional<NodeChangedCallback> node_changed_callback,
|
base::Optional<NodeChangedCallback> node_changed_callback,
|
||||||
bool mark_loop_exits)
|
bool mark_loop_exits)
|
||||||
: temp_zone_(zone),
|
: temp_zone_(zone),
|
||||||
mcgraph_(mcgraph),
|
mcgraph_(mcgraph),
|
||||||
|
default_branch_semantics_(default_branch_semantics),
|
||||||
effect_(nullptr),
|
effect_(nullptr),
|
||||||
control_(nullptr),
|
control_(nullptr),
|
||||||
node_changed_callback_(node_changed_callback),
|
node_changed_callback_(node_changed_callback),
|
||||||
inline_reducers_(zone),
|
inline_reducers_(zone),
|
||||||
inline_reductions_blocked_(false),
|
inline_reductions_blocked_(false),
|
||||||
loop_headers_(zone),
|
loop_headers_(zone),
|
||||||
mark_loop_exits_(mark_loop_exits) {}
|
mark_loop_exits_(mark_loop_exits) {
|
||||||
|
DCHECK_NE(default_branch_semantics_, BranchSemantics::kUnspecified);
|
||||||
|
}
|
||||||
|
|
||||||
GraphAssembler::~GraphAssembler() { DCHECK_EQ(loop_nesting_level_, 0); }
|
GraphAssembler::~GraphAssembler() { DCHECK_EQ(loop_nesting_level_, 0); }
|
||||||
|
|
||||||
@ -52,16 +64,16 @@ Node* GraphAssembler::IntPtrConstant(intptr_t value) {
|
|||||||
return AddClonedNode(mcgraph()->IntPtrConstant(value));
|
return AddClonedNode(mcgraph()->IntPtrConstant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* GraphAssembler::UintPtrConstant(uintptr_t value) {
|
TNode<UintPtrT> GraphAssembler::UintPtrConstant(uintptr_t value) {
|
||||||
return AddClonedNode(mcgraph()->UintPtrConstant(value));
|
return TNode<UintPtrT>::UncheckedCast(mcgraph()->UintPtrConstant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* GraphAssembler::Int32Constant(int32_t value) {
|
Node* GraphAssembler::Int32Constant(int32_t value) {
|
||||||
return AddClonedNode(mcgraph()->Int32Constant(value));
|
return AddClonedNode(mcgraph()->Int32Constant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* GraphAssembler::Uint32Constant(uint32_t value) {
|
TNode<Uint32T> GraphAssembler::Uint32Constant(uint32_t value) {
|
||||||
return AddClonedNode(mcgraph()->Uint32Constant(value));
|
return TNode<Uint32T>::UncheckedCast(mcgraph()->Uint32Constant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* GraphAssembler::Int64Constant(int64_t value) {
|
Node* GraphAssembler::Int64Constant(int64_t value) {
|
||||||
@ -150,8 +162,43 @@ PURE_ASSEMBLER_MACH_UNOP_LIST(PURE_UNOP_DEF)
|
|||||||
Node* GraphAssembler::Name(Node* left, Node* right) { \
|
Node* GraphAssembler::Name(Node* left, Node* right) { \
|
||||||
return AddNode(graph()->NewNode(machine()->Name(), left, right)); \
|
return AddNode(graph()->NewNode(machine()->Name(), left, right)); \
|
||||||
}
|
}
|
||||||
PURE_ASSEMBLER_MACH_BINOP_LIST(PURE_BINOP_DEF)
|
#define PURE_BINOP_DEF_TNODE(Name, Result, Left, Right) \
|
||||||
|
TNode<Result> GraphAssembler::Name(SloppyTNode<Left> left, \
|
||||||
|
SloppyTNode<Right> right) { \
|
||||||
|
return AddNode<Result>(graph()->NewNode(machine()->Name(), left, right)); \
|
||||||
|
}
|
||||||
|
PURE_ASSEMBLER_MACH_BINOP_LIST(PURE_BINOP_DEF, PURE_BINOP_DEF_TNODE)
|
||||||
#undef PURE_BINOP_DEF
|
#undef PURE_BINOP_DEF
|
||||||
|
#undef PURE_BINOP_DEF_TNODE
|
||||||
|
|
||||||
|
TNode<BoolT> GraphAssembler::UintPtrLessThanOrEqual(TNode<UintPtrT> left,
|
||||||
|
TNode<UintPtrT> right) {
|
||||||
|
return kSystemPointerSize == 8
|
||||||
|
? Uint64LessThanOrEqual(TNode<Uint64T>::UncheckedCast(left),
|
||||||
|
TNode<Uint64T>::UncheckedCast(right))
|
||||||
|
: Uint32LessThanOrEqual(TNode<Uint32T>::UncheckedCast(left),
|
||||||
|
TNode<Uint32T>::UncheckedCast(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<UintPtrT> GraphAssembler::UintPtrAdd(TNode<UintPtrT> left,
|
||||||
|
TNode<UintPtrT> right) {
|
||||||
|
return kSystemPointerSize == 8
|
||||||
|
? TNode<UintPtrT>::UncheckedCast(Int64Add(left, right))
|
||||||
|
: TNode<UintPtrT>::UncheckedCast(Int32Add(left, right));
|
||||||
|
}
|
||||||
|
TNode<UintPtrT> GraphAssembler::UintPtrSub(TNode<UintPtrT> left,
|
||||||
|
TNode<UintPtrT> right) {
|
||||||
|
return kSystemPointerSize == 8
|
||||||
|
? TNode<UintPtrT>::UncheckedCast(Int64Sub(left, right))
|
||||||
|
: TNode<UintPtrT>::UncheckedCast(Int32Sub(left, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<UintPtrT> GraphAssembler::UintPtrDiv(TNode<UintPtrT> left,
|
||||||
|
TNode<UintPtrT> right) {
|
||||||
|
return kSystemPointerSize == 8
|
||||||
|
? TNode<UintPtrT>::UncheckedCast(Uint64Div(left, right))
|
||||||
|
: TNode<UintPtrT>::UncheckedCast(Uint32Div(left, right));
|
||||||
|
}
|
||||||
|
|
||||||
#define CHECKED_BINOP_DEF(Name) \
|
#define CHECKED_BINOP_DEF(Name) \
|
||||||
Node* GraphAssembler::Name(Node* left, Node* right) { \
|
Node* GraphAssembler::Name(Node* left, Node* right) { \
|
||||||
@ -226,6 +273,15 @@ Node* JSGraphAssembler::LoadField(FieldAccess const& access, Node* object) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TNode<Uint32T> JSGraphAssembler::LoadElementsKind(TNode<Map> map) {
|
||||||
|
TNode<Uint8T> bit_field2 = EnterMachineGraph<Uint8T>(
|
||||||
|
LoadField<Uint8T>(AccessBuilder::ForMapBitField2(), map),
|
||||||
|
UseInfo::TruncatingWord32());
|
||||||
|
return TNode<Uint32T>::UncheckedCast(
|
||||||
|
Word32Shr(TNode<Word32T>::UncheckedCast(bit_field2),
|
||||||
|
Uint32Constant(Map::Bits2::ElementsKindBits::kShift)));
|
||||||
|
}
|
||||||
|
|
||||||
Node* JSGraphAssembler::LoadElement(ElementAccess const& access, Node* object,
|
Node* JSGraphAssembler::LoadElement(ElementAccess const& access, Node* object,
|
||||||
Node* index) {
|
Node* index) {
|
||||||
Node* value = AddNode(graph()->NewNode(simplified()->LoadElement(access),
|
Node* value = AddNode(graph()->NewNode(simplified()->LoadElement(access),
|
||||||
@ -411,6 +467,363 @@ Node* JSGraphAssembler::StringCharCodeAt(TNode<String> string,
|
|||||||
position, effect(), control()));
|
position, effect(), control()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ArrayBufferViewAccessBuilder {
|
||||||
|
public:
|
||||||
|
explicit ArrayBufferViewAccessBuilder(JSGraphAssembler* assembler,
|
||||||
|
std::set<ElementsKind> candidates)
|
||||||
|
: assembler_(assembler), candidates_(std::move(candidates)) {
|
||||||
|
DCHECK_NOT_NULL(assembler_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maybe_rab_gsab() const {
|
||||||
|
if (candidates_.empty()) return true;
|
||||||
|
return !base::all_of(candidates_, [](auto e) {
|
||||||
|
return !IsRabGsabTypedArrayElementsKind(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
base::Optional<int> TryComputeStaticElementShift() {
|
||||||
|
if (candidates_.empty()) return base::nullopt;
|
||||||
|
int shift = ElementsKindToShiftSize(*candidates_.begin());
|
||||||
|
if (!base::all_of(candidates_, [shift](auto e) {
|
||||||
|
return ElementsKindToShiftSize(e) == shift;
|
||||||
|
})) {
|
||||||
|
return base::nullopt;
|
||||||
|
}
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::Optional<int> TryComputeStaticElementSize() {
|
||||||
|
if (candidates_.empty()) return base::nullopt;
|
||||||
|
int size = ElementsKindToByteSize(*candidates_.begin());
|
||||||
|
if (!base::all_of(candidates_, [size](auto e) {
|
||||||
|
return ElementsKindToByteSize(e) == size;
|
||||||
|
})) {
|
||||||
|
return base::nullopt;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<UintPtrT> BuildLength(TNode<JSArrayBufferView> view,
|
||||||
|
TNode<Context> context) {
|
||||||
|
auto& a = *assembler_;
|
||||||
|
|
||||||
|
// Case 1: Normal (backed by AB/SAB) or non-length tracking backed by GSAB
|
||||||
|
// (can't go oob once constructed)
|
||||||
|
auto GsabFixedOrNormal = [&]() {
|
||||||
|
return MachineLoadField<UintPtrT>(AccessBuilder::ForJSTypedArrayLength(),
|
||||||
|
view, UseInfo::Word());
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we statically know we cannot have rab/gsab backed, we can simply
|
||||||
|
// load from the view.
|
||||||
|
if (!maybe_rab_gsab()) {
|
||||||
|
return GsabFixedOrNormal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we need to generate the checks for the view's bitfield.
|
||||||
|
TNode<Word32T> bitfield = a.EnterMachineGraph<Word32T>(
|
||||||
|
a.LoadField<Word32T>(AccessBuilder::ForJSArrayBufferViewBitField(),
|
||||||
|
view),
|
||||||
|
UseInfo::TruncatingWord32());
|
||||||
|
TNode<Word32T> length_tracking_bit = a.Word32And(
|
||||||
|
bitfield, a.Uint32Constant(JSArrayBufferView::kIsLengthTracking));
|
||||||
|
TNode<Word32T> backed_by_rab_bit = a.Word32And(
|
||||||
|
bitfield, a.Uint32Constant(JSArrayBufferView::kIsBackedByRab));
|
||||||
|
|
||||||
|
// Load the underlying buffer.
|
||||||
|
TNode<HeapObject> buffer = a.LoadField<HeapObject>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewBuffer(), view);
|
||||||
|
|
||||||
|
// Compute the element size.
|
||||||
|
TNode<Uint32T> element_size;
|
||||||
|
if (auto size_opt = TryComputeStaticElementSize()) {
|
||||||
|
element_size = a.Uint32Constant(*size_opt);
|
||||||
|
} else {
|
||||||
|
TNode<Map> typed_array_map = a.LoadField<Map>(
|
||||||
|
AccessBuilder::ForMap(WriteBarrierKind::kNoWriteBarrier), view);
|
||||||
|
TNode<Uint32T> elements_kind = a.LoadElementsKind(typed_array_map);
|
||||||
|
element_size = a.LookupByteSizeForElementsKind(elements_kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Fixed length backed by RAB (can go oob once constructed)
|
||||||
|
auto RabFixed = [&]() {
|
||||||
|
TNode<UintPtrT> unchecked_byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteLength(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
TNode<UintPtrT> underlying_byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferByteLength(), buffer, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
|
||||||
|
TNode<UintPtrT> byte_length =
|
||||||
|
a
|
||||||
|
.MachineSelectIf<UintPtrT>(a.UintPtrLessThanOrEqual(
|
||||||
|
a.UintPtrAdd(byte_offset, unchecked_byte_length),
|
||||||
|
underlying_byte_length))
|
||||||
|
.Then([&]() { return unchecked_byte_length; })
|
||||||
|
.Else([&]() { return a.UintPtrConstant(0); })
|
||||||
|
.Value();
|
||||||
|
return a.UintPtrDiv(byte_length,
|
||||||
|
TNode<UintPtrT>::UncheckedCast(element_size));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3) Length-tracking backed by RAB (JSArrayBuffer stores the length)
|
||||||
|
auto RabTracking = [&]() {
|
||||||
|
TNode<UintPtrT> byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferByteLength(), buffer, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
|
||||||
|
return a
|
||||||
|
.MachineSelectIf<UintPtrT>(
|
||||||
|
a.UintPtrLessThanOrEqual(byte_offset, byte_length))
|
||||||
|
.Then([&]() {
|
||||||
|
// length = floor((byte_length - byte_offset) / element_size)
|
||||||
|
return a.UintPtrDiv(a.UintPtrSub(byte_length, byte_offset),
|
||||||
|
TNode<UintPtrT>::UncheckedCast(element_size));
|
||||||
|
})
|
||||||
|
.Else([&]() { return a.UintPtrConstant(0); })
|
||||||
|
.ExpectTrue()
|
||||||
|
.Value();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4) Length-tracking backed by GSAB (BackingStore stores the length)
|
||||||
|
auto GsabTracking = [&]() {
|
||||||
|
TNode<Number> temp = TNode<Number>::UncheckedCast(a.TypeGuard(
|
||||||
|
TypeCache::Get()->kJSArrayBufferViewByteLengthType,
|
||||||
|
a.JSCallRuntime1(Runtime::kGrowableSharedArrayBufferByteLength,
|
||||||
|
buffer, context, base::nullopt,
|
||||||
|
Operator::kNoWrite)));
|
||||||
|
TNode<UintPtrT> byte_length =
|
||||||
|
a.EnterMachineGraph<UintPtrT>(temp, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
|
||||||
|
return a.UintPtrDiv(a.UintPtrSub(byte_length, byte_offset),
|
||||||
|
TNode<UintPtrT>::UncheckedCast(element_size));
|
||||||
|
};
|
||||||
|
|
||||||
|
return a.MachineSelectIf<UintPtrT>(length_tracking_bit)
|
||||||
|
.Then([&]() {
|
||||||
|
return a.MachineSelectIf<UintPtrT>(backed_by_rab_bit)
|
||||||
|
.Then(RabTracking)
|
||||||
|
.Else(GsabTracking)
|
||||||
|
.Value();
|
||||||
|
})
|
||||||
|
.Else([&]() {
|
||||||
|
return a.MachineSelectIf<UintPtrT>(backed_by_rab_bit)
|
||||||
|
.Then(RabFixed)
|
||||||
|
.Else(GsabFixedOrNormal)
|
||||||
|
.Value();
|
||||||
|
})
|
||||||
|
.Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<UintPtrT> BuildByteLength(TNode<JSArrayBufferView> view,
|
||||||
|
TNode<Context> context) {
|
||||||
|
auto& a = *assembler_;
|
||||||
|
|
||||||
|
// Case 1: Normal (backed by AB/SAB) or non-length tracking backed by GSAB
|
||||||
|
// (can't go oob once constructed)
|
||||||
|
auto GsabFixedOrNormal = [&]() {
|
||||||
|
return MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteLength(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we statically know we cannot have rab/gsab backed, we can simply
|
||||||
|
// use load from the view.
|
||||||
|
if (!maybe_rab_gsab()) {
|
||||||
|
return GsabFixedOrNormal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we need to generate the checks for the view's bitfield.
|
||||||
|
TNode<Word32T> bitfield = a.EnterMachineGraph<Word32T>(
|
||||||
|
a.LoadField<Word32T>(AccessBuilder::ForJSArrayBufferViewBitField(),
|
||||||
|
view),
|
||||||
|
UseInfo::TruncatingWord32());
|
||||||
|
TNode<Word32T> length_tracking_bit = a.Word32And(
|
||||||
|
bitfield, a.Uint32Constant(JSArrayBufferView::kIsLengthTracking));
|
||||||
|
TNode<Word32T> backed_by_rab_bit = a.Word32And(
|
||||||
|
bitfield, a.Uint32Constant(JSArrayBufferView::kIsBackedByRab));
|
||||||
|
|
||||||
|
// Load the underlying buffer.
|
||||||
|
TNode<HeapObject> buffer = a.LoadField<HeapObject>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewBuffer(), view);
|
||||||
|
|
||||||
|
// Case 2: Fixed length backed by RAB (can go oob once constructed)
|
||||||
|
auto RabFixed = [&]() {
|
||||||
|
TNode<UintPtrT> unchecked_byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteLength(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
TNode<UintPtrT> underlying_byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferByteLength(), buffer, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
|
||||||
|
return a
|
||||||
|
.MachineSelectIf<UintPtrT>(a.UintPtrLessThanOrEqual(
|
||||||
|
a.UintPtrAdd(byte_offset, unchecked_byte_length),
|
||||||
|
underlying_byte_length))
|
||||||
|
.Then([&]() { return unchecked_byte_length; })
|
||||||
|
.Else([&]() { return a.UintPtrConstant(0); })
|
||||||
|
.Value();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RoundDownToElementSize = [&](TNode<UintPtrT> byte_size) {
|
||||||
|
if (auto shift_opt = TryComputeStaticElementShift()) {
|
||||||
|
constexpr uintptr_t all_bits = static_cast<uintptr_t>(-1);
|
||||||
|
if (*shift_opt == 0) return byte_size;
|
||||||
|
return TNode<UintPtrT>::UncheckedCast(
|
||||||
|
a.WordAnd(byte_size, a.UintPtrConstant(all_bits << (*shift_opt))));
|
||||||
|
}
|
||||||
|
TNode<Map> typed_array_map = a.LoadField<Map>(
|
||||||
|
AccessBuilder::ForMap(WriteBarrierKind::kNoWriteBarrier), view);
|
||||||
|
TNode<Uint32T> elements_kind = a.LoadElementsKind(typed_array_map);
|
||||||
|
TNode<Uint32T> element_shift =
|
||||||
|
a.LookupByteShiftForElementsKind(elements_kind);
|
||||||
|
return TNode<UintPtrT>::UncheckedCast(
|
||||||
|
a.WordShl(a.WordShr(byte_size, element_shift), element_shift));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Case 3: Length-tracking backed by RAB (JSArrayBuffer stores the length)
|
||||||
|
auto RabTracking = [&]() {
|
||||||
|
TNode<UintPtrT> byte_length = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferByteLength(), buffer, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
|
||||||
|
return a
|
||||||
|
.MachineSelectIf<UintPtrT>(
|
||||||
|
a.UintPtrLessThanOrEqual(byte_offset, byte_length))
|
||||||
|
.Then([&]() {
|
||||||
|
return RoundDownToElementSize(
|
||||||
|
a.UintPtrSub(byte_length, byte_offset));
|
||||||
|
})
|
||||||
|
.Else([&]() { return a.UintPtrConstant(0); })
|
||||||
|
.ExpectTrue()
|
||||||
|
.Value();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Case 4: Length-tracking backed by GSAB (BackingStore stores the length)
|
||||||
|
auto GsabTracking = [&]() {
|
||||||
|
TNode<Number> temp = TNode<Number>::UncheckedCast(a.TypeGuard(
|
||||||
|
TypeCache::Get()->kJSArrayBufferViewByteLengthType,
|
||||||
|
a.JSCallRuntime1(Runtime::kGrowableSharedArrayBufferByteLength,
|
||||||
|
buffer, context, base::nullopt,
|
||||||
|
Operator::kNoWrite)));
|
||||||
|
TNode<UintPtrT> byte_length =
|
||||||
|
a.EnterMachineGraph<UintPtrT>(temp, UseInfo::Word());
|
||||||
|
TNode<UintPtrT> byte_offset = MachineLoadField<UintPtrT>(
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), view,
|
||||||
|
UseInfo::Word());
|
||||||
|
return RoundDownToElementSize(a.UintPtrSub(byte_length, byte_offset));
|
||||||
|
};
|
||||||
|
|
||||||
|
return a.MachineSelectIf<UintPtrT>(length_tracking_bit)
|
||||||
|
.Then([&]() {
|
||||||
|
return a.MachineSelectIf<UintPtrT>(backed_by_rab_bit)
|
||||||
|
.Then(RabTracking)
|
||||||
|
.Else(GsabTracking)
|
||||||
|
.Value();
|
||||||
|
})
|
||||||
|
.Else([&]() {
|
||||||
|
return a.MachineSelectIf<UintPtrT>(backed_by_rab_bit)
|
||||||
|
.Then(RabFixed)
|
||||||
|
.Else(GsabFixedOrNormal)
|
||||||
|
.Value();
|
||||||
|
})
|
||||||
|
.Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
TNode<T> MachineLoadField(FieldAccess const& access, TNode<HeapObject> object,
|
||||||
|
const UseInfo& use_info) {
|
||||||
|
return assembler_->EnterMachineGraph<T>(
|
||||||
|
assembler_->LoadField<T>(access, object), use_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSGraphAssembler* assembler_;
|
||||||
|
std::set<ElementsKind> candidates_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TNode<Number> JSGraphAssembler::ArrayBufferViewByteLength(
|
||||||
|
TNode<JSArrayBufferView> array_buffer_view,
|
||||||
|
std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context) {
|
||||||
|
ArrayBufferViewAccessBuilder builder(this,
|
||||||
|
std::move(elements_kinds_candidates));
|
||||||
|
return ExitMachineGraph<Number>(
|
||||||
|
builder.BuildByteLength(array_buffer_view, context),
|
||||||
|
MachineType::PointerRepresentation(),
|
||||||
|
TypeCache::Get()->kJSArrayBufferByteLengthType);
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Number> JSGraphAssembler::TypedArrayLength(
|
||||||
|
TNode<JSTypedArray> typed_array,
|
||||||
|
std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context) {
|
||||||
|
ArrayBufferViewAccessBuilder builder(this, elements_kinds_candidates);
|
||||||
|
return ExitMachineGraph<Number>(builder.BuildLength(typed_array, context),
|
||||||
|
MachineType::PointerRepresentation(),
|
||||||
|
TypeCache::Get()->kJSTypedArrayLengthType);
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Uint32T> JSGraphAssembler::LookupByteShiftForElementsKind(
|
||||||
|
TNode<Uint32T> elements_kind) {
|
||||||
|
TNode<Uint32T> index = TNode<Uint32T>::UncheckedCast(Int32Sub(
|
||||||
|
elements_kind, Uint32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)));
|
||||||
|
TNode<RawPtrT> shift_table = TNode<RawPtrT>::UncheckedCast(ExternalConstant(
|
||||||
|
ExternalReference::
|
||||||
|
typed_array_and_rab_gsab_typed_array_elements_kind_shifts()));
|
||||||
|
return TNode<Uint8T>::UncheckedCast(
|
||||||
|
Load(MachineType::Uint8(), shift_table, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Uint32T> JSGraphAssembler::LookupByteSizeForElementsKind(
|
||||||
|
TNode<Uint32T> elements_kind) {
|
||||||
|
TNode<Uint32T> index = TNode<Uint32T>::UncheckedCast(Int32Sub(
|
||||||
|
elements_kind, Uint32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)));
|
||||||
|
TNode<RawPtrT> size_table = TNode<RawPtrT>::UncheckedCast(ExternalConstant(
|
||||||
|
ExternalReference::
|
||||||
|
typed_array_and_rab_gsab_typed_array_elements_kind_sizes()));
|
||||||
|
return TNode<Uint8T>::UncheckedCast(
|
||||||
|
Load(MachineType::Uint8(), size_table, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Object> JSGraphAssembler::JSCallRuntime1(
|
||||||
|
Runtime::FunctionId function_id, TNode<Object> arg0, TNode<Context> context,
|
||||||
|
base::Optional<FrameState> frame_state, Operator::Properties properties) {
|
||||||
|
return MayThrow([&]() {
|
||||||
|
if (frame_state.has_value()) {
|
||||||
|
return AddNode<Object>(graph()->NewNode(
|
||||||
|
javascript()->CallRuntime(function_id, 1, properties), arg0, context,
|
||||||
|
static_cast<Node*>(*frame_state), effect(), control()));
|
||||||
|
} else {
|
||||||
|
return AddNode<Object>(graph()->NewNode(
|
||||||
|
javascript()->CallRuntime(function_id, 1, properties), arg0, context,
|
||||||
|
effect(), control()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Object> JSGraphAssembler::JSCallRuntime2(Runtime::FunctionId function_id,
|
||||||
|
TNode<Object> arg0,
|
||||||
|
TNode<Object> arg1,
|
||||||
|
TNode<Context> context,
|
||||||
|
FrameState frame_state) {
|
||||||
|
return MayThrow([&]() {
|
||||||
|
return AddNode<Object>(
|
||||||
|
graph()->NewNode(javascript()->CallRuntime(function_id, 2), arg0, arg1,
|
||||||
|
context, frame_state, effect(), control()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Node* GraphAssembler::TypeGuard(Type type, Node* value) {
|
Node* GraphAssembler::TypeGuard(Type type, Node* value) {
|
||||||
return AddNode(
|
return AddNode(
|
||||||
graph()->NewNode(common()->TypeGuard(type), value, effect(), control()));
|
graph()->NewNode(common()->TypeGuard(type), value, effect(), control()));
|
||||||
@ -590,7 +1003,7 @@ void GraphAssembler::BranchWithCriticalSafetyCheck(
|
|||||||
hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
|
hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
BranchImpl(condition, if_true, if_false, hint);
|
BranchImpl(default_branch_semantics_, condition, if_true, if_false, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphAssembler::ConnectUnreachableToEnd() {
|
void GraphAssembler::ConnectUnreachableToEnd() {
|
||||||
|
@ -5,14 +5,17 @@
|
|||||||
#ifndef V8_COMPILER_GRAPH_ASSEMBLER_H_
|
#ifndef V8_COMPILER_GRAPH_ASSEMBLER_H_
|
||||||
#define V8_COMPILER_GRAPH_ASSEMBLER_H_
|
#define V8_COMPILER_GRAPH_ASSEMBLER_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "src/base/small-vector.h"
|
#include "src/base/small-vector.h"
|
||||||
#include "src/codegen/tnode.h"
|
#include "src/codegen/tnode.h"
|
||||||
|
#include "src/common/globals.h"
|
||||||
#include "src/compiler/feedback-source.h"
|
#include "src/compiler/feedback-source.h"
|
||||||
#include "src/compiler/js-graph.h"
|
#include "src/compiler/js-graph.h"
|
||||||
#include "src/compiler/node.h"
|
#include "src/compiler/node.h"
|
||||||
#include "src/compiler/simplified-operator.h"
|
#include "src/compiler/simplified-operator.h"
|
||||||
|
#include "src/objects/oddball.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -62,7 +65,7 @@ class Reducer;
|
|||||||
V(Word32ReverseBytes) \
|
V(Word32ReverseBytes) \
|
||||||
V(Word64ReverseBytes)
|
V(Word64ReverseBytes)
|
||||||
|
|
||||||
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
|
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V, T) \
|
||||||
V(Float64Add) \
|
V(Float64Add) \
|
||||||
V(Float64Div) \
|
V(Float64Div) \
|
||||||
V(Float64Equal) \
|
V(Float64Equal) \
|
||||||
@ -79,23 +82,24 @@ class Reducer;
|
|||||||
V(Int32LessThanOrEqual) \
|
V(Int32LessThanOrEqual) \
|
||||||
V(Int32Mul) \
|
V(Int32Mul) \
|
||||||
V(Int32Sub) \
|
V(Int32Sub) \
|
||||||
|
V(Int64Add) \
|
||||||
V(Int64Sub) \
|
V(Int64Sub) \
|
||||||
V(IntAdd) \
|
V(IntAdd) \
|
||||||
V(IntLessThan) \
|
V(IntLessThan) \
|
||||||
V(IntMul) \
|
V(IntMul) \
|
||||||
V(IntSub) \
|
V(IntSub) \
|
||||||
V(Uint32LessThan) \
|
V(Uint32LessThan) \
|
||||||
V(Uint32LessThanOrEqual) \
|
T(Uint32LessThanOrEqual, BoolT, Uint32T, Uint32T) \
|
||||||
V(Uint64LessThan) \
|
V(Uint64LessThan) \
|
||||||
V(Uint64LessThanOrEqual) \
|
T(Uint64LessThanOrEqual, BoolT, Uint64T, Uint64T) \
|
||||||
V(UintLessThan) \
|
V(UintLessThan) \
|
||||||
V(Word32And) \
|
T(Word32And, Word32T, Word32T, Word32T) \
|
||||||
V(Word32Equal) \
|
T(Word32Equal, BoolT, Word32T, Word32T) \
|
||||||
V(Word32Or) \
|
V(Word32Or) \
|
||||||
V(Word32Sar) \
|
V(Word32Sar) \
|
||||||
V(Word32SarShiftOutZeros) \
|
V(Word32SarShiftOutZeros) \
|
||||||
V(Word32Shl) \
|
V(Word32Shl) \
|
||||||
V(Word32Shr) \
|
T(Word32Shr, Word32T, Word32T, Word32T) \
|
||||||
V(Word32Xor) \
|
V(Word32Xor) \
|
||||||
V(Word64And) \
|
V(Word64And) \
|
||||||
V(Word64Equal) \
|
V(Word64Equal) \
|
||||||
@ -280,9 +284,11 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
// will maintain the schedule as it updates blocks.
|
// will maintain the schedule as it updates blocks.
|
||||||
GraphAssembler(
|
GraphAssembler(
|
||||||
MachineGraph* jsgraph, Zone* zone,
|
MachineGraph* jsgraph, Zone* zone,
|
||||||
|
BranchSemantics default_branch_semantics,
|
||||||
base::Optional<NodeChangedCallback> node_changed_callback = base::nullopt,
|
base::Optional<NodeChangedCallback> node_changed_callback = base::nullopt,
|
||||||
bool mark_loop_exits = false);
|
bool mark_loop_exits = false);
|
||||||
virtual ~GraphAssembler();
|
virtual ~GraphAssembler();
|
||||||
|
virtual SimplifiedOperatorBuilder* simplified() { UNREACHABLE(); }
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
void InitializeEffectControl(Node* effect, Node* control);
|
void InitializeEffectControl(Node* effect, Node* control);
|
||||||
@ -322,9 +328,9 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
|
|
||||||
// Value creation.
|
// Value creation.
|
||||||
Node* IntPtrConstant(intptr_t value);
|
Node* IntPtrConstant(intptr_t value);
|
||||||
Node* UintPtrConstant(uintptr_t value);
|
TNode<UintPtrT> UintPtrConstant(uintptr_t value);
|
||||||
Node* Int32Constant(int32_t value);
|
Node* Int32Constant(int32_t value);
|
||||||
Node* Uint32Constant(uint32_t value);
|
TNode<Uint32T> Uint32Constant(uint32_t value);
|
||||||
Node* Int64Constant(int64_t value);
|
Node* Int64Constant(int64_t value);
|
||||||
Node* Uint64Constant(uint64_t value);
|
Node* Uint64Constant(uint64_t value);
|
||||||
Node* UniqueIntPtrConstant(intptr_t value);
|
Node* UniqueIntPtrConstant(intptr_t value);
|
||||||
@ -343,9 +349,17 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
#undef PURE_UNOP_DECL
|
#undef PURE_UNOP_DECL
|
||||||
|
|
||||||
#define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
|
#define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
|
||||||
PURE_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
#define BINOP_DECL_TNODE(Name, Result, Left, Right) \
|
||||||
|
TNode<Result> Name(SloppyTNode<Left> left, SloppyTNode<Right> right);
|
||||||
|
PURE_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL, BINOP_DECL_TNODE)
|
||||||
CHECKED_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
CHECKED_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
||||||
#undef BINOP_DECL
|
#undef BINOP_DECL
|
||||||
|
#undef BINOP_DECL_TNODE
|
||||||
|
TNode<BoolT> UintPtrLessThanOrEqual(TNode<UintPtrT> left,
|
||||||
|
TNode<UintPtrT> right);
|
||||||
|
TNode<UintPtrT> UintPtrAdd(TNode<UintPtrT> left, TNode<UintPtrT> right);
|
||||||
|
TNode<UintPtrT> UintPtrSub(TNode<UintPtrT> left, TNode<UintPtrT> right);
|
||||||
|
TNode<UintPtrT> UintPtrDiv(TNode<UintPtrT> left, TNode<UintPtrT> right);
|
||||||
|
|
||||||
#ifdef V8_MAP_PACKING
|
#ifdef V8_MAP_PACKING
|
||||||
Node* PackMapWord(TNode<Map> map);
|
Node* PackMapWord(TNode<Map> map);
|
||||||
@ -385,6 +399,10 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
Node* BitcastMaybeObjectToWord(Node* value);
|
Node* BitcastMaybeObjectToWord(Node* value);
|
||||||
|
|
||||||
Node* TypeGuard(Type type, Node* value);
|
Node* TypeGuard(Type type, Node* value);
|
||||||
|
template <typename T>
|
||||||
|
TNode<T> TypeGuard(Type type, TNode<T> value) {
|
||||||
|
return TNode<T>::UncheckedCast(TypeGuard(type, static_cast<Node*>(value)));
|
||||||
|
}
|
||||||
Node* Checkpoint(FrameState frame_state);
|
Node* Checkpoint(FrameState frame_state);
|
||||||
|
|
||||||
TNode<RawPtrT> StackSlot(int size, int alignment);
|
TNode<RawPtrT> StackSlot(int size, int alignment);
|
||||||
@ -443,6 +461,16 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_false,
|
detail::GraphAssemblerLabelForVars<Vars...>* if_false,
|
||||||
BranchHint hint, Vars...);
|
BranchHint hint, Vars...);
|
||||||
|
template <typename... Vars>
|
||||||
|
void MachineBranch(TNode<Word32T> condition,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false,
|
||||||
|
BranchHint hint, Vars...);
|
||||||
|
template <typename... Vars>
|
||||||
|
void JSBranch(TNode<Boolean> condition,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false, BranchHint hint,
|
||||||
|
Vars...);
|
||||||
|
|
||||||
// Control helpers.
|
// Control helpers.
|
||||||
|
|
||||||
@ -515,6 +543,8 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
Effect effect() const { return Effect(effect_); }
|
Effect effect() const { return Effect(effect_); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
constexpr bool Is64() const { return kSystemPointerSize == 8; }
|
||||||
|
|
||||||
template <typename... Vars>
|
template <typename... Vars>
|
||||||
void MergeState(detail::GraphAssemblerLabelForVars<Vars...>* label,
|
void MergeState(detail::GraphAssemblerLabelForVars<Vars...>* label,
|
||||||
Vars... vars);
|
Vars... vars);
|
||||||
@ -604,13 +634,14 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
|||||||
class BlockInlineReduction;
|
class BlockInlineReduction;
|
||||||
|
|
||||||
template <typename... Vars>
|
template <typename... Vars>
|
||||||
void BranchImpl(Node* condition,
|
void BranchImpl(BranchSemantics semantics, Node* condition,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_false,
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false,
|
||||||
BranchHint hint, Vars...);
|
BranchHint hint, Vars...);
|
||||||
|
|
||||||
Zone* temp_zone_;
|
Zone* temp_zone_;
|
||||||
MachineGraph* mcgraph_;
|
MachineGraph* mcgraph_;
|
||||||
|
BranchSemantics default_branch_semantics_;
|
||||||
Node* effect_;
|
Node* effect_;
|
||||||
Node* control_;
|
Node* control_;
|
||||||
// {node_changed_callback_} should be called when a node outside the
|
// {node_changed_callback_} should be called when a node outside the
|
||||||
@ -788,7 +819,8 @@ void GraphAssembler::Branch(
|
|||||||
hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
|
hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
BranchImpl(condition, if_true, if_false, hint, vars...);
|
BranchImpl(default_branch_semantics_, condition, if_true, if_false, hint,
|
||||||
|
vars...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Vars>
|
template <typename... Vars>
|
||||||
@ -796,17 +828,36 @@ void GraphAssembler::BranchWithHint(
|
|||||||
Node* condition, detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
Node* condition, detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_false, BranchHint hint,
|
detail::GraphAssemblerLabelForVars<Vars...>* if_false, BranchHint hint,
|
||||||
Vars... vars) {
|
Vars... vars) {
|
||||||
BranchImpl(condition, if_true, if_false, hint, vars...);
|
BranchImpl(default_branch_semantics_, condition, if_true, if_false, hint,
|
||||||
|
vars...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Vars>
|
template <typename... Vars>
|
||||||
void GraphAssembler::BranchImpl(
|
void GraphAssembler::MachineBranch(
|
||||||
Node* condition, detail::GraphAssemblerLabelForVars<Vars...>* if_true,
|
TNode<Word32T> condition, GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* if_false, BranchHint hint,
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false, BranchHint hint,
|
||||||
Vars... vars) {
|
Vars... vars) {
|
||||||
|
BranchImpl(BranchSemantics::kMachine, condition, if_true, if_false, hint,
|
||||||
|
vars...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Vars>
|
||||||
|
void GraphAssembler::JSBranch(TNode<Boolean> condition,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false,
|
||||||
|
BranchHint hint, Vars... vars) {
|
||||||
|
BranchImpl(BranchSemantics::kJS, condition, if_true, if_false, hint, vars...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Vars>
|
||||||
|
void GraphAssembler::BranchImpl(BranchSemantics semantics, Node* condition,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_true,
|
||||||
|
GraphAssemblerLabel<sizeof...(Vars)>* if_false,
|
||||||
|
BranchHint hint, Vars... vars) {
|
||||||
DCHECK_NOT_NULL(control());
|
DCHECK_NOT_NULL(control());
|
||||||
|
|
||||||
Node* branch = graph()->NewNode(common()->Branch(hint), condition, control());
|
Node* branch =
|
||||||
|
graph()->NewNode(common()->Branch(hint, semantics), condition, control());
|
||||||
|
|
||||||
control_ = graph()->NewNode(common()->IfTrue(), branch);
|
control_ = graph()->NewNode(common()->IfTrue(), branch);
|
||||||
MergeState(if_true, vars...);
|
MergeState(if_true, vars...);
|
||||||
@ -833,7 +884,8 @@ template <typename... Vars>
|
|||||||
void GraphAssembler::GotoIf(Node* condition,
|
void GraphAssembler::GotoIf(Node* condition,
|
||||||
detail::GraphAssemblerLabelForVars<Vars...>* label,
|
detail::GraphAssemblerLabelForVars<Vars...>* label,
|
||||||
BranchHint hint, Vars... vars) {
|
BranchHint hint, Vars... vars) {
|
||||||
Node* branch = graph()->NewNode(common()->Branch(hint), condition, control());
|
Node* branch = graph()->NewNode(
|
||||||
|
common()->Branch(hint, default_branch_semantics_), condition, control());
|
||||||
|
|
||||||
control_ = graph()->NewNode(common()->IfTrue(), branch);
|
control_ = graph()->NewNode(common()->IfTrue(), branch);
|
||||||
MergeState(label, vars...);
|
MergeState(label, vars...);
|
||||||
@ -845,7 +897,8 @@ template <typename... Vars>
|
|||||||
void GraphAssembler::GotoIfNot(
|
void GraphAssembler::GotoIfNot(
|
||||||
Node* condition, detail::GraphAssemblerLabelForVars<Vars...>* label,
|
Node* condition, detail::GraphAssemblerLabelForVars<Vars...>* label,
|
||||||
BranchHint hint, Vars... vars) {
|
BranchHint hint, Vars... vars) {
|
||||||
Node* branch = graph()->NewNode(common()->Branch(hint), condition, control());
|
Node* branch = graph()->NewNode(
|
||||||
|
common()->Branch(hint, default_branch_semantics_), condition, control());
|
||||||
|
|
||||||
control_ = graph()->NewNode(common()->IfFalse(), branch);
|
control_ = graph()->NewNode(common()->IfFalse(), branch);
|
||||||
MergeState(label, vars...);
|
MergeState(label, vars...);
|
||||||
@ -891,11 +944,16 @@ class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
|||||||
// Constructs a JSGraphAssembler. If {schedule} is not null, the graph
|
// Constructs a JSGraphAssembler. If {schedule} is not null, the graph
|
||||||
// assembler will maintain the schedule as it updates blocks.
|
// assembler will maintain the schedule as it updates blocks.
|
||||||
JSGraphAssembler(
|
JSGraphAssembler(
|
||||||
JSGraph* jsgraph, Zone* zone,
|
JSGraph* jsgraph, Zone* zone, BranchSemantics branch_semantics,
|
||||||
base::Optional<NodeChangedCallback> node_changed_callback = base::nullopt,
|
base::Optional<NodeChangedCallback> node_changed_callback = base::nullopt,
|
||||||
bool mark_loop_exits = false)
|
bool mark_loop_exits = false)
|
||||||
: GraphAssembler(jsgraph, zone, node_changed_callback, mark_loop_exits),
|
: GraphAssembler(jsgraph, zone, branch_semantics, node_changed_callback,
|
||||||
jsgraph_(jsgraph) {}
|
mark_loop_exits),
|
||||||
|
jsgraph_(jsgraph),
|
||||||
|
outermost_catch_scope_(CatchScope::Outermost(zone)),
|
||||||
|
catch_scope_(&outermost_catch_scope_) {
|
||||||
|
outermost_catch_scope_.set_gasm(this);
|
||||||
|
}
|
||||||
|
|
||||||
Node* SmiConstant(int32_t value);
|
Node* SmiConstant(int32_t value);
|
||||||
TNode<HeapObject> HeapConstant(Handle<HeapObject> object);
|
TNode<HeapObject> HeapConstant(Handle<HeapObject> object);
|
||||||
@ -922,6 +980,7 @@ class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
|||||||
// access.machine_type.representation()));
|
// access.machine_type.representation()));
|
||||||
return TNode<T>::UncheckedCast(LoadField(access, object));
|
return TNode<T>::UncheckedCast(LoadField(access, object));
|
||||||
}
|
}
|
||||||
|
TNode<Uint32T> LoadElementsKind(TNode<Map> map);
|
||||||
Node* LoadElement(ElementAccess const&, Node* object, Node* index);
|
Node* LoadElement(ElementAccess const&, Node* object, Node* index);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
TNode<T> LoadElement(ElementAccess const& access, TNode<HeapObject> object,
|
TNode<T> LoadElement(ElementAccess const& access, TNode<HeapObject> object,
|
||||||
@ -949,6 +1008,8 @@ class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
|||||||
TNode<Number> NumberSubtract(TNode<Number> lhs, TNode<Number> rhs);
|
TNode<Number> NumberSubtract(TNode<Number> lhs, TNode<Number> rhs);
|
||||||
TNode<Number> NumberShiftRightLogical(TNode<Number> lhs, TNode<Number> rhs);
|
TNode<Number> NumberShiftRightLogical(TNode<Number> lhs, TNode<Number> rhs);
|
||||||
TNode<Number> NumberBitwiseAnd(TNode<Number> lhs, TNode<Number> rhs);
|
TNode<Number> NumberBitwiseAnd(TNode<Number> lhs, TNode<Number> rhs);
|
||||||
|
TNode<Number> NumberDivide(TNode<Number> lhs, TNode<Number> rhs);
|
||||||
|
TNode<Number> NumberFloor(TNode<Number> value);
|
||||||
TNode<String> StringSubstring(TNode<String> string, TNode<Number> from,
|
TNode<String> StringSubstring(TNode<String> string, TNode<Number> from,
|
||||||
TNode<Number> to);
|
TNode<Number> to);
|
||||||
TNode<Boolean> ObjectIsCallable(TNode<Object> value);
|
TNode<Boolean> ObjectIsCallable(TNode<Object> value);
|
||||||
@ -967,12 +1028,349 @@ class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
|||||||
Node* StringCharCodeAt(TNode<String> string, TNode<Number> position);
|
Node* StringCharCodeAt(TNode<String> string, TNode<Number> position);
|
||||||
TNode<Object> DoubleArrayMax(TNode<JSArray> array);
|
TNode<Object> DoubleArrayMax(TNode<JSArray> array);
|
||||||
TNode<Object> DoubleArrayMin(TNode<JSArray> array);
|
TNode<Object> DoubleArrayMin(TNode<JSArray> array);
|
||||||
|
// Computes the byte length for a given {array_buffer_view}. If the set of
|
||||||
|
// possible ElementsKinds is known statically pass as
|
||||||
|
// {elements_kinds_candidates} to allow the assembler to generate more
|
||||||
|
// efficient code. Pass an empty {elements_kinds_candidates} to generate code
|
||||||
|
// that is generic enough to handle all ElementsKinds.
|
||||||
|
TNode<Number> ArrayBufferViewByteLength(
|
||||||
|
TNode<JSArrayBufferView> array_buffer_view,
|
||||||
|
std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context);
|
||||||
|
// Computes the length for a given {typed_array}. If the set of possible
|
||||||
|
// ElementsKinds is known statically pass as {elements_kinds_candidates} to
|
||||||
|
// allow the assembler to generate more efficient code. Pass an empty
|
||||||
|
// {elements_kinds_candidates} to generate code that is generic enough to
|
||||||
|
// handle all ElementsKinds.
|
||||||
|
TNode<Number> TypedArrayLength(
|
||||||
|
TNode<JSTypedArray> typed_array,
|
||||||
|
std::set<ElementsKind> elements_kinds_candidates, TNode<Context> context);
|
||||||
|
TNode<Uint32T> LookupByteShiftForElementsKind(TNode<Uint32T> elements_kind);
|
||||||
|
TNode<Uint32T> LookupByteSizeForElementsKind(TNode<Uint32T> elements_kind);
|
||||||
|
|
||||||
|
TNode<Object> JSCallRuntime1(
|
||||||
|
Runtime::FunctionId function_id, TNode<Object> arg0,
|
||||||
|
TNode<Context> context, base::Optional<FrameState> frame_state,
|
||||||
|
Operator::Properties properties = Operator::kNoProperties);
|
||||||
|
TNode<Object> JSCallRuntime2(Runtime::FunctionId function_id,
|
||||||
|
TNode<Object> arg0, TNode<Object> arg1,
|
||||||
|
TNode<Context> context, FrameState frame_state);
|
||||||
|
|
||||||
JSGraph* jsgraph() const { return jsgraph_; }
|
JSGraph* jsgraph() const { return jsgraph_; }
|
||||||
Isolate* isolate() const { return jsgraph()->isolate(); }
|
Isolate* isolate() const { return jsgraph()->isolate(); }
|
||||||
SimplifiedOperatorBuilder* simplified() const {
|
SimplifiedOperatorBuilder* simplified() override {
|
||||||
return jsgraph()->simplified();
|
return jsgraph()->simplified();
|
||||||
}
|
}
|
||||||
|
JSOperatorBuilder* javascript() const { return jsgraph()->javascript(); }
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
TNode<T> EnterMachineGraph(TNode<U> input, UseInfo use_info) {
|
||||||
|
DCHECK_EQ(use_info.type_check(), TypeCheckKind::kNone);
|
||||||
|
return AddNode<T>(
|
||||||
|
graph()->NewNode(common()->EnterMachineGraph(use_info), input));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
TNode<T> ExitMachineGraph(TNode<U> input,
|
||||||
|
MachineRepresentation output_representation,
|
||||||
|
Type output_type) {
|
||||||
|
return AddNode<T>(graph()->NewNode(
|
||||||
|
common()->ExitMachineGraph(output_representation, output_type), input));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A catch scope represents a single catch handler. The handler can be
|
||||||
|
// custom catch logic within the reduction itself; or a catch handler in the
|
||||||
|
// outside graph into which the reduction will be integrated (in this case
|
||||||
|
// the scope is called 'outermost').
|
||||||
|
class V8_NODISCARD CatchScope {
|
||||||
|
private:
|
||||||
|
// Only used to partially construct the outermost scope.
|
||||||
|
explicit CatchScope(Zone* zone) : if_exception_nodes_(zone) {}
|
||||||
|
|
||||||
|
// For all inner scopes.
|
||||||
|
CatchScope(Zone* zone, JSGraphAssembler* gasm)
|
||||||
|
: gasm_(gasm),
|
||||||
|
parent_(gasm->catch_scope_),
|
||||||
|
has_handler_(true),
|
||||||
|
if_exception_nodes_(zone) {
|
||||||
|
DCHECK_NOT_NULL(gasm_);
|
||||||
|
gasm_->catch_scope_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
~CatchScope() { gasm_->catch_scope_ = parent_; }
|
||||||
|
|
||||||
|
static CatchScope Outermost(Zone* zone) { return CatchScope{zone}; }
|
||||||
|
static CatchScope Inner(Zone* zone, JSGraphAssembler* gasm) {
|
||||||
|
return {zone, gasm};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_handler() const { return has_handler_; }
|
||||||
|
bool is_outermost() const { return parent_ == nullptr; }
|
||||||
|
CatchScope* parent() const { return parent_; }
|
||||||
|
|
||||||
|
// Should only be used to initialize the outermost scope (inner scopes
|
||||||
|
// always have a handler and are passed the gasm pointer at construction).
|
||||||
|
void set_has_handler(bool v) {
|
||||||
|
DCHECK(is_outermost());
|
||||||
|
has_handler_ = v;
|
||||||
|
}
|
||||||
|
void set_gasm(JSGraphAssembler* v) {
|
||||||
|
DCHECK(is_outermost());
|
||||||
|
DCHECK_NOT_NULL(v);
|
||||||
|
gasm_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_exceptional_control_flow() const {
|
||||||
|
return !if_exception_nodes_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterIfExceptionNode(Node* if_exception) {
|
||||||
|
DCHECK(has_handler());
|
||||||
|
if_exception_nodes_.push_back(if_exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeExceptionalPaths(TNode<Object>* exception_out, Effect* effect_out,
|
||||||
|
Control* control_out) {
|
||||||
|
DCHECK(has_handler());
|
||||||
|
DCHECK(has_exceptional_control_flow());
|
||||||
|
|
||||||
|
const int size = static_cast<int>(if_exception_nodes_.size());
|
||||||
|
|
||||||
|
if (size == 1) {
|
||||||
|
// No merge needed.
|
||||||
|
Node* e = if_exception_nodes_.at(0);
|
||||||
|
*exception_out = TNode<Object>::UncheckedCast(e);
|
||||||
|
*effect_out = Effect(e);
|
||||||
|
*control_out = Control(e);
|
||||||
|
} else {
|
||||||
|
DCHECK_GT(size, 1);
|
||||||
|
|
||||||
|
Node* merge = gasm_->graph()->NewNode(gasm_->common()->Merge(size),
|
||||||
|
size, if_exception_nodes_.data());
|
||||||
|
|
||||||
|
// These phis additionally take {merge} as an input. Temporarily add
|
||||||
|
// it to the list.
|
||||||
|
if_exception_nodes_.push_back(merge);
|
||||||
|
const int size_with_merge =
|
||||||
|
static_cast<int>(if_exception_nodes_.size());
|
||||||
|
|
||||||
|
Node* ephi = gasm_->graph()->NewNode(gasm_->common()->EffectPhi(size),
|
||||||
|
size_with_merge,
|
||||||
|
if_exception_nodes_.data());
|
||||||
|
Node* phi = gasm_->graph()->NewNode(
|
||||||
|
gasm_->common()->Phi(MachineRepresentation::kTagged, size),
|
||||||
|
size_with_merge, if_exception_nodes_.data());
|
||||||
|
if_exception_nodes_.pop_back();
|
||||||
|
|
||||||
|
*exception_out = TNode<Object>::UncheckedCast(phi);
|
||||||
|
*effect_out = Effect(ephi);
|
||||||
|
*control_out = Control(merge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
JSGraphAssembler* gasm_ = nullptr;
|
||||||
|
CatchScope* const parent_ = nullptr;
|
||||||
|
bool has_handler_ = false;
|
||||||
|
NodeVector if_exception_nodes_;
|
||||||
|
};
|
||||||
|
|
||||||
|
CatchScope* catch_scope() const { return catch_scope_; }
|
||||||
|
Node* outermost_handler() const { return outermost_handler_; }
|
||||||
|
|
||||||
|
using NodeGenerator0 = std::function<TNode<Object>()>;
|
||||||
|
// TODO(jgruber): Currently, it's the responsibility of the developer to note
|
||||||
|
// which operations may throw and appropriately wrap these in a call to
|
||||||
|
// MayThrow (see e.g. JSCall3 and CallRuntime2). A more methodical approach
|
||||||
|
// would be good.
|
||||||
|
TNode<Object> MayThrow(const NodeGenerator0& body) {
|
||||||
|
TNode<Object> result = body();
|
||||||
|
|
||||||
|
if (catch_scope()->has_handler()) {
|
||||||
|
// The IfException node is later merged into the outer graph.
|
||||||
|
// Note: AddNode is intentionally not called since effect and control
|
||||||
|
// should not be updated.
|
||||||
|
Node* if_exception =
|
||||||
|
graph()->NewNode(common()->IfException(), effect(), control());
|
||||||
|
catch_scope()->RegisterIfExceptionNode(if_exception);
|
||||||
|
|
||||||
|
// Control resumes here.
|
||||||
|
AddNode(graph()->NewNode(common()->IfSuccess(), control()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
using VoidGenerator0 = std::function<void()>;
|
||||||
|
// TODO(jgruber): Currently IfBuilder0 and IfBuilder1 are implemented as
|
||||||
|
// separate classes. If, in the future, we encounter additional use cases that
|
||||||
|
// return more than 1 value, we should merge these back into a single variadic
|
||||||
|
// implementation.
|
||||||
|
class IfBuilder0 final {
|
||||||
|
public:
|
||||||
|
IfBuilder0(JSGraphAssembler* gasm, TNode<Boolean> cond, bool negate_cond)
|
||||||
|
: gasm_(gasm),
|
||||||
|
cond_(cond),
|
||||||
|
negate_cond_(negate_cond),
|
||||||
|
initial_effect_(gasm->effect()),
|
||||||
|
initial_control_(gasm->control()) {}
|
||||||
|
|
||||||
|
IfBuilder0& ExpectTrue() {
|
||||||
|
DCHECK_EQ(hint_, BranchHint::kNone);
|
||||||
|
hint_ = BranchHint::kTrue;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IfBuilder0& ExpectFalse() {
|
||||||
|
DCHECK_EQ(hint_, BranchHint::kNone);
|
||||||
|
hint_ = BranchHint::kFalse;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IfBuilder0& Then(const VoidGenerator0& body) {
|
||||||
|
then_body_ = body;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IfBuilder0& Else(const VoidGenerator0& body) {
|
||||||
|
else_body_ = body;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~IfBuilder0() {
|
||||||
|
// Ensure correct usage: effect/control must not have been modified while
|
||||||
|
// the IfBuilder0 instance is alive.
|
||||||
|
DCHECK_EQ(gasm_->effect(), initial_effect_);
|
||||||
|
DCHECK_EQ(gasm_->control(), initial_control_);
|
||||||
|
|
||||||
|
// Unlike IfBuilder1, this supports an empty then or else body. This is
|
||||||
|
// possible since the merge does not take any value inputs.
|
||||||
|
DCHECK(then_body_ || else_body_);
|
||||||
|
|
||||||
|
if (negate_cond_) std::swap(then_body_, else_body_);
|
||||||
|
|
||||||
|
auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
|
||||||
|
: gasm_->MakeLabel();
|
||||||
|
auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
|
||||||
|
: gasm_->MakeLabel();
|
||||||
|
auto merge = gasm_->MakeLabel();
|
||||||
|
gasm_->Branch(cond_, &if_true, &if_false);
|
||||||
|
|
||||||
|
gasm_->Bind(&if_true);
|
||||||
|
if (then_body_) then_body_();
|
||||||
|
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
|
||||||
|
|
||||||
|
gasm_->Bind(&if_false);
|
||||||
|
if (else_body_) else_body_();
|
||||||
|
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
|
||||||
|
|
||||||
|
gasm_->Bind(&merge);
|
||||||
|
}
|
||||||
|
|
||||||
|
IfBuilder0(const IfBuilder0&) = delete;
|
||||||
|
IfBuilder0& operator=(const IfBuilder0&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JSGraphAssembler* const gasm_;
|
||||||
|
const TNode<Boolean> cond_;
|
||||||
|
const bool negate_cond_;
|
||||||
|
const Effect initial_effect_;
|
||||||
|
const Control initial_control_;
|
||||||
|
BranchHint hint_ = BranchHint::kNone;
|
||||||
|
VoidGenerator0 then_body_;
|
||||||
|
VoidGenerator0 else_body_;
|
||||||
|
};
|
||||||
|
|
||||||
|
IfBuilder0 If(TNode<Boolean> cond) { return {this, cond, false}; }
|
||||||
|
IfBuilder0 IfNot(TNode<Boolean> cond) { return {this, cond, true}; }
|
||||||
|
|
||||||
|
template <typename T, typename Cond>
|
||||||
|
class IfBuilder1 {
|
||||||
|
using If1BodyFunction = std::function<TNode<T>()>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IfBuilder1(JSGraphAssembler* gasm, TNode<Cond> cond, bool negate_cond)
|
||||||
|
: gasm_(gasm), cond_(cond), negate_cond_(negate_cond) {}
|
||||||
|
|
||||||
|
V8_WARN_UNUSED_RESULT IfBuilder1& ExpectTrue() {
|
||||||
|
DCHECK_EQ(hint_, BranchHint::kNone);
|
||||||
|
hint_ = BranchHint::kTrue;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
V8_WARN_UNUSED_RESULT IfBuilder1& ExpectFalse() {
|
||||||
|
DCHECK_EQ(hint_, BranchHint::kNone);
|
||||||
|
hint_ = BranchHint::kFalse;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
V8_WARN_UNUSED_RESULT IfBuilder1& Then(const If1BodyFunction& body) {
|
||||||
|
then_body_ = body;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
V8_WARN_UNUSED_RESULT IfBuilder1& Else(const If1BodyFunction& body) {
|
||||||
|
else_body_ = body;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
V8_WARN_UNUSED_RESULT TNode<T> Value() {
|
||||||
|
DCHECK(then_body_);
|
||||||
|
DCHECK(else_body_);
|
||||||
|
|
||||||
|
if (negate_cond_) std::swap(then_body_, else_body_);
|
||||||
|
|
||||||
|
auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
|
||||||
|
: gasm_->MakeLabel();
|
||||||
|
auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
|
||||||
|
: gasm_->MakeLabel();
|
||||||
|
auto merge = gasm_->MakeLabel(PhiMachineRepresentationOf<T>);
|
||||||
|
if constexpr (std::is_same_v<Cond, Word32T>) {
|
||||||
|
gasm_->MachineBranch(cond_, &if_true, &if_false, hint_);
|
||||||
|
} else {
|
||||||
|
static_assert(std::is_same_v<Cond, Boolean>);
|
||||||
|
if (hint_ != BranchHint::kNone) {
|
||||||
|
gasm_->BranchWithHint(cond_, &if_true, &if_false, hint_);
|
||||||
|
} else {
|
||||||
|
gasm_->Branch(cond_, &if_true, &if_false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gasm_->Bind(&if_true);
|
||||||
|
TNode<T> then_result = then_body_();
|
||||||
|
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge, then_result);
|
||||||
|
|
||||||
|
gasm_->Bind(&if_false);
|
||||||
|
TNode<T> else_result = else_body_();
|
||||||
|
if (gasm_->HasActiveBlock()) {
|
||||||
|
gasm_->Goto(&merge, else_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
gasm_->Bind(&merge);
|
||||||
|
return merge.template PhiAt<T>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr MachineRepresentation kPhiRepresentation =
|
||||||
|
MachineRepresentation::kTagged;
|
||||||
|
|
||||||
|
JSGraphAssembler* const gasm_;
|
||||||
|
const TNode<Cond> cond_;
|
||||||
|
const bool negate_cond_;
|
||||||
|
BranchHint hint_ = BranchHint::kNone;
|
||||||
|
If1BodyFunction then_body_;
|
||||||
|
If1BodyFunction else_body_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
IfBuilder1<T, Boolean> SelectIf(TNode<Boolean> cond) {
|
||||||
|
return {this, cond, false};
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
IfBuilder1<T, Boolean> SelectIfNot(TNode<Boolean> cond) {
|
||||||
|
return {this, cond, true};
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
IfBuilder1<T, Word32T> MachineSelectIf(TNode<Word32T> cond) {
|
||||||
|
return {this, cond, false};
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Operator const* PlainPrimitiveToNumberOperator();
|
Operator const* PlainPrimitiveToNumberOperator();
|
||||||
@ -980,6 +1378,12 @@ class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
|||||||
private:
|
private:
|
||||||
JSGraph* jsgraph_;
|
JSGraph* jsgraph_;
|
||||||
SetOncePointer<Operator const> to_number_operator_;
|
SetOncePointer<Operator const> to_number_operator_;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CatchScope outermost_catch_scope_;
|
||||||
|
Node* outermost_handler_;
|
||||||
|
CatchScope* catch_scope_;
|
||||||
|
friend class CatchScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "src/compiler/heap-refs.h"
|
#include "src/compiler/heap-refs.h"
|
||||||
|
|
||||||
|
#include "src/objects/elements-kind.h"
|
||||||
|
|
||||||
#ifdef ENABLE_SLOW_DCHECKS
|
#ifdef ENABLE_SLOW_DCHECKS
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#endif
|
#endif
|
||||||
@ -1079,6 +1081,11 @@ bool MapRef::CanInlineElementAccess() const {
|
|||||||
kind != BIGINT64_ELEMENTS) {
|
kind != BIGINT64_ELEMENTS) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (v8_flags.turbo_rab_gsab && IsRabGsabTypedArrayElementsKind(kind) &&
|
||||||
|
kind != RAB_GSAB_BIGUINT64_ELEMENTS &&
|
||||||
|
kind != RAB_GSAB_BIGINT64_ELEMENTS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#include "src/base/container-utils.h"
|
||||||
#include "src/base/small-vector.h"
|
#include "src/base/small-vector.h"
|
||||||
#include "src/builtins/builtins-promise.h"
|
#include "src/builtins/builtins-promise.h"
|
||||||
#include "src/builtins/builtins-utils.h"
|
#include "src/builtins/builtins-utils.h"
|
||||||
@ -28,8 +29,11 @@
|
|||||||
#include "src/compiler/simplified-operator.h"
|
#include "src/compiler/simplified-operator.h"
|
||||||
#include "src/compiler/state-values-utils.h"
|
#include "src/compiler/state-values-utils.h"
|
||||||
#include "src/compiler/type-cache.h"
|
#include "src/compiler/type-cache.h"
|
||||||
|
#include "src/compiler/use-info.h"
|
||||||
|
#include "src/flags/flags.h"
|
||||||
#include "src/ic/call-optimization.h"
|
#include "src/ic/call-optimization.h"
|
||||||
#include "src/objects/elements-kind.h"
|
#include "src/objects/elements-kind.h"
|
||||||
|
#include "src/objects/instance-type.h"
|
||||||
#include "src/objects/js-function.h"
|
#include "src/objects/js-function.h"
|
||||||
#include "src/objects/objects-inl.h"
|
#include "src/objects/objects-inl.h"
|
||||||
#include "src/objects/ordered-hash-table.h"
|
#include "src/objects/ordered-hash-table.h"
|
||||||
@ -46,32 +50,26 @@ namespace compiler {
|
|||||||
#define _ [&]()
|
#define _ [&]()
|
||||||
|
|
||||||
class JSCallReducerAssembler : public JSGraphAssembler {
|
class JSCallReducerAssembler : public JSGraphAssembler {
|
||||||
protected:
|
|
||||||
class CatchScope;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr bool kMarkLoopExits = true;
|
static constexpr bool kMarkLoopExits = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JSCallReducerAssembler(JSCallReducer* reducer, Node* node)
|
JSCallReducerAssembler(JSCallReducer* reducer, Node* node,
|
||||||
|
Node* effect = nullptr, Node* control = nullptr)
|
||||||
: JSGraphAssembler(
|
: JSGraphAssembler(
|
||||||
reducer->JSGraphForGraphAssembler(),
|
reducer->JSGraphForGraphAssembler(),
|
||||||
reducer->ZoneForGraphAssembler(),
|
reducer->ZoneForGraphAssembler(), BranchSemantics::kJS,
|
||||||
[reducer](Node* n) { reducer->RevisitForGraphAssembler(n); },
|
[reducer](Node* n) { reducer->RevisitForGraphAssembler(n); },
|
||||||
kMarkLoopExits),
|
kMarkLoopExits),
|
||||||
dependencies_(reducer->dependencies()),
|
dependencies_(reducer->dependencies()),
|
||||||
node_(node),
|
node_(node) {
|
||||||
outermost_catch_scope_(
|
InitializeEffectControl(
|
||||||
CatchScope::Outermost(reducer->ZoneForGraphAssembler())),
|
effect ? effect : NodeProperties::GetEffectInput(node),
|
||||||
catch_scope_(&outermost_catch_scope_) {
|
control ? control : NodeProperties::GetControlInput(node));
|
||||||
InitializeEffectControl(NodeProperties::GetEffectInput(node),
|
|
||||||
NodeProperties::GetControlInput(node));
|
|
||||||
|
|
||||||
// Finish initializing the outermost catch scope.
|
// Finish initializing the outermost catch scope.
|
||||||
bool has_handler =
|
bool has_handler =
|
||||||
NodeProperties::IsExceptionalCall(node, &outermost_handler_);
|
NodeProperties::IsExceptionalCall(node, &outermost_handler_);
|
||||||
outermost_catch_scope_.set_has_handler(has_handler);
|
outermost_catch_scope_.set_has_handler(has_handler);
|
||||||
outermost_catch_scope_.set_gasm(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<Object> ReduceJSCallWithArrayLikeOrSpreadOfEmpty(
|
TNode<Object> ReduceJSCallWithArrayLikeOrSpreadOfEmpty(
|
||||||
@ -94,164 +92,8 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
|
|
||||||
TNode<Object> ReceiverInput() const { return ReceiverInputAs<Object>(); }
|
TNode<Object> ReceiverInput() const { return ReceiverInputAs<Object>(); }
|
||||||
|
|
||||||
CatchScope* catch_scope() const { return catch_scope_; }
|
|
||||||
Node* outermost_handler() const { return outermost_handler_; }
|
|
||||||
|
|
||||||
Node* node_ptr() const { return node_; }
|
Node* node_ptr() const { return node_; }
|
||||||
|
|
||||||
protected:
|
|
||||||
using NodeGenerator0 = std::function<TNode<Object>()>;
|
|
||||||
using VoidGenerator0 = std::function<void()>;
|
|
||||||
|
|
||||||
// TODO(jgruber): Currently IfBuilder0 and IfBuilder1 are implemented as
|
|
||||||
// separate classes. If, in the future, we encounter additional use cases that
|
|
||||||
// return more than 1 value, we should merge these back into a single variadic
|
|
||||||
// implementation.
|
|
||||||
class IfBuilder0 final {
|
|
||||||
public:
|
|
||||||
IfBuilder0(JSGraphAssembler* gasm, TNode<Boolean> cond, bool negate_cond)
|
|
||||||
: gasm_(gasm),
|
|
||||||
cond_(cond),
|
|
||||||
negate_cond_(negate_cond),
|
|
||||||
initial_effect_(gasm->effect()),
|
|
||||||
initial_control_(gasm->control()) {}
|
|
||||||
|
|
||||||
IfBuilder0& ExpectTrue() {
|
|
||||||
DCHECK_EQ(hint_, BranchHint::kNone);
|
|
||||||
hint_ = BranchHint::kTrue;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
IfBuilder0& ExpectFalse() {
|
|
||||||
DCHECK_EQ(hint_, BranchHint::kNone);
|
|
||||||
hint_ = BranchHint::kFalse;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
IfBuilder0& Then(const VoidGenerator0& body) {
|
|
||||||
then_body_ = body;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
IfBuilder0& Else(const VoidGenerator0& body) {
|
|
||||||
else_body_ = body;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~IfBuilder0() {
|
|
||||||
// Ensure correct usage: effect/control must not have been modified while
|
|
||||||
// the IfBuilder0 instance is alive.
|
|
||||||
DCHECK_EQ(gasm_->effect(), initial_effect_);
|
|
||||||
DCHECK_EQ(gasm_->control(), initial_control_);
|
|
||||||
|
|
||||||
// Unlike IfBuilder1, this supports an empty then or else body. This is
|
|
||||||
// possible since the merge does not take any value inputs.
|
|
||||||
DCHECK(then_body_ || else_body_);
|
|
||||||
|
|
||||||
if (negate_cond_) std::swap(then_body_, else_body_);
|
|
||||||
|
|
||||||
auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
|
|
||||||
: gasm_->MakeLabel();
|
|
||||||
auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
|
|
||||||
: gasm_->MakeLabel();
|
|
||||||
auto merge = gasm_->MakeLabel();
|
|
||||||
gasm_->Branch(cond_, &if_true, &if_false);
|
|
||||||
|
|
||||||
gasm_->Bind(&if_true);
|
|
||||||
if (then_body_) then_body_();
|
|
||||||
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
|
|
||||||
|
|
||||||
gasm_->Bind(&if_false);
|
|
||||||
if (else_body_) else_body_();
|
|
||||||
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge);
|
|
||||||
|
|
||||||
gasm_->Bind(&merge);
|
|
||||||
}
|
|
||||||
|
|
||||||
IfBuilder0(const IfBuilder0&) = delete;
|
|
||||||
IfBuilder0& operator=(const IfBuilder0&) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSGraphAssembler* const gasm_;
|
|
||||||
const TNode<Boolean> cond_;
|
|
||||||
const bool negate_cond_;
|
|
||||||
const Effect initial_effect_;
|
|
||||||
const Control initial_control_;
|
|
||||||
BranchHint hint_ = BranchHint::kNone;
|
|
||||||
VoidGenerator0 then_body_;
|
|
||||||
VoidGenerator0 else_body_;
|
|
||||||
};
|
|
||||||
|
|
||||||
IfBuilder0 If(TNode<Boolean> cond) { return {this, cond, false}; }
|
|
||||||
IfBuilder0 IfNot(TNode<Boolean> cond) { return {this, cond, true}; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class IfBuilder1 {
|
|
||||||
using If1BodyFunction = std::function<TNode<T>()>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
IfBuilder1(JSGraphAssembler* gasm, TNode<Boolean> cond)
|
|
||||||
: gasm_(gasm), cond_(cond) {}
|
|
||||||
|
|
||||||
V8_WARN_UNUSED_RESULT IfBuilder1& ExpectTrue() {
|
|
||||||
DCHECK_EQ(hint_, BranchHint::kNone);
|
|
||||||
hint_ = BranchHint::kTrue;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
V8_WARN_UNUSED_RESULT IfBuilder1& ExpectFalse() {
|
|
||||||
DCHECK_EQ(hint_, BranchHint::kNone);
|
|
||||||
hint_ = BranchHint::kFalse;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
V8_WARN_UNUSED_RESULT IfBuilder1& Then(const If1BodyFunction& body) {
|
|
||||||
then_body_ = body;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
V8_WARN_UNUSED_RESULT IfBuilder1& Else(const If1BodyFunction& body) {
|
|
||||||
else_body_ = body;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
V8_WARN_UNUSED_RESULT TNode<T> Value() {
|
|
||||||
DCHECK(then_body_);
|
|
||||||
DCHECK(else_body_);
|
|
||||||
auto if_true = (hint_ == BranchHint::kFalse) ? gasm_->MakeDeferredLabel()
|
|
||||||
: gasm_->MakeLabel();
|
|
||||||
auto if_false = (hint_ == BranchHint::kTrue) ? gasm_->MakeDeferredLabel()
|
|
||||||
: gasm_->MakeLabel();
|
|
||||||
auto merge = gasm_->MakeLabel(kPhiRepresentation);
|
|
||||||
gasm_->Branch(cond_, &if_true, &if_false);
|
|
||||||
|
|
||||||
gasm_->Bind(&if_true);
|
|
||||||
TNode<T> then_result = then_body_();
|
|
||||||
if (gasm_->HasActiveBlock()) gasm_->Goto(&merge, then_result);
|
|
||||||
|
|
||||||
gasm_->Bind(&if_false);
|
|
||||||
TNode<T> else_result = else_body_();
|
|
||||||
if (gasm_->HasActiveBlock()) {
|
|
||||||
gasm_->Goto(&merge, else_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
gasm_->Bind(&merge);
|
|
||||||
return merge.PhiAt<T>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr MachineRepresentation kPhiRepresentation =
|
|
||||||
MachineRepresentation::kTagged;
|
|
||||||
|
|
||||||
JSGraphAssembler* const gasm_;
|
|
||||||
const TNode<Boolean> cond_;
|
|
||||||
BranchHint hint_ = BranchHint::kNone;
|
|
||||||
If1BodyFunction then_body_;
|
|
||||||
If1BodyFunction else_body_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
IfBuilder1<T> SelectIf(TNode<Boolean> cond) {
|
|
||||||
return {this, cond};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplified operators.
|
// Simplified operators.
|
||||||
TNode<Number> SpeculativeToNumber(
|
TNode<Number> SpeculativeToNumber(
|
||||||
TNode<Object> value,
|
TNode<Object> value,
|
||||||
@ -277,9 +119,6 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
TNode<Object> arg0, TNode<Object> arg1,
|
TNode<Object> arg0, TNode<Object> arg1,
|
||||||
TNode<Object> arg2, TNode<Object> arg3,
|
TNode<Object> arg2, TNode<Object> arg3,
|
||||||
FrameState frame_state);
|
FrameState frame_state);
|
||||||
TNode<Object> JSCallRuntime2(Runtime::FunctionId function_id,
|
|
||||||
TNode<Object> arg0, TNode<Object> arg1,
|
|
||||||
FrameState frame_state);
|
|
||||||
|
|
||||||
// Emplace a copy of the call node into the graph at current effect/control.
|
// Emplace a copy of the call node into the graph at current effect/control.
|
||||||
TNode<Object> CopyNode();
|
TNode<Object> CopyNode();
|
||||||
@ -297,6 +136,20 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
|
|
||||||
TNode<Number> LoadMapElementsKind(TNode<Map> map);
|
TNode<Number> LoadMapElementsKind(TNode<Map> map);
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
TNode<T> EnterMachineGraph(TNode<U> input, UseInfo use_info) {
|
||||||
|
return AddNode<T>(
|
||||||
|
graph()->NewNode(common()->EnterMachineGraph(use_info), input));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
TNode<T> ExitMachineGraph(TNode<U> input,
|
||||||
|
MachineRepresentation output_representation,
|
||||||
|
Type output_type) {
|
||||||
|
return AddNode<T>(graph()->NewNode(
|
||||||
|
common()->ExitMachineGraph(output_representation, output_type), input));
|
||||||
|
}
|
||||||
|
|
||||||
void MaybeInsertMapChecks(MapInference* inference,
|
void MaybeInsertMapChecks(MapInference* inference,
|
||||||
bool has_stability_dependency) {
|
bool has_stability_dependency) {
|
||||||
// TODO(jgruber): Implement MapInference::InsertMapChecks in graph
|
// TODO(jgruber): Implement MapInference::InsertMapChecks in graph
|
||||||
@ -308,124 +161,6 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jgruber): Currently, it's the responsibility of the developer to note
|
|
||||||
// which operations may throw and appropriately wrap these in a call to
|
|
||||||
// MayThrow (see e.g. JSCall3 and CallRuntime2). A more methodical approach
|
|
||||||
// would be good.
|
|
||||||
TNode<Object> MayThrow(const NodeGenerator0& body) {
|
|
||||||
TNode<Object> result = body();
|
|
||||||
|
|
||||||
if (catch_scope()->has_handler()) {
|
|
||||||
// The IfException node is later merged into the outer graph.
|
|
||||||
// Note: AddNode is intentionally not called since effect and control
|
|
||||||
// should not be updated.
|
|
||||||
Node* if_exception =
|
|
||||||
graph()->NewNode(common()->IfException(), effect(), control());
|
|
||||||
catch_scope()->RegisterIfExceptionNode(if_exception);
|
|
||||||
|
|
||||||
// Control resumes here.
|
|
||||||
AddNode(graph()->NewNode(common()->IfSuccess(), control()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A catch scope represents a single catch handler. The handler can be
|
|
||||||
// custom catch logic within the reduction itself; or a catch handler in the
|
|
||||||
// outside graph into which the reduction will be integrated (in this case
|
|
||||||
// the scope is called 'outermost').
|
|
||||||
class V8_NODISCARD CatchScope {
|
|
||||||
private:
|
|
||||||
// Only used to partially construct the outermost scope.
|
|
||||||
explicit CatchScope(Zone* zone) : if_exception_nodes_(zone) {}
|
|
||||||
|
|
||||||
// For all inner scopes.
|
|
||||||
CatchScope(Zone* zone, JSCallReducerAssembler* gasm)
|
|
||||||
: gasm_(gasm),
|
|
||||||
parent_(gasm->catch_scope_),
|
|
||||||
has_handler_(true),
|
|
||||||
if_exception_nodes_(zone) {
|
|
||||||
gasm_->catch_scope_ = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
~CatchScope() { gasm_->catch_scope_ = parent_; }
|
|
||||||
|
|
||||||
static CatchScope Outermost(Zone* zone) { return CatchScope{zone}; }
|
|
||||||
static CatchScope Inner(Zone* zone, JSCallReducerAssembler* gasm) {
|
|
||||||
return {zone, gasm};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_handler() const { return has_handler_; }
|
|
||||||
bool is_outermost() const { return parent_ == nullptr; }
|
|
||||||
CatchScope* parent() const { return parent_; }
|
|
||||||
|
|
||||||
// Should only be used to initialize the outermost scope (inner scopes
|
|
||||||
// always have a handler and are passed the gasm pointer at construction).
|
|
||||||
void set_has_handler(bool v) {
|
|
||||||
DCHECK(is_outermost());
|
|
||||||
has_handler_ = v;
|
|
||||||
}
|
|
||||||
void set_gasm(JSCallReducerAssembler* v) {
|
|
||||||
DCHECK(is_outermost());
|
|
||||||
gasm_ = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_exceptional_control_flow() const {
|
|
||||||
return !if_exception_nodes_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegisterIfExceptionNode(Node* if_exception) {
|
|
||||||
DCHECK(has_handler());
|
|
||||||
if_exception_nodes_.push_back(if_exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MergeExceptionalPaths(TNode<Object>* exception_out, Effect* effect_out,
|
|
||||||
Control* control_out) {
|
|
||||||
DCHECK(has_handler());
|
|
||||||
DCHECK(has_exceptional_control_flow());
|
|
||||||
|
|
||||||
const int size = static_cast<int>(if_exception_nodes_.size());
|
|
||||||
|
|
||||||
if (size == 1) {
|
|
||||||
// No merge needed.
|
|
||||||
Node* e = if_exception_nodes_.at(0);
|
|
||||||
*exception_out = TNode<Object>::UncheckedCast(e);
|
|
||||||
*effect_out = Effect(e);
|
|
||||||
*control_out = Control(e);
|
|
||||||
} else {
|
|
||||||
DCHECK_GT(size, 1);
|
|
||||||
|
|
||||||
Node* merge = gasm_->graph()->NewNode(gasm_->common()->Merge(size),
|
|
||||||
size, if_exception_nodes_.data());
|
|
||||||
|
|
||||||
// These phis additionally take {merge} as an input. Temporarily add
|
|
||||||
// it to the list.
|
|
||||||
if_exception_nodes_.push_back(merge);
|
|
||||||
const int size_with_merge =
|
|
||||||
static_cast<int>(if_exception_nodes_.size());
|
|
||||||
|
|
||||||
Node* ephi = gasm_->graph()->NewNode(gasm_->common()->EffectPhi(size),
|
|
||||||
size_with_merge,
|
|
||||||
if_exception_nodes_.data());
|
|
||||||
Node* phi = gasm_->graph()->NewNode(
|
|
||||||
gasm_->common()->Phi(MachineRepresentation::kTagged, size),
|
|
||||||
size_with_merge, if_exception_nodes_.data());
|
|
||||||
if_exception_nodes_.pop_back();
|
|
||||||
|
|
||||||
*exception_out = TNode<Object>::UncheckedCast(phi);
|
|
||||||
*effect_out = Effect(ephi);
|
|
||||||
*control_out = Control(merge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSCallReducerAssembler* gasm_ = nullptr;
|
|
||||||
CatchScope* const parent_ = nullptr;
|
|
||||||
bool has_handler_ = false;
|
|
||||||
NodeVector if_exception_nodes_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TryCatchBuilder0 {
|
class TryCatchBuilder0 {
|
||||||
public:
|
public:
|
||||||
using TryFunction = VoidGenerator0;
|
using TryFunction = VoidGenerator0;
|
||||||
@ -616,7 +351,7 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
JSCallRuntime2(Runtime::kThrowTypeError,
|
JSCallRuntime2(Runtime::kThrowTypeError,
|
||||||
NumberConstant(static_cast<double>(
|
NumberConstant(static_cast<double>(
|
||||||
MessageTemplate::kCalledNonCallable)),
|
MessageTemplate::kCalledNonCallable)),
|
||||||
maybe_callable, frame_state);
|
maybe_callable, ContextInput(), frame_state);
|
||||||
Unreachable(); // The runtime call throws unconditionally.
|
Unreachable(); // The runtime call throws unconditionally.
|
||||||
})
|
})
|
||||||
.ExpectTrue();
|
.ExpectTrue();
|
||||||
@ -662,17 +397,11 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
|||||||
return FrameState(NodeProperties::GetFrameStateInput(node_));
|
return FrameState(NodeProperties::GetFrameStateInput(node_));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSOperatorBuilder* javascript() const { return jsgraph()->javascript(); }
|
|
||||||
|
|
||||||
CompilationDependencies* dependencies() const { return dependencies_; }
|
CompilationDependencies* dependencies() const { return dependencies_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CompilationDependencies* const dependencies_;
|
CompilationDependencies* const dependencies_;
|
||||||
Node* const node_;
|
Node* const node_;
|
||||||
CatchScope outermost_catch_scope_;
|
|
||||||
Node* outermost_handler_;
|
|
||||||
CatchScope* catch_scope_;
|
|
||||||
friend class CatchScope;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ArrayReduceDirection { kLeft, kRight };
|
enum class ArrayReduceDirection { kLeft, kRight };
|
||||||
@ -1113,16 +842,6 @@ TNode<Object> JSCallReducerAssembler::JSCall4(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<Object> JSCallReducerAssembler::JSCallRuntime2(
|
|
||||||
Runtime::FunctionId function_id, TNode<Object> arg0, TNode<Object> arg1,
|
|
||||||
FrameState frame_state) {
|
|
||||||
return MayThrow(_ {
|
|
||||||
return AddNode<Object>(
|
|
||||||
graph()->NewNode(javascript()->CallRuntime(function_id, 2), arg0, arg1,
|
|
||||||
ContextInput(), frame_state, effect(), control()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<Object> JSCallReducerAssembler::CopyNode() {
|
TNode<Object> JSCallReducerAssembler::CopyNode() {
|
||||||
return MayThrow(_ {
|
return MayThrow(_ {
|
||||||
Node* copy = graph()->CloneNode(node_ptr());
|
Node* copy = graph()->CloneNode(node_ptr());
|
||||||
@ -1138,6 +857,7 @@ TNode<JSArray> JSCallReducerAssembler::CreateArrayNoThrow(
|
|||||||
graph()->NewNode(javascript()->CreateArray(1, base::nullopt), ctor, ctor,
|
graph()->NewNode(javascript()->CreateArray(1, base::nullopt), ctor, ctor,
|
||||||
size, ContextInput(), frame_state, effect(), control()));
|
size, ContextInput(), frame_state, effect(), control()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<JSArray> JSCallReducerAssembler::AllocateEmptyJSArray(
|
TNode<JSArray> JSCallReducerAssembler::AllocateEmptyJSArray(
|
||||||
ElementsKind kind, const NativeContextRef& native_context) {
|
ElementsKind kind, const NativeContextRef& native_context) {
|
||||||
// TODO(jgruber): Port AllocationBuilder to JSGraphAssembler.
|
// TODO(jgruber): Port AllocationBuilder to JSGraphAssembler.
|
||||||
@ -2578,6 +2298,26 @@ TNode<Object> PromiseBuiltinReducerAssembler::ReducePromiseConstructor(
|
|||||||
|
|
||||||
#undef _
|
#undef _
|
||||||
|
|
||||||
|
std::pair<Node*, Node*> JSCallReducer::ReleaseEffectAndControlFromAssembler(
|
||||||
|
JSCallReducerAssembler* gasm) {
|
||||||
|
auto catch_scope = gasm->catch_scope();
|
||||||
|
DCHECK(catch_scope->is_outermost());
|
||||||
|
|
||||||
|
if (catch_scope->has_handler() &&
|
||||||
|
catch_scope->has_exceptional_control_flow()) {
|
||||||
|
TNode<Object> handler_exception;
|
||||||
|
Effect handler_effect{nullptr};
|
||||||
|
Control handler_control{nullptr};
|
||||||
|
gasm->catch_scope()->MergeExceptionalPaths(
|
||||||
|
&handler_exception, &handler_effect, &handler_control);
|
||||||
|
|
||||||
|
ReplaceWithValue(gasm->outermost_handler(), handler_exception,
|
||||||
|
handler_effect, handler_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {gasm->effect(), gasm->control()};
|
||||||
|
}
|
||||||
|
|
||||||
Reduction JSCallReducer::ReplaceWithSubgraph(JSCallReducerAssembler* gasm,
|
Reduction JSCallReducer::ReplaceWithSubgraph(JSCallReducerAssembler* gasm,
|
||||||
Node* subgraph) {
|
Node* subgraph) {
|
||||||
// TODO(jgruber): Consider a less fiddly way of integrating the new subgraph
|
// TODO(jgruber): Consider a less fiddly way of integrating the new subgraph
|
||||||
@ -4898,13 +4638,11 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
|
|||||||
case Builtin::kArrayBufferIsView:
|
case Builtin::kArrayBufferIsView:
|
||||||
return ReduceArrayBufferIsView(node);
|
return ReduceArrayBufferIsView(node);
|
||||||
case Builtin::kDataViewPrototypeGetByteLength:
|
case Builtin::kDataViewPrototypeGetByteLength:
|
||||||
return ReduceArrayBufferViewAccessor(
|
return ReduceArrayBufferViewByteLengthAccessor(node, JS_DATA_VIEW_TYPE);
|
||||||
node, JS_DATA_VIEW_TYPE,
|
|
||||||
AccessBuilder::ForJSArrayBufferViewByteLength());
|
|
||||||
case Builtin::kDataViewPrototypeGetByteOffset:
|
case Builtin::kDataViewPrototypeGetByteOffset:
|
||||||
return ReduceArrayBufferViewAccessor(
|
return ReduceArrayBufferViewAccessor(
|
||||||
node, JS_DATA_VIEW_TYPE,
|
node, JS_DATA_VIEW_TYPE,
|
||||||
AccessBuilder::ForJSArrayBufferViewByteOffset());
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), builtin);
|
||||||
case Builtin::kDataViewPrototypeGetUint8:
|
case Builtin::kDataViewPrototypeGetUint8:
|
||||||
return ReduceDataViewAccess(node, DataViewAccess::kGet,
|
return ReduceDataViewAccess(node, DataViewAccess::kGet,
|
||||||
ExternalArrayType::kExternalUint8Array);
|
ExternalArrayType::kExternalUint8Array);
|
||||||
@ -4954,16 +4692,13 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
|
|||||||
return ReduceDataViewAccess(node, DataViewAccess::kSet,
|
return ReduceDataViewAccess(node, DataViewAccess::kSet,
|
||||||
ExternalArrayType::kExternalFloat64Array);
|
ExternalArrayType::kExternalFloat64Array);
|
||||||
case Builtin::kTypedArrayPrototypeByteLength:
|
case Builtin::kTypedArrayPrototypeByteLength:
|
||||||
return ReduceArrayBufferViewAccessor(
|
return ReduceArrayBufferViewByteLengthAccessor(node, JS_TYPED_ARRAY_TYPE);
|
||||||
node, JS_TYPED_ARRAY_TYPE,
|
|
||||||
AccessBuilder::ForJSArrayBufferViewByteLength());
|
|
||||||
case Builtin::kTypedArrayPrototypeByteOffset:
|
case Builtin::kTypedArrayPrototypeByteOffset:
|
||||||
return ReduceArrayBufferViewAccessor(
|
return ReduceArrayBufferViewAccessor(
|
||||||
node, JS_TYPED_ARRAY_TYPE,
|
node, JS_TYPED_ARRAY_TYPE,
|
||||||
AccessBuilder::ForJSArrayBufferViewByteOffset());
|
AccessBuilder::ForJSArrayBufferViewByteOffset(), builtin);
|
||||||
case Builtin::kTypedArrayPrototypeLength:
|
case Builtin::kTypedArrayPrototypeLength:
|
||||||
return ReduceArrayBufferViewAccessor(
|
return ReduceTypedArrayPrototypeLength(node);
|
||||||
node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength());
|
|
||||||
case Builtin::kTypedArrayPrototypeToStringTag:
|
case Builtin::kTypedArrayPrototypeToStringTag:
|
||||||
return ReduceTypedArrayPrototypeToStringTag(node);
|
return ReduceTypedArrayPrototypeToStringTag(node);
|
||||||
case Builtin::kMathAbs:
|
case Builtin::kMathAbs:
|
||||||
@ -7497,6 +7232,113 @@ Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node) {
|
|||||||
return Replace(value);
|
return Replace(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reduction JSCallReducer::ReduceArrayBufferViewByteLengthAccessor(
|
||||||
|
Node* node, InstanceType instance_type) {
|
||||||
|
DCHECK(instance_type == JS_TYPED_ARRAY_TYPE ||
|
||||||
|
instance_type == JS_DATA_VIEW_TYPE);
|
||||||
|
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
||||||
|
Effect effect{NodeProperties::GetEffectInput(node)};
|
||||||
|
Control control{NodeProperties::GetControlInput(node)};
|
||||||
|
|
||||||
|
MapInference inference(broker(), receiver, effect);
|
||||||
|
if (!inference.HaveMaps() ||
|
||||||
|
!inference.AllOfInstanceTypesAre(instance_type)) {
|
||||||
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<ElementsKind> elements_kinds;
|
||||||
|
bool maybe_rab_gsab = false;
|
||||||
|
if (instance_type == JS_DATA_VIEW_TYPE) {
|
||||||
|
maybe_rab_gsab = true;
|
||||||
|
} else {
|
||||||
|
for (const auto& map : inference.GetMaps()) {
|
||||||
|
ElementsKind kind = map.elements_kind();
|
||||||
|
elements_kinds.insert(kind);
|
||||||
|
if (IsRabGsabTypedArrayElementsKind(kind)) maybe_rab_gsab = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v8_flags.harmony_rab_gsab || !maybe_rab_gsab) {
|
||||||
|
// We do not perform any change depending on this inference.
|
||||||
|
Reduction unused_reduction = inference.NoChange();
|
||||||
|
USE(unused_reduction);
|
||||||
|
// Call default implementation for non-rab/gsab TAs.
|
||||||
|
return ReduceArrayBufferViewAccessor(
|
||||||
|
node, JS_TYPED_ARRAY_TYPE,
|
||||||
|
AccessBuilder::ForJSArrayBufferViewByteLength(),
|
||||||
|
Builtin::kTypedArrayPrototypeByteLength);
|
||||||
|
} else if (!v8_flags.turbo_rab_gsab) {
|
||||||
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
|
||||||
|
control,
|
||||||
|
CallParametersOf(node->op()).feedback());
|
||||||
|
|
||||||
|
const bool depended_on_detaching_protector =
|
||||||
|
dependencies()->DependOnArrayBufferDetachingProtector();
|
||||||
|
if (!depended_on_detaching_protector && instance_type == JS_DATA_VIEW_TYPE) {
|
||||||
|
// DataView prototype accessors throw on detached ArrayBuffers instead of
|
||||||
|
// return 0, so skip the optimization.
|
||||||
|
//
|
||||||
|
// TODO(turbofan): Ideally we would bail out if the buffer is actually
|
||||||
|
// detached.
|
||||||
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSCallReducerAssembler a(this, node);
|
||||||
|
TNode<JSTypedArray> typed_array =
|
||||||
|
TNode<JSTypedArray>::UncheckedCast(receiver);
|
||||||
|
TNode<Number> length = a.ArrayBufferViewByteLength(
|
||||||
|
typed_array, std::move(elements_kinds), a.ContextInput());
|
||||||
|
|
||||||
|
return ReplaceWithSubgraph(&a, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reduction JSCallReducer::ReduceTypedArrayPrototypeLength(Node* node) {
|
||||||
|
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
||||||
|
Effect effect{NodeProperties::GetEffectInput(node)};
|
||||||
|
Control control{NodeProperties::GetControlInput(node)};
|
||||||
|
|
||||||
|
MapInference inference(broker(), receiver, effect);
|
||||||
|
if (!inference.HaveMaps() ||
|
||||||
|
!inference.AllOfInstanceTypesAre(JS_TYPED_ARRAY_TYPE)) {
|
||||||
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<ElementsKind> elements_kinds;
|
||||||
|
bool maybe_rab_gsab = false;
|
||||||
|
for (const auto& map : inference.GetMaps()) {
|
||||||
|
ElementsKind kind = map.elements_kind();
|
||||||
|
elements_kinds.insert(kind);
|
||||||
|
if (IsRabGsabTypedArrayElementsKind(kind)) maybe_rab_gsab = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v8_flags.harmony_rab_gsab || !maybe_rab_gsab) {
|
||||||
|
// We do not perform any change depending on this inference.
|
||||||
|
Reduction unused_reduction = inference.NoChange();
|
||||||
|
USE(unused_reduction);
|
||||||
|
// Call default implementation for non-rab/gsab TAs.
|
||||||
|
return ReduceArrayBufferViewAccessor(node, JS_TYPED_ARRAY_TYPE,
|
||||||
|
AccessBuilder::ForJSTypedArrayLength(),
|
||||||
|
Builtin::kTypedArrayPrototypeLength);
|
||||||
|
} else if (!v8_flags.turbo_rab_gsab) {
|
||||||
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
|
||||||
|
control,
|
||||||
|
CallParametersOf(node->op()).feedback());
|
||||||
|
|
||||||
|
JSCallReducerAssembler a(this, node);
|
||||||
|
TNode<JSTypedArray> typed_array =
|
||||||
|
TNode<JSTypedArray>::UncheckedCast(receiver);
|
||||||
|
TNode<Number> length = a.TypedArrayLength(
|
||||||
|
typed_array, std::move(elements_kinds), a.ContextInput());
|
||||||
|
|
||||||
|
return ReplaceWithSubgraph(&a, length);
|
||||||
|
}
|
||||||
|
|
||||||
// ES #sec-number.isfinite
|
// ES #sec-number.isfinite
|
||||||
Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) {
|
Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) {
|
||||||
JSCallNode n(node);
|
JSCallNode n(node);
|
||||||
@ -8005,7 +7847,8 @@ Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
|
Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
|
||||||
Node* node, InstanceType instance_type, FieldAccess const& access) {
|
Node* node, InstanceType instance_type, FieldAccess const& access,
|
||||||
|
Builtin builtin) {
|
||||||
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
||||||
Effect effect{NodeProperties::GetEffectInput(node)};
|
Effect effect{NodeProperties::GetEffectInput(node)};
|
||||||
Control control{NodeProperties::GetControlInput(node)};
|
Control control{NodeProperties::GetControlInput(node)};
|
||||||
@ -8016,13 +7859,11 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
|
|||||||
return inference.NoChange();
|
return inference.NoChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(v8:11111): We skip this optimization for RAB/GSAB for now. Should
|
DCHECK_IMPLIES((builtin == Builtin::kTypedArrayPrototypeLength ||
|
||||||
// have some optimization here eventually.
|
builtin == Builtin::kTypedArrayPrototypeByteLength),
|
||||||
for (const auto& map : inference.GetMaps()) {
|
base::none_of(inference.GetMaps(), [](const auto& map) {
|
||||||
if (IsRabGsabTypedArrayElementsKind(map.elements_kind())) {
|
return IsRabGsabTypedArrayElementsKind(map.elements_kind());
|
||||||
return inference.NoChange();
|
}));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(inference.RelyOnMapsViaStability(dependencies()));
|
CHECK(inference.RelyOnMapsViaStability(dependencies()));
|
||||||
|
|
||||||
@ -8128,11 +7969,20 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access,
|
|||||||
offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
|
offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
|
||||||
offset, byte_length, effect, control);
|
offset, byte_length, effect, control);
|
||||||
} else {
|
} else {
|
||||||
|
Node* byte_length;
|
||||||
|
if (!v8_flags.harmony_rab_gsab) {
|
||||||
// We only deal with DataViews here that have Smi [[ByteLength]]s.
|
// We only deal with DataViews here that have Smi [[ByteLength]]s.
|
||||||
Node* byte_length = effect =
|
byte_length = effect =
|
||||||
graph()->NewNode(simplified()->LoadField(
|
graph()->NewNode(simplified()->LoadField(
|
||||||
AccessBuilder::ForJSArrayBufferViewByteLength()),
|
AccessBuilder::ForJSArrayBufferViewByteLength()),
|
||||||
receiver, effect, control);
|
receiver, effect, control);
|
||||||
|
} else {
|
||||||
|
JSCallReducerAssembler a(this, node);
|
||||||
|
byte_length = a.ArrayBufferViewByteLength(
|
||||||
|
TNode<JSArrayBufferView>::UncheckedCast(receiver), {},
|
||||||
|
a.ContextInput());
|
||||||
|
std::tie(effect, control) = ReleaseEffectAndControlFromAssembler(&a);
|
||||||
|
}
|
||||||
|
|
||||||
if (element_size > 1) {
|
if (element_size > 1) {
|
||||||
// For non-byte accesses we also need to check that the {offset}
|
// For non-byte accesses we also need to check that the {offset}
|
||||||
|
@ -181,6 +181,9 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
|||||||
Reduction ReduceTypedArrayConstructor(Node* node,
|
Reduction ReduceTypedArrayConstructor(Node* node,
|
||||||
const SharedFunctionInfoRef& shared);
|
const SharedFunctionInfoRef& shared);
|
||||||
Reduction ReduceTypedArrayPrototypeToStringTag(Node* node);
|
Reduction ReduceTypedArrayPrototypeToStringTag(Node* node);
|
||||||
|
Reduction ReduceArrayBufferViewByteLengthAccessor(Node* node,
|
||||||
|
InstanceType instance_type);
|
||||||
|
Reduction ReduceTypedArrayPrototypeLength(Node* node);
|
||||||
|
|
||||||
Reduction ReduceForInsufficientFeedback(Node* node, DeoptimizeReason reason);
|
Reduction ReduceForInsufficientFeedback(Node* node, DeoptimizeReason reason);
|
||||||
|
|
||||||
@ -216,7 +219,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
|||||||
Reduction ReduceArrayBufferIsView(Node* node);
|
Reduction ReduceArrayBufferIsView(Node* node);
|
||||||
Reduction ReduceArrayBufferViewAccessor(Node* node,
|
Reduction ReduceArrayBufferViewAccessor(Node* node,
|
||||||
InstanceType instance_type,
|
InstanceType instance_type,
|
||||||
FieldAccess const& access);
|
FieldAccess const& access,
|
||||||
|
Builtin builtin);
|
||||||
|
|
||||||
enum class DataViewAccess { kGet, kSet };
|
enum class DataViewAccess { kGet, kSet };
|
||||||
Reduction ReduceDataViewAccess(Node* node, DataViewAccess access,
|
Reduction ReduceDataViewAccess(Node* node, DataViewAccess access,
|
||||||
@ -234,6 +238,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
|||||||
|
|
||||||
// The pendant to ReplaceWithValue when using GraphAssembler-based reductions.
|
// The pendant to ReplaceWithValue when using GraphAssembler-based reductions.
|
||||||
Reduction ReplaceWithSubgraph(JSCallReducerAssembler* gasm, Node* subgraph);
|
Reduction ReplaceWithSubgraph(JSCallReducerAssembler* gasm, Node* subgraph);
|
||||||
|
std::pair<Node*, Node*> ReleaseEffectAndControlFromAssembler(
|
||||||
|
JSCallReducerAssembler* gasm);
|
||||||
|
|
||||||
// Helper to verify promise receiver maps are as expected.
|
// Helper to verify promise receiver maps are as expected.
|
||||||
// On bailout from a reduction, be sure to return inference.NoChange().
|
// On bailout from a reduction, be sure to return inference.NoChange().
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "src/compiler/allocation-builder.h"
|
#include "src/compiler/allocation-builder.h"
|
||||||
#include "src/compiler/compilation-dependencies.h"
|
#include "src/compiler/compilation-dependencies.h"
|
||||||
#include "src/compiler/frame-states.h"
|
#include "src/compiler/frame-states.h"
|
||||||
|
#include "src/compiler/graph-assembler.h"
|
||||||
#include "src/compiler/js-graph.h"
|
#include "src/compiler/js-graph.h"
|
||||||
#include "src/compiler/js-heap-broker.h"
|
#include "src/compiler/js-heap-broker.h"
|
||||||
#include "src/compiler/js-operator.h"
|
#include "src/compiler/js-operator.h"
|
||||||
@ -25,9 +26,11 @@
|
|||||||
#include "src/compiler/property-access-builder.h"
|
#include "src/compiler/property-access-builder.h"
|
||||||
#include "src/compiler/simplified-operator.h"
|
#include "src/compiler/simplified-operator.h"
|
||||||
#include "src/compiler/type-cache.h"
|
#include "src/compiler/type-cache.h"
|
||||||
|
#include "src/flags/flags.h"
|
||||||
#include "src/handles/handles.h"
|
#include "src/handles/handles.h"
|
||||||
#include "src/heap/factory.h"
|
#include "src/heap/factory.h"
|
||||||
#include "src/heap/heap-write-barrier-inl.h"
|
#include "src/heap/heap-write-barrier-inl.h"
|
||||||
|
#include "src/objects/elements-kind.h"
|
||||||
#include "src/objects/feedback-vector.h"
|
#include "src/objects/feedback-vector.h"
|
||||||
#include "src/objects/heap-number.h"
|
#include "src/objects/heap-number.h"
|
||||||
#include "src/objects/string.h"
|
#include "src/objects/string.h"
|
||||||
@ -2116,6 +2119,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
|||||||
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
||||||
Effect effect{NodeProperties::GetEffectInput(node)};
|
Effect effect{NodeProperties::GetEffectInput(node)};
|
||||||
Control control{NodeProperties::GetControlInput(node)};
|
Control control{NodeProperties::GetControlInput(node)};
|
||||||
|
Node* context = NodeProperties::GetContextInput(node);
|
||||||
|
|
||||||
// TODO(neis): It's odd that we do optimizations below that don't really care
|
// TODO(neis): It's odd that we do optimizations below that don't really care
|
||||||
// about the feedback, but we don't do them when the feedback is megamorphic.
|
// about the feedback, but we don't do them when the feedback is megamorphic.
|
||||||
@ -2225,8 +2229,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
|||||||
|
|
||||||
// Access the actual element.
|
// Access the actual element.
|
||||||
ValueEffectControl continuation =
|
ValueEffectControl continuation =
|
||||||
BuildElementAccess(receiver, index, value, effect, control, access_info,
|
BuildElementAccess(receiver, index, value, effect, control, context,
|
||||||
feedback.keyed_mode());
|
access_info, feedback.keyed_mode());
|
||||||
value = continuation.value();
|
value = continuation.value();
|
||||||
effect = continuation.effect();
|
effect = continuation.effect();
|
||||||
control = continuation.control();
|
control = continuation.control();
|
||||||
@ -2290,9 +2294,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Access the actual element.
|
// Access the actual element.
|
||||||
ValueEffectControl continuation =
|
ValueEffectControl continuation = BuildElementAccess(
|
||||||
BuildElementAccess(this_receiver, this_index, this_value, this_effect,
|
this_receiver, this_index, this_value, this_effect, this_control,
|
||||||
this_control, access_info, feedback.keyed_mode());
|
context, access_info, feedback.keyed_mode());
|
||||||
values.push_back(continuation.value());
|
values.push_back(continuation.value());
|
||||||
effects.push_back(continuation.effect());
|
effects.push_back(continuation.effect());
|
||||||
controls.push_back(continuation.control());
|
controls.push_back(continuation.control());
|
||||||
@ -3110,6 +3114,7 @@ ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
|
|||||||
switch (kind) {
|
switch (kind) {
|
||||||
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
|
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
|
||||||
case TYPE##_ELEMENTS: \
|
case TYPE##_ELEMENTS: \
|
||||||
|
case RAB_GSAB_##TYPE##_ELEMENTS: \
|
||||||
return kExternal##Type##Array;
|
return kExternal##Type##Array;
|
||||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||||
#undef TYPED_ARRAY_CASE
|
#undef TYPED_ARRAY_CASE
|
||||||
@ -3124,14 +3129,18 @@ ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
|
|||||||
JSNativeContextSpecialization::ValueEffectControl
|
JSNativeContextSpecialization::ValueEffectControl
|
||||||
JSNativeContextSpecialization::BuildElementAccess(
|
JSNativeContextSpecialization::BuildElementAccess(
|
||||||
Node* receiver, Node* index, Node* value, Node* effect, Node* control,
|
Node* receiver, Node* index, Node* value, Node* effect, Node* control,
|
||||||
ElementAccessInfo const& access_info, KeyedAccessMode const& keyed_mode) {
|
Node* context, ElementAccessInfo const& access_info,
|
||||||
|
KeyedAccessMode const& keyed_mode) {
|
||||||
// TODO(bmeurer): We currently specialize based on elements kind. We should
|
// TODO(bmeurer): We currently specialize based on elements kind. We should
|
||||||
// also be able to properly support strings and other JSObjects here.
|
// also be able to properly support strings and other JSObjects here.
|
||||||
ElementsKind elements_kind = access_info.elements_kind();
|
ElementsKind elements_kind = access_info.elements_kind();
|
||||||
|
DCHECK_IMPLIES(IsRabGsabTypedArrayElementsKind(elements_kind),
|
||||||
|
v8_flags.turbo_rab_gsab);
|
||||||
ZoneVector<MapRef> const& receiver_maps =
|
ZoneVector<MapRef> const& receiver_maps =
|
||||||
access_info.lookup_start_object_maps();
|
access_info.lookup_start_object_maps();
|
||||||
|
|
||||||
if (IsTypedArrayElementsKind(elements_kind)) {
|
if (IsTypedArrayElementsKind(elements_kind) ||
|
||||||
|
IsRabGsabTypedArrayElementsKind(elements_kind)) {
|
||||||
Node* buffer_or_receiver = receiver;
|
Node* buffer_or_receiver = receiver;
|
||||||
Node* length;
|
Node* length;
|
||||||
Node* base_pointer;
|
Node* base_pointer;
|
||||||
@ -3141,7 +3150,9 @@ JSNativeContextSpecialization::BuildElementAccess(
|
|||||||
// for asm.js-like code patterns).
|
// for asm.js-like code patterns).
|
||||||
base::Optional<JSTypedArrayRef> typed_array =
|
base::Optional<JSTypedArrayRef> typed_array =
|
||||||
GetTypedArrayConstant(broker(), receiver);
|
GetTypedArrayConstant(broker(), receiver);
|
||||||
if (typed_array.has_value()) {
|
if (typed_array.has_value() &&
|
||||||
|
!IsRabGsabTypedArrayElementsKind(elements_kind)) {
|
||||||
|
// TODO(v8:11111): Add support for rab/gsab here.
|
||||||
length = jsgraph()->Constant(static_cast<double>(typed_array->length()));
|
length = jsgraph()->Constant(static_cast<double>(typed_array->length()));
|
||||||
|
|
||||||
DCHECK(!typed_array->is_on_heap());
|
DCHECK(!typed_array->is_on_heap());
|
||||||
@ -3154,9 +3165,14 @@ JSNativeContextSpecialization::BuildElementAccess(
|
|||||||
external_pointer = jsgraph()->PointerConstant(typed_array->data_ptr());
|
external_pointer = jsgraph()->PointerConstant(typed_array->data_ptr());
|
||||||
} else {
|
} else {
|
||||||
// Load the {receiver}s length.
|
// Load the {receiver}s length.
|
||||||
length = effect = graph()->NewNode(
|
JSGraphAssembler assembler(jsgraph_, zone(), BranchSemantics::kJS,
|
||||||
simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
|
[this](Node* n) { this->Revisit(n); });
|
||||||
receiver, effect, control);
|
assembler.InitializeEffectControl(effect, control);
|
||||||
|
length = assembler.TypedArrayLength(
|
||||||
|
TNode<JSTypedArray>::UncheckedCast(receiver), {elements_kind},
|
||||||
|
TNode<Context>::UncheckedCast(context));
|
||||||
|
std::tie(effect, control) =
|
||||||
|
ReleaseEffectAndControlFromAssembler(&assembler);
|
||||||
|
|
||||||
// Load the base pointer for the {receiver}. This will always be Smi
|
// Load the base pointer for the {receiver}. This will always be Smi
|
||||||
// zero unless we allow on-heap TypedArrays, which is only the case
|
// zero unless we allow on-heap TypedArrays, which is only the case
|
||||||
@ -3927,6 +3943,27 @@ Node* JSNativeContextSpecialization::BuildLoadPrototypeFromObject(
|
|||||||
control);
|
control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<Node*, Node*>
|
||||||
|
JSNativeContextSpecialization::ReleaseEffectAndControlFromAssembler(
|
||||||
|
JSGraphAssembler* gasm) {
|
||||||
|
auto catch_scope = gasm->catch_scope();
|
||||||
|
DCHECK(catch_scope->is_outermost());
|
||||||
|
|
||||||
|
if (catch_scope->has_handler() &&
|
||||||
|
catch_scope->has_exceptional_control_flow()) {
|
||||||
|
TNode<Object> handler_exception;
|
||||||
|
Effect handler_effect{nullptr};
|
||||||
|
Control handler_control{nullptr};
|
||||||
|
gasm->catch_scope()->MergeExceptionalPaths(
|
||||||
|
&handler_exception, &handler_effect, &handler_control);
|
||||||
|
|
||||||
|
ReplaceWithValue(gasm->outermost_handler(), handler_exception,
|
||||||
|
handler_effect, handler_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {gasm->effect(), gasm->control()};
|
||||||
|
}
|
||||||
|
|
||||||
Graph* JSNativeContextSpecialization::graph() const {
|
Graph* JSNativeContextSpecialization::graph() const {
|
||||||
return jsgraph()->graph();
|
return jsgraph()->graph();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "src/base/flags.h"
|
#include "src/base/flags.h"
|
||||||
#include "src/base/optional.h"
|
#include "src/base/optional.h"
|
||||||
|
#include "src/compiler/graph-assembler.h"
|
||||||
#include "src/compiler/graph-reducer.h"
|
#include "src/compiler/graph-reducer.h"
|
||||||
#include "src/compiler/js-heap-broker.h"
|
#include "src/compiler/js-heap-broker.h"
|
||||||
#include "src/deoptimizer/deoptimize-reason.h"
|
#include "src/deoptimizer/deoptimize-reason.h"
|
||||||
@ -189,7 +190,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
|||||||
// Construct the appropriate subgraph for element access.
|
// Construct the appropriate subgraph for element access.
|
||||||
ValueEffectControl BuildElementAccess(Node* receiver, Node* index,
|
ValueEffectControl BuildElementAccess(Node* receiver, Node* index,
|
||||||
Node* value, Node* effect,
|
Node* value, Node* effect,
|
||||||
Node* control,
|
Node* control, Node* context,
|
||||||
ElementAccessInfo const& access_info,
|
ElementAccessInfo const& access_info,
|
||||||
KeyedAccessMode const& keyed_mode);
|
KeyedAccessMode const& keyed_mode);
|
||||||
|
|
||||||
@ -249,6 +250,9 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
|||||||
|
|
||||||
Node* BuildLoadPrototypeFromObject(Node* object, Node* effect, Node* control);
|
Node* BuildLoadPrototypeFromObject(Node* object, Node* effect, Node* control);
|
||||||
|
|
||||||
|
std::pair<Node*, Node*> ReleaseEffectAndControlFromAssembler(
|
||||||
|
JSGraphAssembler* assembler);
|
||||||
|
|
||||||
Graph* graph() const;
|
Graph* graph() const;
|
||||||
JSGraph* jsgraph() const { return jsgraph_; }
|
JSGraph* jsgraph() const { return jsgraph_; }
|
||||||
|
|
||||||
|
@ -916,20 +916,18 @@ const Operator* JSOperatorBuilder::CallRuntime(Runtime::FunctionId id) {
|
|||||||
return CallRuntime(f, f->nargs);
|
return CallRuntime(f, f->nargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Operator* JSOperatorBuilder::CallRuntime(
|
||||||
const Operator* JSOperatorBuilder::CallRuntime(Runtime::FunctionId id,
|
Runtime::FunctionId id, size_t arity, Operator::Properties properties) {
|
||||||
size_t arity) {
|
|
||||||
const Runtime::Function* f = Runtime::FunctionForId(id);
|
const Runtime::Function* f = Runtime::FunctionForId(id);
|
||||||
return CallRuntime(f, arity);
|
return CallRuntime(f, arity, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Operator* JSOperatorBuilder::CallRuntime(
|
||||||
const Operator* JSOperatorBuilder::CallRuntime(const Runtime::Function* f,
|
const Runtime::Function* f, size_t arity, Operator::Properties properties) {
|
||||||
size_t arity) {
|
|
||||||
CallRuntimeParameters parameters(f->function_id, arity);
|
CallRuntimeParameters parameters(f->function_id, arity);
|
||||||
DCHECK(f->nargs == -1 || f->nargs == static_cast<int>(parameters.arity()));
|
DCHECK(f->nargs == -1 || f->nargs == static_cast<int>(parameters.arity()));
|
||||||
return zone()->New<Operator1<CallRuntimeParameters>>( // --
|
return zone()->New<Operator1<CallRuntimeParameters>>( // --
|
||||||
IrOpcode::kJSCallRuntime, Operator::kNoProperties, // opcode
|
IrOpcode::kJSCallRuntime, properties, // opcode
|
||||||
"JSCallRuntime", // name
|
"JSCallRuntime", // name
|
||||||
parameters.arity(), 1, 1, f->result_size, 1, 2, // inputs/outputs
|
parameters.arity(), 1, 1, f->result_size, 1, 2, // inputs/outputs
|
||||||
parameters); // parameter
|
parameters); // parameter
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
#include "src/compiler/node.h"
|
#include "src/compiler/node.h"
|
||||||
#include "src/compiler/opcodes.h"
|
#include "src/compiler/opcodes.h"
|
||||||
|
#include "src/compiler/operator-properties.h"
|
||||||
#include "src/objects/feedback-cell.h"
|
#include "src/objects/feedback-cell.h"
|
||||||
#include "src/runtime/runtime.h"
|
#include "src/runtime/runtime.h"
|
||||||
|
|
||||||
@ -995,8 +996,12 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
|||||||
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
|
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
|
||||||
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kTarget);
|
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kTarget);
|
||||||
const Operator* CallRuntime(Runtime::FunctionId id);
|
const Operator* CallRuntime(Runtime::FunctionId id);
|
||||||
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
|
const Operator* CallRuntime(
|
||||||
const Operator* CallRuntime(const Runtime::Function* function, size_t arity);
|
Runtime::FunctionId id, size_t arity,
|
||||||
|
Operator::Properties properties = Operator::kNoProperties);
|
||||||
|
const Operator* CallRuntime(
|
||||||
|
const Runtime::Function* function, size_t arity,
|
||||||
|
Operator::Properties properties = Operator::kNoProperties);
|
||||||
|
|
||||||
#if V8_ENABLE_WEBASSEMBLY
|
#if V8_ENABLE_WEBASSEMBLY
|
||||||
const Operator* CallWasm(const wasm::WasmModule* wasm_module,
|
const Operator* CallWasm(const wasm::WasmModule* wasm_module,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "src/codegen/code-factory.h"
|
#include "src/codegen/code-factory.h"
|
||||||
#include "src/codegen/interface-descriptors-inl.h"
|
#include "src/codegen/interface-descriptors-inl.h"
|
||||||
#include "src/compiler/access-builder.h"
|
#include "src/compiler/access-builder.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/compilation-dependencies.h"
|
#include "src/compiler/compilation-dependencies.h"
|
||||||
#include "src/compiler/graph-assembler.h"
|
#include "src/compiler/graph-assembler.h"
|
||||||
#include "src/compiler/js-graph.h"
|
#include "src/compiler/js-graph.h"
|
||||||
@ -841,7 +842,7 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node) {
|
|||||||
// then ObjectIsUndetectable(left)
|
// then ObjectIsUndetectable(left)
|
||||||
// else ReferenceEqual(left, right)
|
// else ReferenceEqual(left, right)
|
||||||
#define __ gasm.
|
#define __ gasm.
|
||||||
JSGraphAssembler gasm(jsgraph(), jsgraph()->zone());
|
JSGraphAssembler gasm(jsgraph(), jsgraph()->zone(), BranchSemantics::kJS);
|
||||||
gasm.InitializeEffectControl(r.effect(), r.control());
|
gasm.InitializeEffectControl(r.effect(), r.control());
|
||||||
|
|
||||||
auto lhs = TNode<Object>::UncheckedCast(r.left());
|
auto lhs = TNode<Object>::UncheckedCast(r.left());
|
||||||
|
@ -268,6 +268,7 @@ bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) {
|
|||||||
case Runtime::kAbort:
|
case Runtime::kAbort:
|
||||||
case Runtime::kAllocateInOldGeneration:
|
case Runtime::kAllocateInOldGeneration:
|
||||||
case Runtime::kCreateIterResultObject:
|
case Runtime::kCreateIterResultObject:
|
||||||
|
case Runtime::kGrowableSharedArrayBufferByteLength:
|
||||||
case Runtime::kIncBlockCounter:
|
case Runtime::kIncBlockCounter:
|
||||||
case Runtime::kIsFunction:
|
case Runtime::kIsFunction:
|
||||||
case Runtime::kNewClosure:
|
case Runtime::kNewClosure:
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "src/base/logging.h"
|
#include "src/base/logging.h"
|
||||||
#include "src/codegen/tick-counter.h"
|
#include "src/codegen/tick-counter.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/js-graph.h"
|
#include "src/compiler/js-graph.h"
|
||||||
#include "src/compiler/linkage.h"
|
#include "src/compiler/linkage.h"
|
||||||
#include "src/compiler/node-properties.h"
|
#include "src/compiler/node-properties.h"
|
||||||
@ -183,7 +184,7 @@ MemoryOptimizer::MemoryOptimizer(
|
|||||||
JSGraph* jsgraph, Zone* zone,
|
JSGraph* jsgraph, Zone* zone,
|
||||||
MemoryLowering::AllocationFolding allocation_folding,
|
MemoryLowering::AllocationFolding allocation_folding,
|
||||||
const char* function_debug_name, TickCounter* tick_counter)
|
const char* function_debug_name, TickCounter* tick_counter)
|
||||||
: graph_assembler_(jsgraph, zone),
|
: graph_assembler_(jsgraph, zone, BranchSemantics::kMachine),
|
||||||
memory_lowering_(jsgraph, zone, &graph_assembler_, allocation_folding,
|
memory_lowering_(jsgraph, zone, &graph_assembler_, allocation_folding,
|
||||||
WriteBarrierAssertFailed, function_debug_name),
|
WriteBarrierAssertFailed, function_debug_name),
|
||||||
jsgraph_(jsgraph),
|
jsgraph_(jsgraph),
|
||||||
|
@ -406,7 +406,6 @@ Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity)
|
|||||||
DCHECK_LE(inline_capacity, kMaxInlineCapacity);
|
DCHECK_LE(inline_capacity, kMaxInlineCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Node::AppendUse(Use* use) {
|
void Node::AppendUse(Use* use) {
|
||||||
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
|
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
|
||||||
DCHECK_EQ(this, *use->input_ptr());
|
DCHECK_EQ(this, *use->input_ptr());
|
||||||
|
@ -695,6 +695,13 @@ Node::Uses::const_iterator Node::Uses::begin() const {
|
|||||||
|
|
||||||
Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); }
|
Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); }
|
||||||
|
|
||||||
|
inline Node::Uses::const_iterator begin(const Node::Uses& uses) {
|
||||||
|
return uses.begin();
|
||||||
|
}
|
||||||
|
inline Node::Uses::const_iterator end(const Node::Uses& uses) {
|
||||||
|
return uses.end();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -33,21 +33,27 @@
|
|||||||
V(Throw) \
|
V(Throw) \
|
||||||
V(End)
|
V(End)
|
||||||
|
|
||||||
// Opcodes for constant operators.
|
#define MACHINE_LEVEL_CONSTANT_OP_LIST(V) \
|
||||||
#define CONSTANT_OP_LIST(V) \
|
|
||||||
V(Int32Constant) \
|
V(Int32Constant) \
|
||||||
V(Int64Constant) \
|
V(Int64Constant) \
|
||||||
V(TaggedIndexConstant) \
|
V(TaggedIndexConstant) \
|
||||||
V(Float32Constant) \
|
V(Float32Constant) \
|
||||||
V(Float64Constant) \
|
V(Float64Constant) \
|
||||||
V(ExternalConstant) \
|
|
||||||
V(NumberConstant) \
|
|
||||||
V(PointerConstant) \
|
|
||||||
V(HeapConstant) \
|
|
||||||
V(CompressedHeapConstant) \
|
V(CompressedHeapConstant) \
|
||||||
V(RelocatableInt32Constant) \
|
V(RelocatableInt32Constant) \
|
||||||
V(RelocatableInt64Constant)
|
V(RelocatableInt64Constant)
|
||||||
|
|
||||||
|
#define JS_LEVEL_CONSTANT_OP_LIST(V) \
|
||||||
|
V(ExternalConstant) \
|
||||||
|
V(NumberConstant) \
|
||||||
|
V(PointerConstant) \
|
||||||
|
V(HeapConstant)
|
||||||
|
|
||||||
|
// Opcodes for constant operators.
|
||||||
|
#define CONSTANT_OP_LIST(V) \
|
||||||
|
JS_LEVEL_CONSTANT_OP_LIST(V) \
|
||||||
|
MACHINE_LEVEL_CONSTANT_OP_LIST(V)
|
||||||
|
|
||||||
#define INNER_OP_LIST(V) \
|
#define INNER_OP_LIST(V) \
|
||||||
V(Select) \
|
V(Select) \
|
||||||
V(Phi) \
|
V(Phi) \
|
||||||
@ -74,7 +80,9 @@
|
|||||||
V(Retain) \
|
V(Retain) \
|
||||||
V(MapGuard) \
|
V(MapGuard) \
|
||||||
V(FoldConstant) \
|
V(FoldConstant) \
|
||||||
V(TypeGuard)
|
V(TypeGuard) \
|
||||||
|
V(EnterMachineGraph) \
|
||||||
|
V(ExitMachineGraph)
|
||||||
|
|
||||||
#define COMMON_OP_LIST(V) \
|
#define COMMON_OP_LIST(V) \
|
||||||
CONSTANT_OP_LIST(V) \
|
CONSTANT_OP_LIST(V) \
|
||||||
@ -547,6 +555,13 @@
|
|||||||
SIMPLIFIED_OTHER_OP_LIST(V)
|
SIMPLIFIED_OTHER_OP_LIST(V)
|
||||||
|
|
||||||
// Opcodes for Machine-level operators.
|
// Opcodes for Machine-level operators.
|
||||||
|
#define MACHINE_UNOP_32_LIST(V) \
|
||||||
|
V(Word32Clz) \
|
||||||
|
V(Word32Ctz) \
|
||||||
|
V(Int32AbsWithOverflow) \
|
||||||
|
V(Word32ReverseBits) \
|
||||||
|
V(Word32ReverseBytes)
|
||||||
|
|
||||||
#define MACHINE_COMPARE_BINOP_LIST(V) \
|
#define MACHINE_COMPARE_BINOP_LIST(V) \
|
||||||
V(Word32Equal) \
|
V(Word32Equal) \
|
||||||
V(Word64Equal) \
|
V(Word64Equal) \
|
||||||
@ -565,13 +580,6 @@
|
|||||||
V(Float64LessThan) \
|
V(Float64LessThan) \
|
||||||
V(Float64LessThanOrEqual)
|
V(Float64LessThanOrEqual)
|
||||||
|
|
||||||
#define MACHINE_UNOP_32_LIST(V) \
|
|
||||||
V(Word32Clz) \
|
|
||||||
V(Word32Ctz) \
|
|
||||||
V(Int32AbsWithOverflow) \
|
|
||||||
V(Word32ReverseBits) \
|
|
||||||
V(Word32ReverseBytes)
|
|
||||||
|
|
||||||
#define MACHINE_BINOP_32_LIST(V) \
|
#define MACHINE_BINOP_32_LIST(V) \
|
||||||
V(Word32And) \
|
V(Word32And) \
|
||||||
V(Word32Or) \
|
V(Word32Or) \
|
||||||
@ -1086,6 +1094,24 @@ class V8_EXPORT_PRIVATE IrOpcode {
|
|||||||
return kJSEqual <= value && value <= kJSDebugger;
|
return kJSEqual <= value && value <= kJSDebugger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if opcode for machine operator.
|
||||||
|
static bool IsMachineOpcode(Value value) {
|
||||||
|
return kWord32Clz <= value && value <= kTraceInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true iff opcode is a machine-level constant.
|
||||||
|
static bool IsMachineConstantOpcode(Value value) {
|
||||||
|
switch (value) {
|
||||||
|
#define CASE(name) \
|
||||||
|
case k##name: \
|
||||||
|
return true;
|
||||||
|
MACHINE_LEVEL_CONSTANT_OP_LIST(CASE)
|
||||||
|
#undef CASE
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if opcode for constant operator.
|
// Returns true if opcode for constant operator.
|
||||||
static bool IsConstantOpcode(Value value) {
|
static bool IsConstantOpcode(Value value) {
|
||||||
#define CASE(Name) \
|
#define CASE(Name) \
|
||||||
|
@ -1132,6 +1132,11 @@ SPECULATIVE_NUMBER_BINOP(NumberShiftRight)
|
|||||||
SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical)
|
SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical)
|
||||||
#undef SPECULATIVE_NUMBER_BINOP
|
#undef SPECULATIVE_NUMBER_BINOP
|
||||||
|
|
||||||
|
#define MACHINE_BINOP(Name) \
|
||||||
|
Type OperationTyper::Name(Type, Type) { return Type::Machine(); }
|
||||||
|
TYPER_SUPPORTED_MACHINE_BINOP_LIST(MACHINE_BINOP)
|
||||||
|
#undef MACHINE_BINOP
|
||||||
|
|
||||||
Type OperationTyper::BigIntAdd(Type lhs, Type rhs) {
|
Type OperationTyper::BigIntAdd(Type lhs, Type rhs) {
|
||||||
DCHECK(lhs.Is(Type::BigInt()));
|
DCHECK(lhs.Is(Type::BigInt()));
|
||||||
DCHECK(rhs.Is(Type::BigInt()));
|
DCHECK(rhs.Is(Type::BigInt()));
|
||||||
|
@ -9,6 +9,26 @@
|
|||||||
#include "src/compiler/opcodes.h"
|
#include "src/compiler/opcodes.h"
|
||||||
#include "src/compiler/types.h"
|
#include "src/compiler/types.h"
|
||||||
|
|
||||||
|
#define TYPER_SUPPORTED_MACHINE_BINOP_LIST(V) \
|
||||||
|
V(Int32Add) \
|
||||||
|
V(Int64Add) \
|
||||||
|
V(Int32Sub) \
|
||||||
|
V(Int64Sub) \
|
||||||
|
V(Load) \
|
||||||
|
V(Uint32Div) \
|
||||||
|
V(Uint64Div) \
|
||||||
|
V(Uint32LessThan) \
|
||||||
|
V(Uint32LessThanOrEqual) \
|
||||||
|
V(Uint64LessThanOrEqual) \
|
||||||
|
V(Word32And) \
|
||||||
|
V(Word32Equal) \
|
||||||
|
V(Word32Or) \
|
||||||
|
V(Word32Shl) \
|
||||||
|
V(Word32Shr) \
|
||||||
|
V(Word64And) \
|
||||||
|
V(Word64Shl) \
|
||||||
|
V(Word64Shr)
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@ -54,6 +74,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
|
|||||||
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
|
TYPER_SUPPORTED_MACHINE_BINOP_LIST(DECLARE_METHOD)
|
||||||
#undef DECLARE_METHOD
|
#undef DECLARE_METHOD
|
||||||
|
|
||||||
// Comparison operators.
|
// Comparison operators.
|
||||||
|
@ -1986,7 +1986,8 @@ struct LateOptimizationPhase {
|
|||||||
CommonOperatorReducer common_reducer(
|
CommonOperatorReducer common_reducer(
|
||||||
&graph_reducer, data->graph(), data->broker(), data->common(),
|
&graph_reducer, data->graph(), data->broker(), data->common(),
|
||||||
data->machine(), temp_zone, BranchSemantics::kMachine);
|
data->machine(), temp_zone, BranchSemantics::kMachine);
|
||||||
JSGraphAssembler graph_assembler(data->jsgraph(), temp_zone);
|
JSGraphAssembler graph_assembler(data->jsgraph(), temp_zone,
|
||||||
|
BranchSemantics::kMachine);
|
||||||
SelectLowering select_lowering(&graph_assembler, data->graph());
|
SelectLowering select_lowering(&graph_assembler, data->graph());
|
||||||
AddReducer(data, &graph_reducer, &escape_analysis);
|
AddReducer(data, &graph_reducer, &escape_analysis);
|
||||||
AddReducer(data, &graph_reducer, &branch_condition_elimination);
|
AddReducer(data, &graph_reducer, &branch_condition_elimination);
|
||||||
|
@ -160,6 +160,9 @@ RepresentationChanger::RepresentationChanger(
|
|||||||
Node* RepresentationChanger::GetRepresentationFor(
|
Node* RepresentationChanger::GetRepresentationFor(
|
||||||
Node* node, MachineRepresentation output_rep, Type output_type,
|
Node* node, MachineRepresentation output_rep, Type output_type,
|
||||||
Node* use_node, UseInfo use_info) {
|
Node* use_node, UseInfo use_info) {
|
||||||
|
// We are currently not inserting conversions in machine graphs.
|
||||||
|
// We might add that, though.
|
||||||
|
DCHECK_IMPLIES(!output_type.IsNone(), !output_type.Is(Type::Machine()));
|
||||||
if (output_rep == MachineRepresentation::kNone && !output_type.IsNone()) {
|
if (output_rep == MachineRepresentation::kNone && !output_type.IsNone()) {
|
||||||
// The output representation should be set if the type is inhabited (i.e.,
|
// The output representation should be set if the type is inhabited (i.e.,
|
||||||
// if the value is possible).
|
// if the value is possible).
|
||||||
@ -487,22 +490,16 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
|
|||||||
return TypeError(node, output_rep, output_type,
|
return TypeError(node, output_rep, output_type,
|
||||||
MachineRepresentation::kTaggedPointer);
|
MachineRepresentation::kTaggedPointer);
|
||||||
}
|
}
|
||||||
} else if (CanBeTaggedSigned(output_rep) &&
|
|
||||||
use_info.type_check() == TypeCheckKind::kHeapObject) {
|
|
||||||
if (!output_type.Maybe(Type::SignedSmall())) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
// TODO(turbofan): Consider adding a Bailout operator that just deopts
|
|
||||||
// for TaggedSigned output representation.
|
|
||||||
op = simplified()->CheckedTaggedToTaggedPointer(use_info.feedback());
|
|
||||||
} else if (IsAnyTagged(output_rep)) {
|
} else if (IsAnyTagged(output_rep)) {
|
||||||
if (use_info.type_check() == TypeCheckKind::kBigInt) {
|
if (use_info.type_check() == TypeCheckKind::kBigInt) {
|
||||||
if (output_type.Is(Type::BigInt())) {
|
if (output_type.Is(Type::BigInt())) {
|
||||||
|
DCHECK_NE(output_rep, MachineRepresentation::kTaggedSigned);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
op = simplified()->CheckBigInt(use_info.feedback());
|
op = simplified()->CheckBigInt(use_info.feedback());
|
||||||
} else if (use_info.type_check() == TypeCheckKind::kBigInt64) {
|
} else if (use_info.type_check() == TypeCheckKind::kBigInt64) {
|
||||||
if (output_type.Is(Type::SignedBigInt64())) {
|
if (output_type.Is(Type::SignedBigInt64())) {
|
||||||
|
DCHECK_NE(output_rep, MachineRepresentation::kTaggedSigned);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (!output_type.Is(Type::BigInt())) {
|
if (!output_type.Is(Type::BigInt())) {
|
||||||
@ -515,8 +512,7 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
|
|||||||
DCHECK_NE(output_rep, MachineRepresentation::kTaggedSigned);
|
DCHECK_NE(output_rep, MachineRepresentation::kTaggedSigned);
|
||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
return TypeError(node, output_rep, output_type,
|
op = simplified()->CheckedTaggedToTaggedPointer(use_info.feedback());
|
||||||
MachineRepresentation::kTaggedPointer);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return TypeError(node, output_rep, output_type,
|
return TypeError(node, output_rep, output_type,
|
||||||
@ -1047,9 +1043,11 @@ Node* RepresentationChanger::GetBitRepresentationFor(
|
|||||||
case IrOpcode::kHeapConstant: {
|
case IrOpcode::kHeapConstant: {
|
||||||
HeapObjectMatcher m(node);
|
HeapObjectMatcher m(node);
|
||||||
if (m.Is(factory()->false_value())) {
|
if (m.Is(factory()->false_value())) {
|
||||||
return jsgraph()->Int32Constant(0);
|
return InsertTypeOverrideForVerifier(Type::Boolean(),
|
||||||
|
jsgraph()->Int32Constant(0));
|
||||||
} else if (m.Is(factory()->true_value())) {
|
} else if (m.Is(factory()->true_value())) {
|
||||||
return jsgraph()->Int32Constant(1);
|
return InsertTypeOverrideForVerifier(Type::Boolean(),
|
||||||
|
jsgraph()->Int32Constant(1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "src/compiler/simplified-lowering-verifier.h"
|
#include "src/compiler/simplified-lowering-verifier.h"
|
||||||
|
|
||||||
|
#include "src/compiler/backend/instruction-codes.h"
|
||||||
|
#include "src/compiler/common-operator.h"
|
||||||
#include "src/compiler/operation-typer.h"
|
#include "src/compiler/operation-typer.h"
|
||||||
#include "src/compiler/type-cache.h"
|
#include "src/compiler/type-cache.h"
|
||||||
|
|
||||||
@ -22,7 +24,8 @@ Truncation LeastGeneralTruncation(const Truncation& t1, const Truncation& t2,
|
|||||||
return LeastGeneralTruncation(LeastGeneralTruncation(t1, t2), t3);
|
return LeastGeneralTruncation(LeastGeneralTruncation(t1, t2), t3);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNonTruncatingMachineTypeFor(const MachineType& mt, const Type& type) {
|
bool IsNonTruncatingMachineTypeFor(const MachineType& mt, const Type& type,
|
||||||
|
Zone* graph_zone) {
|
||||||
if (type.IsNone()) return true;
|
if (type.IsNone()) return true;
|
||||||
// TODO(nicohartmann@): Add more cases here.
|
// TODO(nicohartmann@): Add more cases here.
|
||||||
if (type.Is(Type::BigInt())) {
|
if (type.Is(Type::BigInt())) {
|
||||||
@ -37,7 +40,7 @@ bool IsNonTruncatingMachineTypeFor(const MachineType& mt, const Type& type) {
|
|||||||
case MachineRepresentation::kBit:
|
case MachineRepresentation::kBit:
|
||||||
CHECK(mt.semantic() == MachineSemantic::kBool ||
|
CHECK(mt.semantic() == MachineSemantic::kBool ||
|
||||||
mt.semantic() == MachineSemantic::kAny);
|
mt.semantic() == MachineSemantic::kAny);
|
||||||
return type.Is(Type::Boolean());
|
return type.Is(Type::Boolean()) || type.Is(Type::Range(0, 1, graph_zone));
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -75,6 +78,19 @@ void SimplifiedLoweringVerifier::CheckAndSet(Node* node, const Type& type,
|
|||||||
SetTruncation(node, GeneralizeTruncation(trunc, type));
|
SetTruncation(node, GeneralizeTruncation(trunc, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SimplifiedLoweringVerifier::ReportInvalidTypeCombination(
|
||||||
|
Node* node, const std::vector<Type>& types) {
|
||||||
|
std::ostringstream types_str;
|
||||||
|
for (size_t i = 0; i < types.size(); ++i) {
|
||||||
|
if (i != 0) types_str << ", ";
|
||||||
|
types[i].PrintTo(types_str);
|
||||||
|
}
|
||||||
|
FATAL(
|
||||||
|
"SimplifiedLoweringVerifierError: invalid combination of input types %s "
|
||||||
|
" for node #%d:%s",
|
||||||
|
types_str.str().c_str(), node->id(), node->op()->mnemonic());
|
||||||
|
}
|
||||||
|
|
||||||
bool IsModuloTruncation(const Truncation& truncation) {
|
bool IsModuloTruncation(const Truncation& truncation) {
|
||||||
return truncation.IsUsedAsWord32() || truncation.IsUsedAsWord64() ||
|
return truncation.IsUsedAsWord32() || truncation.IsUsedAsWord64() ||
|
||||||
Truncation::Any().IsLessGeneralThan(truncation);
|
Truncation::Any().IsLessGeneralThan(truncation);
|
||||||
@ -134,7 +150,21 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
case IrOpcode::kFrameState:
|
case IrOpcode::kFrameState:
|
||||||
case IrOpcode::kJSStackCheck:
|
case IrOpcode::kJSStackCheck:
|
||||||
break;
|
break;
|
||||||
case IrOpcode::kInt32Constant:
|
case IrOpcode::kInt32Constant: {
|
||||||
|
// NOTE: Constants require special handling as they are shared between
|
||||||
|
// machine graphs and non-machine graphs lowered during SL. The former
|
||||||
|
// might have assigned Type::Machine() to the constant, but to be able
|
||||||
|
// to provide a different type for uses of constants that don't come
|
||||||
|
// from machine graphs, the machine-uses of Int32Constants have been
|
||||||
|
// put behind additional SLVerifierHints to provide the required
|
||||||
|
// Type::Machine() to them, such that we can treat constants here as
|
||||||
|
// having JS types to satisfy their non-machine uses.
|
||||||
|
int32_t value = OpParameter<int32_t>(node->op());
|
||||||
|
Type type = Type::Constant(value, graph_zone());
|
||||||
|
SetType(node, type);
|
||||||
|
SetTruncation(node, GeneralizeTruncation(Truncation::Word32(), type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case IrOpcode::kInt64Constant:
|
case IrOpcode::kInt64Constant:
|
||||||
case IrOpcode::kFloat64Constant: {
|
case IrOpcode::kFloat64Constant: {
|
||||||
// Constants might be untyped, because they are cached in the graph and
|
// Constants might be untyped, because they are cached in the graph and
|
||||||
@ -183,8 +213,22 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrOpcode::kInt32Add: {
|
case IrOpcode::kInt32Add: {
|
||||||
Type output_type =
|
Type left_type = InputType(node, 0);
|
||||||
op_typer.NumberAdd(InputType(node, 0), InputType(node, 1));
|
Type right_type = InputType(node, 1);
|
||||||
|
Type output_type;
|
||||||
|
if (left_type.IsNone() && right_type.IsNone()) {
|
||||||
|
output_type = Type::None();
|
||||||
|
} else if (left_type.Is(Type::Machine()) &&
|
||||||
|
right_type.Is(Type::Machine())) {
|
||||||
|
output_type = Type::Machine();
|
||||||
|
} else if (left_type.Is(Type::NumberOrOddball()) &&
|
||||||
|
right_type.Is(Type::NumberOrOddball())) {
|
||||||
|
left_type = op_typer.ToNumber(left_type);
|
||||||
|
right_type = op_typer.ToNumber(right_type);
|
||||||
|
output_type = op_typer.NumberAdd(left_type, right_type);
|
||||||
|
} else {
|
||||||
|
ReportInvalidTypeCombination(node, {left_type, right_type});
|
||||||
|
}
|
||||||
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
||||||
InputTruncation(node, 1),
|
InputTruncation(node, 1),
|
||||||
Truncation::Word32());
|
Truncation::Word32());
|
||||||
@ -193,8 +237,22 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrOpcode::kInt32Sub: {
|
case IrOpcode::kInt32Sub: {
|
||||||
Type output_type =
|
Type left_type = InputType(node, 0);
|
||||||
op_typer.NumberSubtract(InputType(node, 0), InputType(node, 1));
|
Type right_type = InputType(node, 1);
|
||||||
|
Type output_type;
|
||||||
|
if (left_type.IsNone() && right_type.IsNone()) {
|
||||||
|
output_type = Type::None();
|
||||||
|
} else if (left_type.Is(Type::Machine()) &&
|
||||||
|
right_type.Is(Type::Machine())) {
|
||||||
|
output_type = Type::Machine();
|
||||||
|
} else if (left_type.Is(Type::NumberOrOddball()) &&
|
||||||
|
right_type.Is(Type::NumberOrOddball())) {
|
||||||
|
left_type = op_typer.ToNumber(left_type);
|
||||||
|
right_type = op_typer.ToNumber(right_type);
|
||||||
|
output_type = op_typer.NumberSubtract(left_type, right_type);
|
||||||
|
} else {
|
||||||
|
ReportInvalidTypeCombination(node, {left_type, right_type});
|
||||||
|
}
|
||||||
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
||||||
InputTruncation(node, 1),
|
InputTruncation(node, 1),
|
||||||
Truncation::Word32());
|
Truncation::Word32());
|
||||||
@ -222,9 +280,16 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
case IrOpcode::kInt64Add: {
|
case IrOpcode::kInt64Add: {
|
||||||
Type left_type = InputType(node, 0);
|
Type left_type = InputType(node, 0);
|
||||||
Type right_type = InputType(node, 1);
|
Type right_type = InputType(node, 1);
|
||||||
|
|
||||||
Type output_type;
|
Type output_type;
|
||||||
if (left_type.Is(Type::BigInt()) && right_type.Is(Type::BigInt())) {
|
if (left_type.IsNone() && right_type.IsNone()) {
|
||||||
|
// None x None -> None
|
||||||
|
output_type = Type::None();
|
||||||
|
} else if (left_type.Is(Type::Machine()) &&
|
||||||
|
right_type.Is(Type::Machine())) {
|
||||||
|
// Machine x Machine -> Machine
|
||||||
|
output_type = Type::Machine();
|
||||||
|
} else if (left_type.Is(Type::BigInt()) &&
|
||||||
|
right_type.Is(Type::BigInt())) {
|
||||||
// BigInt x BigInt -> BigInt
|
// BigInt x BigInt -> BigInt
|
||||||
output_type = op_typer.BigIntAdd(left_type, right_type);
|
output_type = op_typer.BigIntAdd(left_type, right_type);
|
||||||
} else if (left_type.Is(Type::Number()) &&
|
} else if (left_type.Is(Type::Number()) &&
|
||||||
@ -233,17 +298,38 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
output_type = op_typer.NumberAdd(left_type, right_type);
|
output_type = op_typer.NumberAdd(left_type, right_type);
|
||||||
} else {
|
} else {
|
||||||
// Invalid type combination.
|
// Invalid type combination.
|
||||||
std::ostringstream left_str, right_str;
|
ReportInvalidTypeCombination(node, {left_type, right_type});
|
||||||
left_type.PrintTo(left_str);
|
}
|
||||||
right_type.PrintTo(right_str);
|
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
||||||
FATAL(
|
InputTruncation(node, 1),
|
||||||
"SimplifiedLoweringVerifierError: invalid combination of input "
|
Truncation::Word64());
|
||||||
"types "
|
CHECK(IsModuloTruncation(output_trunc));
|
||||||
"%s and %s for node #%d:%s",
|
CheckAndSet(node, output_type, output_trunc);
|
||||||
left_str.str().c_str(), right_str.str().c_str(), node->id(),
|
break;
|
||||||
node->op()->mnemonic());
|
}
|
||||||
|
case IrOpcode::kInt64Sub: {
|
||||||
|
Type left_type = InputType(node, 0);
|
||||||
|
Type right_type = InputType(node, 1);
|
||||||
|
Type output_type;
|
||||||
|
if (left_type.IsNone() && right_type.IsNone()) {
|
||||||
|
// None x None -> None
|
||||||
|
output_type = Type::None();
|
||||||
|
} else if (left_type.Is(Type::Machine()) &&
|
||||||
|
right_type.Is(Type::Machine())) {
|
||||||
|
// Machine x Machine -> Machine
|
||||||
|
output_type = Type::Machine();
|
||||||
|
} else if (left_type.Is(Type::BigInt()) &&
|
||||||
|
right_type.Is(Type::BigInt())) {
|
||||||
|
// BigInt x BigInt -> BigInt
|
||||||
|
output_type = op_typer.BigIntSubtract(left_type, right_type);
|
||||||
|
} else if (left_type.Is(Type::Number()) &&
|
||||||
|
right_type.Is(Type::Number())) {
|
||||||
|
// Number x Number -> Number
|
||||||
|
output_type = op_typer.NumberSubtract(left_type, right_type);
|
||||||
|
} else {
|
||||||
|
// Invalid type combination.
|
||||||
|
ReportInvalidTypeCombination(node, {left_type, right_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
|
||||||
InputTruncation(node, 1),
|
InputTruncation(node, 1),
|
||||||
Truncation::Word64());
|
Truncation::Word64());
|
||||||
@ -327,8 +413,10 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrOpcode::kBranch: {
|
case IrOpcode::kBranch: {
|
||||||
CHECK(InputType(node, 0).Is(Type::Boolean()));
|
CHECK_EQ(BranchParametersOf(node->op()).semantics(),
|
||||||
CHECK_EQ(InputTruncation(node, 0), Truncation::Any());
|
BranchSemantics::kMachine);
|
||||||
|
Type input_type = InputType(node, 0);
|
||||||
|
CHECK(input_type.Is(Type::Boolean()) || input_type.Is(Type::Machine()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrOpcode::kTypedStateValues: {
|
case IrOpcode::kTypedStateValues: {
|
||||||
@ -337,7 +425,7 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
// Inputs must not be truncated.
|
// Inputs must not be truncated.
|
||||||
CHECK_EQ(InputTruncation(node, i), Truncation::Any());
|
CHECK_EQ(InputTruncation(node, i), Truncation::Any());
|
||||||
CHECK(IsNonTruncatingMachineTypeFor(machine_types->at(i),
|
CHECK(IsNonTruncatingMachineTypeFor(machine_types->at(i),
|
||||||
InputType(node, i)));
|
InputType(node, i), graph_zone()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -346,6 +434,11 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
SetTruncation(node, Truncation::Any());
|
SetTruncation(node, Truncation::Any());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IrOpcode::kEnterMachineGraph:
|
||||||
|
case IrOpcode::kExitMachineGraph: {
|
||||||
|
// Eliminated during lowering.
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
#define CASE(code, ...) case IrOpcode::k##code:
|
#define CASE(code, ...) case IrOpcode::k##code:
|
||||||
// Control operators
|
// Control operators
|
||||||
@ -489,7 +582,6 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
|
|||||||
CASE(Word64RolLowerable)
|
CASE(Word64RolLowerable)
|
||||||
CASE(Word64RorLowerable)
|
CASE(Word64RorLowerable)
|
||||||
CASE(Int64AddWithOverflow)
|
CASE(Int64AddWithOverflow)
|
||||||
CASE(Int64Sub)
|
|
||||||
CASE(Int64SubWithOverflow)
|
CASE(Int64SubWithOverflow)
|
||||||
CASE(Int64Mul)
|
CASE(Int64Mul)
|
||||||
CASE(Int64MulHigh)
|
CASE(Int64MulHigh)
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#ifndef V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
|
#ifndef V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
|
||||||
#define V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
|
#define V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
|
||||||
|
|
||||||
|
#include "src/base/container-utils.h"
|
||||||
|
#include "src/compiler/opcodes.h"
|
||||||
#include "src/compiler/representation-change.h"
|
#include "src/compiler/representation-change.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -21,7 +23,11 @@ class SimplifiedLoweringVerifier final {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SimplifiedLoweringVerifier(Zone* zone, Graph* graph)
|
SimplifiedLoweringVerifier(Zone* zone, Graph* graph)
|
||||||
: hints_(zone), data_(zone), graph_(graph) {}
|
: hints_(zone),
|
||||||
|
machine_uses_of_constants_(zone),
|
||||||
|
data_(zone),
|
||||||
|
graph_(graph),
|
||||||
|
zone_(zone) {}
|
||||||
|
|
||||||
void VisitNode(Node* node, OperationTyper& op_typer);
|
void VisitNode(Node* node, OperationTyper& op_typer);
|
||||||
|
|
||||||
@ -30,10 +36,33 @@ class SimplifiedLoweringVerifier final {
|
|||||||
hints_.push_back(node);
|
hints_.push_back(node);
|
||||||
}
|
}
|
||||||
const ZoneVector<Node*>& inserted_hints() const { return hints_; }
|
const ZoneVector<Node*>& inserted_hints() const { return hints_; }
|
||||||
|
void RecordMachineUsesOfConstant(Node* constant, Node::Uses uses) {
|
||||||
|
DCHECK(IrOpcode::IsMachineConstantOpcode(constant->opcode()));
|
||||||
|
auto it = machine_uses_of_constants_.find(constant);
|
||||||
|
if (it == machine_uses_of_constants_.end()) {
|
||||||
|
it =
|
||||||
|
machine_uses_of_constants_.emplace(constant, ZoneVector<Node*>(zone_))
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
base::vector_append(it->second, uses);
|
||||||
|
}
|
||||||
|
const ZoneUnorderedMap<Node*, ZoneVector<Node*>>& machine_uses_of_constants()
|
||||||
|
const {
|
||||||
|
return machine_uses_of_constants_;
|
||||||
|
}
|
||||||
|
|
||||||
base::Optional<Type> GetType(Node* node) const {
|
base::Optional<Type> GetType(Node* node) const {
|
||||||
if (NodeProperties::IsTyped(node)) {
|
if (NodeProperties::IsTyped(node)) {
|
||||||
return NodeProperties::GetType(node);
|
Type type = NodeProperties::GetType(node);
|
||||||
|
// We do not use the static type for constants, even if we have one,
|
||||||
|
// because those are cached in the graph and shared between machine
|
||||||
|
// and non-machine subgraphs. The former might have assigned
|
||||||
|
// Type::Machine() to them.
|
||||||
|
if (IrOpcode::IsMachineConstantOpcode(node->opcode())) {
|
||||||
|
DCHECK(type.Is(Type::Machine()));
|
||||||
|
} else {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// For nodes that have not been typed before SL, we use the type that has
|
// For nodes that have not been typed before SL, we use the type that has
|
||||||
// been inferred by the verifier.
|
// been inferred by the verifier.
|
||||||
@ -60,16 +89,7 @@ class SimplifiedLoweringVerifier final {
|
|||||||
Type InputType(Node* node, int input_index) const {
|
Type InputType(Node* node, int input_index) const {
|
||||||
// TODO(nicohartmann): Check that inputs are typed, once all operators are
|
// TODO(nicohartmann): Check that inputs are typed, once all operators are
|
||||||
// supported.
|
// supported.
|
||||||
Node* input = node->InputAt(input_index);
|
auto type_opt = GetType(node->InputAt(input_index));
|
||||||
if (NodeProperties::IsTyped(input)) {
|
|
||||||
return NodeProperties::GetType(input);
|
|
||||||
}
|
|
||||||
// For nodes that have not been typed before SL, we use the type that has
|
|
||||||
// been inferred by the verifier.
|
|
||||||
base::Optional<Type> type_opt;
|
|
||||||
if (input->id() < data_.size()) {
|
|
||||||
type_opt = data_[input->id()].type;
|
|
||||||
}
|
|
||||||
return type_opt.has_value() ? *type_opt : Type::None();
|
return type_opt.has_value() ? *type_opt : Type::None();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +111,7 @@ class SimplifiedLoweringVerifier final {
|
|||||||
|
|
||||||
void CheckType(Node* node, const Type& type);
|
void CheckType(Node* node, const Type& type);
|
||||||
void CheckAndSet(Node* node, const Type& type, const Truncation& trunc);
|
void CheckAndSet(Node* node, const Type& type, const Truncation& trunc);
|
||||||
|
void ReportInvalidTypeCombination(Node* node, const std::vector<Type>& types);
|
||||||
|
|
||||||
// Generalize to a less strict truncation in the context of a given type. For
|
// Generalize to a less strict truncation in the context of a given type. For
|
||||||
// example, a Truncation::kWord32[kIdentifyZeros] does not have any effect on
|
// example, a Truncation::kWord32[kIdentifyZeros] does not have any effect on
|
||||||
@ -104,8 +125,10 @@ class SimplifiedLoweringVerifier final {
|
|||||||
Zone* graph_zone() const { return graph_->zone(); }
|
Zone* graph_zone() const { return graph_->zone(); }
|
||||||
|
|
||||||
ZoneVector<Node*> hints_;
|
ZoneVector<Node*> hints_;
|
||||||
|
ZoneUnorderedMap<Node*, ZoneVector<Node*>> machine_uses_of_constants_;
|
||||||
ZoneVector<PerNodeData> data_;
|
ZoneVector<PerNodeData> data_;
|
||||||
Graph* graph_;
|
Graph* graph_;
|
||||||
|
Zone* zone_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
|
@ -396,6 +396,11 @@ class RepresentationSelector {
|
|||||||
|
|
||||||
bool UpdateFeedbackType(Node* node) {
|
bool UpdateFeedbackType(Node* node) {
|
||||||
if (node->op()->ValueOutputCount() == 0) return false;
|
if (node->op()->ValueOutputCount() == 0) return false;
|
||||||
|
if ((IrOpcode::IsMachineOpcode(node->opcode()) ||
|
||||||
|
IrOpcode::IsMachineConstantOpcode(node->opcode())) &&
|
||||||
|
node->opcode() != IrOpcode::kLoadFramePointer) {
|
||||||
|
DCHECK(NodeProperties::GetType(node).Is(Type::Machine()));
|
||||||
|
}
|
||||||
|
|
||||||
// For any non-phi node just wait until we get all inputs typed. We only
|
// For any non-phi node just wait until we get all inputs typed. We only
|
||||||
// allow untyped inputs for phi nodes because phis are the only places
|
// allow untyped inputs for phi nodes because phis are the only places
|
||||||
@ -595,7 +600,6 @@ class RepresentationSelector {
|
|||||||
while (!stack.empty()) {
|
while (!stack.empty()) {
|
||||||
NodeState& current = stack.top();
|
NodeState& current = stack.top();
|
||||||
Node* node = current.node;
|
Node* node = current.node;
|
||||||
|
|
||||||
// If there is an unvisited input, push it and continue with that node.
|
// If there is an unvisited input, push it and continue with that node.
|
||||||
bool pushed_unvisited = false;
|
bool pushed_unvisited = false;
|
||||||
while (current.input_index < node->InputCount()) {
|
while (current.input_index < node->InputCount()) {
|
||||||
@ -750,6 +754,19 @@ class RepresentationSelector {
|
|||||||
|
|
||||||
TRACE("--{Verify Phase}--\n");
|
TRACE("--{Verify Phase}--\n");
|
||||||
|
|
||||||
|
// Patch pending type overrides.
|
||||||
|
for (auto [constant, uses] : verifier_->machine_uses_of_constants()) {
|
||||||
|
Node* typed_constant =
|
||||||
|
InsertTypeOverrideForVerifier(Type::Machine(), constant);
|
||||||
|
for (auto use : uses) {
|
||||||
|
for (int i = 0; i < use->InputCount(); ++i) {
|
||||||
|
if (use->InputAt(i) == constant) {
|
||||||
|
use->ReplaceInput(i, typed_constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a new traversal containing all the new nodes created during
|
// Generate a new traversal containing all the new nodes created during
|
||||||
// lowering.
|
// lowering.
|
||||||
GenerateTraversal();
|
GenerateTraversal();
|
||||||
@ -774,7 +791,9 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify all nodes.
|
// Verify all nodes.
|
||||||
for (Node* node : traversal_nodes_) verifier_->VisitNode(node, op_typer_);
|
for (Node* node : traversal_nodes_) {
|
||||||
|
verifier_->VisitNode(node, op_typer_);
|
||||||
|
}
|
||||||
|
|
||||||
// Print graph.
|
// Print graph.
|
||||||
if (info != nullptr && info->trace_turbo_json()) {
|
if (info != nullptr && info->trace_turbo_json()) {
|
||||||
@ -1065,7 +1084,7 @@ class RepresentationSelector {
|
|||||||
void VisitNoop(Node* node, Truncation truncation) {
|
void VisitNoop(Node* node, Truncation truncation) {
|
||||||
if (truncation.IsUnused()) return VisitUnused<T>(node);
|
if (truncation.IsUnused()) return VisitUnused<T>(node);
|
||||||
MachineRepresentation representation =
|
MachineRepresentation representation =
|
||||||
GetOutputInfoForPhi(node, TypeOf(node), truncation);
|
GetOutputInfoForPhi(TypeOf(node), truncation);
|
||||||
VisitUnop<T>(node, UseInfo(representation, truncation), representation);
|
VisitUnop<T>(node, UseInfo(representation, truncation), representation);
|
||||||
if (lower<T>()) DeferReplacement(node, node->InputAt(0));
|
if (lower<T>()) DeferReplacement(node, node->InputAt(0));
|
||||||
}
|
}
|
||||||
@ -1141,11 +1160,7 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Infer representation for phi-like nodes.
|
// Infer representation for phi-like nodes.
|
||||||
// The {node} parameter is only used to decide on the int64 representation.
|
MachineRepresentation GetOutputInfoForPhi(Type type, Truncation use) {
|
||||||
// Once the type system supports an external pointer type, the {node}
|
|
||||||
// parameter can be removed.
|
|
||||||
MachineRepresentation GetOutputInfoForPhi(Node* node, Type type,
|
|
||||||
Truncation use) {
|
|
||||||
// Compute the representation.
|
// Compute the representation.
|
||||||
if (type.Is(Type::None())) {
|
if (type.Is(Type::None())) {
|
||||||
return MachineRepresentation::kNone;
|
return MachineRepresentation::kNone;
|
||||||
@ -1183,7 +1198,7 @@ class RepresentationSelector {
|
|||||||
ProcessInput<T>(node, 0, UseInfo::Bool());
|
ProcessInput<T>(node, 0, UseInfo::Bool());
|
||||||
|
|
||||||
MachineRepresentation output =
|
MachineRepresentation output =
|
||||||
GetOutputInfoForPhi(node, TypeOf(node), truncation);
|
GetOutputInfoForPhi(TypeOf(node), truncation);
|
||||||
SetOutput<T>(node, output);
|
SetOutput<T>(node, output);
|
||||||
|
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
@ -1204,8 +1219,13 @@ class RepresentationSelector {
|
|||||||
template <Phase T>
|
template <Phase T>
|
||||||
void VisitPhi(Node* node, Truncation truncation,
|
void VisitPhi(Node* node, Truncation truncation,
|
||||||
SimplifiedLowering* lowering) {
|
SimplifiedLowering* lowering) {
|
||||||
MachineRepresentation output =
|
// If we already have a non-tagged representation set in the Phi node, it
|
||||||
GetOutputInfoForPhi(node, TypeOf(node), truncation);
|
// does come from subgraphs using machine operators we introduced early in
|
||||||
|
// the pipeline. In this case, we just keep the representation.
|
||||||
|
MachineRepresentation output = PhiRepresentationOf(node->op());
|
||||||
|
if (output == MachineRepresentation::kTagged) {
|
||||||
|
output = GetOutputInfoForPhi(TypeOf(node), truncation);
|
||||||
|
}
|
||||||
// Only set the output representation if not running with type
|
// Only set the output representation if not running with type
|
||||||
// feedback. (Feedback typing will set the representation.)
|
// feedback. (Feedback typing will set the representation.)
|
||||||
SetOutput<T>(node, output);
|
SetOutput<T>(node, output);
|
||||||
@ -1232,12 +1252,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(type)) {
|
if (input_type.Is(type)) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(
|
||||||
|
node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(), lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
|
||||||
if (lower<T>() && !input_type.Maybe(type)) {
|
if (lower<T>() && !input_type.Maybe(type)) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(
|
||||||
|
node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(), lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2140,7 +2164,18 @@ class RepresentationSelector {
|
|||||||
->GetParameterType(ParameterIndexOf(node->op()))
|
->GetParameterType(ParameterIndexOf(node->op()))
|
||||||
.representation());
|
.representation());
|
||||||
case IrOpcode::kInt32Constant:
|
case IrOpcode::kInt32Constant:
|
||||||
return VisitLeaf<T>(node, MachineRepresentation::kWord32);
|
DCHECK_EQ(0, node->InputCount());
|
||||||
|
SetOutput<T>(node, MachineRepresentation::kWord32);
|
||||||
|
DCHECK(NodeProperties::GetType(node).Is(Type::Machine()));
|
||||||
|
if (verification_enabled()) {
|
||||||
|
// During lowering, SimplifiedLowering generates Int32Constants which
|
||||||
|
// need to be treated differently by the verifier than the
|
||||||
|
// Int32Constants introduced explicitly in machine graphs. To be able
|
||||||
|
// to distinguish them, we record those that are being visited here
|
||||||
|
// because they were generated before SimplifiedLowering.
|
||||||
|
verifier_->RecordMachineUsesOfConstant(node, node->uses());
|
||||||
|
}
|
||||||
|
return;
|
||||||
case IrOpcode::kInt64Constant:
|
case IrOpcode::kInt64Constant:
|
||||||
return VisitLeaf<T>(node, MachineRepresentation::kWord64);
|
return VisitLeaf<T>(node, MachineRepresentation::kWord64);
|
||||||
case IrOpcode::kExternalConstant:
|
case IrOpcode::kExternalConstant:
|
||||||
@ -2174,8 +2209,19 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case IrOpcode::kBranch: {
|
case IrOpcode::kBranch: {
|
||||||
|
const auto& p = BranchParametersOf(node->op());
|
||||||
|
if (p.semantics() == BranchSemantics::kMachine) {
|
||||||
|
// If this is a machine branch, the condition is a machine operator,
|
||||||
|
// so we enter machine branch here.
|
||||||
|
ProcessInput<T>(node, 0, UseInfo::Any());
|
||||||
|
} else {
|
||||||
DCHECK(TypeOf(node->InputAt(0)).Is(Type::Boolean()));
|
DCHECK(TypeOf(node->InputAt(0)).Is(Type::Boolean()));
|
||||||
ProcessInput<T>(node, 0, UseInfo::Bool());
|
ProcessInput<T>(node, 0, UseInfo::Bool());
|
||||||
|
if (lower<T>()) {
|
||||||
|
ChangeOp(node,
|
||||||
|
common()->Branch(p.hint(), BranchSemantics::kMachine));
|
||||||
|
}
|
||||||
|
}
|
||||||
EnqueueInput<T>(node, NodeProperties::FirstControlIndex(node));
|
EnqueueInput<T>(node, NodeProperties::FirstControlIndex(node));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3737,7 +3783,7 @@ class RepresentationSelector {
|
|||||||
if (InputIs(node, Type::Boolean())) {
|
if (InputIs(node, Type::Boolean())) {
|
||||||
VisitUnop<T>(node, UseInfo::Bool(), MachineRepresentation::kWord32);
|
VisitUnop<T>(node, UseInfo::Bool(), MachineRepresentation::kWord32);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
ChangeToSemanticsHintForVerifier(node, node->op());
|
DeferReplacement(node, node->InputAt(0));
|
||||||
}
|
}
|
||||||
} else if (InputIs(node, Type::String())) {
|
} else if (InputIs(node, Type::String())) {
|
||||||
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
||||||
@ -3750,7 +3796,7 @@ class RepresentationSelector {
|
|||||||
VisitUnop<T>(node, UseInfo::TruncatingWord32(),
|
VisitUnop<T>(node, UseInfo::TruncatingWord32(),
|
||||||
MachineRepresentation::kWord32);
|
MachineRepresentation::kWord32);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
ChangeToSemanticsHintForVerifier(node, node->op());
|
DeferReplacement(node, node->InputAt(0));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
||||||
@ -3764,7 +3810,7 @@ class RepresentationSelector {
|
|||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
MachineRepresentation::kFloat64);
|
MachineRepresentation::kFloat64);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
ChangeToSemanticsHintForVerifier(node, node->op());
|
DeferReplacement(node, node->InputAt(0));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
||||||
@ -3828,12 +3874,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(type_cache_->kSafeInteger)) {
|
if (input_type.Is(type_cache_->kSafeInteger)) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else if (!input_type.Maybe(Type::Number())) {
|
} else if (!input_type.Maybe(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
} else if (input_type.Is(Type::Number())) {
|
} else if (input_type.Is(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
@ -3856,12 +3906,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(type_cache_->kSafeInteger)) {
|
if (input_type.Is(type_cache_->kSafeInteger)) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else if (!input_type.Maybe(Type::Number())) {
|
} else if (!input_type.Maybe(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
} else if (input_type.Is(Type::Number())) {
|
} else if (input_type.Is(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
@ -3882,12 +3936,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(type_cache_->kSafeInteger)) {
|
if (input_type.Is(type_cache_->kSafeInteger)) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else if (!input_type.Maybe(Type::Number())) {
|
} else if (!input_type.Maybe(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
} else if (input_type.Is(Type::Number())) {
|
} else if (input_type.Is(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
@ -3910,12 +3968,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(Type::MinusZero())) {
|
if (input_type.Is(Type::MinusZero())) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else if (!input_type.Maybe(Type::MinusZero())) {
|
} else if (!input_type.Maybe(Type::MinusZero())) {
|
||||||
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
} else if (input_type.Is(Type::Number())) {
|
} else if (input_type.Is(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
@ -3933,12 +3995,16 @@ class RepresentationSelector {
|
|||||||
if (input_type.Is(Type::NaN())) {
|
if (input_type.Is(Type::NaN())) {
|
||||||
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(1)));
|
||||||
}
|
}
|
||||||
} else if (!input_type.Maybe(Type::NaN())) {
|
} else if (!input_type.Maybe(Type::NaN())) {
|
||||||
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit);
|
||||||
if (lower<T>()) {
|
if (lower<T>()) {
|
||||||
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Boolean(),
|
||||||
|
lowering->jsgraph()->Int32Constant(0)));
|
||||||
}
|
}
|
||||||
} else if (input_type.Is(Type::Number())) {
|
} else if (input_type.Is(Type::Number())) {
|
||||||
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
|
||||||
@ -4123,7 +4189,7 @@ class RepresentationSelector {
|
|||||||
// for the sigma's type.
|
// for the sigma's type.
|
||||||
Type type = TypeOf(node);
|
Type type = TypeOf(node);
|
||||||
MachineRepresentation representation =
|
MachineRepresentation representation =
|
||||||
GetOutputInfoForPhi(node, type, truncation);
|
GetOutputInfoForPhi(type, truncation);
|
||||||
|
|
||||||
// Here we pretend that the input has the sigma's type for the
|
// Here we pretend that the input has the sigma's type for the
|
||||||
// conversion.
|
// conversion.
|
||||||
@ -4256,6 +4322,65 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case IrOpcode::kDebugBreak:
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Nodes from machine graphs.
|
||||||
|
case IrOpcode::kEnterMachineGraph: {
|
||||||
|
DCHECK_EQ(1, node->op()->ValueInputCount());
|
||||||
|
UseInfo use_info = OpParameter<UseInfo>(node->op());
|
||||||
|
ProcessInput<T>(node, 0, use_info);
|
||||||
|
SetOutput<T>(node, use_info.representation());
|
||||||
|
if (lower<T>()) {
|
||||||
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
Type::Machine(), node->InputAt(0)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case IrOpcode::kExitMachineGraph: {
|
||||||
|
DCHECK_EQ(1, node->op()->ValueInputCount());
|
||||||
|
ProcessInput<T>(node, 0, UseInfo::Any());
|
||||||
|
const auto& p = ExitMachineGraphParametersOf(node->op());
|
||||||
|
SetOutput<T>(node, p.output_representation(), p.output_type());
|
||||||
|
if (lower<T>()) {
|
||||||
|
DeferReplacement(node, InsertTypeOverrideForVerifier(
|
||||||
|
p.output_type(), node->InputAt(0)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case IrOpcode::kInt32Add:
|
||||||
|
case IrOpcode::kInt32Sub:
|
||||||
|
case IrOpcode::kUint32LessThan:
|
||||||
|
case IrOpcode::kUint32LessThanOrEqual:
|
||||||
|
case IrOpcode::kUint64LessThanOrEqual:
|
||||||
|
case IrOpcode::kUint32Div:
|
||||||
|
case IrOpcode::kWord32And:
|
||||||
|
case IrOpcode::kWord32Equal:
|
||||||
|
case IrOpcode::kWord32Or:
|
||||||
|
case IrOpcode::kWord32Shl:
|
||||||
|
case IrOpcode::kWord32Shr:
|
||||||
|
for (int i = 0; i < node->InputCount(); ++i) {
|
||||||
|
ProcessInput<T>(node, i, UseInfo::Any());
|
||||||
|
}
|
||||||
|
SetOutput<T>(node, MachineRepresentation::kWord32);
|
||||||
|
return;
|
||||||
|
case IrOpcode::kInt64Add:
|
||||||
|
case IrOpcode::kInt64Sub:
|
||||||
|
case IrOpcode::kUint64Div:
|
||||||
|
case IrOpcode::kWord64And:
|
||||||
|
case IrOpcode::kWord64Shl:
|
||||||
|
case IrOpcode::kWord64Shr:
|
||||||
|
for (int i = 0; i < node->InputCount(); ++i) {
|
||||||
|
ProcessInput<T>(node, i, UseInfo::Any());
|
||||||
|
}
|
||||||
|
SetOutput<T>(node, MachineRepresentation::kWord64);
|
||||||
|
return;
|
||||||
|
case IrOpcode::kLoad:
|
||||||
|
for (int i = 0; i < node->InputCount(); ++i) {
|
||||||
|
ProcessInput<T>(node, i, UseInfo::Any());
|
||||||
|
}
|
||||||
|
SetOutput<T>(node, LoadRepresentationOf(node->op()).representation());
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FATAL(
|
FATAL(
|
||||||
@ -4301,18 +4426,6 @@ class RepresentationSelector {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeToSemanticsHintForVerifier(Node* node, const Operator* semantics) {
|
|
||||||
DCHECK_EQ(node->op()->ValueInputCount(), 1);
|
|
||||||
DCHECK_EQ(node->op()->EffectInputCount(), 0);
|
|
||||||
DCHECK_EQ(node->op()->ControlInputCount(), 0);
|
|
||||||
if (verification_enabled()) {
|
|
||||||
ChangeOp(node, common()->SLVerifierHint(semantics, base::nullopt));
|
|
||||||
verifier_->RecordHint(node);
|
|
||||||
} else {
|
|
||||||
DeferReplacement(node, node->InputAt(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ChangeOp(Node* node, const Operator* new_op) {
|
void ChangeOp(Node* node, const Operator* new_op) {
|
||||||
compiler::NodeProperties::ChangeOp(node, new_op);
|
compiler::NodeProperties::ChangeOp(node, new_op);
|
||||||
@ -4565,8 +4678,9 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
|
|||||||
Node* control = node->InputAt(4);
|
Node* control = node->InputAt(4);
|
||||||
|
|
||||||
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
||||||
Node* branch0 =
|
Node* branch0 = graph()->NewNode(
|
||||||
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
|
common()->Branch(BranchHint::kTrue, BranchSemantics::kMachine), check0,
|
||||||
|
control);
|
||||||
|
|
||||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
Node* etrue0 = effect;
|
Node* etrue0 = effect;
|
||||||
@ -4604,7 +4718,9 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
|
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
|
Node* branch1 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine), check1,
|
||||||
|
if_false0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* etrue1 = efalse0;
|
Node* etrue1 = efalse0;
|
||||||
@ -4667,8 +4783,9 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
|
|||||||
Node* control = node->InputAt(4);
|
Node* control = node->InputAt(4);
|
||||||
|
|
||||||
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
||||||
Node* branch0 =
|
Node* branch0 = graph()->NewNode(
|
||||||
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
|
common()->Branch(BranchHint::kTrue, BranchSemantics::kMachine), check0,
|
||||||
|
control);
|
||||||
|
|
||||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
Node* etrue0 = effect;
|
Node* etrue0 = effect;
|
||||||
@ -4703,7 +4820,9 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
|
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
|
Node* branch1 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine), check1,
|
||||||
|
if_false0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* etrue1 = efalse0;
|
Node* etrue1 = efalse0;
|
||||||
@ -4831,7 +4950,8 @@ Node* SimplifiedLowering::Int32Div(Node* const node) {
|
|||||||
common()->Phi(MachineRepresentation::kWord32, 2);
|
common()->Phi(MachineRepresentation::kWord32, 2);
|
||||||
|
|
||||||
Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
|
Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
|
||||||
Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0,
|
Node* branch0 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kTrue, BranchSemantics::kMachine), check0,
|
||||||
graph()->start());
|
graph()->start());
|
||||||
|
|
||||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
@ -4841,7 +4961,9 @@ Node* SimplifiedLowering::Int32Div(Node* const node) {
|
|||||||
Node* false0;
|
Node* false0;
|
||||||
{
|
{
|
||||||
Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
|
Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
|
Node* branch1 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine), check1,
|
||||||
|
if_false0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* true1 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true1);
|
Node* true1 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true1);
|
||||||
@ -4850,7 +4972,9 @@ Node* SimplifiedLowering::Int32Div(Node* const node) {
|
|||||||
Node* false1;
|
Node* false1;
|
||||||
{
|
{
|
||||||
Node* check2 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
Node* check2 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
||||||
Node* branch2 = graph()->NewNode(common()->Branch(), check2, if_false1);
|
Node* branch2 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine),
|
||||||
|
check2, if_false1);
|
||||||
|
|
||||||
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
||||||
Node* true2 = zero;
|
Node* true2 = zero;
|
||||||
@ -4908,7 +5032,8 @@ Node* SimplifiedLowering::Int32Mod(Node* const node) {
|
|||||||
common()->Phi(MachineRepresentation::kWord32, 2);
|
common()->Phi(MachineRepresentation::kWord32, 2);
|
||||||
|
|
||||||
Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
|
Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
|
||||||
Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0,
|
Node* branch0 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kTrue, BranchSemantics::kMachine), check0,
|
||||||
graph()->start());
|
graph()->start());
|
||||||
|
|
||||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
@ -4917,7 +5042,9 @@ Node* SimplifiedLowering::Int32Mod(Node* const node) {
|
|||||||
Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);
|
Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);
|
||||||
|
|
||||||
Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
|
Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0);
|
Node* branch1 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine), check1,
|
||||||
|
if_true0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);
|
Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);
|
||||||
@ -4926,7 +5053,8 @@ Node* SimplifiedLowering::Int32Mod(Node* const node) {
|
|||||||
Node* false1;
|
Node* false1;
|
||||||
{
|
{
|
||||||
Node* check2 = graph()->NewNode(machine()->Int32LessThan(), lhs, zero);
|
Node* check2 = graph()->NewNode(machine()->Int32LessThan(), lhs, zero);
|
||||||
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
Node* branch2 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kFalse, BranchSemantics::kMachine),
|
||||||
check2, if_false1);
|
check2, if_false1);
|
||||||
|
|
||||||
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
||||||
@ -4951,8 +5079,9 @@ Node* SimplifiedLowering::Int32Mod(Node* const node) {
|
|||||||
Node* false0;
|
Node* false0;
|
||||||
{
|
{
|
||||||
Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
|
Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
Node* branch1 = graph()->NewNode(
|
||||||
check1, if_false0);
|
common()->Branch(BranchHint::kTrue, BranchSemantics::kMachine), check1,
|
||||||
|
if_false0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);
|
Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);
|
||||||
@ -4997,7 +5126,8 @@ Node* SimplifiedLowering::Uint32Div(Node* const node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
||||||
Diamond d(graph(), common(), check, BranchHint::kFalse);
|
Diamond d(graph(), common(), check, BranchHint::kFalse,
|
||||||
|
BranchSemantics::kMachine);
|
||||||
Node* div = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, d.if_false);
|
Node* div = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, d.if_false);
|
||||||
return d.Phi(MachineRepresentation::kWord32, zero, div);
|
return d.Phi(MachineRepresentation::kWord32, zero, div);
|
||||||
}
|
}
|
||||||
@ -5034,7 +5164,8 @@ Node* SimplifiedLowering::Uint32Mod(Node* const node) {
|
|||||||
common()->Phi(MachineRepresentation::kWord32, 2);
|
common()->Phi(MachineRepresentation::kWord32, 2);
|
||||||
|
|
||||||
Node* check0 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
Node* check0 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
|
||||||
Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0,
|
Node* branch0 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kFalse, BranchSemantics::kMachine), check0,
|
||||||
graph()->start());
|
graph()->start());
|
||||||
|
|
||||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
@ -5046,7 +5177,9 @@ Node* SimplifiedLowering::Uint32Mod(Node* const node) {
|
|||||||
Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);
|
Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);
|
||||||
|
|
||||||
Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
|
Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
|
||||||
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
|
Node* branch1 = graph()->NewNode(
|
||||||
|
common()->Branch(BranchHint::kNone, BranchSemantics::kMachine), check1,
|
||||||
|
if_false0);
|
||||||
|
|
||||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
Node* true1 = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, if_true1);
|
Node* true1 = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, if_true1);
|
||||||
|
@ -88,6 +88,7 @@ class Typer::Visitor : public Reducer {
|
|||||||
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_BINARY_CASE)
|
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_BINARY_CASE)
|
||||||
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_BINARY_CASE)
|
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_BINARY_CASE)
|
||||||
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_BINARY_CASE)
|
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_BINARY_CASE)
|
||||||
|
TYPER_SUPPORTED_MACHINE_BINOP_LIST(DECLARE_BINARY_CASE)
|
||||||
#undef DECLARE_BINARY_CASE
|
#undef DECLARE_BINARY_CASE
|
||||||
#define DECLARE_OTHER_CASE(x, ...) \
|
#define DECLARE_OTHER_CASE(x, ...) \
|
||||||
case IrOpcode::k##x: \
|
case IrOpcode::k##x: \
|
||||||
@ -125,7 +126,139 @@ class Typer::Visitor : public Reducer {
|
|||||||
SIMPLIFIED_CHECKED_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
SIMPLIFIED_CHECKED_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
IF_WASM(SIMPLIFIED_WASM_OP_LIST, DECLARE_IMPOSSIBLE_CASE)
|
IF_WASM(SIMPLIFIED_WASM_OP_LIST, DECLARE_IMPOSSIBLE_CASE)
|
||||||
MACHINE_SIMD_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
MACHINE_SIMD_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
MACHINE_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
MACHINE_UNOP_32_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Xor)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Sar)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Rol)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Ror)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32AddWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32SubWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32Mul)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32MulWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32MulHigh)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32Div)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32Mod)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Uint32Mod)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Uint32MulHigh)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Or)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Xor)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Sar)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Rol)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Ror)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64RolLowerable)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64RorLowerable)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64AddWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64SubWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64Mul)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64MulHigh)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64MulWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64Div)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64Mod)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Uint64Mod)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Uint64MulHigh)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Equal)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32LessThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32LessThanOrEqual)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64LessThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64LessThanOrEqual)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Uint64LessThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float32Equal)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float32LessThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float32LessThanOrEqual)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64Equal)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64LessThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64LessThanOrEqual)
|
||||||
|
MACHINE_FLOAT32_BINOP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
MACHINE_FLOAT32_UNOP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
MACHINE_FLOAT64_BINOP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
MACHINE_FLOAT64_UNOP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
MACHINE_ATOMIC_OP_LIST(DECLARE_IMPOSSIBLE_CASE)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(AbortCSADcheck)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(DebugBreak)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Comment)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(LoadImmutable)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Store)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(StackSlot)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Popcnt)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Popcnt)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Clz)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Ctz)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64ClzLowerable)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64CtzLowerable)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64ReverseBits)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64ReverseBytes)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Simd128ReverseBytes)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int64AbsWithOverflow)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastTaggedToWord)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastTaggedToWordForTagAndSmiBits)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastWordToTagged)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastWordToTaggedSigned)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat64ToWord32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeFloat32ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeFloat64ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeFloat64ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeFloat64ToUint32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeFloat64ToUint64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64SilenceNaN)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat64ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat64ToUint32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat32ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat32ToUint32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat32ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat64ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat32ToUint64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat64ToUint64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat64ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TryTruncateFloat64ToUint32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeInt32ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastWord32ToWord64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeInt32ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeInt64ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeUint32ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ChangeUint32ToUint64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateFloat64ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TruncateInt64ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundFloat64ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundInt32ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundInt64ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundInt64ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundUint32ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundUint64ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(RoundUint64ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastFloat32ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastFloat64ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastInt32ToFloat32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(BitcastInt64ToFloat64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64ExtractLowWord32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64ExtractHighWord32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64InsertLowWord32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64InsertHighWord32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32Select)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word64Select)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float32Select)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Float64Select)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(LoadStackCheckOffset)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(LoadFramePointer)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(LoadParentFramePointer)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(UnalignedLoad)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(UnalignedStore)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32PairAdd)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32PairSub)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Int32PairMul)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32PairShl)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32PairShr)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(Word32PairSar)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ProtectedLoad)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(ProtectedStore)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(MemoryBarrier)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(SignExtendWord8ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(SignExtendWord16ToInt32)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(SignExtendWord8ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(SignExtendWord16ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(SignExtendWord32ToInt64)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(StackPointerGreaterThan)
|
||||||
|
DECLARE_IMPOSSIBLE_CASE(TraceInstruction)
|
||||||
|
|
||||||
#undef DECLARE_IMPOSSIBLE_CASE
|
#undef DECLARE_IMPOSSIBLE_CASE
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -228,6 +361,7 @@ class Typer::Visitor : public Reducer {
|
|||||||
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
|
TYPER_SUPPORTED_MACHINE_BINOP_LIST(DECLARE_METHOD)
|
||||||
#undef DECLARE_METHOD
|
#undef DECLARE_METHOD
|
||||||
#define DECLARE_METHOD(Name, ...) \
|
#define DECLARE_METHOD(Name, ...) \
|
||||||
inline Type Type##Name(Type left, Type right) { \
|
inline Type Type##Name(Type left, Type right) { \
|
||||||
@ -243,6 +377,7 @@ class Typer::Visitor : public Reducer {
|
|||||||
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
|
||||||
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
|
||||||
|
TYPER_SUPPORTED_MACHINE_BINOP_LIST(DECLARE_METHOD)
|
||||||
#undef DECLARE_METHOD
|
#undef DECLARE_METHOD
|
||||||
#define DECLARE_METHOD(Name, ...) \
|
#define DECLARE_METHOD(Name, ...) \
|
||||||
inline Type Type##Name(Type input) { return TypeUnaryOp(input, Name); }
|
inline Type Type##Name(Type input) { return TypeUnaryOp(input, Name); }
|
||||||
@ -728,9 +863,9 @@ Type Typer::Visitor::TypeOsrValue(Node* node) {
|
|||||||
|
|
||||||
Type Typer::Visitor::TypeRetain(Node* node) { UNREACHABLE(); }
|
Type Typer::Visitor::TypeRetain(Node* node) { UNREACHABLE(); }
|
||||||
|
|
||||||
Type Typer::Visitor::TypeInt32Constant(Node* node) { UNREACHABLE(); }
|
Type Typer::Visitor::TypeInt32Constant(Node* node) { return Type::Machine(); }
|
||||||
|
|
||||||
Type Typer::Visitor::TypeInt64Constant(Node* node) { UNREACHABLE(); }
|
Type Typer::Visitor::TypeInt64Constant(Node* node) { return Type::Machine(); }
|
||||||
|
|
||||||
Type Typer::Visitor::TypeTaggedIndexConstant(Node* node) { UNREACHABLE(); }
|
Type Typer::Visitor::TypeTaggedIndexConstant(Node* node) { UNREACHABLE(); }
|
||||||
|
|
||||||
@ -774,6 +909,14 @@ Type Typer::Visitor::TypePhi(Node* node) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type Typer::Visitor::TypeEnterMachineGraph(Node* node) {
|
||||||
|
return Type::Machine();
|
||||||
|
}
|
||||||
|
|
||||||
|
Type Typer::Visitor::TypeExitMachineGraph(Node* node) {
|
||||||
|
return ExitMachineGraphParametersOf(node->op()).output_type();
|
||||||
|
}
|
||||||
|
|
||||||
Type Typer::Visitor::TypeInductionVariablePhi(Node* node) {
|
Type Typer::Visitor::TypeInductionVariablePhi(Node* node) {
|
||||||
int arity = NodeProperties::GetControlInput(node)->op()->ControlInputCount();
|
int arity = NodeProperties::GetControlInput(node)->op()->ControlInputCount();
|
||||||
DCHECK_EQ(IrOpcode::kLoop, NodeProperties::GetControlInput(node)->opcode());
|
DCHECK_EQ(IrOpcode::kLoop, NodeProperties::GetControlInput(node)->opcode());
|
||||||
|
@ -49,6 +49,7 @@ namespace compiler {
|
|||||||
//
|
//
|
||||||
// Constant(x) < T iff instance_type(map(x)) < T
|
// Constant(x) < T iff instance_type(map(x)) < T
|
||||||
//
|
//
|
||||||
|
// None <= Machine <= Any
|
||||||
//
|
//
|
||||||
// RANGE TYPES
|
// RANGE TYPES
|
||||||
//
|
//
|
||||||
@ -140,7 +141,8 @@ namespace compiler {
|
|||||||
// We split the macro list into two parts because the Torque equivalent in
|
// We split the macro list into two parts because the Torque equivalent in
|
||||||
// turbofan-types.tq uses two 32bit bitfield structs.
|
// turbofan-types.tq uses two 32bit bitfield structs.
|
||||||
#define PROPER_ATOMIC_BITSET_TYPE_HIGH_LIST(V) \
|
#define PROPER_ATOMIC_BITSET_TYPE_HIGH_LIST(V) \
|
||||||
V(SandboxedPointer, uint64_t{1} << 32)
|
V(SandboxedPointer, uint64_t{1} << 32) \
|
||||||
|
V(Machine, uint64_t{1} << 33)
|
||||||
|
|
||||||
#define PROPER_BITSET_TYPE_LIST(V) \
|
#define PROPER_BITSET_TYPE_LIST(V) \
|
||||||
V(None, uint64_t{0}) \
|
V(None, uint64_t{0}) \
|
||||||
|
@ -312,8 +312,16 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
}
|
}
|
||||||
CHECK_EQ(1, count_true);
|
CHECK_EQ(1, count_true);
|
||||||
CHECK_EQ(1, count_false);
|
CHECK_EQ(1, count_false);
|
||||||
|
switch (BranchParametersOf(node->op()).semantics()) {
|
||||||
|
case BranchSemantics::kJS:
|
||||||
|
case BranchSemantics::kUnspecified:
|
||||||
// The condition must be a Boolean.
|
// The condition must be a Boolean.
|
||||||
CheckValueInputIs(node, 0, Type::Boolean());
|
CheckValueInputIs(node, 0, Type::Boolean());
|
||||||
|
break;
|
||||||
|
case BranchSemantics::kMachine:
|
||||||
|
CheckValueInputIs(node, 0, Type::Machine());
|
||||||
|
break;
|
||||||
|
}
|
||||||
CheckNotTyped(node);
|
CheckNotTyped(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -606,6 +614,12 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
case IrOpcode::kTailCall:
|
case IrOpcode::kTailCall:
|
||||||
// TODO(bmeurer): what are the constraints on these?
|
// TODO(bmeurer): what are the constraints on these?
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kEnterMachineGraph:
|
||||||
|
CheckTypeIs(node, Type::Machine());
|
||||||
|
break;
|
||||||
|
case IrOpcode::kExitMachineGraph:
|
||||||
|
CheckValueInputIs(node, 0, Type::Machine());
|
||||||
|
break;
|
||||||
|
|
||||||
// JavaScript operators
|
// JavaScript operators
|
||||||
// --------------------
|
// --------------------
|
||||||
|
@ -41,11 +41,12 @@ ObjectAccess ObjectAccessForGCStores(wasm::ValueType type);
|
|||||||
class WasmGraphAssembler : public GraphAssembler {
|
class WasmGraphAssembler : public GraphAssembler {
|
||||||
public:
|
public:
|
||||||
WasmGraphAssembler(MachineGraph* mcgraph, Zone* zone)
|
WasmGraphAssembler(MachineGraph* mcgraph, Zone* zone)
|
||||||
: GraphAssembler(mcgraph, zone), simplified_(zone) {}
|
: GraphAssembler(mcgraph, zone, BranchSemantics::kMachine),
|
||||||
|
simplified_(zone) {}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Node* CallRuntimeStub(wasm::WasmCode::RuntimeStubId stub_id,
|
Node* CallRuntimeStub(wasm::WasmCode::RuntimeStubId stub_id,
|
||||||
Operator::Properties properties, Args*... args) {
|
Operator::Properties properties, Args... args) {
|
||||||
auto* call_descriptor = GetBuiltinCallDescriptor(
|
auto* call_descriptor = GetBuiltinCallDescriptor(
|
||||||
WasmRuntimeStubIdToBuiltinName(stub_id), temp_zone(),
|
WasmRuntimeStubIdToBuiltinName(stub_id), temp_zone(),
|
||||||
StubCallMode::kCallWasmRuntimeStub, false, properties);
|
StubCallMode::kCallWasmRuntimeStub, false, properties);
|
||||||
@ -63,7 +64,7 @@ class WasmGraphAssembler : public GraphAssembler {
|
|||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Node* CallBuiltin(Builtin name, Operator::Properties properties,
|
Node* CallBuiltin(Builtin name, Operator::Properties properties,
|
||||||
Args*... args) {
|
Args... args) {
|
||||||
auto* call_descriptor = GetBuiltinCallDescriptor(
|
auto* call_descriptor = GetBuiltinCallDescriptor(
|
||||||
name, temp_zone(), StubCallMode::kCallBuiltinPointer, false,
|
name, temp_zone(), StubCallMode::kCallBuiltinPointer, false,
|
||||||
properties);
|
properties);
|
||||||
@ -268,7 +269,7 @@ class WasmGraphAssembler : public GraphAssembler {
|
|||||||
effect(), control()));
|
effect(), control()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
|
SimplifiedOperatorBuilder* simplified() override { return &simplified_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SimplifiedOperatorBuilder simplified_;
|
SimplifiedOperatorBuilder simplified_;
|
||||||
|
@ -833,7 +833,7 @@ DEFINE_BOOL(verify_csa, DEBUG_BOOL,
|
|||||||
// non-ENABLE_VERIFY_CSA configuration.
|
// non-ENABLE_VERIFY_CSA configuration.
|
||||||
DEFINE_BOOL_READONLY(verify_csa, false,
|
DEFINE_BOOL_READONLY(verify_csa, false,
|
||||||
"verify TurboFan machine graph of code stubs")
|
"verify TurboFan machine graph of code stubs")
|
||||||
#endif
|
#endif // ENABLE_VERIFY_CSA
|
||||||
DEFINE_BOOL(trace_verify_csa, false, "trace code stubs verification")
|
DEFINE_BOOL(trace_verify_csa, false, "trace code stubs verification")
|
||||||
DEFINE_STRING(csa_trap_on_node, nullptr,
|
DEFINE_STRING(csa_trap_on_node, nullptr,
|
||||||
"trigger break point when a node with given id is created in "
|
"trigger break point when a node with given id is created in "
|
||||||
@ -934,6 +934,9 @@ DEFINE_BOOL_READONLY(turbo_rewrite_far_jumps, false,
|
|||||||
"rewrite far to near jumps (ia32,x64)")
|
"rewrite far to near jumps (ia32,x64)")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
DEFINE_BOOL(
|
||||||
|
turbo_rab_gsab, true,
|
||||||
|
"optimize ResizableArrayBuffer / GrowableSharedArrayBuffer in TurboFan")
|
||||||
DEFINE_BOOL(
|
DEFINE_BOOL(
|
||||||
stress_gc_during_compilation, false,
|
stress_gc_during_compilation, false,
|
||||||
"simulate GC/compiler thread race related to https://crbug.com/v8/8520")
|
"simulate GC/compiler thread race related to https://crbug.com/v8/8520")
|
||||||
|
@ -12,62 +12,65 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
int ElementsKindToShiftSize(ElementsKind elements_kind) {
|
namespace {
|
||||||
switch (elements_kind) {
|
constexpr size_t size_to_shift(size_t size) {
|
||||||
case UINT8_ELEMENTS:
|
switch (size) {
|
||||||
case INT8_ELEMENTS:
|
case 1:
|
||||||
case UINT8_CLAMPED_ELEMENTS:
|
|
||||||
case RAB_GSAB_UINT8_ELEMENTS:
|
|
||||||
case RAB_GSAB_INT8_ELEMENTS:
|
|
||||||
case RAB_GSAB_UINT8_CLAMPED_ELEMENTS:
|
|
||||||
return 0;
|
return 0;
|
||||||
case UINT16_ELEMENTS:
|
case 2:
|
||||||
case INT16_ELEMENTS:
|
|
||||||
case RAB_GSAB_UINT16_ELEMENTS:
|
|
||||||
case RAB_GSAB_INT16_ELEMENTS:
|
|
||||||
return 1;
|
return 1;
|
||||||
case UINT32_ELEMENTS:
|
case 4:
|
||||||
case INT32_ELEMENTS:
|
|
||||||
case FLOAT32_ELEMENTS:
|
|
||||||
case RAB_GSAB_UINT32_ELEMENTS:
|
|
||||||
case RAB_GSAB_INT32_ELEMENTS:
|
|
||||||
case RAB_GSAB_FLOAT32_ELEMENTS:
|
|
||||||
return 2;
|
return 2;
|
||||||
case PACKED_DOUBLE_ELEMENTS:
|
case 8:
|
||||||
case HOLEY_DOUBLE_ELEMENTS:
|
|
||||||
case FLOAT64_ELEMENTS:
|
|
||||||
case BIGINT64_ELEMENTS:
|
|
||||||
case BIGUINT64_ELEMENTS:
|
|
||||||
case RAB_GSAB_FLOAT64_ELEMENTS:
|
|
||||||
case RAB_GSAB_BIGINT64_ELEMENTS:
|
|
||||||
case RAB_GSAB_BIGUINT64_ELEMENTS:
|
|
||||||
return 3;
|
return 3;
|
||||||
case PACKED_SMI_ELEMENTS:
|
default:
|
||||||
case PACKED_ELEMENTS:
|
|
||||||
case PACKED_FROZEN_ELEMENTS:
|
|
||||||
case PACKED_SEALED_ELEMENTS:
|
|
||||||
case PACKED_NONEXTENSIBLE_ELEMENTS:
|
|
||||||
case HOLEY_SMI_ELEMENTS:
|
|
||||||
case HOLEY_ELEMENTS:
|
|
||||||
case HOLEY_FROZEN_ELEMENTS:
|
|
||||||
case HOLEY_SEALED_ELEMENTS:
|
|
||||||
case HOLEY_NONEXTENSIBLE_ELEMENTS:
|
|
||||||
case DICTIONARY_ELEMENTS:
|
|
||||||
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
|
|
||||||
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
|
|
||||||
case FAST_STRING_WRAPPER_ELEMENTS:
|
|
||||||
case SLOW_STRING_WRAPPER_ELEMENTS:
|
|
||||||
case SHARED_ARRAY_ELEMENTS:
|
|
||||||
return kTaggedSizeLog2;
|
|
||||||
case WASM_ARRAY_ELEMENTS:
|
|
||||||
case NO_ELEMENTS:
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
constexpr uint8_t kTypedArrayAndRabGsabTypedArrayElementsKindShifts[] = {
|
||||||
|
#define SHIFT(Type, type, TYPE, ctype) size_to_shift(sizeof(ctype)),
|
||||||
|
TYPED_ARRAYS(SHIFT) RAB_GSAB_TYPED_ARRAYS(SHIFT)
|
||||||
|
#undef SHIFT
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint8_t kTypedArrayAndRabGsabTypedArrayElementsKindSizes[] = {
|
||||||
|
#define SIZE(Type, type, TYPE, ctype) sizeof(ctype),
|
||||||
|
TYPED_ARRAYS(SIZE) RAB_GSAB_TYPED_ARRAYS(SIZE)
|
||||||
|
#undef SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VERIFY_SHIFT(Type, type, TYPE, ctype) \
|
||||||
|
static_assert( \
|
||||||
|
kTypedArrayAndRabGsabTypedArrayElementsKindShifts \
|
||||||
|
[ElementsKind::TYPE##_ELEMENTS - \
|
||||||
|
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND] == \
|
||||||
|
ElementsKindToShiftSize(ElementsKind::TYPE##_ELEMENTS), \
|
||||||
|
"Shift of ElementsKind::" #TYPE \
|
||||||
|
"_ELEMENTS does not match in static table");
|
||||||
|
TYPED_ARRAYS(VERIFY_SHIFT)
|
||||||
|
RAB_GSAB_TYPED_ARRAYS(VERIFY_SHIFT)
|
||||||
|
#undef VERIFY_SHIFT
|
||||||
|
|
||||||
|
#define VERIFY_SIZE(Type, type, TYPE, ctype) \
|
||||||
|
static_assert( \
|
||||||
|
kTypedArrayAndRabGsabTypedArrayElementsKindSizes \
|
||||||
|
[ElementsKind::TYPE##_ELEMENTS - \
|
||||||
|
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND] == \
|
||||||
|
ElementsKindToByteSize(ElementsKind::TYPE##_ELEMENTS), \
|
||||||
|
"Size of ElementsKind::" #TYPE \
|
||||||
|
"_ELEMENTS does not match in static table");
|
||||||
|
TYPED_ARRAYS(VERIFY_SIZE)
|
||||||
|
RAB_GSAB_TYPED_ARRAYS(VERIFY_SIZE)
|
||||||
|
#undef VERIFY_SIZE
|
||||||
|
|
||||||
|
const uint8_t* TypedArrayAndRabGsabTypedArrayElementsKindShifts() {
|
||||||
|
return &kTypedArrayAndRabGsabTypedArrayElementsKindShifts[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int ElementsKindToByteSize(ElementsKind elements_kind) {
|
const uint8_t* TypedArrayAndRabGsabTypedArrayElementsKindSizes() {
|
||||||
return 1 << ElementsKindToShiftSize(elements_kind);
|
return &kTypedArrayAndRabGsabTypedArrayElementsKindSizes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetDefaultHeaderSizeForElementsKind(ElementsKind elements_kind) {
|
int GetDefaultHeaderSizeForElementsKind(ElementsKind elements_kind) {
|
||||||
|
@ -169,8 +169,64 @@ constexpr int kFastElementsKindBits = 3;
|
|||||||
static_assert((1 << kFastElementsKindBits) > LAST_FAST_ELEMENTS_KIND);
|
static_assert((1 << kFastElementsKindBits) > LAST_FAST_ELEMENTS_KIND);
|
||||||
static_assert((1 << (kFastElementsKindBits - 1)) <= LAST_FAST_ELEMENTS_KIND);
|
static_assert((1 << (kFastElementsKindBits - 1)) <= LAST_FAST_ELEMENTS_KIND);
|
||||||
|
|
||||||
V8_EXPORT_PRIVATE int ElementsKindToShiftSize(ElementsKind elements_kind);
|
const uint8_t* TypedArrayAndRabGsabTypedArrayElementsKindShifts();
|
||||||
V8_EXPORT_PRIVATE int ElementsKindToByteSize(ElementsKind elements_kind);
|
const uint8_t* TypedArrayAndRabGsabTypedArrayElementsKindSizes();
|
||||||
|
inline constexpr int ElementsKindToShiftSize(ElementsKind elements_kind) {
|
||||||
|
switch (elements_kind) {
|
||||||
|
case UINT8_ELEMENTS:
|
||||||
|
case INT8_ELEMENTS:
|
||||||
|
case UINT8_CLAMPED_ELEMENTS:
|
||||||
|
case RAB_GSAB_UINT8_ELEMENTS:
|
||||||
|
case RAB_GSAB_INT8_ELEMENTS:
|
||||||
|
case RAB_GSAB_UINT8_CLAMPED_ELEMENTS:
|
||||||
|
return 0;
|
||||||
|
case UINT16_ELEMENTS:
|
||||||
|
case INT16_ELEMENTS:
|
||||||
|
case RAB_GSAB_UINT16_ELEMENTS:
|
||||||
|
case RAB_GSAB_INT16_ELEMENTS:
|
||||||
|
return 1;
|
||||||
|
case UINT32_ELEMENTS:
|
||||||
|
case INT32_ELEMENTS:
|
||||||
|
case FLOAT32_ELEMENTS:
|
||||||
|
case RAB_GSAB_UINT32_ELEMENTS:
|
||||||
|
case RAB_GSAB_INT32_ELEMENTS:
|
||||||
|
case RAB_GSAB_FLOAT32_ELEMENTS:
|
||||||
|
return 2;
|
||||||
|
case PACKED_DOUBLE_ELEMENTS:
|
||||||
|
case HOLEY_DOUBLE_ELEMENTS:
|
||||||
|
case FLOAT64_ELEMENTS:
|
||||||
|
case BIGINT64_ELEMENTS:
|
||||||
|
case BIGUINT64_ELEMENTS:
|
||||||
|
case RAB_GSAB_FLOAT64_ELEMENTS:
|
||||||
|
case RAB_GSAB_BIGINT64_ELEMENTS:
|
||||||
|
case RAB_GSAB_BIGUINT64_ELEMENTS:
|
||||||
|
return 3;
|
||||||
|
case PACKED_SMI_ELEMENTS:
|
||||||
|
case PACKED_ELEMENTS:
|
||||||
|
case PACKED_FROZEN_ELEMENTS:
|
||||||
|
case PACKED_SEALED_ELEMENTS:
|
||||||
|
case PACKED_NONEXTENSIBLE_ELEMENTS:
|
||||||
|
case HOLEY_SMI_ELEMENTS:
|
||||||
|
case HOLEY_ELEMENTS:
|
||||||
|
case HOLEY_FROZEN_ELEMENTS:
|
||||||
|
case HOLEY_SEALED_ELEMENTS:
|
||||||
|
case HOLEY_NONEXTENSIBLE_ELEMENTS:
|
||||||
|
case DICTIONARY_ELEMENTS:
|
||||||
|
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
|
||||||
|
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
|
||||||
|
case FAST_STRING_WRAPPER_ELEMENTS:
|
||||||
|
case SLOW_STRING_WRAPPER_ELEMENTS:
|
||||||
|
case SHARED_ARRAY_ELEMENTS:
|
||||||
|
return kTaggedSizeLog2;
|
||||||
|
case WASM_ARRAY_ELEMENTS:
|
||||||
|
case NO_ELEMENTS:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
inline constexpr int ElementsKindToByteSize(ElementsKind elements_kind) {
|
||||||
|
return 1 << ElementsKindToShiftSize(elements_kind);
|
||||||
|
}
|
||||||
int GetDefaultHeaderSizeForElementsKind(ElementsKind elements_kind);
|
int GetDefaultHeaderSizeForElementsKind(ElementsKind elements_kind);
|
||||||
const char* ElementsKindToString(ElementsKind kind);
|
const char* ElementsKindToString(ElementsKind kind);
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ bitfield struct TurbofanTypeLowBits extends uint32 {
|
|||||||
|
|
||||||
bitfield struct TurbofanTypeHighBits extends uint32 {
|
bitfield struct TurbofanTypeHighBits extends uint32 {
|
||||||
sandboxed_pointer: bool: 1 bit;
|
sandboxed_pointer: bool: 1 bit;
|
||||||
|
machine: bool: 1 bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@export
|
@export
|
||||||
|
@ -3,25 +3,823 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// Flags: --harmony-rab-gsab --allow-natives-syntax --turbofan
|
// Flags: --harmony-rab-gsab --allow-natives-syntax --turbofan
|
||||||
|
// Flags: --no-always-turbofan --turbo-rab-gsab
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
d8.file.execute('test/mjsunit/typedarray-helpers.js');
|
d8.file.execute('test/mjsunit/typedarray-helpers.js');
|
||||||
|
|
||||||
(function TypedArrayLength() {
|
const is_little_endian = (() => {
|
||||||
for(let ctor of ctors) {
|
var buffer = new ArrayBuffer(4);
|
||||||
// We have to make sure that we construct a new string for each case to
|
const HEAP32 = new Int32Array(buffer);
|
||||||
// prevent the compiled function from being reused with spoiled feedback.
|
const HEAPU8 = new Uint8Array(buffer);
|
||||||
const test = new Function('\
|
HEAP32[0] = 255;
|
||||||
const rab = CreateResizableArrayBuffer(16, 40); \
|
return (HEAPU8[0] === 255 && HEAPU8[3] === 0);
|
||||||
const ta = new ' + ctor.name + '(rab); \
|
})();
|
||||||
rab.resize(32); \
|
|
||||||
return ta.length;');
|
function FillBuffer(buffer) {
|
||||||
|
const view = new Uint8Array(buffer);
|
||||||
%PrepareFunctionForOptimization(test);
|
for (let i = 0; i < view.length; ++i) {
|
||||||
assertEquals(32 / ctor.BYTES_PER_ELEMENT, test(ctor));
|
view[i] = i;
|
||||||
assertEquals(32 / ctor.BYTES_PER_ELEMENT, test(ctor));
|
}
|
||||||
%OptimizeFunctionOnNextCall(test);
|
}
|
||||||
assertEquals(32 / ctor.BYTES_PER_ELEMENT, test(ctor));
|
%NeverOptimizeFunction(FillBuffer);
|
||||||
}
|
|
||||||
|
function asU16(index) {
|
||||||
|
const start = index * 2;
|
||||||
|
if (is_little_endian) {
|
||||||
|
return (start + 1) * 256 + start;
|
||||||
|
} else {
|
||||||
|
return start * 256 + start + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(asU16);
|
||||||
|
|
||||||
|
function asU32(index) {
|
||||||
|
const start = index * 4;
|
||||||
|
if (is_little_endian) {
|
||||||
|
return (((start + 3) * 256 + start + 2) * 256 + start + 1) * 256 + start;
|
||||||
|
} else {
|
||||||
|
return ((((start * 256) + start + 1) * 256) + start + 2) * 256 + start + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(asU32);
|
||||||
|
|
||||||
|
function asF32(index) {
|
||||||
|
const start = index * 4;
|
||||||
|
const ab = new ArrayBuffer(4);
|
||||||
|
const ta = new Uint8Array(ab);
|
||||||
|
for (let i = 0; i < 4; ++i) ta[i] = start + i;
|
||||||
|
return new Float32Array(ab)[0];
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(asF32);
|
||||||
|
|
||||||
|
function asF64(index) {
|
||||||
|
const start = index * 8;
|
||||||
|
const ab = new ArrayBuffer(8);
|
||||||
|
const ta = new Uint8Array(ab);
|
||||||
|
for (let i = 0; i < 8; ++i) ta[i] = start + i;
|
||||||
|
return new Float64Array(ab)[0];
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(asF64);
|
||||||
|
|
||||||
|
function asB64(index) {
|
||||||
|
const start = index * 8;
|
||||||
|
let result = 0n;
|
||||||
|
if (is_little_endian) {
|
||||||
|
for (let i = 0; i < 8; ++i) {
|
||||||
|
result = result << 8n;
|
||||||
|
result += BigInt(start + 7 - i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < 8; ++i) {
|
||||||
|
result = result << 8n;
|
||||||
|
result += BigInt(start + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(asB64);
|
||||||
|
|
||||||
|
function CreateBuffer(shared, len, max_len) {
|
||||||
|
return shared ? new SharedArrayBuffer(len, {maxByteLength: max_len}) :
|
||||||
|
new ArrayBuffer(len, {maxByteLength: max_len});
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(CreateBuffer);
|
||||||
|
|
||||||
|
function MakeResize(target, shared, offset, fixed_len) {
|
||||||
|
const bpe = target.name === 'DataView' ? 1 : target.BYTES_PER_ELEMENT;
|
||||||
|
function RoundDownToElementSize(blen) {
|
||||||
|
return Math.floor(blen / bpe) * bpe;
|
||||||
|
}
|
||||||
|
if (!shared) {
|
||||||
|
if (fixed_len === undefined) {
|
||||||
|
return (b, len) => {
|
||||||
|
b.resize(len);
|
||||||
|
const blen = Math.max(0, len - offset);
|
||||||
|
return RoundDownToElementSize(blen);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const fixed_blen = fixed_len * bpe;
|
||||||
|
return (b, len) => {
|
||||||
|
b.resize(len);
|
||||||
|
const blen = fixed_blen <= (len - offset) ? fixed_blen : 0;
|
||||||
|
return RoundDownToElementSize(blen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fixed_len === undefined) {
|
||||||
|
return (b, len) => {
|
||||||
|
let blen = 0;
|
||||||
|
if (len > b.byteLength) {
|
||||||
|
b.grow(len);
|
||||||
|
blen = Math.max(0, len - offset);
|
||||||
|
} else {
|
||||||
|
blen = b.byteLength - offset;
|
||||||
|
}
|
||||||
|
return RoundDownToElementSize(blen);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return (b, len) => {
|
||||||
|
if (len > b.byteLength) {
|
||||||
|
b.grow(len);
|
||||||
|
}
|
||||||
|
return fixed_len * bpe;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(MakeResize);
|
||||||
|
|
||||||
|
function MakeElement(target, offset) {
|
||||||
|
const o = offset / target.BYTES_PER_ELEMENT;
|
||||||
|
if (target.name === 'Int8Array') {
|
||||||
|
return (index) => {
|
||||||
|
return o + index;
|
||||||
|
};
|
||||||
|
} else if (target.name === 'Uint32Array') {
|
||||||
|
return (index) => {
|
||||||
|
return asU32(o + index);
|
||||||
|
};
|
||||||
|
} else if (target.name === 'Float64Array') {
|
||||||
|
return (index) => {
|
||||||
|
return asF64(o + index);
|
||||||
|
};
|
||||||
|
} else if (target.name === 'BigInt64Array') {
|
||||||
|
return (index) => {
|
||||||
|
return asB64(o + index);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.log(`unimplemented: MakeElement(${target.name})`);
|
||||||
|
return () => undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(MakeElement);
|
||||||
|
|
||||||
|
function MakeCheckBuffer(target, offset) {
|
||||||
|
return (ab, up_to) => {
|
||||||
|
const view = new Uint8Array(ab);
|
||||||
|
for (let i = 0; i < offset; ++i) {
|
||||||
|
assertEquals(0, view[i]);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < (up_to * target.BYTES_PER_ELEMENT) + 1; ++i) {
|
||||||
|
// Use PrintBuffer(ab) for debugging.
|
||||||
|
assertEquals(offset + i, view[offset + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(MakeCheckBuffer);
|
||||||
|
|
||||||
|
function ClearBuffer(ab) {
|
||||||
|
for (let i = 0; i < ab.byteLength; ++i) ab[i] = 0;
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(ClearBuffer);
|
||||||
|
|
||||||
|
// Use this for debugging these tests.
|
||||||
|
function PrintBuffer(buffer) {
|
||||||
|
const view = new Uint8Array(buffer);
|
||||||
|
for (let i = 0; i < 32; ++i) {
|
||||||
|
console.log(`[${i}]: ${view[i]}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%NeverOptimizeFunction(PrintBuffer);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
for (let shared of [false, true]) {
|
||||||
|
for (let length_tracking of [false, true]) {
|
||||||
|
for (let with_offset of [false, true]) {
|
||||||
|
for (let target
|
||||||
|
of [Int8Array, Uint32Array, Float64Array, BigInt64Array]) {
|
||||||
|
const test_case = `Testing: Length_${shared ? 'GSAB' : 'RAB'}_${
|
||||||
|
length_tracking ? 'LengthTracking' : 'FixedLength'}${
|
||||||
|
with_offset ? 'WithOffset' : ''}_${target.name}`;
|
||||||
|
// console.log(test_case);
|
||||||
|
|
||||||
|
const byte_length_code = 'return ta.byteLength; // ' + test_case;
|
||||||
|
const ByteLength = new Function('ta', byte_length_code);
|
||||||
|
const length_code = 'return ta.length; // ' + test_case;
|
||||||
|
const Length = new Function('ta', length_code);
|
||||||
|
const offset = with_offset ? 8 : 0;
|
||||||
|
|
||||||
|
let blen = 16 - offset;
|
||||||
|
const fixed_len =
|
||||||
|
length_tracking ? undefined : (blen / target.BYTES_PER_ELEMENT);
|
||||||
|
const ab = CreateBuffer(shared, 16, 40);
|
||||||
|
const ta = new target(ab, offset, fixed_len);
|
||||||
|
const Resize = MakeResize(target, shared, offset, fixed_len);
|
||||||
|
|
||||||
|
assertUnoptimized(ByteLength);
|
||||||
|
assertUnoptimized(Length);
|
||||||
|
%PrepareFunctionForOptimization(ByteLength);
|
||||||
|
%PrepareFunctionForOptimization(Length);
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
%OptimizeFunctionOnNextCall(ByteLength);
|
||||||
|
%OptimizeFunctionOnNextCall(Length);
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
blen = Resize(ab, 32);
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
blen = Resize(ab, 9);
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
assertOptimized(Length);
|
||||||
|
blen = Resize(ab, 24);
|
||||||
|
assertEquals(blen, ByteLength(ta));
|
||||||
|
assertEquals(Math.floor(blen / target.BYTES_PER_ELEMENT), Length(ta));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
if (!shared) {
|
||||||
|
%ArrayBufferDetach(ab);
|
||||||
|
assertEquals(0, ByteLength(ta));
|
||||||
|
assertEquals(0, Length(ta));
|
||||||
|
assertOptimized(Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
for (let shared of [false, true]) {
|
||||||
|
for (let length_tracking of [false, true]) {
|
||||||
|
for (let with_offset of [false, true]) {
|
||||||
|
for (let target
|
||||||
|
of [Int8Array, Uint32Array, Float64Array, BigInt64Array]) {
|
||||||
|
const test_case = `Testing: Read_${shared ? 'GSAB' : 'RAB'}_${
|
||||||
|
length_tracking ? 'LengthTracking' : 'FixedLength'}${
|
||||||
|
with_offset ? 'WithOffset' : ''}_${target.name}`;
|
||||||
|
// console.log(test_case);
|
||||||
|
|
||||||
|
const read_code = 'return ta[index]; // ' + test_case;
|
||||||
|
const Read = new Function('ta', 'index', read_code);
|
||||||
|
const offset = with_offset ? 8 : 0;
|
||||||
|
|
||||||
|
let blen = 16 - offset;
|
||||||
|
let len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
const fixed_len = length_tracking ? undefined : len;
|
||||||
|
const ab = CreateBuffer(shared, 16, 40);
|
||||||
|
const ta = new target(ab, offset, fixed_len);
|
||||||
|
const Resize = MakeResize(target, shared, offset, fixed_len);
|
||||||
|
const Element = MakeElement(target, offset);
|
||||||
|
FillBuffer(ab);
|
||||||
|
|
||||||
|
assertUnoptimized(Read);
|
||||||
|
%PrepareFunctionForOptimization(Read);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
%OptimizeFunctionOnNextCall(Read);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
assertOptimized(Read);
|
||||||
|
blen = Resize(ab, 32);
|
||||||
|
FillBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
assertOptimized(Read);
|
||||||
|
blen = Resize(ab, 9);
|
||||||
|
FillBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
assertOptimized(Read);
|
||||||
|
blen = Resize(ab, 0);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
assertOptimized(Read);
|
||||||
|
blen = Resize(ab, 24);
|
||||||
|
FillBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len * 2; ++i)
|
||||||
|
assertEquals(i < len ? Element(i) : undefined, Read(ta, i));
|
||||||
|
assertOptimized(Read);
|
||||||
|
|
||||||
|
if (!shared) {
|
||||||
|
%ArrayBufferDetach(ab);
|
||||||
|
assertEquals(undefined, Read(ta, 0));
|
||||||
|
// assertOptimized(Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
for (let shared of [false, true]) {
|
||||||
|
for (let length_tracking of [false, true]) {
|
||||||
|
for (let with_offset of [false, true]) {
|
||||||
|
for (let target
|
||||||
|
of [Int8Array, Uint32Array, Float64Array, BigInt64Array]) {
|
||||||
|
const test_case = `Testing: Write_${shared ? 'GSAB' : 'RAB'}_${
|
||||||
|
length_tracking ? 'LengthTracking' : 'FixedLength'}${
|
||||||
|
with_offset ? 'WithOffset' : ''}_${target.name}`;
|
||||||
|
// console.log(test_case);
|
||||||
|
|
||||||
|
const write_code = 'ta[index] = value; // ' + test_case;
|
||||||
|
const Write = new Function('ta', 'index', 'value', write_code);
|
||||||
|
const offset = with_offset ? 8 : 0;
|
||||||
|
|
||||||
|
let blen = 16 - offset;
|
||||||
|
let len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
const fixed_len = length_tracking ? undefined : len;
|
||||||
|
const ab = CreateBuffer(shared, 16, 40);
|
||||||
|
const ta = new target(ab, offset, fixed_len);
|
||||||
|
const Resize = MakeResize(target, shared, offset, fixed_len);
|
||||||
|
const Element = MakeElement(target, offset);
|
||||||
|
const CheckBuffer = MakeCheckBuffer(target, offset);
|
||||||
|
ClearBuffer(ab);
|
||||||
|
|
||||||
|
assertUnoptimized(Write);
|
||||||
|
%PrepareFunctionForOptimization(Write);
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
Write(ta, i, Element(i));
|
||||||
|
CheckBuffer(ab, i);
|
||||||
|
}
|
||||||
|
ClearBuffer(ab);
|
||||||
|
%OptimizeFunctionOnNextCall(Write);
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
Write(ta, i, Element(i));
|
||||||
|
CheckBuffer(ab, i);
|
||||||
|
}
|
||||||
|
assertOptimized(Write);
|
||||||
|
blen = Resize(ab, 32);
|
||||||
|
ClearBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
Write(ta, i, Element(i));
|
||||||
|
CheckBuffer(ab, i);
|
||||||
|
}
|
||||||
|
assertOptimized(Write);
|
||||||
|
blen = Resize(ab, 9);
|
||||||
|
ClearBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
Write(ta, i, Element(i));
|
||||||
|
CheckBuffer(ab, i);
|
||||||
|
}
|
||||||
|
assertOptimized(Write);
|
||||||
|
blen = Resize(ab, 24);
|
||||||
|
ClearBuffer(ab);
|
||||||
|
len = Math.floor(blen / target.BYTES_PER_ELEMENT);
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
Write(ta, i, Element(i));
|
||||||
|
CheckBuffer(ab, i);
|
||||||
|
}
|
||||||
|
assertOptimized(Write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
for (let shared of [false, true]) {
|
||||||
|
for (let length_tracking of [false, true]) {
|
||||||
|
for (let with_offset of [false, true]) {
|
||||||
|
const test_case = `Testing: ByteLength_${shared ? 'GSAB' : 'RAB'}_${
|
||||||
|
length_tracking ?
|
||||||
|
'LengthTracking' :
|
||||||
|
'FixedLength'}${with_offset ? 'WithOffset' : ''}_DataView`;
|
||||||
|
// console.log(test_case);
|
||||||
|
|
||||||
|
const byte_length_code = 'return dv.byteLength; // ' + test_case;
|
||||||
|
const ByteLength = new Function('dv', byte_length_code);
|
||||||
|
const offset = with_offset ? 8 : 0;
|
||||||
|
|
||||||
|
let blen = 16 - offset;
|
||||||
|
const fixed_blen = length_tracking ? undefined : blen;
|
||||||
|
const ab = CreateBuffer(shared, 16, 40);
|
||||||
|
const dv = new DataView(ab, offset, fixed_blen);
|
||||||
|
const Resize = MakeResize(DataView, shared, offset, fixed_blen);
|
||||||
|
|
||||||
|
assertUnoptimized(ByteLength);
|
||||||
|
%PrepareFunctionForOptimization(ByteLength);
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
%OptimizeFunctionOnNextCall(ByteLength);
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
blen = Resize(ab, 32);
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
blen = Resize(ab, 9);
|
||||||
|
if (length_tracking || shared) {
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
} else {
|
||||||
|
// For fixed length rabs, Resize(ab, 9) will put the ArrayBuffer in
|
||||||
|
// detached state, for which DataView.prototype.byteLength has to throw.
|
||||||
|
assertThrows(() => { ByteLength(dv); }, TypeError);
|
||||||
|
}
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
blen = Resize(ab, 24);
|
||||||
|
assertEquals(blen, ByteLength(dv));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
|
||||||
|
if (!shared) {
|
||||||
|
%ArrayBufferDetach(ab);
|
||||||
|
assertThrows(() => { ByteLength(dv); }, TypeError);
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function ByteLength_RAB_LengthTrackingWithOffset_DataView(dv) {
|
||||||
|
return dv.byteLength;
|
||||||
|
}
|
||||||
|
const ByteLength = ByteLength_RAB_LengthTrackingWithOffset_DataView;
|
||||||
|
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
const dv = new DataView(rab, 7);
|
||||||
|
|
||||||
|
%PrepareFunctionForOptimization(ByteLength);
|
||||||
|
assertEquals(9, ByteLength(dv));
|
||||||
|
assertEquals(9, ByteLength(dv));
|
||||||
|
%OptimizeFunctionOnNextCall(ByteLength);
|
||||||
|
assertEquals(9, ByteLength(dv));
|
||||||
|
assertOptimized(ByteLength);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function Read_TA_RAB_LengthTracking_Mixed(ta, index) {
|
||||||
|
return ta[index];
|
||||||
|
}
|
||||||
|
const Get = Read_TA_RAB_LengthTracking_Mixed;
|
||||||
|
|
||||||
|
const ab = new ArrayBuffer(16);
|
||||||
|
FillBuffer(ab);
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
FillBuffer(rab);
|
||||||
|
let ta_int8 = new Int8Array(ab);
|
||||||
|
let ta_uint16 = new Uint16Array(rab);
|
||||||
|
let ta_float32 = new Float32Array(ab);
|
||||||
|
let ta_float64 = new Float64Array(rab);
|
||||||
|
|
||||||
|
// Train with feedback for all elements kinds.
|
||||||
|
%PrepareFunctionForOptimization(Get);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(7), Get(ta_uint16, 7));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 8));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 2));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
%OptimizeFunctionOnNextCall(Get);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(7), Get(ta_uint16, 7));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 8));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 2));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
rab.resize(32);
|
||||||
|
FillBuffer(rab);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(15), Get(ta_uint16, 15));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 16));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 40));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(asF64(3), Get(ta_float64, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
rab.resize(9);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 4));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
|
||||||
|
// Call with a different map to trigger deoptimization. We use this
|
||||||
|
// to verify that we have actually specialized on the above maps only.
|
||||||
|
let ta_uint8 = new Uint8Array(rab);
|
||||||
|
assertEquals(7, Get(ta_uint8, 7));
|
||||||
|
assertUnoptimized(Get);
|
||||||
|
}());
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function Read_TA_RAB_LengthTracking_Mixed(ta, index) {
|
||||||
|
return ta[index];
|
||||||
|
}
|
||||||
|
const Get = Read_TA_RAB_LengthTracking_Mixed;
|
||||||
|
|
||||||
|
const ab = new ArrayBuffer(16);
|
||||||
|
FillBuffer(ab);
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
FillBuffer(rab);
|
||||||
|
let ta_int8 = new Int8Array(ab);
|
||||||
|
let ta_uint16 = new Uint16Array(rab);
|
||||||
|
let ta_float32 = new Float32Array(ab);
|
||||||
|
let ta_float64 = new Float64Array(rab);
|
||||||
|
|
||||||
|
// Train with feedback for all elements kinds.
|
||||||
|
%PrepareFunctionForOptimization(Get);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(7), Get(ta_uint16, 7));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 8));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 2));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
%OptimizeFunctionOnNextCall(Get);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(7), Get(ta_uint16, 7));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 8));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 2));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
rab.resize(32);
|
||||||
|
FillBuffer(rab);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(asU16(15), Get(ta_uint16, 15));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 16));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 40));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(asF64(1), Get(ta_float64, 1));
|
||||||
|
assertEquals(asF64(3), Get(ta_float64, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
rab.resize(9);
|
||||||
|
assertEquals(0, Get(ta_int8, 0));
|
||||||
|
assertEquals(3, Get(ta_int8, 3));
|
||||||
|
assertEquals(15, Get(ta_int8, 15));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 16));
|
||||||
|
assertEquals(undefined, Get(ta_int8, 32));
|
||||||
|
assertEquals(asU16(0), Get(ta_uint16, 0));
|
||||||
|
assertEquals(asU16(3), Get(ta_uint16, 3));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 4));
|
||||||
|
assertEquals(undefined, Get(ta_uint16, 12));
|
||||||
|
assertEquals(asF32(0), Get(ta_float32, 0));
|
||||||
|
assertEquals(asF32(3), Get(ta_float32, 3));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 4));
|
||||||
|
assertEquals(undefined, Get(ta_float32, 12));
|
||||||
|
assertEquals(asF64(0), Get(ta_float64, 0));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 1));
|
||||||
|
assertEquals(undefined, Get(ta_float64, 12));
|
||||||
|
assertOptimized(Get);
|
||||||
|
|
||||||
|
// Call with a different map to trigger deoptimization. We use this
|
||||||
|
// to verify that we have actually specialized on the above maps only.
|
||||||
|
let ta_uint8 = new Uint8Array(rab);
|
||||||
|
Get(7, Get(ta_uint8, 7));
|
||||||
|
assertUnoptimized(Get);
|
||||||
|
}());
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function Length_TA_RAB_LengthTracking_Mixed(ta) {
|
||||||
|
return ta.length;
|
||||||
|
}
|
||||||
|
let Length = Length_TA_RAB_LengthTracking_Mixed;
|
||||||
|
|
||||||
|
const ab = new ArrayBuffer(32);
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
let ta_int8 = new Int8Array(ab);
|
||||||
|
let ta_uint16 = new Uint16Array(rab);
|
||||||
|
let ta_float32 = new Float32Array(ab);
|
||||||
|
let ta_bigint64 = new BigInt64Array(rab);
|
||||||
|
|
||||||
|
// Train with feedback for all elements kinds.
|
||||||
|
%PrepareFunctionForOptimization(Length);
|
||||||
|
assertEquals(32, Length(ta_int8));
|
||||||
|
assertEquals(8, Length(ta_uint16));
|
||||||
|
assertEquals(8, Length(ta_float32));
|
||||||
|
assertEquals(2, Length(ta_bigint64));
|
||||||
|
%OptimizeFunctionOnNextCall(Length);
|
||||||
|
assertEquals(32, Length(ta_int8));
|
||||||
|
assertEquals(8, Length(ta_uint16));
|
||||||
|
assertEquals(8, Length(ta_float32));
|
||||||
|
assertEquals(2, Length(ta_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
}());
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function Length_RAB_GSAB_LengthTrackingWithOffset_Mixed(ta) {
|
||||||
|
return ta.length;
|
||||||
|
}
|
||||||
|
const Length = Length_RAB_GSAB_LengthTrackingWithOffset_Mixed;
|
||||||
|
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
let ta_int8 = new Int8Array(rab);
|
||||||
|
let ta_float64 = new Float64Array(rab);
|
||||||
|
|
||||||
|
// Train with feedback for Int8Array and Float64Array.
|
||||||
|
%PrepareFunctionForOptimization(Length);
|
||||||
|
assertEquals(16, Length(ta_int8));
|
||||||
|
assertEquals(2, Length(ta_float64));
|
||||||
|
%OptimizeFunctionOnNextCall(Length);
|
||||||
|
assertEquals(16, Length(ta_int8));
|
||||||
|
assertEquals(2, Length(ta_float64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
let ta_uint32 = new Uint32Array(rab);
|
||||||
|
let ta_bigint64 = new BigInt64Array(rab);
|
||||||
|
// Calling with Uint32Array will deopt because of the map check on length.
|
||||||
|
assertEquals(4, Length(ta_uint32));
|
||||||
|
assertUnoptimized(Length);
|
||||||
|
%PrepareFunctionForOptimization(Length);
|
||||||
|
assertEquals(2, Length(ta_bigint64));
|
||||||
|
// Recompile with additional feedback for Uint32Array and BigInt64Array.
|
||||||
|
%OptimizeFunctionOnNextCall(Length);
|
||||||
|
assertEquals(2, Length(ta_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Length handles all four TypedArrays without deopting.
|
||||||
|
assertEquals(16, Length(ta_int8));
|
||||||
|
assertEquals(2, Length(ta_float64));
|
||||||
|
assertEquals(4, Length(ta_uint32));
|
||||||
|
assertEquals(2, Length(ta_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Length handles corresponding gsab-backed TypedArrays without deopting.
|
||||||
|
const gsab = CreateGrowableSharedArrayBuffer(16, 40);
|
||||||
|
let ta2_uint32 = new Uint32Array(gsab, 8);
|
||||||
|
let ta2_float64 = new Float64Array(gsab, 8);
|
||||||
|
let ta2_bigint64 = new BigInt64Array(gsab, 8);
|
||||||
|
let ta2_int8 = new Int8Array(gsab, 8);
|
||||||
|
assertEquals(8, Length(ta2_int8));
|
||||||
|
assertEquals(1, Length(ta2_float64));
|
||||||
|
assertEquals(2, Length(ta2_uint32));
|
||||||
|
assertEquals(1, Length(ta2_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Test Length after rab has been resized to a smaller size.
|
||||||
|
rab.resize(5);
|
||||||
|
assertEquals(5, Length(ta_int8));
|
||||||
|
assertEquals(0, Length(ta_float64));
|
||||||
|
assertEquals(1, Length(ta_uint32));
|
||||||
|
assertEquals(0, Length(ta_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Test Length after rab has been resized to a larger size.
|
||||||
|
rab.resize(40);
|
||||||
|
assertEquals(40, Length(ta_int8));
|
||||||
|
assertEquals(5, Length(ta_float64));
|
||||||
|
assertEquals(10, Length(ta_uint32));
|
||||||
|
assertEquals(5, Length(ta_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Test Length after gsab has been grown to a larger size.
|
||||||
|
gsab.grow(25);
|
||||||
|
assertEquals(17, Length(ta2_int8));
|
||||||
|
assertEquals(2, Length(ta2_float64));
|
||||||
|
assertEquals(4, Length(ta2_uint32));
|
||||||
|
assertEquals(2, Length(ta2_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function Length_AB_RAB_GSAB_LengthTrackingWithOffset_Mixed(ta) {
|
||||||
|
return ta.length;
|
||||||
|
}
|
||||||
|
const Length = Length_AB_RAB_GSAB_LengthTrackingWithOffset_Mixed;
|
||||||
|
|
||||||
|
let ab = new ArrayBuffer(32);
|
||||||
|
let rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
let gsab = CreateGrowableSharedArrayBuffer(16, 40);
|
||||||
|
|
||||||
|
let ta_ab_int32 = new Int32Array(ab, 8, 3);
|
||||||
|
let ta_rab_int32 = new Int32Array(rab, 4);
|
||||||
|
let ta_gsab_float64 = new Float64Array(gsab);
|
||||||
|
let ta_gsab_bigint64 = new BigInt64Array(gsab, 0, 2);
|
||||||
|
|
||||||
|
// Optimize Length with polymorphic feedback.
|
||||||
|
%PrepareFunctionForOptimization(Length);
|
||||||
|
assertEquals(3, Length(ta_ab_int32));
|
||||||
|
assertEquals(3, Length(ta_rab_int32));
|
||||||
|
assertEquals(2, Length(ta_gsab_float64));
|
||||||
|
assertEquals(2, Length(ta_gsab_bigint64));
|
||||||
|
%OptimizeFunctionOnNextCall(Length);
|
||||||
|
assertEquals(3, Length(ta_ab_int32));
|
||||||
|
assertEquals(3, Length(ta_rab_int32));
|
||||||
|
assertEquals(2, Length(ta_gsab_float64));
|
||||||
|
assertEquals(2, Length(ta_gsab_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Test resizing and growing the underlying rab/gsab buffers.
|
||||||
|
rab.resize(8);
|
||||||
|
gsab.grow(36);
|
||||||
|
assertEquals(3, Length(ta_ab_int32));
|
||||||
|
assertEquals(1, Length(ta_rab_int32));
|
||||||
|
assertEquals(4, Length(ta_gsab_float64));
|
||||||
|
assertEquals(2, Length(ta_gsab_bigint64));
|
||||||
|
assertOptimized(Length);
|
||||||
|
|
||||||
|
// Construct additional TypedArrays with the same ElementsKind.
|
||||||
|
let ta2_ab_bigint64 = new BigInt64Array(ab, 0, 1);
|
||||||
|
let ta2_gsab_int32 = new Int32Array(gsab, 16);
|
||||||
|
let ta2_rab_float64 = new Float64Array(rab, 8);
|
||||||
|
let ta2_rab_int32 = new Int32Array(rab, 0, 1);
|
||||||
|
assertEquals(1, Length(ta2_ab_bigint64));
|
||||||
|
assertEquals(5, Length(ta2_gsab_int32));
|
||||||
|
assertEquals(0, Length(ta2_rab_float64));
|
||||||
|
assertEquals(1, Length(ta2_rab_int32));
|
||||||
|
assertOptimized(Length);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function ByteOffset(ta) {
|
||||||
|
return ta.byteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rab = CreateResizableArrayBuffer(16, 40);
|
||||||
|
const ta = new Int32Array(rab, 4);
|
||||||
|
|
||||||
|
%PrepareFunctionForOptimization(ByteOffset);
|
||||||
|
assertEquals(4, ByteOffset(ta));
|
||||||
|
assertEquals(4, ByteOffset(ta));
|
||||||
|
%OptimizeFunctionOnNextCall(ByteOffset);
|
||||||
|
assertEquals(4, ByteOffset(ta));
|
||||||
|
assertOptimized(ByteOffset);
|
||||||
})();
|
})();
|
||||||
|
51
test/mjsunit/regress-1358505.js
Normal file
51
test/mjsunit/regress-1358505.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2022 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.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --harmony-rab-gsab
|
||||||
|
|
||||||
|
(function Test_OOB() {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
const buffer = new ArrayBuffer(42, {'maxByteLength': 42});
|
||||||
|
const view = new DataView(buffer, 0, 42);
|
||||||
|
// Resize the buffer to smaller than the view.
|
||||||
|
buffer.resize(20);
|
||||||
|
// Any access in the view should throw.
|
||||||
|
view.setInt8(11, 0xab);
|
||||||
|
return 'did not prevent out-of-bounds access';
|
||||||
|
} catch (e) {
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%PrepareFunctionForOptimization(f);
|
||||||
|
assertEquals('ok', f());
|
||||||
|
assertEquals('ok', f());
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals('ok', f());
|
||||||
|
assertEquals('ok', f());
|
||||||
|
}());
|
||||||
|
|
||||||
|
(function Test_OOB_WithOffset() {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
const buffer = new ArrayBuffer(42, {'maxByteLength': 42});
|
||||||
|
const view = new DataView(buffer, 30, 42);
|
||||||
|
// Resize the buffer to smaller than the view.
|
||||||
|
buffer.resize(40);
|
||||||
|
// Any access in the view should throw.
|
||||||
|
view.setInt8(11, 0xab);
|
||||||
|
return 'did not prevent out-of-bounds access';
|
||||||
|
} catch (e) {
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%PrepareFunctionForOptimization(f);
|
||||||
|
assertEquals('ok', f());
|
||||||
|
assertEquals('ok', f());
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals('ok', f());
|
||||||
|
assertEquals('ok', f());
|
||||||
|
}());
|
Loading…
Reference in New Issue
Block a user