1/*
2 * Copyright (C) 2018-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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "GetPutInfo.h"
29#include "Interpreter.h"
30#include "Label.h"
31#include "OpcodeSize.h"
32#include "ProfileTypeBytecodeFlag.h"
33#include "PutByIdFlags.h"
34#include "ResultType.h"
35#include "SpecialPointer.h"
36#include "SymbolTableOrScopeDepth.h"
37#include "VirtualRegister.h"
38#include <type_traits>
39
40namespace JSC {
41
42enum FitsAssertion {
43 Assert,
44 NoAssert
45};
46
47// Fits template
48template<typename, OpcodeSize, typename = std::true_type>
49struct Fits;
50
51// Implicit conversion for types of the same size
52template<typename T, OpcodeSize size>
53struct Fits<T, size, std::enable_if_t<sizeof(T) == size, std::true_type>> {
54 static bool check(T) { return true; }
55
56 static typename TypeBySize<size>::type convert(T t) { return bitwise_cast<typename TypeBySize<size>::type>(t); }
57
58 template<class T1 = T, OpcodeSize size1 = size, typename = std::enable_if_t<!std::is_same<T1, typename TypeBySize<size1>::type>::value, std::true_type>>
59 static T1 convert(typename TypeBySize<size1>::type t) { return bitwise_cast<T1>(t); }
60};
61
62template<typename T, OpcodeSize size>
63struct Fits<T, size, std::enable_if_t<sizeof(T) < size, std::true_type>> {
64 static bool check(T) { return true; }
65
66 static typename TypeBySize<size>::type convert(T t) { return static_cast<typename TypeBySize<size>::type>(t); }
67
68 template<class T1 = T, OpcodeSize size1 = size, typename = std::enable_if_t<!std::is_same<T1, typename TypeBySize<size1>::type>::value, std::true_type>>
69 static T1 convert(typename TypeBySize<size1>::type t) { return static_cast<T1>(t); }
70};
71
72template<>
73struct Fits<uint32_t, OpcodeSize::Narrow> {
74 static bool check(unsigned u) { return u <= UINT8_MAX; }
75
76 static uint8_t convert(unsigned u)
77 {
78 ASSERT(check(u));
79 return static_cast<uint8_t>(u);
80 }
81 static unsigned convert(uint8_t u)
82 {
83 return u;
84 }
85};
86
87template<>
88struct Fits<int, OpcodeSize::Narrow> {
89 static bool check(int i)
90 {
91 return i >= INT8_MIN && i <= INT8_MAX;
92 }
93
94 static uint8_t convert(int i)
95 {
96 ASSERT(check(i));
97 return static_cast<uint8_t>(i);
98 }
99
100 static int convert(uint8_t i)
101 {
102 return static_cast<int8_t>(i);
103 }
104};
105
106template<>
107struct Fits<VirtualRegister, OpcodeSize::Narrow> {
108 // -128..-1 local variables
109 // 0..15 arguments
110 // 16..127 constants
111 static constexpr int s_firstConstantIndex = 16;
112 static bool check(VirtualRegister r)
113 {
114 if (r.isConstant())
115 return (s_firstConstantIndex + r.toConstantIndex()) <= INT8_MAX;
116 return r.offset() >= INT8_MIN && r.offset() < s_firstConstantIndex;
117 }
118
119 static uint8_t convert(VirtualRegister r)
120 {
121 ASSERT(check(r));
122 if (r.isConstant())
123 return static_cast<int8_t>(s_firstConstantIndex + r.toConstantIndex());
124 return static_cast<int8_t>(r.offset());
125 }
126
127 static VirtualRegister convert(uint8_t u)
128 {
129 int i = static_cast<int>(static_cast<int8_t>(u));
130 if (i >= s_firstConstantIndex)
131 return VirtualRegister { (i - s_firstConstantIndex) + FirstConstantRegisterIndex };
132 return VirtualRegister { i };
133 }
134};
135
136template<>
137struct Fits<SymbolTableOrScopeDepth, OpcodeSize::Narrow> {
138 static bool check(SymbolTableOrScopeDepth u)
139 {
140 return u.raw() <= UINT8_MAX;
141 }
142
143 static uint8_t convert(SymbolTableOrScopeDepth u)
144 {
145 ASSERT(check(u));
146 return static_cast<uint8_t>(u.raw());
147 }
148
149 static SymbolTableOrScopeDepth convert(uint8_t u)
150 {
151 return SymbolTableOrScopeDepth::raw(u);
152 }
153};
154
155template<>
156struct Fits<Special::Pointer, OpcodeSize::Narrow> : Fits<int, OpcodeSize::Narrow> {
157 using Base = Fits<int, OpcodeSize::Narrow>;
158 static bool check(Special::Pointer sp) { return Base::check(static_cast<int>(sp)); }
159 static uint8_t convert(Special::Pointer sp)
160 {
161 return Base::convert(static_cast<int>(sp));
162 }
163 static Special::Pointer convert(uint8_t sp)
164 {
165 return static_cast<Special::Pointer>(Base::convert(sp));
166 }
167};
168
169template<>
170struct Fits<GetPutInfo, OpcodeSize::Narrow> {
171 // 13 Resolve Types
172 // 3 Initialization Modes
173 // 2 Resolve Modes
174 //
175 // Try to encode encode as
176 //
177 // initialization mode
178 // v
179 // free bit-> 0|0000|00|0
180 // ^ ^
181 // resolve type resolve mode
182 static constexpr int s_resolveTypeMax = 1 << 4;
183 static constexpr int s_initializationModeMax = 1 << 2;
184 static constexpr int s_resolveModeMax = 1 << 1;
185
186 static constexpr int s_resolveTypeBits = (s_resolveTypeMax - 1) << 3;
187 static constexpr int s_initializationModeBits = (s_initializationModeMax - 1) << 1;
188 static constexpr int s_resolveModeBits = (s_resolveModeMax - 1);
189
190 static_assert(!(s_resolveTypeBits & s_initializationModeBits & s_resolveModeBits), "There should be no intersection between ResolveMode, ResolveType and InitializationMode");
191
192 static bool check(GetPutInfo gpi)
193 {
194 auto resolveType = static_cast<int>(gpi.resolveType());
195 auto initializationMode = static_cast<int>(gpi.initializationMode());
196 auto resolveMode = static_cast<int>(gpi.resolveMode());
197 return resolveType < s_resolveTypeMax && initializationMode < s_initializationModeMax && resolveMode < s_resolveModeMax;
198 }
199
200 static uint8_t convert(GetPutInfo gpi)
201 {
202 ASSERT(check(gpi));
203 auto resolveType = static_cast<uint8_t>(gpi.resolveType());
204 auto initializationMode = static_cast<uint8_t>(gpi.initializationMode());
205 auto resolveMode = static_cast<uint8_t>(gpi.resolveMode());
206 return (resolveType << 3) | (initializationMode << 1) | resolveMode;
207 }
208
209 static GetPutInfo convert(uint8_t gpi)
210 {
211 auto resolveType = static_cast<ResolveType>((gpi & s_resolveTypeBits) >> 3);
212 auto initializationMode = static_cast<InitializationMode>((gpi & s_initializationModeBits) >> 1);
213 auto resolveMode = static_cast<ResolveMode>(gpi & s_resolveModeBits);
214 return GetPutInfo(resolveMode, resolveType, initializationMode);
215 }
216};
217
218template<>
219struct Fits<DebugHookType, OpcodeSize::Narrow> : Fits<int, OpcodeSize::Narrow> {
220 using Base = Fits<int, OpcodeSize::Narrow>;
221 static bool check(DebugHookType dht) { return Base::check(static_cast<int>(dht)); }
222 static uint8_t convert(DebugHookType dht)
223 {
224 return Base::convert(static_cast<int>(dht));
225 }
226 static DebugHookType convert(uint8_t dht)
227 {
228 return static_cast<DebugHookType>(Base::convert(dht));
229 }
230};
231
232template<>
233struct Fits<ProfileTypeBytecodeFlag, OpcodeSize::Narrow> : Fits<int, OpcodeSize::Narrow> {
234 using Base = Fits<int, OpcodeSize::Narrow>;
235 static bool check(ProfileTypeBytecodeFlag ptbf) { return Base::check(static_cast<int>(ptbf)); }
236 static uint8_t convert(ProfileTypeBytecodeFlag ptbf)
237 {
238 return Base::convert(static_cast<int>(ptbf));
239 }
240 static ProfileTypeBytecodeFlag convert(uint8_t ptbf)
241 {
242 return static_cast<ProfileTypeBytecodeFlag>(Base::convert(ptbf));
243 }
244};
245
246template<>
247struct Fits<ResolveType, OpcodeSize::Narrow> : Fits<int, OpcodeSize::Narrow> {
248 using Base = Fits<int, OpcodeSize::Narrow>;
249 static bool check(ResolveType rt) { return Base::check(static_cast<int>(rt)); }
250 static uint8_t convert(ResolveType rt)
251 {
252 return Base::convert(static_cast<int>(rt));
253 }
254
255 static ResolveType convert(uint8_t rt)
256 {
257 return static_cast<ResolveType>(Base::convert(rt));
258 }
259};
260
261template<>
262struct Fits<OperandTypes, OpcodeSize::Narrow> {
263 // a pair of (ResultType::Type, ResultType::Type) - try to fit each type into 4 bits
264 // additionally, encode unknown types as 0 rather than the | of all types
265 static constexpr int s_maxType = 0x10;
266
267 static bool check(OperandTypes types)
268 {
269 auto first = types.first().bits();
270 auto second = types.second().bits();
271 if (first == ResultType::unknownType().bits())
272 first = 0;
273 if (second == ResultType::unknownType().bits())
274 second = 0;
275 return first < s_maxType && second < s_maxType;
276 }
277
278 static uint8_t convert(OperandTypes types)
279 {
280 ASSERT(check(types));
281 auto first = types.first().bits();
282 auto second = types.second().bits();
283 if (first == ResultType::unknownType().bits())
284 first = 0;
285 if (second == ResultType::unknownType().bits())
286 second = 0;
287 return (first << 4) | second;
288 }
289
290 static OperandTypes convert(uint8_t types)
291 {
292 auto first = (types & (0xf << 4)) >> 4;
293 auto second = (types & 0xf);
294 if (!first)
295 first = ResultType::unknownType().bits();
296 if (!second)
297 second = ResultType::unknownType().bits();
298 return OperandTypes(ResultType(first), ResultType(second));
299 }
300};
301
302template<>
303struct Fits<PutByIdFlags, OpcodeSize::Narrow> : Fits<int, OpcodeSize::Narrow> {
304 // only ever encoded in the bytecode stream as 0 or 1, so the trivial encoding should be good enough
305 using Base = Fits<int, OpcodeSize::Narrow>;
306 static bool check(PutByIdFlags flags) { return Base::check(static_cast<int>(flags)); }
307 static uint8_t convert(PutByIdFlags flags)
308 {
309 return Base::convert(static_cast<int>(flags));
310 }
311
312 static PutByIdFlags convert(uint8_t flags)
313 {
314 return static_cast<PutByIdFlags>(Base::convert(flags));
315 }
316};
317
318template<OpcodeSize size>
319struct Fits<BoundLabel, size> : Fits<int, size> {
320 // This is a bit hacky: we need to delay computing jump targets, since we
321 // might have to emit `nop`s to align the instructions stream. Additionally,
322 // we have to compute the target before we start writing to the instruction
323 // stream, since the offset is computed from the start of the bytecode. We
324 // achieve this by computing the target when we `check` and saving it, then
325 // later we use the saved target when we call convert.
326
327 using Base = Fits<int, size>;
328 static bool check(BoundLabel& label)
329 {
330 return Base::check(label.saveTarget());
331 }
332
333 static typename TypeBySize<size>::type convert(BoundLabel& label)
334 {
335 return Base::convert(label.commitTarget());
336 }
337
338 static BoundLabel convert(typename TypeBySize<size>::type target)
339 {
340 return BoundLabel(Base::convert(target));
341 }
342};
343
344} // namespace JSC
345