/ bytecode / Fits.h
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 "PrivateFieldPutKind.h"
 33  #include "ProfileTypeBytecodeFlag.h"
 34  #include "PutByIdFlags.h"
 35  #include "ResultType.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::is_constructible<T>::value, 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      // 1 bit isStrict flag
179      //
180      // Try to encode encode as
181      //
182      //            initialization mode
183      //                     v
184      // isStrict -> 0|0000|00|0
185      //                  ^    ^
186      //      resolve  type    resolve mode
187      static constexpr int s_resolveTypeMax = 1 << 4;
188      static constexpr int s_initializationModeMax = 1 << 2;
189      static constexpr int s_resolveModeMax = 1 << 1;
190  
191      static constexpr int s_isStrictBit = 1 << 7;
192      static constexpr int s_resolveTypeBits = (s_resolveTypeMax - 1) << 3;
193      static constexpr int s_initializationModeBits = (s_initializationModeMax - 1) << 1;
194      static constexpr int s_resolveModeBits = (s_resolveModeMax - 1);
195  
196      static_assert(!(s_resolveTypeBits & s_initializationModeBits & s_resolveModeBits), "There should be no intersection between ResolveMode, ResolveType and InitializationMode");
197  
198      static bool check(GetPutInfo gpi)
199      {
200          auto resolveType = static_cast<int>(gpi.resolveType());
201          auto initializationMode = static_cast<int>(gpi.initializationMode());
202          auto resolveMode = static_cast<int>(gpi.resolveMode());
203          return resolveType < s_resolveTypeMax && initializationMode < s_initializationModeMax && resolveMode < s_resolveModeMax;
204      }
205  
206      static TargetType convert(GetPutInfo gpi)
207      {
208          ASSERT(check(gpi));
209          auto resolveType = static_cast<uint8_t>(gpi.resolveType());
210          auto initializationMode = static_cast<uint8_t>(gpi.initializationMode());
211          auto resolveMode = static_cast<uint8_t>(gpi.resolveMode());
212          auto isStrict = static_cast<uint8_t>(gpi.ecmaMode().isStrict());
213          return (isStrict << 7) | (resolveType << 3) | (initializationMode << 1) | resolveMode;
214      }
215  
216      static GetPutInfo convert(TargetType gpi)
217      {
218          auto resolveType = static_cast<ResolveType>((gpi & s_resolveTypeBits) >> 3);
219          auto initializationMode = static_cast<InitializationMode>((gpi & s_initializationModeBits) >> 1);
220          auto resolveMode = static_cast<ResolveMode>(gpi & s_resolveModeBits);
221          auto isStrict = static_cast<bool>(gpi & s_isStrictBit);
222          return GetPutInfo(resolveMode, resolveType, initializationMode, isStrict ? ECMAMode::strict() : ECMAMode::sloppy());
223      }
224  };
225  
226  template<OpcodeSize size>
227  struct Fits<PutByIdFlags, size> {
228      using TargetType = typename TypeBySize<size>::unsignedType;
229  
230      // PutByIdFlags is just two boolean values encoded as
231      //
232      //        isStrict
233      //           v
234      //    000000|0|0
235      //             ^
236      //         isDirect
237      static constexpr int s_isDirectBit = 1;
238      static constexpr int s_isStrictBit = 2;
239  
240      static bool check(PutByIdFlags)
241      {
242          return true;
243      }
244  
245      static TargetType convert(PutByIdFlags flags)
246      {
247          auto isDirect = static_cast<uint8_t>(flags.isDirect());
248          auto isStrict = static_cast<uint8_t>(flags.ecmaMode().isStrict());
249          return (isStrict << 1) | isDirect;
250      }
251  
252      static PutByIdFlags convert(TargetType gpi)
253      {
254          auto isDirect = static_cast<bool>(gpi & s_isDirectBit);
255          auto isStrict = static_cast<bool>(gpi & s_isStrictBit);
256          auto ecmaMode = isStrict ? ECMAMode::strict() : ECMAMode::sloppy();
257          return isDirect ? PutByIdFlags::createDirect(ecmaMode) : PutByIdFlags::create(ecmaMode);
258      }
259  };
260  
261  template<typename E, OpcodeSize size>
262  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> {
263      using Base = Fits<std::underlying_type_t<E>, size>;
264  
265      static bool check(E e) { return Base::check(static_cast<std::underlying_type_t<E>>(e)); }
266  
267      static typename Base::TargetType convert(E e)
268      {
269          return Base::convert(static_cast<std::underlying_type_t<E>>(e));
270      }
271  
272      static E convert(typename Base::TargetType e)
273      {
274          return static_cast<E>(Base::convert(e));
275      }
276  };
277  
278  template<OpcodeSize size>
279  struct Fits<ResultType, size, std::enable_if_t<sizeof(ResultType) != size, std::true_type>> : public Fits<uint8_t, size> {
280      static_assert(sizeof(ResultType) == sizeof(uint8_t));
281      using Base = Fits<uint8_t, size>;
282  
283      static bool check(ResultType type) { return Base::check(type.bits()); }
284  
285      static typename Base::TargetType convert(ResultType type) { return Base::convert(type.bits()); }
286  
287      static ResultType convert(typename Base::TargetType type) { return ResultType(Base::convert(type)); }
288  };
289  
290  template<OpcodeSize size>
291  struct Fits<OperandTypes, size, std::enable_if_t<sizeof(OperandTypes) != size, std::true_type>> {
292      static_assert(sizeof(OperandTypes) == sizeof(uint16_t));
293      using TargetType = typename TypeBySize<size>::unsignedType;
294  
295      // a pair of (ResultType::Type, ResultType::Type) - try to fit each type into 4 bits
296      // additionally, encode unknown types as 0 rather than the | of all types
297      static constexpr unsigned typeWidth = 4;
298      static constexpr unsigned maxType = (1 << typeWidth) - 1;
299  
300      static bool check(OperandTypes types)
301      {
302          if (size == OpcodeSize::Narrow) {
303              auto first = types.first().bits();
304              auto second = types.second().bits();
305              if (first == ResultType::unknownType().bits())
306                  first = 0;
307              if (second == ResultType::unknownType().bits())
308                  second = 0;
309              return first <= maxType && second <= maxType;
310          }
311          return true;
312      }
313  
314      static TargetType convert(OperandTypes types)
315      {
316          if (size == OpcodeSize::Narrow) {
317              ASSERT(check(types));
318              auto first = types.first().bits();
319              auto second = types.second().bits();
320              if (first == ResultType::unknownType().bits())
321                  first = 0;
322              if (second == ResultType::unknownType().bits())
323                  second = 0;
324              return (first << typeWidth) | second;
325          }
326          return static_cast<TargetType>(types.bits());
327      }
328  
329      static OperandTypes convert(TargetType types)
330      {
331          if (size == OpcodeSize::Narrow) {
332              auto first = types >> typeWidth;
333              auto second = types & maxType;
334              if (!first)
335                  first = ResultType::unknownType().bits();
336              if (!second)
337                  second = ResultType::unknownType().bits();
338              return OperandTypes(ResultType(first), ResultType(second));
339          }
340          return OperandTypes::fromBits(static_cast<uint16_t>(types));
341      }
342  };
343  
344  template<OpcodeSize size, typename GeneratorTraits>
345  struct Fits<GenericBoundLabel<GeneratorTraits>, size> : public Fits<int, size> {
346      // This is a bit hacky: we need to delay computing jump targets, since we
347      // might have to emit `nop`s to align the instructions stream. Additionally,
348      // we have to compute the target before we start writing to the instruction
349      // stream, since the offset is computed from the start of the bytecode. We
350      // achieve this by computing the target when we `check` and saving it, then
351      // later we use the saved target when we call convert.
352  
353      using Base = Fits<int, size>;
354      static bool check(GenericBoundLabel<GeneratorTraits>& label)
355      {
356          return Base::check(label.saveTarget());
357      }
358  
359      static typename Base::TargetType convert(GenericBoundLabel<GeneratorTraits>& label)
360      {
361          return Base::convert(label.commitTarget());
362      }
363  
364      static GenericBoundLabel<GeneratorTraits> convert(typename Base::TargetType target)
365      {
366          return GenericBoundLabel<GeneratorTraits>(Base::convert(target));
367      }
368  };
369  
370  template<OpcodeSize size>
371  struct Fits<ECMAMode, size> : public Fits<uint8_t, size> {
372      using Base = Fits<uint8_t, size>;
373  
374      static bool check(ECMAMode ecmaMode)
375      {
376          return Base::check(ecmaMode.value());
377      }
378  
379      static typename Base::TargetType convert(ECMAMode ecmaMode)
380      {
381          return Base::convert(ecmaMode.value());
382      }
383  
384      static ECMAMode convert(typename Base::TargetType ecmaMode)
385      {
386          return ECMAMode::fromByte(Base::convert(ecmaMode));
387      }
388  };
389  
390  template<OpcodeSize size>
391  struct Fits<PrivateFieldPutKind, size> : public Fits<uint8_t, size> {
392      using Base = Fits<uint8_t, size>;
393  
394      static bool check(PrivateFieldPutKind putMode)
395      {
396          return Base::check(putMode.value());
397      }
398  
399      static typename Base::TargetType convert(PrivateFieldPutKind putMode)
400      {
401          return Base::convert(putMode.value());
402      }
403  
404      static PrivateFieldPutKind convert(typename Base::TargetType putMode)
405      {
406          return PrivateFieldPutKind::fromByte(Base::convert(putMode));
407      }
408  };
409  
410  } // namespace JSC