c29401683c44b16646addb8f8668a4ce3e75472b
[WebKit-https.git] / Source / WebCore / cssjit / FunctionCall.h
1 /*
2  * Copyright (C) 2013, 2014 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 #pragma once
27
28 #if ENABLE(CSS_SELECTOR_JIT)
29
30 #include "RegisterAllocator.h"
31 #include "StackAllocator.h"
32 #include <JavaScriptCore/GPRInfo.h>
33 #include <JavaScriptCore/MacroAssembler.h>
34
35 namespace WebCore {
36
37 class FunctionCall {
38 public:
39     FunctionCall(JSC::MacroAssembler& assembler, RegisterAllocator& registerAllocator, StackAllocator& stackAllocator, Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr>, 32>& callRegistry)
40         : m_assembler(assembler)
41         , m_registerAllocator(registerAllocator)
42         , m_stackAllocator(stackAllocator)
43         , m_callRegistry(callRegistry)
44         , m_argumentCount(0)
45         , m_firstArgument(InvalidGPRReg)
46         , m_secondArgument(InvalidGPRReg)
47     {
48     }
49
50     void setFunctionAddress(JSC::FunctionPtr functionAddress)
51     {
52         m_functionAddress = functionAddress;
53     }
54
55     void setOneArgument(const JSC::MacroAssembler::RegisterID& registerID)
56     {
57         m_argumentCount = 1;
58         m_firstArgument = registerID;
59     }
60
61     void setTwoArguments(const JSC::MacroAssembler::RegisterID& firstRegisterID, const JSC::MacroAssembler::RegisterID& secondRegisterID)
62     {
63         m_argumentCount = 2;
64         m_firstArgument = firstRegisterID;
65         m_secondArgument = secondRegisterID;
66     }
67
68     void call()
69     {
70         prepareAndCall();
71         cleanupPostCall();
72     }
73
74     JSC::MacroAssembler::Jump callAndBranchOnBooleanReturnValue(JSC::MacroAssembler::ResultCondition condition)
75     {
76 #if CPU(X86) || CPU(X86_64)
77         return callAndBranchOnCondition(condition, JSC::MacroAssembler::TrustedImm32(0xff));
78 #elif CPU(ARM64) || CPU(ARM)
79         return callAndBranchOnCondition(condition, JSC::MacroAssembler::TrustedImm32(-1));
80 #else
81 #error Missing implementationg for matching boolean return values.
82 #endif
83     }
84
85 private:
86     JSC::MacroAssembler::Jump callAndBranchOnCondition(JSC::MacroAssembler::ResultCondition condition, JSC::MacroAssembler::TrustedImm32 mask)
87     {
88         prepareAndCall();
89         m_assembler.test32(JSC::GPRInfo::returnValueGPR, mask);
90         cleanupPostCall();
91         return m_assembler.branch(condition);
92     }
93
94     void swapArguments()
95     {
96         JSC::MacroAssembler::RegisterID a = m_firstArgument;
97         JSC::MacroAssembler::RegisterID b = m_secondArgument;
98         // x86 can swap without a temporary register. On other architectures, we need allocate a temporary register to switch the values.
99 #if CPU(X86) || CPU(X86_64)
100         m_assembler.swap(a, b);
101 #elif CPU(ARM64) || CPU(ARM_THUMB2)
102         m_assembler.move(a, tempRegister);
103         m_assembler.move(b, a);
104         m_assembler.move(tempRegister, b);
105 #else
106 #error Missing implementationg for matching swapping argument registers.
107 #endif
108     }
109
110     void prepareAndCall()
111     {
112         ASSERT(m_functionAddress.executableAddress());
113         ASSERT(!m_firstArgument || (m_firstArgument && !m_secondArgument) || (m_firstArgument && m_secondArgument));
114
115         saveAllocatedCallerSavedRegisters();
116         m_stackAllocator.alignStackPreFunctionCall();
117
118         if (m_argumentCount == 2) {
119             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
120             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_secondArgument));
121
122             if (m_firstArgument != JSC::GPRInfo::argumentGPR0) {
123                 // If firstArgument is not in argumentGPR0, we need to handle potential conflicts:
124                 // -if secondArgument and firstArgument are in inversted registers, just swap the values.
125                 // -if secondArgument is in argumentGPR0 but firstArgument is not taking argumentGPR1, we can move in order secondArgument->argumentGPR1, firstArgument->argumentGPR0
126                 // -if secondArgument does not take argumentGPR0, firstArgument and secondArgument can be moved safely to destination.
127                 if (m_secondArgument == JSC::GPRInfo::argumentGPR0) {
128                     if (m_firstArgument == JSC::GPRInfo::argumentGPR1)
129                         swapArguments();
130                     else {
131                         m_assembler.move(JSC::GPRInfo::argumentGPR0, JSC::GPRInfo::argumentGPR1);
132                         m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
133                     }
134                 } else {
135                     m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
136                     if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
137                         m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
138                 }
139             } else {
140                 // We know firstArgument is already in place, we can safely move secondArgument.
141                 if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
142                     m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
143             }
144         } else if (m_argumentCount == 1) {
145             RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
146             if (m_firstArgument != JSC::GPRInfo::argumentGPR0)
147                 m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
148         }
149
150         JSC::MacroAssembler::Call call = m_assembler.call();
151         m_callRegistry.append(std::make_pair(call, m_functionAddress));
152     }
153
154     void cleanupPostCall()
155     {
156         m_stackAllocator.unalignStackPostFunctionCall();
157         restoreAllocatedCallerSavedRegisters();
158     }
159
160     void saveAllocatedCallerSavedRegisters()
161     {
162         ASSERT(m_savedRegisterStackReferences.isEmpty());
163         ASSERT(m_savedRegisters.isEmpty());
164         const RegisterVector& allocatedRegisters = m_registerAllocator.allocatedRegisters();
165         for (auto registerID : allocatedRegisters) {
166             if (RegisterAllocator::isCallerSavedRegister(registerID))
167                 m_savedRegisters.append(registerID);
168         }
169         m_savedRegisterStackReferences = m_stackAllocator.push(m_savedRegisters);
170     }
171
172     void restoreAllocatedCallerSavedRegisters()
173     {
174         m_stackAllocator.pop(m_savedRegisterStackReferences, m_savedRegisters);
175         m_savedRegisterStackReferences.clear();
176     }
177
178     JSC::MacroAssembler& m_assembler;
179     RegisterAllocator& m_registerAllocator;
180     StackAllocator& m_stackAllocator;
181     Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr>, 32>& m_callRegistry;
182
183     RegisterVector m_savedRegisters;
184     StackAllocator::StackReferenceVector 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)