blob: a32c58f9cd9586fb167941cbe2851a03efc3f046 [file] [log] [blame]
fpizlo@apple.com21ea9452015-11-10 00:01:24 +00001/*
fpizlo@apple.com84976a472017-02-24 22:50:00 +00002 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
fpizlo@apple.com21ea9452015-11-10 00:01:24 +00003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "B3MoveConstants.h"
28
29#if ENABLE(B3_JIT)
30
fpizlo@apple.com84976a472017-02-24 22:50:00 +000031#include "AirArg.h"
fpizlo@apple.com21ea9452015-11-10 00:01:24 +000032#include "B3BasicBlockInlines.h"
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000033#include "B3Dominators.h"
fpizlo@apple.com21ea9452015-11-10 00:01:24 +000034#include "B3InsertionSetInlines.h"
fpizlo@apple.com42624fe2017-03-10 17:49:42 +000035#include "B3MemoryValueInlines.h"
fpizlo@apple.com21ea9452015-11-10 00:01:24 +000036#include "B3PhaseScope.h"
37#include "B3ProcedureInlines.h"
38#include "B3ValueInlines.h"
39#include "B3ValueKeyInlines.h"
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000040#include <wtf/HashMap.h>
41#include <wtf/Vector.h>
fpizlo@apple.com21ea9452015-11-10 00:01:24 +000042
43namespace JSC { namespace B3 {
44
45namespace {
46
47class MoveConstants {
48public:
49 MoveConstants(Procedure& proc)
50 : m_proc(proc)
51 , m_insertionSet(proc)
52 {
53 }
54
55 void run()
56 {
fpizlo@apple.com65e043c2016-01-19 20:55:34 +000057 hoistConstants(
58 [&] (const ValueKey& key) -> bool {
59 return key.opcode() == ConstFloat || key.opcode() == ConstDouble;
60 });
61
62 lowerFPConstants();
63
64 hoistConstants(
65 [&] (const ValueKey& key) -> bool {
fpizlo@apple.com8c6262b2016-07-24 20:33:40 +000066 return key.opcode() == Const32 || key.opcode() == Const64 || key.opcode() == ArgumentReg;
fpizlo@apple.com65e043c2016-01-19 20:55:34 +000067 });
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000068 }
69
70private:
fpizlo@apple.com65e043c2016-01-19 20:55:34 +000071 template<typename Filter>
72 void hoistConstants(const Filter& filter)
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000073 {
74 Dominators& dominators = m_proc.dominators();
75 HashMap<ValueKey, Value*> valueForConstant;
fpizlo@apple.com0c9128d2017-03-30 22:55:44 +000076 IndexMap<BasicBlock*, Vector<Value*>> materializations(m_proc.size());
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000077
78 // We determine where things get materialized based on where they are used.
79 for (BasicBlock* block : m_proc) {
80 for (Value* value : *block) {
81 for (Value*& child : value->children()) {
82 ValueKey key = child->key();
fpizlo@apple.com65e043c2016-01-19 20:55:34 +000083 if (!filter(key))
fpizlo@apple.com06abfa02015-12-08 02:46:22 +000084 continue;
85
86 auto result = valueForConstant.add(key, child);
87 if (result.isNewEntry) {
88 // Assume that this block is where we want to materialize the value.
89 child->owner = block;
90 continue;
91 }
92
93 // Make 'value' use the canonical constant rather than the one it was using.
94 child = result.iterator->value;
95
96 // Determine the least common dominator. That's the lowest place in the CFG where
97 // we could materialize the constant while still having only one materialization
98 // in the resulting code.
99 while (!dominators.dominates(child->owner, block))
100 child->owner = dominators.idom(child->owner);
101 }
102 }
103 }
104
105 // Make sure that each basic block knows what to materialize. This also refines the
106 // materialization block based on execution frequency. It finds the minimum block frequency
107 // of all of its dominators, and selects the closest block amongst those that are tied for
108 // lowest frequency.
109 for (auto& entry : valueForConstant) {
110 Value* value = entry.value;
111 for (BasicBlock* block = value->owner; block; block = dominators.idom(block)) {
112 if (block->frequency() < value->owner->frequency())
113 value->owner = block;
114 }
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000115 materializations[value->owner].append(value);
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000116 }
117
118 // Get rid of Value's that are fast constants but aren't canonical. Also remove the canonical
119 // ones from the CFG, since we're going to reinsert them elsewhere.
120 for (BasicBlock* block : m_proc) {
121 for (Value*& value : *block) {
122 ValueKey key = value->key();
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000123 if (!filter(key))
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000124 continue;
125
126 if (valueForConstant.get(key) == value)
127 value = m_proc.add<Value>(Nop, value->origin());
128 else
fpizlo@apple.comf5dc5202016-06-27 18:35:09 +0000129 value->replaceWithNopIgnoringType();
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000130 }
131 }
132
133 // Now make sure that we move constants to where they are supposed to go. Again, we do this
134 // based on uses.
135 for (BasicBlock* block : m_proc) {
136 for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
137 Value* value = block->at(valueIndex);
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000138
139 // This finds the outermost (best) block last. So, the functor overrides the result
140 // each time it finds something acceptable.
141 auto findBestConstant = [&] (const auto& predicate) -> Value* {
142 Value* result = nullptr;
143 dominators.forAllDominatorsOf(
144 block,
145 [&] (BasicBlock* dominator) {
146 for (Value* value : materializations[dominator]) {
147 if (predicate(value)) {
148 result = value;
149 break;
150 }
151 }
152 });
153 return result;
154 };
155
156 // We call this when we have found a constant that we'd like to use. It's possible that
157 // we have computed that the constant should be meterialized in this block, but we
158 // haven't inserted it yet. This inserts the constant if necessary.
159 auto materialize = [&] (Value* child) {
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000160 ValueKey key = child->key();
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000161 if (!filter(key))
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000162 return;
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000163
164 // If we encounter a fast constant, then it must be canonical, since we already
165 // got rid of the non-canonical ones.
166 ASSERT(valueForConstant.get(key) == child);
167
168 if (child->owner != block) {
169 // This constant isn't our problem. It's going to be materialized in another
170 // block.
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000171 return;
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000172 }
173
174 // We're supposed to materialize this constant in this block, and we haven't
175 // done it yet.
176 m_insertionSet.insertValue(valueIndex, child);
177 child->owner = nullptr;
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000178 };
179
180 if (MemoryValue* memoryValue = value->as<MemoryValue>()) {
181 Value* pointer = memoryValue->lastChild();
182 if (pointer->hasIntPtr() && filter(pointer->key())) {
183 auto desiredOffset = [&] (Value* otherPointer) -> intptr_t {
184 // We would turn this:
185 //
186 // Load(@p, offset = c)
187 //
188 // into this:
189 //
190 // Load(@q, offset = ?)
191 //
192 // The offset should be c + @p - @q, because then we're loading from:
193 //
194 // @q + c + @p - @q
195 uintptr_t c = static_cast<uintptr_t>(static_cast<intptr_t>(memoryValue->offset()));
196 uintptr_t p = pointer->asIntPtr();
197 uintptr_t q = otherPointer->asIntPtr();
198 return c + p - q;
199 };
200
201 Value* bestPointer = findBestConstant(
202 [&] (Value* candidatePointer) -> bool {
203 if (!candidatePointer->hasIntPtr())
204 return false;
205
fpizlo@apple.com42624fe2017-03-10 17:49:42 +0000206 int64_t offset = desiredOffset(candidatePointer);
207 return memoryValue->isLegalOffset(offset);
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000208 });
209
210 if (bestPointer) {
211 memoryValue->lastChild() = bestPointer;
212 memoryValue->setOffset(desiredOffset(bestPointer));
213 }
214 }
215 } else {
216 switch (value->opcode()) {
217 case Add:
218 case Sub: {
219 Value* addend = value->child(1);
220 if (!addend->hasInt() || !filter(addend->key()))
221 break;
222 int64_t addendConst = addend->asInt();
223 Value* bestAddend = findBestConstant(
224 [&] (Value* candidateAddend) -> bool {
225 if (candidateAddend->type() != addend->type())
226 return false;
227 if (!candidateAddend->hasInt())
228 return false;
229 return candidateAddend == addend
230 || candidateAddend->asInt() == -addendConst;
231 });
232 if (!bestAddend || bestAddend == addend)
233 break;
234 materialize(value->child(0));
235 materialize(bestAddend);
236 value->replaceWithIdentity(
237 m_insertionSet.insert<Value>(
238 valueIndex, value->opcode() == Add ? Sub : Add, value->origin(),
239 value->child(0), bestAddend));
240 break;
241 }
242 default:
243 break;
244 }
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000245 }
fpizlo@apple.com20f04ec2016-09-30 22:29:24 +0000246
247 for (Value* child : value->children())
248 materialize(child);
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000249 }
250
251 // We may have some constants that need to be materialized right at the end of this
252 // block.
253 for (Value* value : materializations[block]) {
254 if (!value->owner) {
255 // It's already materialized in this block.
256 continue;
257 }
258
259 m_insertionSet.insertValue(block->size() - 1, value);
260 }
261 m_insertionSet.execute(block);
262 }
263 }
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000264
265 void lowerFPConstants()
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000266 {
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000267 for (Value* value : m_proc.values()) {
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000268 ValueKey key = value->key();
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000269 if (goesInTable(key))
270 m_constTable.add(key, m_constTable.size());
271 }
272
273 m_dataSection = static_cast<int64_t*>(m_proc.addDataSection(m_constTable.size() * sizeof(int64_t)));
274 for (auto& entry : m_constTable)
275 m_dataSection[entry.value] = entry.key.value();
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000276
fpizlo@apple.com0c9128d2017-03-30 22:55:44 +0000277 IndexSet<Value*> offLimits;
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000278 for (BasicBlock* block : m_proc) {
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000279 for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000280 StackmapValue* value = block->at(valueIndex)->as<StackmapValue>();
281 if (!value)
282 continue;
283
fpizlo@apple.comc48e4322015-11-12 20:41:06 +0000284 for (unsigned childIndex = 0; childIndex < value->numChildren(); ++childIndex) {
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000285 if (!value->constrainedChild(childIndex).rep().isAny())
286 continue;
287
fpizlo@apple.comc48e4322015-11-12 20:41:06 +0000288 Value*& child = value->child(childIndex);
fpizlo@apple.com06abfa02015-12-08 02:46:22 +0000289 ValueKey key = child->key();
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000290 if (!goesInTable(key))
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000291 continue;
292
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000293 child = m_insertionSet.insertValue(
294 valueIndex, key.materialize(m_proc, value->origin()));
295 offLimits.add(child);
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000296 }
297 }
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000298
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000299 m_insertionSet.execute(block);
300 }
301
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000302 for (BasicBlock* block : m_proc) {
303 for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
304 Value* value = block->at(valueIndex);
305 ValueKey key = value->key();
306 if (!goesInTable(key))
307 continue;
308 if (offLimits.contains(value))
309 continue;
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000310
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000311 Value* tableBase = m_insertionSet.insertIntConstant(
312 valueIndex, value->origin(), pointerType(),
313 bitwise_cast<intptr_t>(m_dataSection));
314 Value* result = m_insertionSet.insert<MemoryValue>(
315 valueIndex, Load, value->type(), value->origin(), tableBase,
316 sizeof(int64_t) * m_constTable.get(key));
317 value->replaceWithIdentity(result);
318 }
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000319
fpizlo@apple.com65e043c2016-01-19 20:55:34 +0000320 m_insertionSet.execute(block);
321 }
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000322 }
323
324 bool goesInTable(const ValueKey& key)
325 {
benjamin@webkit.org02dd0142015-12-08 02:56:16 +0000326 return (key.opcode() == ConstDouble && key != doubleZero())
fpizlo@apple.com7c57c052015-12-08 04:05:24 +0000327 || (key.opcode() == ConstFloat && key != floatZero());
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000328 }
329
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000330 static ValueKey doubleZero()
331 {
332 return ValueKey(ConstDouble, Double, 0.0);
333 }
334
benjamin@webkit.org02dd0142015-12-08 02:56:16 +0000335 static ValueKey floatZero()
336 {
337 return ValueKey(ConstFloat, Double, 0.0);
338 }
339
fpizlo@apple.com21ea9452015-11-10 00:01:24 +0000340 Procedure& m_proc;
341 Vector<Value*> m_toRemove;
342 HashMap<ValueKey, unsigned> m_constTable;
343 int64_t* m_dataSection;
344 HashMap<ValueKey, Value*> m_constants;
345 InsertionSet m_insertionSet;
346};
347
348} // anonymous namespace
349
350void moveConstants(Procedure& proc)
351{
352 PhaseScope phaseScope(proc, "moveConstants");
353 MoveConstants moveConstants(proc);
354 moveConstants.run();
355}
356
357} } // namespace JSC::B3
358
359#endif // ENABLE(B3_JIT)
360