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