Compare commits

...

4 commits

Author SHA1 Message Date
Seonghyun Kim
5575cad622 wip
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2022-05-23 18:53:57 +09:00
Seonghyun Kim
c100122bf4 profile
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2022-05-20 13:53:52 +09:00
Seonghyun Kim
9e667ccdd2 Allow to save Int32 convertible double value as double in Value for performance
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2022-05-20 09:03:17 +09:00
Seonghyun Kim
3631ae6097 Update Value constructors
Improve Value::Value(double) performance.
Add missing double and int32 type testing on interpreter

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
2022-05-20 09:03:17 +09:00
20 changed files with 485 additions and 579 deletions

View file

@ -64,13 +64,13 @@ Value builtinArrayConstructor(ExecutionState& state, Value thisValue, size_t arg
ArrayObject* array = new ArrayObject(state, proto, size);
if (interpretArgumentsAsElements) {
Value val = argv[0];
if (argc > 1 || !val.isInt32()) {
if (argc > 1 || !argv[0].isNumber()) {
if (array->isFastModeArray()) {
for (size_t idx = 0; idx < argc; idx++) {
array->m_fastModeData[idx] = argv[idx];
}
} else {
Value val = argv[0];
for (size_t idx = 0; idx < argc; idx++) {
array->defineOwnProperty(state, ObjectPropertyName(state, idx), ObjectPropertyDescriptor(val, ObjectPropertyDescriptor::AllPresent));
val = argv[idx + 1];
@ -1238,7 +1238,7 @@ static Value builtinArrayIncludes(ExecutionState& state, Value thisValue, size_t
// Let elementK be the result of ? Get(O, ! ToString(k)).
Value elementK = O->get(state, ObjectPropertyName(state, Value(doubleK))).value(state, O);
// If SameValueZero(searchElement, elementK) is true, return true.
if (elementK.equalsToByTheSameValueZeroAlgorithm(state, searchElement)) {
if (elementK.equalsToByTheSameValueZeroAlgorithm(searchElement)) {
return Value(true);
}
// Increase k by 1.

View file

@ -596,7 +596,7 @@ static Value builtinObjectIs(ExecutionState& state, Value thisValue, size_t argc
// 19.1.2.10 Object.is ( value1, value2 )
// Return SameValue(value1, value2).
return Value(argv[0].equalsToByTheSameValueAlgorithm(state, argv[1]));
return Value(argv[0].equalsToByTheSameValueAlgorithm(argv[1]));
}
static Value builtinObjectKeys(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)

View file

@ -227,13 +227,13 @@ static Value builtinRegExpSearch(ExecutionState& state, Value thisValue, size_t
RESOLVE_THIS_BINDING_TO_OBJECT(rx, Object, search);
String* s = argv[0].toString(state);
Value previousLastIndex = rx->get(state, ObjectPropertyName(state.context()->staticStrings().lastIndex)).value(state, thisValue);
if (!previousLastIndex.equalsToByTheSameValueAlgorithm(state, Value(0))) {
if (!previousLastIndex.equalsToByTheSameValueAlgorithm(Value(0))) {
rx->setThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lastIndex), Value(0), thisValue);
}
Value result = regExpExec(state, rx, s);
Value currentLastIndex = rx->get(state, ObjectPropertyName(state.context()->staticStrings().lastIndex)).value(state, thisValue);
if (!previousLastIndex.equalsToByTheSameValueAlgorithm(state, currentLastIndex)) {
if (!previousLastIndex.equalsToByTheSameValueAlgorithm(currentLastIndex)) {
rx->setThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lastIndex), previousLastIndex, thisValue);
}
if (result.isNull()) {

View file

@ -658,7 +658,7 @@ static Value builtinTypedArrayIncludes(ExecutionState& state, Value thisValue, s
// Let elementK be the result of ? Get(O, ! ToString(k)).
Value elementK = O->getIndexedProperty(state, Value(k)).value(state, O);
// If SameValueZero(searchElement, elementK) is true, return true.
if (elementK.equalsToByTheSameValueZeroAlgorithm(state, searchElement)) {
if (elementK.equalsToByTheSameValueZeroAlgorithm(searchElement)) {
return Value(true);
}
// Increase k by 1.

View file

@ -57,21 +57,13 @@ void getNextValidInValueVector(GC_word* ptr, GC_word* end, GC_word** next_ptr, G
{
while (ptr < end) {
Value* current = (Value*)ptr;
if (current->isPointerValue()) {
#ifdef ESCARGOT_32
*next_ptr = ptr + 2;
#else
if (current->isHeapValue()) {
*next_ptr = ptr + 1;
#endif
*from = ptr;
*to = (GC_word*)current->asPointerValue();
*to = (GC_word*)current->asRawData();
return;
}
#ifdef ESCARGOT_32
ptr = ptr + 2;
#else
ptr = ptr + 1;
#endif
}
*next_ptr = end;
@ -353,13 +345,13 @@ template <>
Value* CustomAllocator<Value>::allocate(size_type GC_n, const void*)
{
// Un-comment this to use default allocator
// return (Value*)GC_MALLOC(sizeof(Value) * GC_n);
int kind = s_gcKinds[HeapObjectKind::ValueVectorKind];
size_t size = sizeof(Value) * GC_n;
return (Value*)GC_MALLOC(sizeof(Value) * GC_n);
// int kind = s_gcKinds[HeapObjectKind::ValueVectorKind];
// size_t size = sizeof(Value) * GC_n;
Value* ret;
ret = (Value*)GC_GENERIC_MALLOC(size, kind);
return ret;
// Value* ret;
// ret = (Value*)GC_GENERIC_MALLOC(size, kind);
// return ret;
}
template <>

View file

@ -246,7 +246,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
BinaryPlus* code = (BinaryPlus*)programCounter;
const Value& v0 = registerFile[code->m_srcIndex0];
const Value& v1 = registerFile[code->m_srcIndex1];
Value ret(Value::ForceUninitialized);
Value& ret = registerFile[code->m_dstIndex];
if (v0.isInt32() && v1.isInt32()) {
int32_t a = v0.asInt32();
int32_t b = v1.asInt32();
@ -258,11 +258,11 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
ret = Value(Value::EncodeAsDouble, (double)a + (double)b);
}
} else if (v0.isNumber() && v1.isNumber()) {
ret = Value(v0.asNumber() + v1.asNumber());
// most cases are double
ret = Value(Value::EncodeAsDouble, v0.asNumber() + v1.asNumber());
} else {
ret = plusSlowCase(*state, v0, v1);
}
registerFile[code->m_dstIndex] = ret;
ADD_PROGRAM_COUNTER(BinaryPlus);
NEXT_INSTRUCTION();
}
@ -273,7 +273,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
BinaryMinus* code = (BinaryMinus*)programCounter;
const Value& left = registerFile[code->m_srcIndex0];
const Value& right = registerFile[code->m_srcIndex1];
Value ret(Value::ForceUninitialized);
Value& ret = registerFile[code->m_dstIndex];
if (left.isInt32() && right.isInt32()) {
int32_t a = left.asInt32();
int32_t b = right.asInt32();
@ -285,11 +285,11 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
ret = Value(Value::EncodeAsDouble, (double)a - (double)b);
}
} else if (LIKELY(left.isNumber() && right.isNumber())) {
ret = Value(left.asNumber() - right.asNumber());
// most cases are double
ret = Value(Value::EncodeAsDouble, left.asNumber() - right.asNumber());
} else {
ret = minusSlowCase(*state, left, right);
}
registerFile[code->m_dstIndex] = ret;
ADD_PROGRAM_COUNTER(BinaryMinus);
NEXT_INSTRUCTION();
}
@ -300,7 +300,7 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
BinaryMultiply* code = (BinaryMultiply*)programCounter;
const Value& left = registerFile[code->m_srcIndex0];
const Value& right = registerFile[code->m_srcIndex1];
Value ret(Value::ForceUninitialized);
Value& ret = registerFile[code->m_dstIndex];
if (left.isInt32() && right.isInt32()) {
int32_t a = left.asInt32();
int32_t b = right.asInt32();
@ -316,11 +316,11 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
}
}
} else if (LIKELY(left.isNumber() && right.isNumber())) {
// most cases are double
ret = Value(Value::EncodeAsDouble, left.asNumber() * right.asNumber());
} else {
ret = multiplySlowCase(*state, left, right);
}
registerFile[code->m_dstIndex] = ret;
ADD_PROGRAM_COUNTER(BinaryMultiply);
NEXT_INSTRUCTION();
}
@ -331,10 +331,12 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
BinaryDivision* code = (BinaryDivision*)programCounter;
const Value& left = registerFile[code->m_srcIndex0];
const Value& right = registerFile[code->m_srcIndex1];
Value& ret = registerFile[code->m_dstIndex];
if (LIKELY(left.isNumber() && right.isNumber())) {
registerFile[code->m_dstIndex] = Value(left.asNumber() / right.asNumber());
// most cases are double
ret = Value(Value::EncodeAsDouble, left.asNumber() / right.asNumber());
} else {
registerFile[code->m_dstIndex] = divisionSlowCase(*state, left, right);
ret = divisionSlowCase(*state, left, right);
}
ADD_PROGRAM_COUNTER(BinaryDivision);
NEXT_INSTRUCTION();
@ -490,7 +492,11 @@ Value ByteCodeInterpreter::interpret(ExecutionState* state, ByteCodeBlock* byteC
if (LIKELY(arr->isFastModeArray())) {
uint32_t idx = property.tryToUseAsIndexProperty(*state);
if (LIKELY(idx < arr->arrayLength(*state))) {
registerFile[code->m_storeRegisterIndex] = arr->m_fastModeData[idx].toValue<true>();
if (UNLIKELY(arr->m_fastModeData[idx].isEmpty())) {
registerFile[code->m_storeRegisterIndex] = Value();
} else {
registerFile[code->m_storeRegisterIndex] = arr->m_fastModeData[idx];
}
ADD_PROGRAM_COUNTER(GetObject);
NEXT_INSTRUCTION();
}
@ -1633,7 +1639,7 @@ NEVER_INLINE Value ByteCodeInterpreter::multiplySlowCase(ExecutionState& state,
if (UNLIKELY(lnum.second)) {
return Value(lnum.first.asBigInt()->multiply(state, rnum.first.asBigInt()));
} else {
return Value(Value::EncodeAsDouble, lnum.first.asNumber() * rnum.first.asNumber());
return Value(lnum.first.asNumber() * rnum.first.asNumber());
}
}
@ -1650,7 +1656,7 @@ NEVER_INLINE Value ByteCodeInterpreter::divisionSlowCase(ExecutionState& state,
}
return Value(lnum.first.asBigInt()->division(state, rnum.first.asBigInt()));
} else {
return Value(Value::EncodeAsDouble, lnum.first.asNumber() / rnum.first.asNumber());
return Value(lnum.first.asNumber() / rnum.first.asNumber());
}
}

View file

@ -50,8 +50,10 @@ public:
}
} else {
ASSERT(m_value.asPointerValue()->isBigInt());
codeBlock->m_otherLiteralData.pushBack(m_value.asPointerValue()->asBigInt());
codeBlock->m_otherLiteralData.pushBack(m_value.asPointerValue());
}
} else if (m_value.isHeapValue()) {
codeBlock->m_otherLiteralData.pushBack(reinterpret_cast<void*>(m_value.asRawData()));
}
if (dstRegister < REGULAR_REGISTER_LIMIT + VARIABLE_LIMIT) {
codeBlock->pushCode(LoadLiteral(ByteCodeLOC(m_loc.index), dstRegister, m_value), context, this);

View file

@ -1078,6 +1078,10 @@ public:
// raw = this->getTokenRaw(token);
if (builder.isNodeGenerator()) {
auto d = token->valueNumberLiteral(this->scanner);
if (d.first.asRawData() == 0) {
puts("123");
token->valueNumberLiteral(this->scanner);
}
if (LIKELY(!d.second)) {
if (minus) {
d.first = Value(-d.first.asNumber());

View file

@ -335,7 +335,7 @@ void ArgumentsObject::setIndexedPropertyValueQuickly(ExecutionState& state, size
ASSERT(m_targetRecord != nullptr);
ArgumentsObjectNativeSetter(state, this, value, m_targetRecord, m_sourceFunctionObject->interpretedCodeBlock(), m_parameterMap[index].second);
} else {
return m_parameterMap[index].first = value;
m_parameterMap[index].first = value;
}
}

View file

@ -91,6 +91,26 @@ Context::Context(VMInstance* instance)
ExecutionState stateForInit(this);
m_globalObjectProxy = m_globalObject = new GlobalObject(stateForInit);
m_globalObject->initializeBuiltins(stateForInit);
#if !defined(NDEBUG)
// Int32 convertible double value test
Value v(Value::EncodeAsDouble, 2.0);
ASSERT(v.isDouble());
ASSERT(v.isNumber());
ASSERT(v.isInteger(stateForInit));
ASSERT(v.toIndex(stateForInit) == 2);
ASSERT(v.toIndex32(stateForInit) == 2);
ASSERT(v.toInt32(stateForInit) == 2);
ASSERT(v.toInteger(stateForInit) == 2);
ASSERT(v.toLength(stateForInit) == 2);
ASSERT(v.toNumber(stateForInit) == 2);
ASSERT(v.toNumeric(stateForInit).first.asDouble() == 2);
ASSERT(v.toPropertyKey(stateForInit).toIndex(stateForInit) == 2);
ASSERT(v.tryToUseAsIndex(stateForInit) == 2);
ASSERT(v.tryToUseAsIndex32(stateForInit) == 2);
ASSERT(v.equalsTo(stateForInit, Value(2)));
ASSERT(v.abstractEqualsTo(stateForInit, Value(2)));
#endif
}
void Context::throwException(ExecutionState& state, const Value& exception)

View file

@ -34,7 +34,7 @@ class PointerValue;
#ifdef ESCARGOT_32
COMPILE_ASSERT(sizeof(EncodedValueData) == 4, "");
COMPILE_ASSERT(sizeof(Value) == 8, "");
COMPILE_ASSERT(sizeof(Value) == 4, "");
#else
COMPILE_ASSERT(sizeof(EncodedValueData) == 8, "");
#endif
@ -99,85 +99,103 @@ private:
};
#pragma pack(pop)
namespace EncodedValueImpl {
class EncodedValue {
public:
enum ForceUninitializedTag { ForceUninitialized };
enum EmptyValueInitTag { EmptyValue };
COMPILE_ASSERT(EncodedValue::EmptyValue == 0, "");
friend class EncodedSmallValue;
const int kApiAlignSize = 4;
const int kApiIntSize = sizeof(int);
const int kApiInt64Size = sizeof(int64_t);
EncodedValue(const Value& v = Value())
: m_value(v)
{
}
// Tag information for small immediate. Other values are heap objects.
const int kSmiTag = 1;
const int kSmiTagSize = 1;
const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
explicit EncodedValue(PointerValue* v)
: m_value(v)
{
}
template <size_t ptr_size>
struct SmiTagging;
static EncodedValue fromPayload(const void* p)
{
return Value(Value::FromPayload, reinterpret_cast<intptr_t>(p));
}
template <int kSmiShiftSize>
inline int32_t IntToSmiT(int value)
{
int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
uintptr_t tagged_value = (static_cast<uintptr_t>(value) << smi_shift_bits) | kSmiTag;
return (int32_t)(tagged_value);
}
explicit EncodedValue(const uint32_t from)
: m_value(Value(from))
{
}
// Smi constants for 32-bit systems.
template <>
struct SmiTagging<4> {
enum {
kSmiShiftSize = 0,
kSmiValueSize = 31
};
static int SmiShiftSize()
EncodedValue(ForceUninitializedTag)
{
return kSmiShiftSize;
}
static int SmiValueSize()
EncodedValue(EmptyValueInitTag)
{
return kSmiValueSize;
m_value = Value(Value::EmptyValue);
}
inline static int SmiToInt(intptr_t value)
const EncodedValue& operator=(PointerValue* from)
{
int shift_bits = kSmiTagSize + kSmiShiftSize;
// Throw away top 32 bits and shift down (requires >> to be sign extending).
return static_cast<int>(value >> shift_bits);
ASSERT(from);
m_value = Value(from);
return *this;
}
inline static int32_t IntToSmi(int value)
bool operator==(const EncodedValue& other) const
{
return IntToSmiT<kSmiShiftSize>(value);
return Value(m_value) == Value(other);
}
inline static bool IsValidSmi(intptr_t value)
bool isStoredInHeap() const
{
// To be representable as an tagged small integer, the two
// most-significant bits of 'value' must be either 00 or 11 due to
// sign-extension. To check this we add 01 to the two
// most-significant bits, and check if the most-significant bit is 0
//
// CAUTION: The original code below:
// bool result = ((value + 0x40000000) & 0x80000000) == 0;
// may lead to incorrect results according to the C language spec, and
// in fact doesn't work correctly with gcc4.1.1 in some cases: The
// compiler may produce undefined results in case of signed integer
// overflow. The computation must be done w/ unsigned ints.
return static_cast<uintptr_t>(value + 0x40000000U) < 0x80000000U;
return m_value.isHeapValue();
}
intptr_t payload() const
{
return m_value.asRawData();
}
bool isEmpty() const
{
return m_value.isEmpty();
}
bool isInt32()
{
return m_value.isInt32();
}
bool isUInt32()
{
return m_value.isUInt32();
}
int32_t asInt32()
{
return m_value.asInt32();
}
uint32_t asUInt32()
{
return m_value.asUInt32();
}
ALWAYS_INLINE operator Value&()
{
return m_value;
}
ALWAYS_INLINE operator const Value&() const
{
return m_value;
}
private:
Value m_value;
};
typedef SmiTagging<kApiAlignSize> PlatformSmiTagging;
const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
#define HAS_SMI_TAG(value) \
((static_cast<intptr_t>((long int)value) & ::Escargot::EncodedValueImpl::kSmiTagMask) == ::Escargot::EncodedValueImpl::kSmiTag)
#else
#define HAS_SMI_TAG(value) \
((static_cast<intptr_t>(value) & ::Escargot::EncodedValueImpl::kSmiTagMask) == ::Escargot::EncodedValueImpl::kSmiTag)
#endif
} // namespace EncodedValueImpl
/*
// EncodedValue turns int, double values into pointer or odd value
// so there is no conservative gc leak(there is no even value looks like pointer without pointers)
// developers should use this class if want to save some Value on Heap
@ -326,10 +344,11 @@ public:
return operator Escargot::Value().toUint32(state);
}
void operator=(PointerValue* from)
const EncodedValue& operator=(PointerValue* from)
{
ASSERT(from);
m_data.payload = (intptr_t)from;
return *this;
}
bool operator==(const EncodedValue& other) const
@ -337,35 +356,39 @@ public:
return m_data.payload == other.payload();
}
ALWAYS_INLINE void operator=(const Value& from)
ALWAYS_INLINE const EncodedValue& operator=(const Value& from)
{
if (from.isPointerValue()) {
#ifdef ESCARGOT_32
ASSERT(!from.isEmpty());
#endif
m_data.payload = (intptr_t)from.asPointerValue();
return;
return *this;
}
int32_t i32;
if (from.isInt32() && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32 = from.asInt32())) {
m_data.payload = EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32);
return;
return *this;
}
if (from.isNumber()) {
Value mutableFrom(from);
if (UNLIKELY(Value::isInt32ConvertibleDouble(mutableFrom.asNumber(), i32))) {
mutableFrom = Value(i32);
}
intptr_t payload = m_data.payload;
if (!HAS_SMI_TAG(payload) && ((size_t)payload > (size_t)ValueLast)) {
void* v = (void*)payload;
if (readPointerIsNumberEncodedValue(v)) {
((NumberInEncodedValue*)m_data.payload)->setValue(from);
return;
((NumberInEncodedValue*)m_data.payload)->setValue(mutableFrom);
return *this;
}
}
m_data.payload = reinterpret_cast<intptr_t>(new NumberInEncodedValue(from));
m_data.payload = reinterpret_cast<intptr_t>(new NumberInEncodedValue(mutableFrom));
ASSERT(readPointerIsNumberEncodedValue((void*)m_data.payload));
return;
return *this;
}
#ifdef ESCARGOT_32
@ -373,6 +396,7 @@ public:
#else
m_data.payload = from.payload();
#endif
return *this;
}
protected:
@ -394,7 +418,11 @@ protected:
if (from.isInt32() && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32 = from.asInt32())) {
m_data.payload = EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32);
} else if (from.isNumber()) {
m_data.payload = reinterpret_cast<intptr_t>(new NumberInEncodedValue(from));
if (UNLIKELY(Value::isInt32ConvertibleDouble(from.asNumber(), i32) && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32))) {
m_data.payload = EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32);
} else {
m_data.payload = reinterpret_cast<intptr_t>(new NumberInEncodedValue(from));
}
} else {
#ifdef ESCARGOT_32
m_data.payload = ~from.tag();
@ -413,6 +441,7 @@ protected:
private:
EncodedValueData m_data;
};
*/
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
class EncodedSmallValue {
@ -439,7 +468,11 @@ public:
if (from.isInt32() && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32 = from.asInt32())) {
setPayload(EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32));
} else if (from.isNumber()) {
setPayload(reinterpret_cast<intptr_t>(new NumberInEncodedValue(from)));
if (UNLIKELY(Value::isInt32ConvertibleDouble(from.asNumber(), i32) && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32))) {
setPayload(EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32));
} else {
setPayload(reinterpret_cast<intptr_t>(new NumberInEncodedValue(from)));
}
} else {
setPayload(from.payload());
}
@ -518,38 +551,44 @@ public:
return payload() == other.payload();
}
ALWAYS_INLINE void operator=(const EncodedValue& from)
ALWAYS_INLINE const EncodedSmallValue& operator=(const EncodedValue& from)
{
setPayload(from.payload());
return *this;
}
ALWAYS_INLINE void operator=(const Value& from)
ALWAYS_INLINE const EncodedSmallValue& operator=(const Value& from)
{
if (from.isPointerValue()) {
setPayload(reinterpret_cast<intptr_t>(from.asPointerValue()));
return;
return *this;
}
int32_t i32;
if (from.isInt32() && EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i32 = from.asInt32())) {
setPayload(EncodedValueImpl::PlatformSmiTagging::IntToSmi(i32));
return;
return *this;
}
if (from.isNumber()) {
Value mutableFrom(from);
if (UNLIKELY(Value::isInt32ConvertibleDouble(mutableFrom.asNumber(), i32))) {
mutableFrom = Value(i32);
}
auto pl = payload();
if (!isSMI() && ((size_t)pl > (size_t)ValueLast)) {
void* v = reinterpret_cast<void*>(pl);
if (EncodedValue::readPointerIsNumberEncodedValue(v)) {
reinterpret_cast<NumberInEncodedValue*>(v)->setValue(from);
return;
reinterpret_cast<NumberInEncodedValue*>(v)->setValue(mutableFrom);
return *this;
}
}
setPayload(reinterpret_cast<intptr_t>(new NumberInEncodedValue(from)));
setPayload(reinterpret_cast<intptr_t>(new NumberInEncodedValue(mutableFrom)));
ASSERT(EncodedValue::readPointerIsNumberEncodedValue((void*)payload()));
return;
return *this;
}
setPayload(from.payload());
return *this;
}
private:

