v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
decompression-optimization.cc
Go to the documentation of this file.
1// Copyright 2022 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
6
11
13
14namespace {
15
16// Analyze the uses of values to determine if a compressed value has any uses
17// that need it to be decompressed. Since this analysis looks at uses, we
18// iterate the graph backwards, updating the analysis state for the inputs of an
19// operation. Due to loop phis, we need to compute a fixed-point. Therefore, we
20// re-visit the loop if a loop phi backedge changes something. As a performance
21// optimization, we keep track of operations (`candidates`) that need to be
22// updated potentially, so that we don't have to walk the whole graph again.
23struct DecompressionAnalyzer {
24 const Graph& graph;
26 // We use `uint8_t` instead of `bool` here to avoid the bitvector optimization
27 // of std::vector.
28 FixedOpIndexSidetable<uint8_t> needs_decompression;
29 ZoneVector<OpIndex> candidates;
30
31 DecompressionAnalyzer(const Graph& graph, Zone* phase_zone)
32 : graph(graph),
34 needs_decompression(graph.op_id_count(), phase_zone, &graph),
36 candidates.reserve(graph.op_id_count() / 8);
37 }
38
39 void Run() {
40 for (int32_t next_block_id = graph.block_count() - 1; next_block_id >= 0;) {
41 BlockIndex block_index = BlockIndex(next_block_id);
42 --next_block_id;
43 const Block& block = graph.Get(block_index);
44 if (block.IsLoop()) {
45 ProcessBlock<true>(block, &next_block_id);
46 } else {
47 ProcessBlock<false>(block, &next_block_id);
48 }
49 }
50 }
51
52 bool NeedsDecompression(OpIndex op) { return needs_decompression[op]; }
53 bool NeedsDecompression(const Operation& op) {
54 return NeedsDecompression(graph.Index(op));
55 }
56 bool MarkAsNeedsDecompression(OpIndex op) {
57 return (needs_decompression[op] = true);
58 }
59
60 template <bool is_loop>
61 void ProcessBlock(const Block& block, int32_t* next_block_id) {
62 for (const Operation& op : base::Reversed(graph.operations(block))) {
63 if (is_loop && op.Is<PhiOp>() && NeedsDecompression(op)) {
64 const PhiOp& phi = op.Cast<PhiOp>();
65 if (!NeedsDecompression(phi.input(1))) {
66 Block* backedge = block.LastPredecessor();
67 *next_block_id =
68 std::max<int32_t>(*next_block_id, backedge->index().id());
69 }
70 }
71 ProcessOperation(op);
72 }
73 }
74 void ProcessOperation(const Operation& op);
75 void MarkAddressingBase(OpIndex base_idx);
76};
77
78void DecompressionAnalyzer::ProcessOperation(const Operation& op) {
79 switch (op.opcode) {
80 case Opcode::kStore: {
81 auto& store = op.Cast<StoreOp>();
82 MarkAsNeedsDecompression(store.base());
83 if (store.index().valid()) {
84 MarkAsNeedsDecompression(store.index().value());
85 }
86 if (!store.stored_rep.IsCompressibleTagged()) {
87 MarkAsNeedsDecompression(store.value());
88 }
89 break;
90 }
91 case Opcode::kFrameState:
92 // The deopt code knows how to handle compressed inputs.
93 break;
94 case Opcode::kPhi: {
95 // Replicate the phi's state for its inputs.
96 auto& phi = op.Cast<PhiOp>();
97 if (NeedsDecompression(op)) {
98 for (OpIndex input : phi.inputs()) {
99 MarkAsNeedsDecompression(input);
100 }
101 } else {
102 candidates.push_back(graph.Index(op));
103 }
104 break;
105 }
106 case Opcode::kComparison: {
107 auto& comp = op.Cast<ComparisonOp>();
108 if (comp.rep == WordRepresentation::Word64()) {
109 MarkAsNeedsDecompression(comp.left());
110 MarkAsNeedsDecompression(comp.right());
111 }
112 break;
113 }
114 case Opcode::kWordBinop: {
115 auto& binary_op = op.Cast<WordBinopOp>();
116 if (binary_op.rep == WordRepresentation::Word64()) {
117 MarkAsNeedsDecompression(binary_op.left());
118 MarkAsNeedsDecompression(binary_op.right());
119 }
120 break;
121 }
122 case Opcode::kShift: {
123 auto& shift_op = op.Cast<ShiftOp>();
124 if (shift_op.rep == WordRepresentation::Word64()) {
125 MarkAsNeedsDecompression(shift_op.left());
126 }
127 break;
128 }
129 case Opcode::kChange: {
130 auto& change = op.Cast<ChangeOp>();
131 if (change.to == WordRepresentation::Word64() && NeedsDecompression(op)) {
132 MarkAsNeedsDecompression(change.input());
133 }
134 break;
135 }
136 case Opcode::kTaggedBitcast: {
137 auto& bitcast = op.Cast<TaggedBitcastOp>();
138 if (bitcast.kind != TaggedBitcastOp::Kind::kSmi &&
139 NeedsDecompression(op)) {
140 MarkAsNeedsDecompression(bitcast.input());
141 } else {
142 candidates.push_back(graph.Index(op));
143 }
144 break;
145 }
146 case Opcode::kConstant:
147 if (!NeedsDecompression(op)) {
148 candidates.push_back(graph.Index(op));
149 }
150 break;
151 case Opcode::kLoad: {
152 if (!NeedsDecompression(op)) {
153 candidates.push_back(graph.Index(op));
154 }
155 const LoadOp& load = op.Cast<LoadOp>();
156 if (DECOMPRESS_POINTER_BY_ADDRESSING_MODE && !load.index().valid() &&
157 graph.Get(load.base()).saturated_use_count.IsOne()) {
158 // On x64, if the Index is invalid, we can rely on complex addressing
159 // mode to decompress the base, and can thus keep it compressed.
160 // We only do this if the use-count of the base is 1, in order to avoid
161 // having to decompress multiple time the same value.
162 MarkAddressingBase(load.base());
163 } else {
164 MarkAsNeedsDecompression(load.base());
165 if (load.index().valid()) {
166 MarkAsNeedsDecompression(load.index().value());
167 }
168 }
169 break;
170 }
171 default:
172 for (OpIndex input : op.inputs()) {
173 MarkAsNeedsDecompression(input);
174 }
175 break;
176 }
177}
178
179// Checks if {base_idx} (which should be the base of a LoadOp) can be kept
180// compressed and decompressed using complex addressing mode. If not, marks it
181// as needing decompressiong.
182void DecompressionAnalyzer::MarkAddressingBase(OpIndex base_idx) {
184 const Operation& base = graph.Get(base_idx);
185 if (const LoadOp* load = base.TryCast<LoadOp>();
186 load && load->loaded_rep.IsCompressibleTagged()) {
187 // We can keep {load} (the base) as compressed and untag with complex
188 // addressing mode.
189 return;
190 }
191 if (base.Is<PhiOp>()) {
192 bool keep_compressed = true;
193 for (OpIndex input_idx : base.inputs()) {
194 const Operation& input = graph.Get(input_idx);
195 if (!input.Is<LoadOp>() || !base.IsOnlyUserOf(input, graph) ||
196 !input.Cast<LoadOp>().loaded_rep.IsCompressibleTagged()) {
197 keep_compressed = false;
198 break;
199 }
200 }
201 if (keep_compressed) return;
202 }
203 MarkAsNeedsDecompression(base_idx);
204}
205
206} // namespace
207
208// Instead of using `CopyingPhase`, we directly mutate the operations after
209// the analysis. Doing it in-place is possible because we only modify operation
210// options.
212 DecompressionAnalyzer analyzer(graph, phase_zone);
213 analyzer.Run();
214 for (OpIndex op_idx : analyzer.candidates) {
215 Operation& op = graph.Get(op_idx);
216 if (analyzer.NeedsDecompression(op)) continue;
217 switch (op.opcode) {
218 case Opcode::kConstant: {
219 auto& constant = op.Cast<ConstantOp>();
220 if (constant.kind == ConstantOp::Kind::kHeapObject) {
222 }
223 break;
224 }
225 case Opcode::kPhi: {
226 auto& phi = op.Cast<PhiOp>();
227 if (phi.rep == RegisterRepresentation::Tagged()) {
229 }
230 break;
231 }
232 case Opcode::kLoad: {
233 auto& load = op.Cast<LoadOp>();
234 if (load.loaded_rep.IsCompressibleTagged()) {
235 DCHECK_EQ(load.result_rep,
238 load.result_rep = RegisterRepresentation::Compressed();
239 }
240 break;
241 }
242 case Opcode::kTaggedBitcast: {
243 auto& bitcast = op.Cast<TaggedBitcastOp>();
244 if (bitcast.from == RegisterRepresentation::Tagged() &&
245 (bitcast.to == RegisterRepresentation::WordPtr() ||
246 bitcast.kind == TaggedBitcastOp::Kind::kSmi)) {
247 bitcast.from = RegisterRepresentation::Compressed();
248 bitcast.to = RegisterRepresentation::Word32();
249 }
250 break;
251 }
252 default:
253 break;
254 }
255 }
256}
257
258} // namespace v8::internal::compiler::turboshaft
friend Zone
Definition asm-types.cc:195
static constexpr RegisterRepresentation Compressed()
static constexpr RegisterRepresentation Word32()
static constexpr RegisterRepresentation WordPtr()
static constexpr RegisterRepresentation Tagged()
#define DECOMPRESS_POINTER_BY_ADDRESSING_MODE
Definition globals.h:105
FixedOpIndexSidetable< uint8_t > needs_decompression
ZoneVector< OpIndex > candidates
auto Reversed(T &t)
Definition iterator.h:105
void RunDecompressionOptimization(Graph &graph, Zone *phase_zone)
Operation
Definition operation.h:43
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
underlying_operation_t< Op > & Cast()
Definition operations.h:980