/* * Copyright (C) 2018-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "GetPutInfo.h" #include "Interpreter.h" #include "Label.h" #include "OpcodeSize.h" #include "PrivateFieldPutKind.h" #include "ProfileTypeBytecodeFlag.h" #include "PutByIdFlags.h" #include "ResultType.h" #include "SymbolTableOrScopeDepth.h" #include "VirtualRegister.h" #include namespace JSC { enum FitsAssertion { Assert, NoAssert }; // Fits template template struct Fits; // Implicit conversion for types of the same size template struct Fits::value, std::true_type>> { using TargetType = typename TypeBySize::unsignedType; static bool check(T) { return true; } static TargetType convert(T t) { return bitwise_cast(t); } template::value, std::true_type>> static T1 convert(TargetType t) { return bitwise_cast(t); } }; template struct Fits::value && sizeof(T) != size && !std::is_same::value, std::true_type>> { using TargetType = std::conditional_t::value, typename TypeBySize::unsignedType, typename TypeBySize::signedType>; static bool check(T t) { return t >= std::numeric_limits::min() && t <= std::numeric_limits::max(); } static TargetType convert(T t) { ASSERT(check(t)); return static_cast(t); } template::value, std::true_type>> static T1 convert(TargetType1 t) { return static_cast(t); } }; template struct Fits> : public Fits { using Base = Fits; static bool check(bool e) { return Base::check(static_cast(e)); } static typename Base::TargetType convert(bool e) { return Base::convert(static_cast(e)); } static bool convert(typename Base::TargetType e) { return Base::convert(e); } }; template struct FirstConstant; template<> struct FirstConstant { static constexpr int index = FirstConstantRegisterIndex8; }; template<> struct FirstConstant { static constexpr int index = FirstConstantRegisterIndex16; }; template struct Fits> { // Narrow: // -128..-1 local variables // 0..15 arguments // 16..127 constants // // Wide16: // -2**15..-1 local variables // 0..64 arguments // 64..2**15-1 constants using TargetType = typename TypeBySize::signedType; static constexpr int s_firstConstantIndex = FirstConstant::index; static bool check(VirtualRegister r) { if (r.isConstant()) return (s_firstConstantIndex + r.toConstantIndex()) <= std::numeric_limits::max(); return r.offset() >= std::numeric_limits::min() && r.offset() < s_firstConstantIndex; } static TargetType convert(VirtualRegister r) { ASSERT(check(r)); if (r.isConstant()) return static_cast(s_firstConstantIndex + r.toConstantIndex()); return static_cast(r.offset()); } static VirtualRegister convert(TargetType u) { int i = static_cast(static_cast(u)); if (i >= s_firstConstantIndex) return VirtualRegister { (i - s_firstConstantIndex) + FirstConstantRegisterIndex }; return VirtualRegister { i }; } }; template struct Fits> : public Fits { static_assert(sizeof(SymbolTableOrScopeDepth) == sizeof(unsigned)); using TargetType = typename TypeBySize::unsignedType; using Base = Fits; static bool check(SymbolTableOrScopeDepth u) { return Base::check(u.raw()); } static TargetType convert(SymbolTableOrScopeDepth u) { return Base::convert(u.raw()); } static SymbolTableOrScopeDepth convert(TargetType u) { return SymbolTableOrScopeDepth::raw(Base::convert(u)); } }; template struct Fits> { using TargetType = typename TypeBySize::unsignedType; // 13 Resolve Types // 3 Initialization Modes // 2 Resolve Modes // 1 bit isStrict flag // // Try to encode encode as // // initialization mode // v // isStrict -> 0|0000|00|0 // ^ ^ // resolve type resolve mode static constexpr int s_resolveTypeMax = 1 << 4; static constexpr int s_initializationModeMax = 1 << 2; static constexpr int s_resolveModeMax = 1 << 1; static constexpr int s_isStrictBit = 1 << 7; static constexpr int s_resolveTypeBits = (s_resolveTypeMax - 1) << 3; static constexpr int s_initializationModeBits = (s_initializationModeMax - 1) << 1; static constexpr int s_resolveModeBits = (s_resolveModeMax - 1); static_assert(!(s_resolveTypeBits & s_initializationModeBits & s_resolveModeBits), "There should be no intersection between ResolveMode, ResolveType and InitializationMode"); static bool check(GetPutInfo gpi) { auto resolveType = static_cast(gpi.resolveType()); auto initializationMode = static_cast(gpi.initializationMode()); auto resolveMode = static_cast(gpi.resolveMode()); return resolveType < s_resolveTypeMax && initializationMode < s_initializationModeMax && resolveMode < s_resolveModeMax; } static TargetType convert(GetPutInfo gpi) { ASSERT(check(gpi)); auto resolveType = static_cast(gpi.resolveType()); auto initializationMode = static_cast(gpi.initializationMode()); auto resolveMode = static_cast(gpi.resolveMode()); auto isStrict = static_cast(gpi.ecmaMode().isStrict()); return (isStrict << 7) | (resolveType << 3) | (initializationMode << 1) | resolveMode; } static GetPutInfo convert(TargetType gpi) { auto resolveType = static_cast((gpi & s_resolveTypeBits) >> 3); auto initializationMode = static_cast((gpi & s_initializationModeBits) >> 1); auto resolveMode = static_cast(gpi & s_resolveModeBits); auto isStrict = static_cast(gpi & s_isStrictBit); return GetPutInfo(resolveMode, resolveType, initializationMode, isStrict ? ECMAMode::strict() : ECMAMode::sloppy()); } }; template struct Fits { using TargetType = typename TypeBySize::unsignedType; // PutByIdFlags is just two boolean values encoded as // // isStrict // v // 000000|0|0 // ^ // isDirect static constexpr int s_isDirectBit = 1; static constexpr int s_isStrictBit = 2; static bool check(PutByIdFlags) { return true; } static TargetType convert(PutByIdFlags flags) { auto isDirect = static_cast(flags.isDirect()); auto isStrict = static_cast(flags.ecmaMode().isStrict()); return (isStrict << 1) | isDirect; } static PutByIdFlags convert(TargetType gpi) { auto isDirect = static_cast(gpi & s_isDirectBit); auto isStrict = static_cast(gpi & s_isStrictBit); auto ecmaMode = isStrict ? ECMAMode::strict() : ECMAMode::sloppy(); return isDirect ? PutByIdFlags::createDirect(ecmaMode) : PutByIdFlags::create(ecmaMode); } }; template struct Fits::value, std::true_type>> : public Fits, size> { using Base = Fits, size>; static bool check(E e) { return Base::check(static_cast>(e)); } static typename Base::TargetType convert(E e) { return Base::convert(static_cast>(e)); } static E convert(typename Base::TargetType e) { return static_cast(Base::convert(e)); } }; template struct Fits> : public Fits { static_assert(sizeof(ResultType) == sizeof(uint8_t)); using Base = Fits; static bool check(ResultType type) { return Base::check(type.bits()); } static typename Base::TargetType convert(ResultType type) { return Base::convert(type.bits()); } static ResultType convert(typename Base::TargetType type) { return ResultType(Base::convert(type)); } }; template struct Fits> { static_assert(sizeof(OperandTypes) == sizeof(uint16_t)); using TargetType = typename TypeBySize::unsignedType; // a pair of (ResultType::Type, ResultType::Type) - try to fit each type into 4 bits // additionally, encode unknown types as 0 rather than the | of all types static constexpr unsigned typeWidth = 4; static constexpr unsigned maxType = (1 << typeWidth) - 1; static bool check(OperandTypes types) { if (size == OpcodeSize::Narrow) { auto first = types.first().bits(); auto second = types.second().bits(); if (first == ResultType::unknownType().bits()) first = 0; if (second == ResultType::unknownType().bits()) second = 0; return first <= maxType && second <= maxType; } return true; } static TargetType convert(OperandTypes types) { if (size == OpcodeSize::Narrow) { ASSERT(check(types)); auto first = types.first().bits(); auto second = types.second().bits(); if (first == ResultType::unknownType().bits()) first = 0; if (second == ResultType::unknownType().bits()) second = 0; return (first << typeWidth) | second; } return static_cast(types.bits()); } static OperandTypes convert(TargetType types) { if (size == OpcodeSize::Narrow) { auto first = types >> typeWidth; auto second = types & maxType; if (!first) first = ResultType::unknownType().bits(); if (!second) second = ResultType::unknownType().bits(); return OperandTypes(ResultType(first), ResultType(second)); } return OperandTypes::fromBits(static_cast(types)); } }; template struct Fits, size> : public Fits { // This is a bit hacky: we need to delay computing jump targets, since we // might have to emit `nop`s to align the instructions stream. Additionally, // we have to compute the target before we start writing to the instruction // stream, since the offset is computed from the start of the bytecode. We // achieve this by computing the target when we `check` and saving it, then // later we use the saved target when we call convert. using Base = Fits; static bool check(GenericBoundLabel& label) { return Base::check(label.saveTarget()); } static typename Base::TargetType convert(GenericBoundLabel& label) { return Base::convert(label.commitTarget()); } static GenericBoundLabel convert(typename Base::TargetType target) { return GenericBoundLabel(Base::convert(target)); } }; template struct Fits : public Fits { using Base = Fits; static bool check(ECMAMode ecmaMode) { return Base::check(ecmaMode.value()); } static typename Base::TargetType convert(ECMAMode ecmaMode) { return Base::convert(ecmaMode.value()); } static ECMAMode convert(typename Base::TargetType ecmaMode) { return ECMAMode::fromByte(Base::convert(ecmaMode)); } }; template struct Fits : public Fits { using Base = Fits; static bool check(PrivateFieldPutKind putMode) { return Base::check(putMode.value()); } static typename Base::TargetType convert(PrivateFieldPutKind putMode) { return Base::convert(putMode.value()); } static PrivateFieldPutKind convert(typename Base::TargetType putMode) { return PrivateFieldPutKind::fromByte(Base::convert(putMode)); } }; } // namespace JSC