21 if (v8_flags.turboshaft_trace_load_elimination) \
22 StdoutStream() << x << std::endl; \
29 return os <<
"MemoryAddress{base=" << mem.
base <<
", index=" << mem.
index
30 <<
", offset=" << mem.
offset <<
", elem_size_log2="
32 <<
", size=" <<
static_cast<uint32_t
>(mem.
size) <<
"}";
36 TRACE(
"LateLoadElimination: Starting analysis");
40 bool compute_start_snapshot =
true;
45 compute_start_snapshot =
true;
49 if (last->destination->IsLoop() &&
50 last->destination->LastPredecessor() == block) {
51 TRACE(
"> Considering reprocessing loop header " << last->destination);
52 const Block* loop_header = last->destination;
57 TRACE(
">> Will need to revisit loop");
68 const Block* loop_1st_pred =
75 pred_snapshots->alias_snapshot);
80 compute_start_snapshot =
false;
82 TRACE(
">> No need to revisit loop");
95 auto& truncations = it->second;
100 total_use_counts[load_idx] +=
graph_.
Get(load_idx).saturated_use_count;
102 if (total_use_counts[load_idx].IsSaturated() ||
103 total_use_counts[load_idx].
Get() > truncations.size()) {
134 total_use_counts[replaced_by_idx] +=
135 graph_.
Get(load_idx).saturated_use_count;
136 it2->second.insert(truncations.begin(), truncations.end());
147 if (int32_truncations.empty())
continue;
148 if (!total_use_counts[load_idx].IsSaturated() &&
149 total_use_counts[load_idx].
Get() == int32_truncations.size()) {
153 for (
const auto [change_idx, bitcast_idx] : int32_truncations) {
164 bool compute_start_snapshot) {
165 TRACE(
"> ProcessBlock(" << block.index() <<
")");
166 if (compute_start_snapshot) {
192 case Opcode::kAllocate:
200 case Opcode::kAssumeMap:
204 case Opcode::kChange:
209 case Opcode::kWordBinop:
215 case Opcode::kFrameState:
216 case Opcode::kDeoptimizeIf:
217 case Opcode::kComparison:
218#ifdef V8_ENABLE_WEBASSEMBLY
219 case Opcode::kTrapIf:
228 case Opcode::kDeoptimize:
229 case Opcode::kReturn:
237 case Opcode::kCatchBlockBegin:
238 case Opcode::kRetain:
239 case Opcode::kDidntThrow:
240 case Opcode::kCheckException:
241 case Opcode::kAtomicRMW:
242 case Opcode::kAtomicWord32Pair:
243 case Opcode::kMemoryBarrier:
244 case Opcode::kParameter:
245 case Opcode::kDebugBreak:
246 case Opcode::kJSStackCheck:
247#ifdef V8_ENABLE_WEBASSEMBLY
248 case Opcode::kWasmStackCheck:
249 case Opcode::kSimd128LaneMemory:
250 case Opcode::kGlobalSet:
251 case Opcode::kArraySet:
252 case Opcode::kStructSet:
253 case Opcode::kSetStackPointer:
273 TRACE(
"> Process other op (id=" << op_idx <<
")");
309 return expected_reg_repr == actual;
316 TRACE(
"> ProcessLoad(" << op_idx <<
")");
318 if (!load.kind.load_eliminable) {
321 TRACE(
">> Not load-eliminable; skipping");
324 if (load.kind.is_atomic) {
327 TRACE(
">> Atomic load, invalidating related memory");
344 TRACE(
">> Found potential replacement at offset " << existing);
347 TRACE(
">>> Confirming replacement");
351 TRACE(
">>> Replacement has wrong representation: "
353 << load.outputs_rep()[0]);
370 TRACE(
">> Ignoring load from External constant");
379 TRACE(
"> ProcessStore(" << op_idx <<
")");
383 const bool invalidate_maybe_aliasing =
384 !store.kind.tagged_base &&
387 if (invalidate_maybe_aliasing) {
389 ">> Raw base or maybe inner pointer ==> Invalidating whole "
390 "maybe-aliasing memory");
394 if (!store.kind.load_eliminable) {
397 TRACE(
">> Not load-eliminable; skipping");
408 TRACE(
">> Invalidate non-alias for value=" << value);
415 TRACE(
">> Wiping map\n");
427 TRACE(
"> ProcessCall(" << op_idx <<
")");
432 if (external_constant->external_reference() ==
433 ExternalReference::check_object_type()) {
443 TRACE(
">> Call doesn't write, skipping");
450 TRACE(
">> Call is loop stack check, skipping");
457 switch (*builtin_id) {
459 case Builtin::kCopyFastSmiOrObjectElements:
464 ">> Call is CopyFastSmiOrObjectElements, invalidating only "
468 JSObject::kElementsOffset);
480 switch (*builtin_id) {
481 case Builtin::kCreateShallowObjectLiteral:
520 TRACE(
">> InvalidateAllNonAliasingInputs for " << op);
532 TRACE(
">> InvalidateIfAlias: invalidating for id=" << op_idx);
538 ">> InvalidateIfAlias: recursively invalidating for FrameState inputs "
547 for (
OpIndex input : frame_state->inputs()) {
555 TRACE(
"> ProcessAllocate(" << op_idx <<
") ==> Fresh non-aliasing object");
561 TRACE(
"> ProcessAssumeMap(" << op_idx <<
")");
570 DCHECK_EQ(change_idx, graph.Index(change));
574 graph.Get(change.
input())
576 if (bitcast ==
nullptr)
return false;
583 if (load ==
nullptr)
return false;
584 if (load->loaded_rep.SizeInBytesLog2() !=
588 if (bitcast_idx) *bitcast_idx = change.
input();
589 if (load_idx) *load_idx = bitcast->
input();
595 TRACE(
"> ProcessChange(" << op_idx <<
")");
621 const Block& loop_header) {
628 Snapshot{non_aliasing_snapshot, object_maps_snapshot,
637 const Block& loop_header)
const {
643template <
bool for_loop_revisit>
661 block->IsLoop() && block->LastPredecessor() == p);
662 if (!pred_snapshots.has_value()) {
663 DCHECK(!for_loop_revisit);
674 if (p->NeighboringPredecessor() !=
nullptr || !block->IsLoop() ||
675 block->LastPredecessor() != p) {
686 constexpr int kBackedgeOffset = 0;
687 constexpr int kForwardEdgeOffset = 1;
689 bool loop_needs_revisit =
false;
694 if (for_loop_revisit && predecessors[kForwardEdgeOffset] &&
695 !predecessors[kBackedgeOffset]) {
698 loop_needs_revisit =
true;
730 if (for_loop_revisit && predecessors[kForwardEdgeOffset].valid() &&
731 predecessors[kBackedgeOffset] != predecessors[kForwardEdgeOffset]) {
735 loop_needs_revisit =
true;
742 if (block->IsLoop())
return loop_needs_revisit;
static constexpr int kMapOffset
void push_back(const T &value)
void MarkLoopForRevisit()
Block * NeighboringPredecessor() const
NeighboringPredecessorIterable PredecessorsIterable() const
Block * LastPredecessor() const
void StartNewSnapshot(base::Vector< const Snapshot > predecessors)
base::iterator_range< OpIndexIterator > OperationIndices(const Block &block) const
V8_INLINE const Operation & Get(OpIndex i) const
uint32_t op_id_count() const
void ProcessStore(OpIndex op_idx, const StoreOp &op)
bool BeginBlock(const Block *block)
ZoneVector< MapSnapshot > predecessor_maps_snapshots_
RawBaseAssumption raw_base_assumption_
void ProcessAssumeMap(OpIndex op_idx, const AssumeMapOp &op)
AliasTable non_aliasing_objects_
FixedOpIndexSidetable< Replacement > replacements_
std::map< OpIndex, base::SmallMap< std::map< OpIndex, OpIndex >, 4 > > int32_truncated_loads_
void ProcessLoad(OpIndex op_idx, const LoadOp &op)
void FinishBlock(const Block *block)
Replacement GetReplacement(OpIndex index)
void ProcessCall(OpIndex op_idx, const CallOp &op)
MemoryContentTable memory_
bool BackedgeHasSnapshot(const Block &loop_header) const
void ProcessAllocate(OpIndex op_idx, const AllocateOp &op)
FixedBlockSidetable< std::optional< Snapshot > > block_to_snapshot_mapping_
void DcheckWordBinop(OpIndex op_idx, const WordBinopOp &binop)
void InvalidateAllNonAliasingInputs(const Operation &op)
void InvalidateIfAlias(OpIndex op_idx)
void ProcessBlock(const Block &block, bool compute_start_snapshot)
ZoneVector< MemorySnapshot > predecessor_memory_snapshots_
ZoneVector< AliasSnapshot > predecessor_alias_snapshots_
void ProcessChange(OpIndex op_idx, const ChangeOp &change)
void StoreLoopSnapshotInForwardPredecessor(const Block &loop_header)
static LoadEliminationReplacement LoadElimination(OpIndex replacement)
static LoadEliminationReplacement TaggedLoadToInt32Load()
OpIndex replacement() const
static LoadEliminationReplacement Int32TruncationElimination(OpIndex replacement)
static LoadEliminationReplacement None()
static LoadEliminationReplacement TaggedBitcastElimination()
bool IsLoadElimination() const
OpIndex Find(const LoadOp &load)
void Insert(const StoreOp &store)
void Invalidate(const StoreOp &store)
void InvalidateMaybeAliasing()
static MemoryRepresentation FromRegisterRepresentation(RegisterRepresentation repr, bool is_signed)
static constexpr MemoryRepresentation Int32()
constexpr uint8_t SizeInBytesLog2() const
constexpr uint8_t SizeInBytes() const
static constexpr OpIndex Invalid()
constexpr bool valid() const
void StartNewSnapshot(base::Vector< const Snapshot > predecessors, const ChangeCallback &change_callback={})
bool Set(OpIndex idx, Value new_value)
std::optional< Key > TryGetKeyFor(OpIndex idx) const
bool HasKeyFor(OpIndex idx) const
Value Get(OpIndex idx) const
bool all_equal(const C &container)
constexpr Vector< T > VectorOf(T *start, size_t size)
bool all_of(const C &container, const P &predicate)
MapMaskAndOr ComputeMinMaxHash(ZoneRefSet< Map > maps)
std::ostream & operator<<(std::ostream &os, PaddingSpace padding)
bool RepIsCompatible(RegisterRepresentation actual, RegisterRepresentation expected_reg_repr, uint8_t in_memory_size)
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
MapMaskAndOr CombineMinMax(MapMaskAndOr a, MapMaskAndOr b)
V8_EXPORT_PRIVATE V8_INLINE bool ShouldSkipOperation(const Operation &op)
std::optional< Builtin > TryGetBuiltinId(const ConstantOp *target, JSHeapBroker *broker)
bool IsInt32TruncatedLoadPattern(const Graph &graph, OpIndex change_idx, const ChangeOp &change, OpIndex *bitcast_idx, OpIndex *load_idx)
V8_EXPORT_PRIVATE bool ShouldSkipOptimizationStep()
base::SmallVector< Block *, 4 > SuccessorBlocks(const Block &block, const Graph &graph)
bool TryCast(Tagged< From > value, Tagged< To > *out)
bool IsNone(Tagged< FieldType > obj)
const intptr_t kSmiTagMask
#define DCHECK_IMPLIES(v1, v2)
#define DCHECK(condition)
#define DCHECK_EQ(v1, v2)
V< HeapObject > heap_object() const
base::Vector< const OpIndex > arguments() const
V< CallTarget > callee() const
V8_EXPORT_PRIVATE bool IsStackCheck(const Graph &graph, JSHeapBroker *broker, StackCheckKind kind) const
OpEffects Effects() const
uint8_t element_size_log2
bool IsBlockTerminator() const
base::Vector< const OpIndex > inputs() const
SaturatedUint8 saturated_use_count
const underlying_operation_t< Op > * TryCast() const
underlying_operation_t< Op > & Cast()
base::Vector< const RegisterRepresentation > outputs_rep() const
OpEffects Effects() const
V< WordType > left() const
V< WordType > right() const