Reduce dynamic memory allocation in css jit.
[WebKit-https.git] / Source / WebCore / cssjit / StackAllocator.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 StackAllocator_h
27 #define StackAllocator_h
28
29 #if ENABLE(CSS_SELECTOR_JIT)
30
31 #include "RegisterAllocator.h"
32 #include <JavaScriptCore/MacroAssembler.h>
33
34 namespace WebCore {
35
36 class StackAllocator {
37 public:
38     class StackReference {
39     public:
40         StackReference()
41             : m_offsetFromTop(-1)
42         { }
43         explicit StackReference(unsigned offset)
44             : m_offsetFromTop(offset)
45         { }
46         operator unsigned() const { return m_offsetFromTop; }
47     private:
48         unsigned m_offsetFromTop;
49     };
50
51     StackAllocator(JSC::MacroAssembler& assembler)
52         : m_assembler(assembler)
53         , m_offsetFromTop(0)
54         , m_hasFunctionCallPadding(false)
55     {
56     }
57
58     ~StackAllocator()
59     {
60         RELEASE_ASSERT(!m_offsetFromTop);
61         RELEASE_ASSERT(!m_hasFunctionCallPadding);
62     }
63
64     StackReference allocateUninitialized()
65     {
66         RELEASE_ASSERT(!m_hasFunctionCallPadding);
67         m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(-stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
68         m_offsetFromTop += stackUnitInBytes();
69         return StackReference(m_offsetFromTop);
70     }
71
72     void push(Vector<StackReference, registerCount>& stackReferences, const Vector<JSC::MacroAssembler::RegisterID>& registerIDs)
73     {
74         RELEASE_ASSERT(!m_hasFunctionCallPadding);
75 #if CPU(ARM64)
76         for (unsigned i = 0; i < registerIDs.size() - 1; i += 2) {
77             m_assembler.pushPair(registerIDs[i + 1], registerIDs[i]);
78             m_offsetFromTop += stackUnitInBytes();
79             stackReferences.append(StackReference(m_offsetFromTop - stackUnitInBytes() / 2));
80             stackReferences.append(StackReference(m_offsetFromTop));
81         }
82         if (registerCount % 2)
83             stackReferences.append(push(registerIDs[registerCount - 1]));
84 #else
85         for (auto registerID : registerIDs)
86             stackReferences.append(push(registerID));
87 #endif
88     }
89
90     StackReference push(JSC::MacroAssembler::RegisterID registerID)
91     {
92         RELEASE_ASSERT(!m_hasFunctionCallPadding);
93         m_assembler.pushToSave(registerID);
94         m_offsetFromTop += stackUnitInBytes();
95         return StackReference(m_offsetFromTop);
96     }
97
98     void pop(const Vector<StackReference>& stackReferences, const Vector<JSC::MacroAssembler::RegisterID>& registerIDs)
99     {
100         RELEASE_ASSERT(!m_hasFunctionCallPadding);
101
102         unsigned registerCount = registerIDs.size();
103         RELEASE_ASSERT(stackReferences.size() == registerCount);
104 #if CPU(ARM64)
105         ASSERT(m_offsetFromTop >= stackUnitInBytes() * ((registerCount + 1) / 2));
106         unsigned registerCountOdd = registerCount % 2;
107         if (registerCountOdd)
108             pop(stackReferences[registerCount - 1], registerIDs[registerCount - 1]);
109         for (unsigned i = registerCount - registerCountOdd; i > 0; i -= 2) {
110             RELEASE_ASSERT(stackReferences[i - 1] == m_offsetFromTop);
111             RELEASE_ASSERT(stackReferences[i - 2] == m_offsetFromTop - stackUnitInBytes() / 2);
112             RELEASE_ASSERT(m_offsetFromTop >= stackUnitInBytes());
113             m_offsetFromTop -= stackUnitInBytes();
114             m_assembler.popPair(registerIDs[i - 1], registerIDs[i - 2]);
115         }
116 #else
117         ASSERT(m_offsetFromTop >= stackUnitInBytes() * registerCount);
118         for (unsigned i = registerCount; i > 0; --i)
119             pop(stackReferences[i - 1], registerIDs[i - 1]);
120 #endif
121     }
122
123     void pop(StackReference stackReference, JSC::MacroAssembler::RegisterID registerID)
124     {
125         RELEASE_ASSERT(stackReference == m_offsetFromTop);
126         RELEASE_ASSERT(!m_hasFunctionCallPadding);
127         RELEASE_ASSERT(m_offsetFromTop >= stackUnitInBytes());
128         m_offsetFromTop -= stackUnitInBytes();
129         m_assembler.popToRestore(registerID);
130     }
131
132     void popAndDiscard(StackReference stackReference)
133     {
134         RELEASE_ASSERT(stackReference == m_offsetFromTop);
135         m_assembler.addPtr(JSC::MacroAssembler::TrustedImm32(stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
136         m_offsetFromTop -= stackUnitInBytes();
137     }
138
139     void popAndDiscardUpTo(StackReference stackReference)
140     {
141         unsigned positionBeforeStackReference = stackReference - stackUnitInBytes();
142         RELEASE_ASSERT(positionBeforeStackReference < m_offsetFromTop);
143
144         unsigned stackDelta = m_offsetFromTop - positionBeforeStackReference;
145         m_assembler.addPtr(JSC::MacroAssembler::TrustedImm32(stackDelta), JSC::MacroAssembler::stackPointerRegister);
146         m_offsetFromTop -= stackDelta;
147     }
148
149     void alignStackPreFunctionCall()
150     {
151 #if CPU(X86_64)
152         RELEASE_ASSERT(!m_hasFunctionCallPadding);
153         unsigned topAlignment = stackUnitInBytes();
154         if ((topAlignment + m_offsetFromTop) % 16) {
155             m_hasFunctionCallPadding = true;
156             m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(-stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
157         }
158 #endif
159     }
160
161     void unalignStackPostFunctionCall()
162     {
163 #if CPU(X86_64)
164         if (m_hasFunctionCallPadding) {
165             m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
166             m_hasFunctionCallPadding = false;
167         }
168 #endif
169     }
170
171     void merge(StackAllocator&& stackA, StackAllocator&& stackB)
172     {
173         RELEASE_ASSERT(stackA.m_offsetFromTop == stackB.m_offsetFromTop);
174         RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackB.m_hasFunctionCallPadding);
175         ASSERT(&stackA.m_assembler == &stackB.m_assembler);
176         ASSERT(&m_assembler == &stackB.m_assembler);
177
178         m_offsetFromTop = stackA.m_offsetFromTop;
179         m_hasFunctionCallPadding = stackA.m_hasFunctionCallPadding;
180
181         stackA.reset();
182         stackB.reset();
183     }
184
185     void merge(StackAllocator&& stackA, StackAllocator&& stackB, StackAllocator&& stackC)
186     {
187         RELEASE_ASSERT(stackA.m_offsetFromTop == stackB.m_offsetFromTop);
188         RELEASE_ASSERT(stackA.m_offsetFromTop == stackC.m_offsetFromTop);
189         RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackB.m_hasFunctionCallPadding);
190         RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackC.m_hasFunctionCallPadding);
191         ASSERT(&stackA.m_assembler == &stackB.m_assembler);
192         ASSERT(&stackA.m_assembler == &stackC.m_assembler);
193         ASSERT(&m_assembler == &stackB.m_assembler);
194
195         m_offsetFromTop = stackA.m_offsetFromTop;
196         m_hasFunctionCallPadding = stackA.m_hasFunctionCallPadding;
197
198         stackA.reset();
199         stackB.reset();
200         stackC.reset();
201     }
202
203     unsigned offsetToStackReference(StackReference stackReference)
204     {
205         RELEASE_ASSERT(m_offsetFromTop >= stackReference);
206         return m_offsetFromTop - stackReference;
207     }
208
209 private:
210     static unsigned stackUnitInBytes()
211     {
212         return JSC::MacroAssembler::pushToSaveByteOffset();
213     }
214
215     void reset()
216     {
217         m_offsetFromTop = 0;
218         m_hasFunctionCallPadding = false;
219     }
220
221     JSC::MacroAssembler& m_assembler;
222     unsigned m_offsetFromTop;
223     bool m_hasFunctionCallPadding;
224 };
225
226 } // namespace WebCore
227
228 #endif // ENABLE(CSS_SELECTOR_JIT)
229
230 #endif // StackAllocator_h