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
41namespace JSC { namespace B3 { namespace Air {
42
43static 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
56Code::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
98Code::~Code()
99{
100}
101
102void Code::emitDefaultPrologue(CCallHelpers& jit)
103{
104 defaultPrologueGenerator(jit, *this);
105}
106
107void 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
118void 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
128RegisterSet Code::mutableGPRs()
129{
130 RegisterSet result = m_mutableRegs;
131 result.filter(RegisterSet::allGPRs());
132 return result;
133}
134
135RegisterSet Code::mutableFPRs()
136{
137 RegisterSet result = m_mutableRegs;
138 result.filter(RegisterSet::allFPRs());
139 return result;
140}
141
142bool Code::needsUsedRegisters() const
143{
144 return m_proc.needsUsedRegisters();
145}
146
147BasicBlock* 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
155StackSlot* 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
167StackSlot* Code::addStackSlot(B3::StackSlot* b3Slot)
168{
169 return addStackSlot(b3Slot->byteSize(), StackSlotKind::Locked, b3Slot);
170}
171
172Special* Code::addSpecial(std::unique_ptr<Special> special)
173{
174 special->m_code = this;
175 return m_specials.add(WTFMove(special));
176}
177
178CCallSpecial* 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
188bool 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
202Optional<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
212void 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
220RegisterAtOffsetList 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
234void 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
250void 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
275unsigned Code::findFirstBlockIndex(unsigned index) const
276{
277 while (index < size() && !at(index))
278 index++;
279 return index;
280}
281
282unsigned Code::findNextBlockIndex(unsigned index) const
283{
284 return findFirstBlockIndex(index + 1);
285}
286
287BasicBlock* 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
295void Code::addFastTmp(Tmp tmp)
296{
297 m_fastTmps.add(tmp);
298}
299
300void* Code::addDataSection(size_t size)
301{
302 return m_proc.addDataSection(size);
303}
304
305unsigned 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
328void 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