v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
load-store-simplification-reducer.h
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_COMPILER_TURBOSHAFT_LOAD_STORE_SIMPLIFICATION_REDUCER_H_
6#define V8_COMPILER_TURBOSHAFT_LOAD_STORE_SIMPLIFICATION_REDUCER_H_
7
12
14
16
18 // TODO(12783): This needs to be extended for all architectures that don't
19 // have loads with the base + index * element_size + offset pattern.
20#if V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_RISCV64 || \
21 V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
22 V8_TARGET_ARCH_RISCV32
23 // As tagged loads result in modfiying the offset by -1, those loads are
24 // converted into raw loads.
25 static constexpr bool kNeedsUntaggedBase = true;
26 // By setting {kMinOffset} > {kMaxOffset}, we ensure that all offsets
27 // (including 0) are merged into the computed index.
28 static constexpr int32_t kMinOffset = 1;
29 static constexpr int32_t kMaxOffset = 0;
30 // Turboshaft's loads and stores follow the pattern of
31 // *(base + index * element_size_log2 + displacement), but architectures
32 // typically support only a limited `element_size_log2`.
33 static constexpr int kMaxElementSizeLog2 = 0;
34#elif V8_TARGET_ARCH_S390X
35 static constexpr bool kNeedsUntaggedBase = false;
36 // s390x supports *(base + index + displacement), element_size isn't
37 // supported.
38 static constexpr int32_t kDisplacementBits = 20; // 20 bit signed integer.
39 static constexpr int32_t kMinOffset =
40 -(static_cast<int32_t>(1) << (kDisplacementBits - 1));
41 static constexpr int32_t kMaxOffset =
42 (static_cast<int32_t>(1) << (kDisplacementBits - 1)) - 1;
43 static constexpr int kMaxElementSizeLog2 = 0;
44#else
45 static constexpr bool kNeedsUntaggedBase = false;
46 // We don't want to encode INT32_MIN in the offset becauce instruction
47 // selection might not be able to put this into an immediate operand.
48 static constexpr int32_t kMinOffset = std::numeric_limits<int32_t>::min() + 1;
49 static constexpr int32_t kMaxOffset = std::numeric_limits<int32_t>::max();
50 // Turboshaft's loads and stores follow the pattern of
51 // *(base + index * element_size_log2 + displacement), but architectures
52 // typically support only a limited `element_size_log2`.
53 static constexpr int kMaxElementSizeLog2 = 3;
54#endif
55};
56
57// This reducer simplifies Turboshaft's "complex" loads and stores into
58// simplified ones that are supported on the given target architecture.
59template <class Next>
62 public:
63 TURBOSHAFT_REDUCER_BOILERPLATE(LoadStoreSimplification)
64
66 MemoryRepresentation loaded_rep,
67 RegisterRepresentation result_rep, int32_t offset,
68 uint8_t element_size_log2) {
69 SimplifyLoadStore(base, index, kind, offset, element_size_log2);
70 return Next::ReduceLoad(base, index, kind, loaded_rep, result_rep, offset,
71 element_size_log2);
72 }
73
76 WriteBarrierKind write_barrier, int32_t offset,
77 uint8_t element_size_log2,
78 bool maybe_initializing_or_transitioning,
79 IndirectPointerTag maybe_indirect_pointer_tag) {
80 SimplifyLoadStore(base, index, kind, offset, element_size_log2);
81 if (write_barrier != WriteBarrierKind::kNoWriteBarrier &&
82 !index.has_value() && __ Get(base).template Is<ConstantOp>()) {
83 const ConstantOp& const_base = __ Get(base).template Cast<ConstantOp>();
84 if (const_base.IsIntegral() ||
85 const_base.kind == ConstantOp::Kind::kSmi) {
86 // It never makes sense to have a WriteBarrier for a store to a raw
87 // address. We should thus be in unreachable code.
88 // The instruction selector / register allocator don't handle this very
89 // well, so it's easier to emit an Unreachable rather than emitting a
90 // weird store that will never be executed.
91 __ Unreachable();
92 return OpIndex::Invalid();
93 }
94 }
95 return Next::ReduceStore(base, index, value, kind, stored_rep,
96 write_barrier, offset, element_size_log2,
97 maybe_initializing_or_transitioning,
98 maybe_indirect_pointer_tag);
99 }
100
102 OptionalV<Word32> value_low,
103 OptionalV<Word32> value_high,
104 OptionalV<Word32> expected_low,
105 OptionalV<Word32> expected_high,
107 int32_t offset) {
110 if (!index.valid()) {
111 index = __ IntPtrConstant(offset);
112 offset = 0;
113 } else if (offset != 0) {
114 index = __ WordPtrAdd(index.value(), offset);
115 offset = 0;
116 }
117 }
118 return Next::ReduceAtomicWord32Pair(base, index, value_low, value_high,
119 expected_low, expected_high, kind,
120 offset);
121 }
122
123 private:
124 bool CanEncodeOffset(int32_t offset, bool tagged_base) const {
125 // If the base is tagged we also need to subtract the kHeapObjectTag
126 // eventually.
127 const int32_t min = kMinOffset + (tagged_base ? kHeapObjectTag : 0);
128 if (min <= offset && offset <= kMaxOffset) {
129 DCHECK(LoadOp::OffsetIsValid(offset, tagged_base));
130 return true;
131 }
132 return false;
133 }
134
135 bool CanEncodeAtomic(OptionalOpIndex index, uint8_t element_size_log2,
136 int32_t offset) const {
137 if (element_size_log2 != 0) return false;
138 return !(index.has_value() && offset != 0);
139 }
140
142 LoadOp::Kind& kind, int32_t& offset,
143 uint8_t& element_size_log2) {
144 if (element_size_log2 > kMaxElementSizeLog2) {
145 DCHECK(index.valid());
146 index = __ WordPtrShiftLeft(index.value(), element_size_log2);
147 element_size_log2 = 0;
148 }
149
150 if (kNeedsUntaggedBase) {
151 if (kind.tagged_base) {
152 kind.tagged_base = false;
153 DCHECK_LE(std::numeric_limits<int32_t>::min() + kHeapObjectTag, offset);
155 base = __ BitcastHeapObjectToWordPtr(base);
156 }
157 }
158
159 // TODO(nicohartmann@): Remove the case for atomics once crrev.com/c/5237267
160 // is ported to x64.
161 if (!CanEncodeOffset(offset, kind.tagged_base) ||
162 (kind.is_atomic &&
163 !CanEncodeAtomic(index, element_size_log2, offset))) {
164 // If an index is present, the element_size_log2 is changed to zero.
165 // So any load follows the form *(base + offset). To simplify
166 // instruction selection, both static and dynamic offsets are stored in
167 // the index input.
168 // As tagged loads result in modifying the offset by -1, those loads are
169 // converted into raw loads (above).
170 if (!index.has_value() || matcher_.MatchIntegralZero(index.value())) {
171 index = __ IntPtrConstant(offset);
172 element_size_log2 = 0;
173 offset = 0;
174 } else if (element_size_log2 != 0) {
175 index = __ WordPtrShiftLeft(index.value(), element_size_log2);
176 element_size_log2 = 0;
177 }
178 if (offset != 0) {
179 index = __ WordPtrAdd(index.value(), offset);
180 offset = 0;
181 }
182 DCHECK_EQ(offset, 0);
183 DCHECK_EQ(element_size_log2, 0);
184 }
185 }
186
187 OperationMatcher matcher_{__ output_graph()};
188};
189
191
192} // namespace v8::internal::compiler::turboshaft
193
194#endif // V8_COMPILER_TURBOSHAFT_LOAD_STORE_SIMPLIFICATION_REDUCER_H_
#define REDUCE(operation)
Builtins::Kind kind
Definition builtins.cc:40
bool CanEncodeAtomic(OptionalOpIndex index, uint8_t element_size_log2, int32_t offset) const
void SimplifyLoadStore(OpIndex &base, OptionalOpIndex &index, LoadOp::Kind &kind, int32_t &offset, uint8_t &element_size_log2)
OpIndex REDUCE Load(OpIndex base, OptionalOpIndex index, LoadOp::Kind kind, MemoryRepresentation loaded_rep, RegisterRepresentation result_rep, int32_t offset, uint8_t element_size_log2)
OpIndex REDUCE AtomicWord32Pair(V< WordPtr > base, OptionalV< WordPtr > index, OptionalV< Word32 > value_low, OptionalV< Word32 > value_high, OptionalV< Word32 > expected_low, OptionalV< Word32 > expected_high, AtomicWord32PairOp::Kind kind, int32_t offset)
OpIndex REDUCE Store(OpIndex base, OptionalOpIndex index, OpIndex value, StoreOp::Kind kind, MemoryRepresentation stored_rep, WriteBarrierKind write_barrier, int32_t offset, uint8_t element_size_log2, bool maybe_initializing_or_transitioning, IndirectPointerTag maybe_indirect_pointer_tag)
static constexpr OpIndex Invalid()
Definition index.h:88
#define TURBOSHAFT_REDUCER_BOILERPLATE(Name)
Definition assembler.h:823
int32_t offset
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
static const Operator * IntPtrConstant(CommonOperatorBuilder *common, intptr_t value)
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
const int kHeapObjectTag
Definition v8-internal.h:72
return value
Definition map-inl.h:893
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
static constexpr bool OffsetIsValid(int32_t offset, bool tagged_base)