View file

@ -38,14 +38,16 @@ public:
RELEASE_ASSERT_NOT_REACHED();
}
Object* m_object;
uint32_t m_arrayLength;
size_t m_index;
EncodedValueTightVector m_keys;
protected:
EnumerateObject(Object* obj)
: m_index(0)
, m_object(obj)
: m_object(obj)
, m_arrayLength(0)
, m_index(0)
{
ASSERT(!!m_object);
}
@ -54,9 +56,6 @@ protected:
virtual void executeEnumeration(ExecutionState& state, EncodedValueTightVector& keys) = 0;
virtual bool checkIfModified(ExecutionState& state);
Object* m_object;
uint32_t m_arrayLength;
};
// enumerate object for destruction operation e.g. var obj = { a, ...b };

View file

@ -75,7 +75,7 @@ bool MapObject::deleteOperation(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
m_storage[i] = std::make_pair(Value(Value::EmptyValue), Value(Value::EmptyValue));
return true;
}
@ -90,7 +90,7 @@ Value MapObject::get(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
return m_storage[i].second;
}
}
@ -104,7 +104,7 @@ bool MapObject::has(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
return true;
}
}
@ -118,7 +118,7 @@ void MapObject::set(ExecutionState& state, const Value& key, const Value& value)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
m_storage[i].second = value;
return;
}

