1 | /* |
2 | * Copyright (C) 2015-2018 Apple Inc. All rights reserved. |
3 | * |
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 "AirCode.h" |
28 | |
29 | #if ENABLE(B3_JIT) |
30 | |
31 | #include "AirAllocateRegistersAndStackAndGenerateCode.h" |
32 | #include "AirCCallSpecial.h" |
33 | #include "AirCFG.h" |
34 | #include "AllowMacroScratchRegisterUsageIf.h" |
35 | #include "B3BasicBlockUtils.h" |
36 | #include "B3Procedure.h" |
37 | #include "B3StackSlot.h" |
38 | #include <wtf/ListDump.h> |
39 | #include <wtf/MathExtras.h> |
40 | |
41 | namespace JSC { namespace B3 { namespace Air { |
42 | |
43 | static void defaultPrologueGenerator(CCallHelpers& jit, Code& code) |
44 | { |
45 | jit.emitFunctionPrologue(); |
46 | if (code.frameSize()) { |
47 | AllowMacroScratchRegisterUsageIf allowScratch(jit, isARM64()); |
48 | jit.addPtr(MacroAssembler::TrustedImm32(-code.frameSize()), MacroAssembler::framePointerRegister, MacroAssembler::stackPointerRegister); |
49 | if (Options::zeroStackFrame()) |
50 | jit.clearStackFrame(MacroAssembler::framePointerRegister, MacroAssembler::stackPointerRegister, GPRInfo::nonArgGPR0, code.frameSize()); |
51 | } |
52 | |
53 | jit.emitSave(code.calleeSaveRegisterAtOffsetList()); |
54 | } |
55 | |
56 | Code::Code(Procedure& proc) |
57 | : m_proc(proc) |
58 | , m_cfg(new CFG(*this)) |
59 | , m_lastPhaseName("initial" ) |
60 | , m_defaultPrologueGenerator(createSharedTask<PrologueGeneratorFunction>(&defaultPrologueGenerator)) |
61 | { |
62 | // Come up with initial orderings of registers. The user may replace this with something else. |
63 | forEachBank( |
64 | [&] (Bank bank) { |
65 | Vector<Reg> volatileRegs; |
66 | Vector<Reg> calleeSaveRegs; |
67 | RegisterSet all = bank == GP ? RegisterSet::allGPRs() : RegisterSet::allFPRs(); |
68 | all.exclude(RegisterSet::stackRegisters()); |
69 | all.exclude(RegisterSet::reservedHardwareRegisters()); |
70 | RegisterSet calleeSave = RegisterSet::calleeSaveRegisters(); |
71 | all.forEach( |
72 | [&] (Reg reg) { |
73 | if (!calleeSave.get(reg)) |
74 | volatileRegs.append(reg); |
75 | }); |
76 | all.forEach( |
77 | [&] (Reg reg) { |
78 | if (calleeSave.get(reg)) |
79 | calleeSaveRegs.append(reg); |
80 | }); |
81 | if (Options::airRandomizeRegs()) { |
82 | WeakRandom random(Options::airRandomizeRegsSeed() ? Options::airRandomizeRegsSeed() : m_weakRandom.getUint32()); |
83 | shuffleVector(volatileRegs, [&] (unsigned limit) { return random.getUint32(limit); }); |
84 | shuffleVector(calleeSaveRegs, [&] (unsigned limit) { return random.getUint32(limit); }); |
85 | } |
86 | Vector<Reg> result; |
87 | result.appendVector(volatileRegs); |
88 | result.appendVector(calleeSaveRegs); |
89 | setRegsInPriorityOrder(bank, result); |
90 | }); |
91 | |
92 | if (auto reg = pinnedExtendedOffsetAddrRegister()) |
93 | pinRegister(*reg); |
94 | |
95 | m_pinnedRegs.set(MacroAssembler::framePointerRegister); |
96 | } |
97 | |
98 | Code::~Code() |
99 | { |
100 | } |
101 | |
102 | void Code::emitDefaultPrologue(CCallHelpers& jit) |
103 | { |
104 | defaultPrologueGenerator(jit, *this); |
105 | } |
106 | |
107 | void Code::setRegsInPriorityOrder(Bank bank, const Vector<Reg>& regs) |
108 | { |
109 | regsInPriorityOrderImpl(bank) = regs; |
110 | m_mutableRegs = { }; |
111 | forEachBank( |
112 | [&] (Bank bank) { |
113 | for (Reg reg : regsInPriorityOrder(bank)) |
114 | m_mutableRegs.set(reg); |
115 | }); |
116 | } |
117 | |
118 | void Code::pinRegister(Reg reg) |
119 | { |
120 | Vector<Reg>& regs = regsInPriorityOrderImpl(Arg(Tmp(reg)).bank()); |
121 | ASSERT(regs.contains(reg)); |
122 | regs.removeFirst(reg); |
123 | m_mutableRegs.clear(reg); |
124 | ASSERT(!regs.contains(reg)); |
125 | m_pinnedRegs.set(reg); |
126 | } |
127 | |
128 | RegisterSet Code::mutableGPRs() |
129 | { |
130 | RegisterSet result = m_mutableRegs; |
131 | result.filter(RegisterSet::allGPRs()); |
132 | return result; |
133 | } |
134 | |
135 | RegisterSet Code::mutableFPRs() |
136 | { |
137 | RegisterSet result = m_mutableRegs; |
138 | result.filter(RegisterSet::allFPRs()); |
139 | return result; |
140 | } |
141 | |
142 | bool Code::needsUsedRegisters() const |
143 | { |
144 | return m_proc.needsUsedRegisters(); |
145 | } |
146 | |
147 | BasicBlock* Code::addBlock(double frequency) |
148 | { |
149 | std::unique_ptr<BasicBlock> block(new BasicBlock(m_blocks.size(), frequency)); |
150 | BasicBlock* result = block.get(); |
151 | m_blocks.append(WTFMove(block)); |
152 | return result; |
153 | } |
154 | |
155 | StackSlot* Code::addStackSlot(unsigned byteSize, StackSlotKind kind, B3::StackSlot* b3Slot) |
156 | { |
157 | StackSlot* result = m_stackSlots.addNew(byteSize, kind, b3Slot); |
158 | if (m_stackIsAllocated) { |
159 | // FIXME: This is unnecessarily awful. Fortunately, it doesn't run often. |
160 | unsigned extent = WTF::roundUpToMultipleOf(result->alignment(), frameSize() + byteSize); |
161 | result->setOffsetFromFP(-static_cast<ptrdiff_t>(extent)); |
162 | setFrameSize(WTF::roundUpToMultipleOf(stackAlignmentBytes(), extent)); |
163 | } |
164 | return result; |
165 | } |
166 | |
167 | StackSlot* Code::addStackSlot(B3::StackSlot* b3Slot) |
168 | { |
169 | return addStackSlot(b3Slot->byteSize(), StackSlotKind::Locked, b3Slot); |
170 | } |
171 | |
172 | Special* Code::addSpecial(std::unique_ptr<Special> special) |
173 | { |
174 | special->m_code = this; |
175 | return m_specials.add(WTFMove(special)); |
176 | } |
177 | |
178 | CCallSpecial* Code::cCallSpecial() |
179 | { |
180 | if (!m_cCallSpecial) { |
181 | m_cCallSpecial = static_cast<CCallSpecial*>( |
182 | addSpecial(makeUnique<CCallSpecial>())); |
183 | } |
184 | |
185 | return m_cCallSpecial; |
186 | } |
187 | |
188 | bool Code::isEntrypoint(BasicBlock* block) const |
189 | { |
190 | // Note: This function must work both before and after LowerEntrySwitch. |
191 | |
192 | if (m_entrypoints.isEmpty()) |
193 | return !block->index(); |
194 | |
195 | for (const FrequentedBlock& entrypoint : m_entrypoints) { |
196 | if (entrypoint.block() == block) |
197 | return true; |
198 | } |
199 | return false; |
200 | } |
201 | |
202 | Optional<unsigned> Code::entrypointIndex(BasicBlock* block) const |
203 | { |
204 | RELEASE_ASSERT(m_entrypoints.size()); |
205 | for (unsigned i = 0; i < m_entrypoints.size(); ++i) { |
206 | if (m_entrypoints[i].block() == block) |
207 | return i; |
208 | } |
209 | return WTF::nullopt; |
210 | } |
211 | |
212 | void Code::setCalleeSaveRegisterAtOffsetList(RegisterAtOffsetList&& registerAtOffsetList, StackSlot* slot) |
213 | { |
214 | m_uncorrectedCalleeSaveRegisterAtOffsetList = WTFMove(registerAtOffsetList); |
215 | for (const RegisterAtOffset& registerAtOffset : m_uncorrectedCalleeSaveRegisterAtOffsetList) |
216 | m_calleeSaveRegisters.set(registerAtOffset.reg()); |
217 | m_calleeSaveStackSlot = slot; |
218 | } |
219 | |
220 | RegisterAtOffsetList Code::calleeSaveRegisterAtOffsetList() const |
221 | { |
222 | RegisterAtOffsetList result = m_uncorrectedCalleeSaveRegisterAtOffsetList; |
223 | if (StackSlot* slot = m_calleeSaveStackSlot) { |
224 | ptrdiff_t offset = slot->byteSize() + slot->offsetFromFP(); |
225 | for (size_t i = result.size(); i--;) { |
226 | result.at(i) = RegisterAtOffset( |
227 | result.at(i).reg(), |
228 | result.at(i).offset() + offset); |
229 | } |
230 | } |
231 | return result; |
232 | } |
233 | |
234 | void Code::resetReachability() |
235 | { |
236 | clearPredecessors(m_blocks); |
237 | if (m_entrypoints.isEmpty()) |
238 | updatePredecessorsAfter(m_blocks[0].get()); |
239 | else { |
240 | for (const FrequentedBlock& entrypoint : m_entrypoints) |
241 | updatePredecessorsAfter(entrypoint.block()); |
242 | } |
243 | |
244 | for (auto& block : m_blocks) { |
245 | if (isBlockDead(block.get()) && !isEntrypoint(block.get())) |
246 | block = nullptr; |
247 | } |
248 | } |
249 | |
250 | void Code::dump(PrintStream& out) const |
251 | { |
252 | if (!m_entrypoints.isEmpty()) |
253 | out.print("Entrypoints: " , listDump(m_entrypoints), "\n" ); |
254 | for (BasicBlock* block : *this) |
255 | out.print(deepDump(block)); |
256 | if (stackSlots().size()) { |
257 | out.print("Stack slots:\n" ); |
258 | for (StackSlot* slot : stackSlots()) |
259 | out.print(" " , pointerDump(slot), ": " , deepDump(slot), "\n" ); |
260 | } |
261 | if (specials().size()) { |
262 | out.print("Specials:\n" ); |
263 | for (Special* special : specials()) |
264 | out.print(" " , deepDump(special), "\n" ); |
265 | } |
266 | if (m_frameSize || m_stackIsAllocated) |
267 | out.print("Frame size: " , m_frameSize, m_stackIsAllocated ? " (Allocated)" : "" , "\n" ); |
268 | if (m_callArgAreaSize) |
269 | out.print("Call arg area size: " , m_callArgAreaSize, "\n" ); |
270 | RegisterAtOffsetList calleeSaveRegisters = this->calleeSaveRegisterAtOffsetList(); |
271 | if (calleeSaveRegisters.size()) |
272 | out.print("Callee saves: " , calleeSaveRegisters, "\n" ); |
273 | } |
274 | |
275 | unsigned Code::findFirstBlockIndex(unsigned index) const |
276 | { |
277 | while (index < size() && !at(index)) |
278 | index++; |
279 | return index; |
280 | } |
281 | |
282 | unsigned Code::findNextBlockIndex(unsigned index) const |
283 | { |
284 | return findFirstBlockIndex(index + 1); |
285 | } |
286 | |
287 | BasicBlock* Code::findNextBlock(BasicBlock* block) const |
288 | { |
289 | unsigned index = findNextBlockIndex(block->index()); |
290 | if (index < size()) |
291 | return at(index); |
292 | return nullptr; |
293 | } |
294 | |
295 | void Code::addFastTmp(Tmp tmp) |
296 | { |
297 | m_fastTmps.add(tmp); |
298 | } |
299 | |
300 | void* Code::addDataSection(size_t size) |
301 | { |
302 | return m_proc.addDataSection(size); |
303 | } |
304 | |
305 | unsigned Code::jsHash() const |
306 | { |
307 | unsigned result = 0; |
308 | |
309 | for (BasicBlock* block : *this) { |
310 | result *= 1000001; |
311 | for (Inst& inst : *block) { |
312 | result *= 97; |
313 | result += inst.jsHash(); |
314 | } |
315 | for (BasicBlock* successor : block->successorBlocks()) { |
316 | result *= 7; |
317 | result += successor->index(); |
318 | } |
319 | } |
320 | for (StackSlot* slot : stackSlots()) { |
321 | result *= 101; |
322 | result += slot->jsHash(); |
323 | } |
324 | |
325 | return result; |
326 | } |
327 | |
328 | void Code::setNumEntrypoints(unsigned numEntrypoints) |
329 | { |
330 | m_prologueGenerators.clear(); |
331 | m_prologueGenerators.reserveCapacity(numEntrypoints); |
332 | for (unsigned i = 0; i < numEntrypoints; ++i) |
333 | m_prologueGenerators.uncheckedAppend(m_defaultPrologueGenerator.copyRef()); |
334 | } |
335 | |
336 | } } } // namespace JSC::B3::Air |
337 | |
338 | #endif // ENABLE(B3_JIT) |
339 | |