Make CSS JIT run on ARM64.
[WebKit-https.git] / Source / WebCore / cssjit / FunctionCall.h
1 /*
2  * Copyright (C) 2013 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #ifndef FunctionCall_h
27 #define FunctionCall_h
28
29 #if ENABLE(CSS_SELECTOR_JIT)
30
31 #include "RegisterAllocator.h"
32 #include "StackAllocator.h"
33 #include <JavaScriptCore/GPRInfo.h>
34 #include <JavaScriptCore/MacroAssembler.h>
35
36 namespace WebCore {
37
38 class FunctionCall {
39 public:
40     FunctionCall(JSC::MacroAssembler& assembler, RegisterAllocator& registerAllocator, StackAllocator& stackAllocator, Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr>>& callRegistry)
41         : m_assembler(assembler)
42         , m_registerAllocator(registerAllocator)
43         , m_stackAllocator(stackAllocator)
44         , m_callRegistry(callRegistry)
45         , m_argumentCount(0)
46         , m_firstArgument(InvalidGPRReg)
47         , m_secondArgument(InvalidGPRReg)
48     {
49     }
50
51     void setFunctionAddress(JSC::FunctionPtr functionAddress)
52     {
53         m_functionAddress = functionAddress;
54     }
55
56     void setOneArgument(const JSC::MacroAssembler::RegisterID& registerID)
57     {
58         m_argumentCount = 1;
59         m_firstArgument = registerID;
60     }
61
62     void setTwoArguments(const JSC::MacroAssembler::RegisterID& firstRegisterID, const JSC::MacroAssembler::RegisterID& secondRegisterID)
63     {
64         m_argumentCount = 2;
65         m_firstArgument = firstRegisterID;
66         m_secondArgument = secondRegisterID;
67     }
68
69     void call()
70     {
71         prepareAndCall();
72         cleanupPostCall();
73     }
74
75     JSC::MacroAssembler::Jump callAndBranchOnCondition(JSC::MacroAssembler::ResultCondition condition)
76     {
77         prepareAndCall();
78         m_assembler.test32(condition, JSC::GPRInfo::returnValueGPR, JSC::MacroAssembler::TrustedImm32(0xff));
79         cleanupPostCall();
80         return m_assembler.branch(condition);
81     }
82
83 private:
84     void swapArguments()
85     {
86         JSC::MacroAssembler::RegisterID a = m_firstArgument;
87         JSC::MacroAssembler::RegisterID b = m_secondArgument;
88         // x86 can swap without a temporary register. On other architectures, we need allocate a temporary register to switch the values.
89 #if CPU(X86) || CPU(X86_64)
90         m_assembler.swap(a, b);
91 #else
92         if (m_registerAllocator.availableRegisterCount()) {
93             // Usually we can just use a free register.
94             LocalRegister tempValue(m_registerAllocator);
95             m_assembler.move(a, tempValue);
96             m_assembler.move(b, a);
97             m_assembler.move(tempValue, b);
98         } else {
99             // If there is no free register, everything should be on the stack at this point. We can take
100             // the first of those saved registers and use it as a temporary.
101             JSC::MacroAssembler::RegisterID pushedRegister;
102             for (unsigned i = 0; i < m_registerAllocator.allocatedRegisters().size(); ++i) {
103                 pushedRegister = m_registerAllocator.allocatedRegisters()[i];
104                 if (pushedRegister != a && pushedRegister != b)
105                     break;
106             }
107             ASSERT(pushedRegister != a && pushedRegister != b);
108             m_assembler.move(a, pushedRegister);
109             m_assembler.move(b, a);
110             m_assembler.move(pushedRegister, b);
111         }
112 #endif
113     }
114
115     void prepareAndCall()
116     {
117         ASSERT(m_functionAddress.executableAddress());
118         ASSERT(!m_firstArgument || (m_firstArgument && !m_secondArgument) || (m_firstArgument && m_secondArgument));
119
120         saveAllocatedRegisters();
121         m_stackAllocator.alignStackPreFunctionCall();
122
123         if (m_argumentCount == 2) {
124             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
125             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_secondArgument));
126
127             if (m_firstArgument != JSC::GPRInfo::argumentGPR0) {
128                 // If firstArgument is not in argumentGPR0, we need to handle potential conflicts:
129                 // -if secondArgument and firstArgument are in inversted registers, just swap the values.
130                 // -if secondArgument is in argumentGPR0 but firstArgument is not taking argumentGPR1, we can move in order secondArgument->argumentGPR1, firstArgument->argumentGPR0
131                 // -if secondArgument does not take argumentGPR0, firstArgument and secondArgument can be moved safely to destination.
132                 if (m_secondArgument == JSC::GPRInfo::argumentGPR0) {
133                     if (m_firstArgument == JSC::GPRInfo::argumentGPR1)
134                         swapArguments();
135                     else {
136                         m_assembler.move(JSC::GPRInfo::argumentGPR0, JSC::GPRInfo::argumentGPR1);
137                         m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
138                     }
139                 } else {
140                     m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
141                     if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
142                         m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
143                 }
144             } else {
145                 // We know firstArgument is already in place, we can safely move secondArgument.
146                 if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
147                     m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
148             }
149         } else if (m_argumentCount == 1) {
150             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
151             if (m_firstArgument != JSC::GPRInfo::argumentGPR0)
152                 m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
153         }
154
155         JSC::MacroAssembler::Call call = m_assembler.call();
156         m_callRegistry.append(std::make_pair(call, m_functionAddress));
157     }
158
159     void cleanupPostCall()
160     {
161         m_stackAllocator.unalignStackPostFunctionCall();
162         restoreAllocatedRegisters();
163     }
164
165     void saveAllocatedRegisters()
166     {
167         ASSERT(m_savedRegisterStackReferences.isEmpty());
168         const Vector<JSC::MacroAssembler::RegisterID, registerCount>& allocatedRegisters = m_registerAllocator.allocatedRegisters();
169         Vector<StackAllocator::StackReference> stackReferences = m_stackAllocator.push(allocatedRegisters);
170         m_savedRegisterStackReferences.appendVector(stackReferences);
171     }
172
173     void restoreAllocatedRegisters()
174     {
175         m_stackAllocator.pop(m_savedRegisterStackReferences, m_registerAllocator.allocatedRegisters());
176         m_savedRegisterStackReferences.clear();
177     }
178
179     JSC::MacroAssembler& m_assembler;
180     RegisterAllocator& m_registerAllocator;
181     StackAllocator& m_stackAllocator;
182     Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr>>& m_callRegistry;
183
184     Vector<StackAllocator::StackReference> m_savedRegisterStackReferences;
185
186     JSC::FunctionPtr m_functionAddress;
187     unsigned m_argumentCount;
188     JSC::MacroAssembler::RegisterID m_firstArgument;
189     JSC::MacroAssembler::RegisterID m_secondArgument;
190 };
191
192 } // namespace WebCore
193
194 #endif // ENABLE(CSS_SELECTOR_JIT)
195
196 #endif // FunctionCall_h