View file

@ -121,7 +121,7 @@ bool ModuleNamespaceObject::defineOwnProperty(ExecutionState& state, const Objec
}
// If Desc.[[Value]] is present, return SameValue(Desc.[[Value]], current.[[Value]]).
if (desc.isValuePresent()) {
return desc.value().equalsToByTheSameValueAlgorithm(state, current.value(state, this));
return desc.value().equalsToByTheSameValueAlgorithm(current.value(state, this));
}
// Return true.
return true;

View file

@ -809,7 +809,7 @@ bool Object::defineOwnPropertyMethod(ExecutionState& state, const ObjectProperty
}
// If the [[Writable]] field of current is false, then
// Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
if (!item->m_descriptor.isWritable() && desc.isValuePresent() && !desc.value().equalsToByTheSameValueAlgorithm(state, getOwnDataPropertyUtilForObject(state, idx, this))) {
if (!item->m_descriptor.isWritable() && desc.isValuePresent() && !desc.value().equalsToByTheSameValueAlgorithm(getOwnDataPropertyUtilForObject(state, idx, this))) {
return false;
}
}
@ -1471,7 +1471,7 @@ bool Object::isCompatiblePropertyDescriptor(ExecutionState& state, bool extensib
// ii. If the [[Writable]] field of current is false, then
if (!current.isWritable()) {
// 1. Return false, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
if (desc.isValuePresent() && !desc.value().equalsToByTheSameValueAlgorithm(state, current.value(state, Value()))) {
if (desc.isValuePresent() && !desc.value().equalsToByTheSameValueAlgorithm(current.value(state, Value()))) {
return false;
}
}

View file

@ -62,7 +62,7 @@ bool SetObject::deleteOperation(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
m_storage[i] = Value(Value::EmptyValue);
return true;
}
@ -77,7 +77,7 @@ void SetObject::add(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
return;
}
}
@ -97,7 +97,7 @@ bool SetObject::has(ExecutionState& state, const Value& key)
if (existingKey.isEmpty()) {
continue;
}
if (existingKey.equalsToByTheSameValueZeroAlgorithm(state, key)) {
if (existingKey.equalsToByTheSameValueZeroAlgorithm(key)) {
return true;
}
}

View file

@ -316,7 +316,7 @@ bool Value::abstractEqualsToSlowCase(ExecutionState& state, const Value& val) co
return a == b;
} else {
if (u.asInt64 == val.u.asInt64) {
if (m_data.payload == val.m_data.payload) {
return true;
}
@ -443,9 +443,9 @@ bool Value::equalsToSlowCase(ExecutionState& state, const Value& val) const
return a == b;
}
return u.asInt64 == val.u.asInt64;
return m_data.payload == val.m_data.payload;
} else {
if (u.asInt64 == val.u.asInt64) {
if (m_data.payload == val.m_data.payload) {
return true;
}
@ -470,7 +470,7 @@ bool Value::equalsToSlowCase(ExecutionState& state, const Value& val) const
return false;
}
bool Value::equalsToByTheSameValueAlgorithm(ExecutionState& ec, const Value& val) const
bool Value::equalsToByTheSameValueAlgorithm(const Value& val) const
{
if (!val.isPointerValue()) {
if (isNumber() && val.isNumber()) {
@ -489,9 +489,9 @@ bool Value::equalsToByTheSameValueAlgorithm(ExecutionState& ec, const Value& val
return a == b && std::signbit(a) == std::signbit(b);
}
return u.asInt64 == val.u.asInt64;
return m_data.payload == val.m_data.payload;
} else {
if (u.asInt64 == val.u.asInt64) {
if (m_data.payload == val.m_data.payload) {
return true;
}
@ -518,7 +518,7 @@ bool Value::equalsToByTheSameValueAlgorithm(ExecutionState& ec, const Value& val
return false;
}
bool Value::equalsToByTheSameValueZeroAlgorithm(ExecutionState& ec, const Value& val) const
bool Value::equalsToByTheSameValueZeroAlgorithm(const Value& val) const
{
if (LIKELY(!val.isPointerValue())) {
if (isNumber() && val.isNumber()) {
@ -532,9 +532,9 @@ bool Value::equalsToByTheSameValueZeroAlgorithm(ExecutionState& ec, const Value&
return a == b;
}
return u.asInt64 == val.u.asInt64;
return m_data.payload == val.m_data.payload;
} else {
if (u.asInt64 == val.u.asInt64) {
if (m_data.payload == val.m_data.payload) {
return true;
}

View file

@ -28,6 +28,96 @@
namespace Escargot {
namespace EncodedValueImpl {
const int kApiAlignSize = 4;
const int kApiIntSize = sizeof(int);
const int kApiInt64Size = sizeof(int64_t);
// Tag information for small immediate. Other values are heap objects.
const int kSmiTag = 1;
const int kSmiTagSize = 1;
const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
template <size_t ptr_size>
struct SmiTagging;
template <int kSmiShiftSize>
inline int32_t IntToSmiT(int value)
{
int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
uintptr_t tagged_value = (static_cast<uintptr_t>(value) << smi_shift_bits) | kSmiTag;
return (int32_t)(tagged_value);
}
// Smi constants for 32-bit systems.
template <>
struct SmiTagging<4> {
enum {
kSmiShiftSize = 0,
kSmiValueSize = 31
};
static int SmiShiftSize()
{
return kSmiShiftSize;
}
static int SmiValueSize()
{
return kSmiValueSize;
}
inline static int SmiToInt(intptr_t value)
{
int shift_bits = kSmiTagSize + kSmiShiftSize;
// Throw away top 32 bits and shift down (requires >> to be sign extending).
return static_cast<int>(value >> shift_bits);
}
inline static int32_t IntToSmi(int value)
{
return IntToSmiT<kSmiShiftSize>(value);
}
inline static bool IsValidSmi(intptr_t value)
{
// To be representable as an tagged small integer, the two
// most-significant bits of 'value' must be either 00 or 11 due to
// sign-extension. To check this we add 01 to the two
// most-significant bits, and check if the most-significant bit is 0
//
// CAUTION: The original code below:
// bool result = ((value + 0x40000000) & 0x80000000) == 0;
// may lead to incorrect results according to the C language spec, and
// in fact doesn't work correctly with gcc4.1.1 in some cases: The
// compiler may produce undefined results in case of signed integer
// overflow. The computation must be done w/ unsigned ints.
return static_cast<uintptr_t>(value + 0x40000000U) < 0x80000000U;
}
};
typedef SmiTagging<kApiAlignSize> PlatformSmiTagging;
const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
#define HAS_SMI_TAG(value) \
((static_cast<intptr_t>((long int)value) & ::Escargot::EncodedValueImpl::kSmiTagMask) == ::Escargot::EncodedValueImpl::kSmiTag)
#else
#define HAS_SMI_TAG(value) \
((static_cast<intptr_t>(value) & ::Escargot::EncodedValueImpl::kSmiTagMask) == ::Escargot::EncodedValueImpl::kSmiTag)
#endif
} // namespace EncodedValueImpl
struct ValueData {
intptr_t payload;
ValueData()
: payload(0)
{
}
explicit ValueData(void* ptr)
: payload((intptr_t)ptr)
{
}
};
class PointerValue;
class ExecutionState;
class Object;
@ -89,6 +179,10 @@ inline ToType bitwise_cast(FromType from)
#define CellPayloadOffset PayloadOffset
#endif
// basic class for representing ECMAScript Value
// Int32 convertible double value can exist rarely(for interpreter performance)
// but Int32 convertible double case is not exist with EncodedValue
// so, public API users should not care the case
class Value {
public:
static constexpr const double MinusZeroIndex = std::numeric_limits<double>::min();
@ -127,6 +221,10 @@ public:
explicit Value(TrueInitTag);
explicit Value(FalseInitTag);
explicit Value(FromPayloadTag, intptr_t ptr);
explicit Value(PointerValue* ptr);
Value(const PointerValue* ptr);
/*
#ifdef ESCARGOT_64
explicit Value(PointerValue* ptr);
Value(const PointerValue* ptr);
@ -141,10 +239,10 @@ public:
Value(const String* ptr);
explicit Value(FromTagTag, uint32_t tag);
#endif
*/
// Numbers
Value(EncodeAsDoubleTag, double);
explicit Value(double);
Value(EncodeAsDoubleTag, const double&);
explicit Value(const double&);
explicit Value(bool);
explicit Value(char);
explicit Value(unsigned char);
@ -171,7 +269,7 @@ public:
double asDouble() const;
bool asBoolean() const;
double asNumber() const;
uint64_t asRawData() const;
intptr_t asRawData() const;
inline PointerValue* asPointerValue() const;
inline Object* asObject() const;
inline FunctionObject* asFunction() const;
@ -198,6 +296,7 @@ public:
inline bool isPrimitive() const;
bool isGetterSetter() const;
bool isCustomGetterSetter() const;
inline bool isHeapValue() const;
inline bool isPointerValue() const;
bool isObject() const;
bool isCallable() const;
@ -246,36 +345,20 @@ public:
bool abstractEqualsToSlowCase(ExecutionState& ec, const Value& val) const;
inline bool equalsTo(ExecutionState& ec, const Value& val) const;
bool equalsToSlowCase(ExecutionState& ec, const Value& val) const;
bool equalsToByTheSameValueAlgorithm(ExecutionState& ec, const Value& val) const;
bool equalsToByTheSameValueZeroAlgorithm(ExecutionState& ec, const Value& val) const;
bool equalsToByTheSameValueAlgorithm(const Value& val) const;
bool equalsToByTheSameValueZeroAlgorithm(const Value& val) const;
bool instanceOf(ExecutionState& ec, const Value& other) const;
#ifdef ESCARGOT_32
uint32_t tag() const;
#elif ESCARGOT_64
// These values are #defines since using static const integers here is a ~1% regression!
// This value is 2^48, used to encode doubles such that the encoded value will begin
// with a 16-bit pattern within the range 0x0001..0xFFFE.
#define DoubleEncodeOffset 0x1000000000000ll
// DoubleEncodeOffset assumes that double value will begin with 0x0000..0xFFFD,
// but there can be invalid inputs start with 0xFFFE or 0xFFFF.
// So it is used to filter those values.
#define DoubleInvalidBeginning 0xfffe000000000000ll
// If all bits in the mask are set, this indicates an integer number,
// if any but not all are set this value is a double precision number.
#define TagTypeNumber 0xffff000000000000ll
// TagMask is used to check for all types of immediate values (either number or 'other').
#define TagMask (TagTypeNumber | TagBitTypeOther)
#endif
intptr_t payload() const;
static constexpr double maximumLength();
static bool isInt32ConvertibleDouble(const double& d);
static bool isInt32ConvertibleDouble(const double& d, int32_t& asInt32);
private:
ValueDescriptor u;
const uint8_t readPointerValueTag() const
{
return *(reinterpret_cast<size_t*>(m_data.payload) + 1);
}
ValueData m_data;
double toNumberSlowCase(ExecutionState& ec) const; // $7.1.3 ToNumber
std::pair<Value, bool> toNumericSlowCase(ExecutionState& ec) const;
String* toStringSlowCase(ExecutionState& ec) const; // $7.1.12 ToString

View file

@ -50,436 +50,243 @@ constexpr double Value::maximumLength()
return 9007199254740991.0;
}
#pragma pack(push, 1)
// NumberInEncodedValue stores its tag in `this + sizeof(size_t)`
// the location is same with PointerValues
// store double
class DoubleInValue : public gc {
public:
explicit DoubleInValue(const double& v)
#ifdef ESCARGOT_32
{
m_buffer[1] = POINTER_VALUE_NUMBER_TAG_IN_DATA;
setValue(v);
}
#else
: m_value(v)
, m_typeTag(POINTER_VALUE_NUMBER_TAG_IN_DATA)
{
}
#endif
void* operator new(size_t size)
{
return GC_MALLOC_ATOMIC(size);
}
void* operator new[](size_t size) = delete;
double value() const
{
#ifdef ESCARGOT_32
double ret;
uint32_t* buf = reinterpret_cast<uint32_t*>(&ret);
buf[0] = m_buffer[0];
buf[1] = m_buffer[2];
return ret;
#else
return m_value;
#endif
}
void setValue(const double& v)
{
#ifdef ESCARGOT_32
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&v);
m_buffer[0] = buf[0];
m_buffer[2] = buf[1];
#else
m_value = v;
#endif
}
private:
#ifdef ESCARGOT_32
uint32_t m_buffer[3];
#else
double m_value;
size_t m_typeTag;
#endif
};
#pragma pack(pop)
// ==============================================================================
// ===32-bit architecture========================================================
// ==============================================================================
#ifdef ESCARGOT_32
inline Value::Value(ForceUninitializedTag)
{
}
inline Value::Value()
{
u.asBits.tag = UndefinedTag;
u.asBits.payload = 0;
}
inline Value::Value(NullInitTag)
{
u.asBits.tag = NullTag;
u.asBits.payload = 0;
}
inline Value::Value(UndefinedInitTag)
{
u.asBits.tag = UndefinedTag;
u.asBits.payload = 0;
}
inline Value::Value(EmptyValueInitTag)
{
u.asBits.tag = EmptyValueTag;
u.asBits.payload = 0;
}
inline Value::Value(TrueInitTag)
{
u.asBits.tag = BooleanTrueTag;
u.asBits.payload = 0;
}
inline Value::Value(FalseInitTag)
{
u.asBits.tag = BooleanFalseTag;
u.asBits.payload = 0;
}
inline Value::Value(bool b)
{
u.asBits.tag = BooleanFalseTag ^ (((uint32_t)b) << TagTypeShift);
u.asBits.payload = 0;
}
inline Value::Value(FromPayloadTag, intptr_t ptr)
{
u.asBits.tag = OtherPointerTag;
#if defined(COMPILER_MSVC)
u.asBits.payload = (int32_t)(ptr);
#else
u.asBits.payload = reinterpret_cast<int32_t>(ptr);
#endif
}
inline Value::Value(PointerValue* ptr)
{
// other type of PointerValue(Object) has pointer in first data area
if (ptr->getTypeTag() & (POINTER_VALUE_NOT_OBJECT_TAG_IN_DATA)) {
u.asBits.tag = OtherPointerTag;
} else {
u.asBits.tag = ObjectPointerTag;
}
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<PointerValue*>(ptr));
}
inline Value::Value(const PointerValue* ptr)
{
if (ptr->isObject()) {
u.asBits.tag = ObjectPointerTag;
} else {
u.asBits.tag = OtherPointerTag;
}
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<PointerValue*>(ptr));
}
inline Value::Value(const PointerValue* ptr, FromNonObjectPointerTag)
{
ASSERT(!ptr->isObject());
u.asBits.tag = OtherPointerTag;
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<PointerValue*>(ptr));
}
inline Value::Value(String* ptr)
{
ASSERT(ptr != NULL);
u.asBits.tag = OtherPointerTag;
u.asBits.payload = reinterpret_cast<int32_t>(ptr);
}
inline Value::Value(const String* ptr)
{
ASSERT(ptr != NULL);
u.asBits.tag = OtherPointerTag;
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<String*>(ptr));
}
inline Value::Value(Object* ptr)
{
u.asBits.tag = ObjectPointerTag;
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<Object*>(ptr));
}
inline Value::Value(const Object* ptr)
{
u.asBits.tag = ObjectPointerTag;
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<Object*>(ptr));
}
inline Value::Value(FromTagTag, uint32_t tag)
{
ASSERT(tag == BooleanFalseTag || tag == BooleanTrueTag || tag == NullTag
|| tag == UndefinedTag || tag == EmptyValueTag);
u.asBits.tag = tag;
u.asBits.payload = 0;
}
inline Value::Value(EncodeAsDoubleTag, double d)
{
u.asDouble = d;
}
inline Value::Value(int i)
{
u.asBits.tag = Int32Tag;
u.asBits.payload = i;
}
inline bool Value::operator==(const Value& other) const
{
return u.asInt64 == other.u.asInt64;
}
inline bool Value::operator!=(const Value& other) const
{
return u.asInt64 != other.u.asInt64;
}
inline uint32_t Value::tag() const
{
return u.asBits.tag;
}
inline int32_t Value::payload() const
{
return u.asBits.payload;
}
ALWAYS_INLINE bool Value::isInt32() const
{
return tag() == Int32Tag;
}
ALWAYS_INLINE bool Value::isDouble() const
{
return tag() < LowestTag;
}
inline int32_t Value::asInt32() const
{
ASSERT(isInt32());
return u.asBits.payload;
}
inline bool Value::asBoolean() const
{
ASSERT(isBoolean());
return u.asBits.tag == BooleanTrueTag;
}
inline double Value::asDouble() const
{
ASSERT(isDouble());
return u.asDouble;
}
inline bool Value::isEmpty() const
{
return tag() == EmptyValueTag;
}
ALWAYS_INLINE bool Value::isNumber() const
{
return isInt32() || isDouble();
}
inline bool Value::isPointerValue() const
{
return (tag() == ObjectPointerTag) || (tag() == OtherPointerTag);
}
inline bool Value::isUndefined() const
{
return tag() == UndefinedTag;
}
inline bool Value::isNull() const
{
return tag() == NullTag;
}
inline bool Value::isBoolean() const
{
/* BooleanTrueTag and BooleanFalseTag are inverted values
of ValueTrue and ValueFalse respectively. */
COMPILE_ASSERT(BooleanFalseTag == (BooleanTrueTag | (1 << TagTypeShift)), "");
return (tag() | (1 << TagTypeShift)) == BooleanFalseTag;
}
inline bool Value::isTrue() const
{
return tag() == BooleanTrueTag;
}
inline bool Value::isFalse() const
{
return tag() == BooleanFalseTag;
}
inline PointerValue* Value::asPointerValue() const
{
ASSERT(isPointerValue());
return reinterpret_cast<PointerValue*>(u.asBits.payload);
}
inline bool Value::isString() const
{
return tag() == OtherPointerTag && asPointerValue()->isString();
}
inline bool Value::isSymbol() const
{
return tag() == OtherPointerTag && asPointerValue()->isSymbol();
}
inline bool Value::isBigInt() const
{
return tag() == OtherPointerTag && asPointerValue()->isBigInt();
}
inline String* Value::asString() const
{
ASSERT(isString());
return asPointerValue()->asString();
}
inline Symbol* Value::asSymbol() const
{
ASSERT(isSymbol());
return asPointerValue()->asSymbol();
}
inline BigInt* Value::asBigInt() const
{
ASSERT(isBigInt());
return asPointerValue()->asBigInt();
}
inline bool Value::isObject() const
{
return tag() == ObjectPointerTag;
}
inline Object* Value::asObject() const
{
return asPointerValue()->asObject();
}
inline bool Value::isFunction() const
{
return isObject() && asPointerValue()->isFunctionObject();
}
inline bool Value::isExtendedNativeFunctionObject() const
{
return isObject() && asPointerValue()->isExtendedNativeFunctionObject();
}
inline FunctionObject* Value::asFunction() const
{
return asPointerValue()->asFunctionObject();
}
#else
// ==============================================================================
// ===64-bit architecture========================================================
// ==============================================================================
#endif
inline Value::Value()
{
u.asInt64 = ValueUndefined;
}
// ==============================================================================
// ===common architecture========================================================
// ==============================================================================
inline Value::Value(ForceUninitializedTag)
{
}
inline Value::Value()
{
m_data.payload = ValueUndefined;
}
inline Value::Value(NullInitTag)
{
u.asInt64 = ValueNull;
m_data.payload = ValueNull;
}
inline Value::Value(UndefinedInitTag)
{
u.asInt64 = ValueUndefined;
m_data.payload = ValueUndefined;
}
inline Value::Value(EmptyValueInitTag)
{
u.asInt64 = ValueEmpty;
m_data.payload = ValueEmpty;
}
inline Value::Value(TrueInitTag)
{
u.asInt64 = ValueTrue;
m_data.payload = ValueTrue;
}
inline Value::Value(FalseInitTag)
{
u.asInt64 = ValueFalse;
}
inline Value::Value(FromPayloadTag, intptr_t ptr)
{
u.ptr = (PointerValue*)ptr;
m_data.payload = ValueFalse;
}
inline Value::Value(bool b)
{
u.asInt64 = (TagBitTypeOther | (b << TagTypeShift));
m_data.payload = b ? ValueTrue : ValueFalse;
}
inline Value::Value(FromPayloadTag, intptr_t ptr)
{
m_data.payload = ptr;
}
inline Value::Value(PointerValue* ptr)
{
u.ptr = ptr;
m_data.payload = reinterpret_cast<intptr_t>(ptr);
}
inline Value::Value(const PointerValue* ptr)
{
u.ptr = const_cast<PointerValue*>(ptr);
}
inline int64_t reinterpretDoubleToInt64(double value)
{
return bitwise_cast<int64_t>(value);
}
inline double reinterpretInt64ToDouble(int64_t value)
{
return bitwise_cast<double>(value);
}
inline Value::Value(EncodeAsDoubleTag, double d)
{
u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset;
m_data.payload = reinterpret_cast<intptr_t>(ptr);
}
inline Value::Value(int i)
{
u.asInt64 = TagTypeNumber | static_cast<uint32_t>(i);
if (LIKELY(EncodedValueImpl::PlatformSmiTagging::IsValidSmi(i))) {
m_data.payload = EncodedValueImpl::PlatformSmiTagging::IntToSmi(i);
} else {
*this = Value(Value::EncodeAsDouble, static_cast<double>(i));
}
}
inline Value::Value(EncodeAsDoubleTag, const double& v)
{
m_data.payload = reinterpret_cast<intptr_t>(new DoubleInValue(v));
}
inline intptr_t Value::asRawData() const
{
return m_data.payload;
}
inline bool Value::operator==(const Value& other) const
{
return u.asInt64 == other.u.asInt64;
return equalsToByTheSameValueAlgorithm(other);
}
inline bool Value::operator!=(const Value& other) const
{
return u.asInt64 != other.u.asInt64;
return !equalsToByTheSameValueAlgorithm(other);
}
ALWAYS_INLINE bool Value::isInt32() const
{
#ifdef ESCARGOT_LITTLE_ENDIAN
ASSERT(sizeof(short) == 2);
unsigned short* firstByte = (unsigned short*)&u.asInt64;
return firstByte[3] == 0xffff;
#else
return (u.asInt64 & TagTypeNumber) == TagTypeNumber;
#endif
return HAS_SMI_TAG(m_data.payload);
}
inline bool Value::isDouble() const
ALWAYS_INLINE bool Value::isDouble() const
{
return isNumber() && !isInt32();
return isHeapValue() && readPointerValueTag() == POINTER_VALUE_NUMBER_TAG_IN_DATA;
}
inline int32_t Value::asInt32() const
{
ASSERT(isInt32());
return static_cast<int32_t>(u.asInt64);
return EncodedValueImpl::PlatformSmiTagging::SmiToInt(m_data.payload);
}
inline bool Value::asBoolean() const
{
ASSERT(isBoolean());
return u.asInt64 == ValueTrue;
return m_data.payload == ValueTrue;
}
inline double Value::asDouble() const
{
ASSERT(isDouble());
return reinterpretInt64ToDouble(u.asInt64 - DoubleEncodeOffset);
return reinterpret_cast<DoubleInValue*>(m_data.payload)->value();
}
inline bool Value::isEmpty() const
{
return u.asInt64 == ValueEmpty;
return m_data.payload == ValueEmpty;
}
ALWAYS_INLINE bool Value::isNumber() const
{
#ifdef ESCARGOT_LITTLE_ENDIAN
ASSERT(sizeof(short) == 2);
unsigned short* firstByte = (unsigned short*)&u.asInt64;
return firstByte[3];
#else
return u.asInt64 & TagTypeNumber;
#endif
return isInt32() || isDouble();
}
inline bool Value::isHeapValue() const
{
return !(m_data.payload & (EncodedValueImpl::kSmiTagMask | TagBitTypeOther));
}
inline bool Value::isPointerValue() const
{
return isHeapValue() && readPointerValueTag() != POINTER_VALUE_NUMBER_TAG_IN_DATA;
}
inline bool Value::isUndefined() const
{
return m_data.payload == ValueUndefined;
}
inline bool Value::isNull() const
{
return m_data.payload == ValueNull;
}
inline bool Value::isBoolean() const
{
return (m_data.payload | (1 << TagTypeShift)) == ValueTrue;
}
inline bool Value::isTrue() const
{
return m_data.payload == ValueTrue;
}
inline bool Value::isFalse() const
{
return m_data.payload == ValueFalse;
}
inline PointerValue* Value::asPointerValue() const
{
ASSERT(isPointerValue());
return reinterpret_cast<PointerValue*>(m_data.payload);
}
inline bool Value::isString() const
@ -515,47 +322,14 @@ inline BigInt* Value::asBigInt() const
return asPointerValue()->asBigInt();
}
inline bool Value::isPointerValue() const
{
return !(u.asInt64 & TagMask);
}
inline bool Value::isUndefined() const
{
return u.asInt64 == ValueUndefined;
}
inline bool Value::isNull() const
{
return u.asInt64 == ValueNull;
}
inline bool Value::isBoolean() const
{
COMPILE_ASSERT(ValueTrue == (ValueFalse | (1 << TagTypeShift)), "");
return (u.asInt64 | (1 << TagTypeShift)) == ValueTrue;
}
inline bool Value::isTrue() const
{
return u.asInt64 == ValueTrue;
}
inline bool Value::isFalse() const
{
return u.asInt64 == ValueFalse;
}
inline PointerValue* Value::asPointerValue() const
{
ASSERT(isPointerValue());
return u.ptr;
}
inline bool Value::isObject() const
{
return isPointerValue() && asPointerValue()->isObject();
if (LIKELY(isHeapValue())) {
if (LIKELY(!(readPointerValueTag() & POINTER_VALUE_NOT_OBJECT_TAG_IN_DATA))) {
return true;
}
}
return false;
}
inline Object* Value::asObject() const
@ -568,40 +342,42 @@ inline bool Value::isFunction() const
return isPointerValue() && asPointerValue()->isFunctionObject();
}
inline bool Value::isExtendedNativeFunctionObject() const
{
return isPointerValue() && asPointerValue()->isExtendedNativeFunctionObject();
}
inline FunctionObject* Value::asFunction() const
{
return asPointerValue()->asFunctionObject();
}
inline ExtendedNativeFunctionObject* Value::asExtendedNativeFunctionObject() const
ALWAYS_INLINE bool Value::isInt32ConvertibleDouble(const double& d)
{
return asPointerValue()->asExtendedNativeFunctionObject();
int32_t asInt32 = static_cast<int32_t>(d);
if (LIKELY(LIKELY(asInt32 != d) || UNLIKELY(!asInt32 && std::signbit(d)))) { // true for -0.0
return false;
}
return true;
}
inline intptr_t Value::payload() const
ALWAYS_INLINE bool Value::isInt32ConvertibleDouble(const double& d, int32_t& asInt32)
{
return u.asInt64;
asInt32 = static_cast<int32_t>(d);
if (LIKELY(LIKELY(asInt32 != d) || UNLIKELY(!asInt32 && std::signbit(d)))) { // true for -0.0
return false;
}
return true;
}
#endif
// ==============================================================================
// ===common architecture========================================================
// ==============================================================================
inline Value::Value(double d)
inline Value::Value(const double& d)
{
const int32_t asInt32 = static_cast<int32_t>(d);
if (asInt32 != d || (!asInt32 && std::signbit(d))) { // true for -0.0
#ifdef ESCARGOT_64
if (UNLIKELY((bitwise_cast<int64_t>(d) & DoubleInvalidBeginning) == DoubleInvalidBeginning)) {
d = std::numeric_limits<double>::quiet_NaN();
}
#endif
*this = Value(EncodeAsDouble, d);
int32_t asInt32;
if (UNLIKELY(isInt32ConvertibleDouble(d, asInt32))) {
*this = Value(asInt32);
return;
}
*this = Value(static_cast<int32_t>(d));
*this = Value(EncodeAsDouble, d);
}
inline Value::Value(char i)
@ -626,25 +402,28 @@ inline Value::Value(unsigned short i)
inline Value::Value(unsigned i)
{
if (static_cast<int32_t>(i) < 0) {
const int32_t asInt32 = static_cast<int32_t>(i);
if (UNLIKELY(asInt32 < 0)) {
*this = Value(EncodeAsDouble, static_cast<double>(i));
return;
}
*this = Value(static_cast<int32_t>(i));
*this = Value(asInt32);
}
inline Value::Value(long i)
{
if (static_cast<int32_t>(i) != i) {
const int32_t asInt32 = static_cast<int32_t>(i);
if (UNLIKELY(asInt32 != i)) {
*this = Value(EncodeAsDouble, static_cast<double>(i));
return;
}
*this = Value(static_cast<int32_t>(i));
*this = Value(asInt32);
}
inline Value::Value(unsigned long i)
{
if (static_cast<uint32_t>(i) != i) {
const uint32_t asInt32 = static_cast<uint32_t>(i);
if (UNLIKELY(asInt32 != i)) {
*this = Value(EncodeAsDouble, static_cast<double>(i));
return;
}
@ -653,20 +432,22 @@ inline Value::Value(unsigned long i)
inline Value::Value(long long i)
{
if (static_cast<int32_t>(i) != i) {
const int32_t asInt32 = static_cast<int32_t>(i);
if (UNLIKELY(asInt32 != i)) {
*this = Value(EncodeAsDouble, static_cast<double>(i));
return;
}
*this = Value(static_cast<int32_t>(i));
*this = Value(asInt32);
}
inline Value::Value(unsigned long long i)
{
if (static_cast<uint32_t>(i) != i) {
const uint32_t asInt32 = static_cast<uint32_t>(i);
if (UNLIKELY(asInt32 != i)) {
*this = Value(EncodeAsDouble, static_cast<double>(i));
return;
}
*this = Value(static_cast<uint32_t>(i));
*this = Value(asInt32);
}
inline bool Value::isUInt32() const
@ -688,11 +469,7 @@ ALWAYS_INLINE double Value::asNumber() const
inline bool Value::isPrimitive() const
{
#ifdef ESCARGOT_32
return tag() != ObjectPointerTag;
#else
return isUndefined() || isNull() || isNumber() || isString() || isBoolean() || isSymbol() || isBigInt();
#endif
}
inline bool Value::isCallable() const
@ -706,21 +483,10 @@ inline bool Value::isCallable() const
// https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
inline double Value::toNumber(ExecutionState& state) const
{
#ifdef ESCARGOT_64
auto n = u.asInt64 & TagTypeNumber;
if (LIKELY(n)) {
if (n == TagTypeNumber) {
return FastI2D(asInt32());
} else {
return asDouble();
}
}
#else
if (LIKELY(isInt32()))
return FastI2D(asInt32());
else if (isDouble())
return asDouble();
#endif
else if (isUndefined())
return std::numeric_limits<double>::quiet_NaN();
else if (isNull())
@ -734,20 +500,11 @@ inline double Value::toNumber(ExecutionState& state) const
inline std::pair<Value, bool> Value::toNumeric(ExecutionState& state) const // <Value, isBigInt>
{
// fast path
#ifdef ESCARGOT_64
auto n = u.asInt64 & TagTypeNumber;
if (LIKELY(n)) {
return std::make_pair(*this, false);
}
#else
if (LIKELY(isInt32())) {
return std::make_pair(*this, false);
} else if (isDouble()) {
return std::make_pair(*this, false);
}
#endif
else if (isUndefined()) {
} else if (isUndefined()) {
return std::make_pair(Value(std::numeric_limits<double>::quiet_NaN()), false);
} else if (isNull()) {
return std::make_pair(Value(0), false);
@ -779,11 +536,7 @@ inline Value Value::toPrimitive(ExecutionState& ec, PrimitiveTypeHint preferredT
inline bool Value::abstractEqualsTo(ExecutionState& state, const Value& val) const
{
if (isInt32() && val.isInt32()) {
#ifdef ESCARGOT_64
return u.asInt64 == val.u.asInt64;
#else
return u.asBits.payload == val.u.asBits.payload;
#endif
return m_data.payload == val.m_data.payload;
} else {
return abstractEqualsToSlowCase(state, val);
}
@ -792,11 +545,7 @@ inline bool Value::abstractEqualsTo(ExecutionState& state, const Value& val) con
inline bool Value::equalsTo(ExecutionState& state, const Value& val) const
{
if (isInt32() && val.isInt32()) {
#ifdef ESCARGOT_64
return u.asInt64 == val.u.asInt64;
#else
return u.asBits.payload == val.u.asBits.payload;
#endif
return m_data.payload == val.m_data.payload;
} else {
return equalsToSlowCase(state, val);
}
@ -887,7 +636,7 @@ uint32_t Value::tryToUseAsIndexProperty(ExecutionState& ec) const
inline double Value::toInteger(ExecutionState& state) const
{
if (isInt32()) {
if (LIKELY(isInt32())) {
return asInt32();
}
@ -903,7 +652,7 @@ inline double Value::toInteger(ExecutionState& state) const
inline bool Value::isInteger(ExecutionState& state) const
{
if (isInt32()) {
if (LIKELY(isInt32())) {
return true;
}
@ -917,7 +666,7 @@ inline bool Value::isInteger(ExecutionState& state) const
inline uint64_t Value::toLength(ExecutionState& state) const
{
double len = toInteger(state);
if (len <= 0.0) {
if (UNLIKELY(len <= 0.0)) {
return 0;
}
return std::min(len, maximumLength());

View file

@ -921,8 +921,16 @@ PersistentRefHolder<ContextRef> createEscargotContext(VMInstanceRef* instance, b
return context;
}
#if defined(ESCARGOT_GPERF)
#include <gperftools/profiler.h>
#endif
int main(int argc, char* argv[])
{
#if defined(ESCARGOT_GPERF)
ProfilerStart("profile.log");
#endif
#ifndef NDEBUG
setbuf(stdout, NULL);
setbuf(stderr, NULL);
@ -1090,5 +1098,9 @@ int main(int argc, char* argv[])
Globals::finalize();
#if defined(ESCARGOT_GPERF)
ProfilerStop();
#endif
return 0;
}