1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Anderson <danderson@mozilla.com>
25 : * David Mandelin <dmandelin@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "StubCalls.h"
42 : #include "StubCompiler.h"
43 : #include "Compiler.h"
44 : #include "assembler/assembler/LinkBuffer.h"
45 : #include "FrameState-inl.h"
46 :
47 : using namespace js;
48 : using namespace mjit;
49 :
50 134091 : StubCompiler::StubCompiler(JSContext *cx, mjit::Compiler &cc, FrameState &frame)
51 : : cx(cx),
52 : cc(cc),
53 : frame(frame),
54 : generation(1),
55 : lastGeneration(0),
56 : exits(CompilerAllocPolicy(cx, cc)),
57 : joins(CompilerAllocPolicy(cx, cc)),
58 : scriptJoins(CompilerAllocPolicy(cx, cc)),
59 134091 : jumpList(SystemAllocPolicy())
60 : {
61 : #ifdef DEBUG
62 134091 : masm.setSpewPath(true);
63 : #endif
64 134091 : }
65 :
66 : void
67 4975357 : StubCompiler::linkExitDirect(Jump j, Label L)
68 : {
69 4975357 : exits.append(CrossPatch(j, L));
70 4975357 : }
71 :
72 : JSC::MacroAssembler::Label
73 3062839 : StubCompiler::syncExit(Uses uses)
74 : {
75 3062839 : JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW MERGE CODE ---- \n");
76 :
77 3062839 : if (lastGeneration == generation) {
78 461515 : Jump j2 = masm.jump();
79 461515 : jumpList.append(j2);
80 : }
81 :
82 3062839 : Label l = masm.label();
83 3062839 : frame.sync(masm, uses);
84 3062839 : lastGeneration = generation;
85 :
86 3062839 : JaegerSpew(JSpew_Insns, " ---- END SLOW MERGE CODE ---- \n");
87 :
88 : return l;
89 : }
90 :
91 : JSC::MacroAssembler::Label
92 621731 : StubCompiler::syncExitAndJump(Uses uses)
93 : {
94 621731 : Label l = syncExit(uses);
95 621731 : Jump j2 = masm.jump();
96 621731 : jumpList.append(j2);
97 : /* Suppress jumping on next sync/link. */
98 621731 : generation++;
99 : return l;
100 : }
101 :
102 : // Link an exit from the fast path to a slow path. This does two main things:
103 : // (a) links the given jump to the slow path, and (b) generates a prolog for
104 : // the slow path that syncs frame state for a slow call that uses |uses|
105 : // values from the top of the stack.
106 : //
107 : // The return value is the label for the start of the merge code. This is
108 : // the correct place to jump to in order to execute the slow path being
109 : // generated here.
110 : //
111 : // Note 1: Slow path generation is interleaved with fast path generation, but
112 : // the slow path goes into a separate buffer. The slow path code is appended
113 : // to the fast path code to keep it nearby in code memory.
114 : //
115 : // Note 2: A jump from the fast path to the slow path is called an "exit".
116 : // A jump from the slow path to the fast path is called a "rejoin".
117 : JSC::MacroAssembler::Label
118 2151857 : StubCompiler::linkExit(Jump j, Uses uses)
119 : {
120 2151857 : Label l = syncExit(uses);
121 2151857 : linkExitDirect(j, l);
122 : return l;
123 : }
124 :
125 : // Special version of linkExit that is used when there is a JavaScript
126 : // control-flow branch after the slow path. Our compilation strategy
127 : // requires the JS frame to be fully materialized in memory across branches.
128 : // This function does a linkExit and also fully materializes the frame.
129 : void
130 79027 : StubCompiler::linkExitForBranch(Jump j)
131 : {
132 79027 : Label l = syncExit(Uses(frame.frameSlots()));
133 79027 : linkExitDirect(j, l);
134 79027 : }
135 :
136 : void
137 2671320 : StubCompiler::leave()
138 : {
139 2671320 : JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW LEAVE CODE ---- \n");
140 3754566 : for (size_t i = 0; i < jumpList.length(); i++)
141 1083246 : jumpList[i].linkTo(masm.label(), &masm);
142 2671320 : jumpList.clear();
143 2671320 : generation++;
144 2671320 : JaegerSpew(JSpew_Insns, " ---- END SLOW LEAVE CODE ---- \n");
145 2671320 : }
146 :
147 : void
148 3017272 : StubCompiler::rejoin(Changes changes)
149 : {
150 3017272 : JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW RESTORE CODE ---- \n");
151 :
152 3017272 : frame.merge(masm, changes);
153 :
154 3017272 : unsigned index = crossJump(masm.jump(), cc.getLabel());
155 3017272 : if (cc.loop)
156 365808 : cc.loop->addJoin(index, false);
157 :
158 3017272 : JaegerSpew(JSpew_Insns, " ---- END SLOW RESTORE CODE ---- \n");
159 3017272 : }
160 :
161 : void
162 564349 : StubCompiler::linkRejoin(Jump j)
163 : {
164 564349 : crossJump(j, cc.getLabel());
165 564349 : }
166 :
167 : typedef JSC::MacroAssembler::RegisterID RegisterID;
168 : typedef JSC::MacroAssembler::ImmPtr ImmPtr;
169 : typedef JSC::MacroAssembler::Imm32 Imm32;
170 : typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr;
171 :
172 : JSC::MacroAssembler::Call
173 3503238 : StubCompiler::emitStubCall(void *ptr, RejoinState rejoin, Uses uses)
174 : {
175 3503238 : return emitStubCall(ptr, rejoin, uses, frame.totalDepth());
176 : }
177 :
178 : JSC::MacroAssembler::Call
179 3666774 : StubCompiler::emitStubCall(void *ptr, RejoinState rejoin, Uses uses, int32_t slots)
180 : {
181 3666774 : JaegerSpew(JSpew_Insns, " ---- BEGIN SLOW CALL CODE ---- \n");
182 3666774 : masm.bumpStubCounter(cc.script, cc.PC, Registers::tempCallReg());
183 3666774 : DataLabelPtr inlinePatch;
184 3666774 : Call cl = masm.fallibleVMCall(cx->typeInferenceEnabled(),
185 7333548 : ptr, cc.outerPC(), &inlinePatch, slots);
186 3666774 : JaegerSpew(JSpew_Insns, " ---- END SLOW CALL CODE ---- \n");
187 :
188 : /* Add the call site for debugging and recompilation. */
189 : Compiler::InternalCallSite site(masm.callReturnOffset(cl),
190 : cc.inlineIndex(), cc.inlinePC(),
191 3666774 : rejoin, true);
192 3666774 : site.inlinePatch = inlinePatch;
193 :
194 : /* Add a hook for restoring loop invariants if necessary. */
195 3666774 : if (cc.loop && cc.loop->generatingInvariants()) {
196 30100 : site.loopJumpLabel = masm.label();
197 30100 : Jump j = masm.jump();
198 30100 : Label l = masm.label();
199 : /* MissedBoundsCheck* are not actually called, so f.regs need to be written before InvariantFailure. */
200 : bool entry = (ptr == JS_FUNC_TO_DATA_PTR(void *, stubs::MissedBoundsCheckEntry))
201 30100 : || (ptr == JS_FUNC_TO_DATA_PTR(void *, stubs::MissedBoundsCheckHead));
202 30100 : cc.loop->addInvariantCall(j, l, true, entry, cc.callSites.length(), uses);
203 : }
204 :
205 3666774 : cc.addCallSite(site);
206 : return cl;
207 : }
208 :
209 : void
210 128827 : StubCompiler::fixCrossJumps(uint8_t *ncode, size_t offset, size_t total)
211 : {
212 257654 : JSC::LinkBuffer fast(ncode, total, JSC::METHOD_CODE);
213 257654 : JSC::LinkBuffer slow(ncode + offset, total - offset, JSC::METHOD_CODE);
214 :
215 5103866 : for (size_t i = 0; i < exits.length(); i++)
216 4975039 : fast.link(exits[i].from, slow.locationOf(exits[i].to));
217 :
218 270499 : for (size_t i = 0; i < scriptJoins.length(); i++) {
219 141672 : const CrossJumpInScript &cj = scriptJoins[i];
220 141672 : slow.link(cj.from, fast.locationOf(cc.labelOf(cj.pc, cj.inlineIndex)));
221 : }
222 :
223 4282235 : for (size_t i = 0; i < joins.length(); i++)
224 4153408 : slow.link(joins[i].from, fast.locationOf(joins[i].to));
225 128827 : }
226 :
227 : unsigned
228 4153780 : StubCompiler::crossJump(Jump j, Label L)
229 : {
230 4153780 : joins.append(CrossPatch(j, L));
231 :
232 : /* This won't underflow, as joins has space preallocated for some entries. */
233 4153780 : return joins.length() - 1;
234 : }
235 :
236 : bool
237 204028 : StubCompiler::jumpInScript(Jump j, jsbytecode *target)
238 : {
239 204028 : if (cc.knownJump(target)) {
240 62334 : unsigned index = crossJump(j, cc.labelOf(target, cc.inlineIndex()));
241 62334 : if (cc.loop)
242 36409 : cc.loop->addJoin(index, false);
243 : } else {
244 141694 : if (!scriptJoins.append(CrossJumpInScript(j, target, cc.inlineIndex())))
245 0 : return false;
246 141694 : if (cc.loop)
247 44365 : cc.loop->addJoin(scriptJoins.length() - 1, true);
248 : }
249 204028 : return true;
250 : }
251 :
252 : void
253 33864 : StubCompiler::patchJoin(unsigned i, bool script, Assembler::Address address, AnyRegisterID reg)
254 : {
255 33864 : Jump &j = script ? scriptJoins[i].from : joins[i].from;
256 33864 : j.linkTo(masm.label(), &masm);
257 :
258 33864 : if (reg.isReg())
259 33433 : masm.loadPayload(address, reg.reg());
260 : else
261 431 : masm.loadDouble(address, reg.fpreg());
262 :
263 33864 : j = masm.jump();
264 33864 : }
|