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