1/*
2 * Copyright (C) 2010-2019 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(JIT)
29
30#include "JIT.h"
31#include "JITInlines.h"
32#include "JSInterfaceJIT.h"
33#include "LinkBuffer.h"
34
35namespace JSC {
36
37 class SpecializedThunkJIT : public JSInterfaceJIT {
38 public:
39 static constexpr int ThisArgument = -1;
40 SpecializedThunkJIT(VM& vm, int expectedArgCount)
41 : JSInterfaceJIT(&vm)
42 {
43 emitFunctionPrologue();
44 emitSaveThenMaterializeTagRegisters();
45 // Check that we have the expected number of arguments
46 m_failures.append(branch32(NotEqual, payloadFor(CallFrameSlot::argumentCount), TrustedImm32(expectedArgCount + 1)));
47 }
48
49 explicit SpecializedThunkJIT(VM& vm)
50 : JSInterfaceJIT(&vm)
51 {
52 emitFunctionPrologue();
53 emitSaveThenMaterializeTagRegisters();
54 }
55
56 void loadDoubleArgument(int argument, FPRegisterID dst, RegisterID scratch)
57 {
58 unsigned src = CallFrame::argumentOffset(argument);
59 m_failures.append(emitLoadDouble(src, dst, scratch));
60 }
61
62 void loadCellArgument(int argument, RegisterID dst)
63 {
64 unsigned src = CallFrame::argumentOffset(argument);
65 m_failures.append(emitLoadJSCell(src, dst));
66 }
67
68 void loadJSStringArgument(int argument, RegisterID dst)
69 {
70 loadCellArgument(argument, dst);
71 m_failures.append(branchIfNotString(dst));
72 }
73
74 void loadInt32Argument(int argument, RegisterID dst, Jump& failTarget)
75 {
76 unsigned src = CallFrame::argumentOffset(argument);
77 failTarget = emitLoadInt32(src, dst);
78 }
79
80 void loadInt32Argument(int argument, RegisterID dst)
81 {
82 Jump conversionFailed;
83 loadInt32Argument(argument, dst, conversionFailed);
84 m_failures.append(conversionFailed);
85 }
86
87 void appendFailure(const Jump& failure)
88 {
89 m_failures.append(failure);
90 }
91#if USE(JSVALUE64)
92 void returnJSValue(RegisterID src)
93 {
94 if (src != regT0)
95 move(src, regT0);
96
97 emitRestoreSavedTagRegisters();
98 emitFunctionEpilogue();
99 ret();
100 }
101#else
102 void returnJSValue(RegisterID payload, RegisterID tag)
103 {
104 ASSERT_UNUSED(payload, payload == regT0);
105 ASSERT_UNUSED(tag, tag == regT1);
106 emitRestoreSavedTagRegisters();
107 emitFunctionEpilogue();
108 ret();
109 }
110#endif
111
112 void returnDouble(FPRegisterID src)
113 {
114#if USE(JSVALUE64)
115 moveDoubleTo64(src, regT0);
116 Jump zero = branchTest64(Zero, regT0);
117 sub64(numberTagRegister, regT0);
118 Jump done = jump();
119 zero.link(this);
120 move(numberTagRegister, regT0);
121 done.link(this);
122#else
123 moveDoubleToInts(src, regT0, regT1);
124 Jump lowNonZero = branchTestPtr(NonZero, regT1);
125 Jump highNonZero = branchTestPtr(NonZero, regT0);
126 move(TrustedImm32(0), regT0);
127 move(TrustedImm32(JSValue::Int32Tag), regT1);
128 lowNonZero.link(this);
129 highNonZero.link(this);
130#endif
131 emitRestoreSavedTagRegisters();
132 emitFunctionEpilogue();
133 ret();
134 }
135
136 void returnInt32(RegisterID src)
137 {
138 if (src != regT0)
139 move(src, regT0);
140 tagReturnAsInt32();
141 emitRestoreSavedTagRegisters();
142 emitFunctionEpilogue();
143 ret();
144 }
145
146 void returnJSCell(RegisterID src)
147 {
148 if (src != regT0)
149 move(src, regT0);
150 tagReturnAsJSCell();
151 emitRestoreSavedTagRegisters();
152 emitFunctionEpilogue();
153 ret();
154 }
155
156 MacroAssemblerCodeRef<JITThunkPtrTag> finalize(MacroAssemblerCodePtr<JITThunkPtrTag> fallback, const char* thunkKind)
157 {
158 LinkBuffer patchBuffer(*this, GLOBAL_THUNK_ID);
159 patchBuffer.link(m_failures, CodeLocationLabel<JITThunkPtrTag>(fallback));
160 for (unsigned i = 0; i < m_calls.size(); i++)
161 patchBuffer.link(m_calls[i].first, m_calls[i].second);
162 return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "Specialized thunk for %s", thunkKind);
163 }
164
165 // Assumes that the target function uses fpRegister0 as the first argument
166 // and return value. Like any sensible architecture would.
167 void callDoubleToDouble(FunctionPtr<CFunctionPtrTag> function)
168 {
169 m_calls.append(std::make_pair(call(JITThunkPtrTag), function.retagged<JITThunkPtrTag>()));
170 }
171
172 void callDoubleToDoublePreservingReturn(FunctionPtr<CFunctionPtrTag> function)
173 {
174 if (!isX86())
175 preserveReturnAddressAfterCall(regT3);
176 callDoubleToDouble(function);
177 if (!isX86())
178 restoreReturnAddressBeforeReturn(regT3);
179 }
180
181 private:
182 void tagReturnAsInt32()
183 {
184#if USE(JSVALUE64)
185 or64(numberTagRegister, regT0);
186#else
187 move(TrustedImm32(JSValue::Int32Tag), regT1);
188#endif
189 }
190
191 void tagReturnAsJSCell()
192 {
193#if USE(JSVALUE32_64)
194 move(TrustedImm32(JSValue::CellTag), regT1);
195#endif
196 }
197
198 MacroAssembler::JumpList m_failures;
199 Vector<std::pair<Call, FunctionPtr<JITThunkPtrTag>>> m_calls;
200 };
201
202}
203
204#endif // ENABLE(JIT)
205