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