+2007-11-08 Darin Adler <darin@apple.com>
+
+ Reviewed by Oliver.
+
+ - http://bugs.webkit.org/show_bug.cgi?id=15904
+ more speed-ups possible by tightening up int version of JSImmediate
+
+ 1% improvement of SunSpider
+
+ * kjs/JSImmediate.h: Eliminate the now-unneeded FPBitValues struct template.
+ (KJS::JSImmediate::from): Overload for most numeric types; many types can
+ do fewer branches and checks.
+ (KJS::JSImmediate::getUInt32): Removed unneeded check for undefined.
+ (KJS::JSImmediate::getTruncatedInt32): Ditto.
+ (KJS::JSImmediate::getTruncatedUInt32): Ditto. There's no difference any more
+ between getUInt32 and getTruncatedUInt32, so that's worth a rename and merge later.
+
+ * kjs/grammar.y: Update since fromDouble is now just from.
+ * kjs/nodes.h: Ditto.
+
+ * kjs/value.h: (KJS::jsNumber): Overload for most numeric types.
+
2007-11-08 Kevin Ollivier <kevino@theolliviers.com>
Bakefiles for building JavaScriptCore, needed by wx port.
return (getTag(v) == UndefinedType);
}
+ static JSValue* from(char);
+ static JSValue* from(signed char);
+ static JSValue* from(unsigned char);
+ static JSValue* from(short);
+ static JSValue* from(unsigned short);
+ static JSValue* from(int);
+ static JSValue* from(unsigned);
+ static JSValue* from(long);
+ static JSValue* from(unsigned long);
+ static JSValue* from(long long);
+ static JSValue* from(unsigned long long);
+ static JSValue* from(double);
+
static ALWAYS_INLINE bool areBothImmediateNumbers(const JSValue* v1, const JSValue* v2)
{
return (reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2) & TagMask) == NumberType;
return reinterpret_cast<JSValue*>(reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2));
}
- static JSValue* fromDouble(double d);
static double toDouble(const JSValue*);
static bool toBoolean(const JSValue*);
static JSObject* toObject(const JSValue*, ExecState*);
static bool getTruncatedInt32(const JSValue*, int32_t&);
static bool getTruncatedUInt32(const JSValue*, uint32_t&);
- // It would nice just to use fromDouble() to create these values, but that would prevent them from
- // turning into compile-time constants.
static JSValue* trueImmediate();
static JSValue* falseImmediate();
static JSValue* undefinedImmediate();
private:
static const uintptr_t TagMask = 3; // type tags are 2 bits long
+
+ // Immediate values are restricted to a 30 bit signed value.
+ static const int minImmediateInt = -(1 << 29);
+ static const int maxImmediateInt = (1 << 29) - 1;
+ static const unsigned maxImmediateUInt = maxImmediateInt;
static ALWAYS_INLINE JSValue* tag(uintptr_t bits, uintptr_t tag)
{
{
return reinterpret_cast<uintptr_t>(v) & TagMask;
}
-
- // we support 32-bit platforms with sizes like this
- static const bool is32bit =
- sizeof(float) == sizeof(uint32_t) && sizeof(double) == sizeof(uint64_t) && sizeof(uintptr_t) == sizeof(uint32_t);
+};
- // we support 64-bit platforms with sizes like this
- static const bool is64bit =
- sizeof(float) == sizeof(uint32_t) && sizeof(double) == sizeof(uint64_t) && sizeof(uintptr_t) == sizeof(uint64_t);
+ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(1 << 2, BooleanType); }
+ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(0, BooleanType); }
+ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(1 << 2, UndefinedType); }
+ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(0, UndefinedType); }
- template<bool for32bit, bool for64bit> struct FPBitValues {};
-};
+ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v)
+{
+ ASSERT(isImmediate(v));
+ uintptr_t bits = unTag(v);
+ return (bits != 0) & (JSImmediate::getTag(v) != UndefinedType);
+}
-template<> struct JSImmediate::FPBitValues<true, false> {
- static const uint32_t nanAsBits = 0x7fc00000;
- static const uint32_t oneAsBits = 0x3f800000;
- static const uint32_t zeroAsBits = 0x0;
+ALWAYS_INLINE JSValue* JSImmediate::from(char i)
+{
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE JSValue* fromDouble(double d)
- {
- const int32_t intVal = static_cast<int>(d);
-
- // On 32 bit systems immediate values are restricted to a 30 bit signed value
- if ((intVal <= -(1 << 29)) | (intVal >= ((1 << 29) - 1)))
- return 0;
-
- // Check for data loss from conversion to int. This
- // will reject NaN, +/-Inf, and -0.
- // Happily none of these are as common as raw int values
- if ((intVal != d) || (signbit(d) && !intVal))
- return 0;
-
- return tag(intVal << 2, NumberType);
- }
+ALWAYS_INLINE JSValue* JSImmediate::from(signed char i)
+{
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE double toDouble(const JSValue* v)
- {
- ASSERT(isImmediate(v));
- const int32_t i = static_cast<int32_t>(unTag(v)) >> 2;
- if (JSImmediate::getTag(v) == UndefinedType && i)
- return std::numeric_limits<double>::quiet_NaN();
- return i;
- }
-
- static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
- {
- i = static_cast<int32_t>(unTag(v)) >> 2;
- if (JSImmediate::getTag(v) == UndefinedType && i)
- return false;
- return isNumber(v);
- }
-
- static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
- {
- int32_t& si = reinterpret_cast<int&>(i);
- return getTruncatedInt32(v, si) & (si >= 0);
- }
-};
+ALWAYS_INLINE JSValue* JSImmediate::from(unsigned char i)
+{
+ return tag(i << 2, NumberType);
+}
-template<> struct JSImmediate::FPBitValues<false, true> {
- static const uint64_t nanAsBits = 0x7ff80000ULL << 32;
- static const uint64_t oneAsBits = 0x3ff00000ULL << 32;
- static const uint64_t zeroAsBits = 0x0;
+ALWAYS_INLINE JSValue* JSImmediate::from(short i)
+{
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE JSValue* fromDouble(double d)
- {
- const int64_t intVal = static_cast<int>(d);
-
- // Technically we could fit a 60 bit signed int here, however that would
- // required more branches when extracting int values.
- if ((intVal <= -(1L << 29)) | (intVal >= ((1 << 29) - 1)))
- return 0;
-
- // Check for data loss from conversion to int. This
- // will reject NaN, +/-Inf, and -0.
- // Happily none of these are as common as raw int values
- if ((intVal != d) || (signbit(d) && !intVal))
- return 0;
-
- return tag(static_cast<uintptr_t>(intVal << 2), NumberType);
- }
+ALWAYS_INLINE JSValue* JSImmediate::from(unsigned short i)
+{
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE double toDouble(const JSValue* v)
- {
- ASSERT(isImmediate(v));
- const int32_t i = static_cast<int32_t>(unTag(v) >> 2);
- if (JSImmediate::getTag(v) == UndefinedType && i)
- return std::numeric_limits<double>::quiet_NaN();
- return i;
- }
+ALWAYS_INLINE JSValue* JSImmediate::from(int i)
+{
+ if ((i < minImmediateInt) | (i > maxImmediateInt))
+ return 0;
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
- {
- i = static_cast<int32_t>(unTag(v) >> 2);
- if (JSImmediate::getTag(v) == UndefinedType && i)
- return false;
- return isNumber(v);
- }
+ALWAYS_INLINE JSValue* JSImmediate::from(unsigned i)
+{
+ if (i > maxImmediateUInt)
+ return 0;
+ return tag(i << 2, NumberType);
+}
- static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
- {
- int& si = reinterpret_cast<int&>(i);
- return getTruncatedInt32(v, si) & (si >= 0);
- }
-};
+ALWAYS_INLINE JSValue* JSImmediate::from(long i)
+{
+ if ((i < minImmediateInt) | (i > maxImmediateInt))
+ return 0;
+ return tag(i << 2, NumberType);
+}
-ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(1 << 2, BooleanType); }
-ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(0, BooleanType); }
-ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(1 << 2, UndefinedType); }
-ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(0, UndefinedType); }
+ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long i)
+{
+ if (i > maxImmediateUInt)
+ return 0;
+ return tag(i << 2, NumberType);
+}
-ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v)
+ALWAYS_INLINE JSValue* JSImmediate::from(long long i)
{
- ASSERT(isImmediate(v));
- uintptr_t bits = unTag(v);
- return (bits != 0) & (JSImmediate::getTag(v) != UndefinedType);
+ if ((i < minImmediateInt) | (i > maxImmediateInt))
+ return 0;
+ return tag(static_cast<uintptr_t>(i) << 2, NumberType);
+}
+
+ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long long i)
+{
+ if (i > maxImmediateUInt)
+ return 0;
+ return tag(static_cast<uintptr_t>(i) << 2, NumberType);
}
-ALWAYS_INLINE JSValue* JSImmediate::fromDouble(double d)
+ALWAYS_INLINE JSValue* JSImmediate::from(double d)
{
- return FPBitValues<is32bit, is64bit>::fromDouble(d);
+ const int intVal = static_cast<int>(d);
+
+ if ((intVal < minImmediateInt) | (intVal > maxImmediateInt))
+ return 0;
+
+ // Check for data loss from conversion to int.
+ if ((intVal != d) || (signbit(d) && !intVal))
+ return 0;
+
+ return tag(intVal << 2, NumberType);
}
ALWAYS_INLINE double JSImmediate::toDouble(const JSValue* v)
{
- return FPBitValues<is32bit, is64bit>::toDouble(v);
+ ASSERT(isImmediate(v));
+ const int32_t i = static_cast<int32_t>(unTag(v)) >> 2;
+ if (JSImmediate::getTag(v) == UndefinedType && i)
+ return std::numeric_limits<double>::quiet_NaN();
+ return i;
}
ALWAYS_INLINE bool JSImmediate::getUInt32(const JSValue* v, uint32_t& i)
{
- return FPBitValues<is32bit, is64bit>::getTruncatedUInt32(v, i);
+ const int32_t si = static_cast<int32_t>(unTag(v)) >> 2;
+ i = si;
+ return isNumber(v) & (si >= 0);
}
ALWAYS_INLINE bool JSImmediate::getTruncatedInt32(const JSValue* v, int32_t& i)
{
- return FPBitValues<is32bit, is64bit>::getTruncatedInt32(v, i);
+ i = static_cast<int32_t>(unTag(v)) >> 2;
+ return isNumber(v);
}
ALWAYS_INLINE bool JSImmediate::getTruncatedUInt32(const JSValue* v, uint32_t& i)
{
- return FPBitValues<is32bit, is64bit>::getTruncatedUInt32(v, i);
+ return getUInt32(v, i);
}
} // namespace KJS
%%
Literal:
- NULLTOKEN { $$ = new NullNode(); }
- | TRUETOKEN { $$ = new BooleanNode(true); }
- | FALSETOKEN { $$ = new BooleanNode(false); }
+ NULLTOKEN { $$ = new NullNode; }
+ | TRUETOKEN { $$ = new TrueNode; }
+ | FALSETOKEN { $$ = new FalseNode; }
| NUMBER { $$ = makeNumberNode($1); }
| STRING { $$ = new StringNode($1); }
| '/' /* regexp */ {
static NumberNode* makeNumberNode(double d)
{
- JSValue* value = JSImmediate::fromDouble(d);
+ JSValue* value = JSImmediate::from(d);
if (value)
return new ImmediateNumberNode(value, d);
return new NumberNode(d);
nodeStack.append(expr.get());
}
-JSValue *ArgumentListNode::evaluate(ExecState *)
-{
- ASSERT(0);
- return 0; // dummy, see evaluateList()
-}
-
// ECMA 11.2.4
void ArgumentListNode::evaluateList(ExecState* exec, List& list)
{
nodeStack.append(listNode.get());
}
-JSValue *ArgumentsNode::evaluate(ExecState *)
-{
- ASSERT(0);
- return 0; // dummy, see evaluateList()
-}
-
// ------------------------------ NewExprNode ----------------------------------
void NewExprNode::optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack& nodeStack)
-// -*- c-basic-offset: 2 -*-
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
FunctionStack& functionStack;
};
- class Node {
+ class Node : Noncopyable {
public:
Node() KJS_FAST_CALL;
Node(PlacementNewAdoptType) KJS_FAST_CALL { }
// Used for iterative, depth-first traversal of the node tree. Does not cross function call boundaries.
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL { }
- // Used to optimize those nodes that do extra work when returning a result, even if the result has no semantic relevance
- virtual void optimizeForUnnecessaryResult() { }
-
protected:
Completion createErrorCompletion(ExecState *, ErrorType, const char *msg) KJS_FAST_CALL;
Completion createErrorCompletion(ExecState *, ErrorType, const char *msg, const Identifier &) KJS_FAST_CALL;
int m_line : 31;
bool m_mayHaveDeclarations : 1;
- private:
- // disallow assignment
- Node& operator=(const Node&) KJS_FAST_CALL;
- Node(const Node &other) KJS_FAST_CALL;
};
class ExpressionNode : public Node {
virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL = 0;
virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL;
- };
+
+ // Used to optimize those nodes that do extra work when returning a result, even if the result has no semantic relevance
+ virtual void optimizeForUnnecessaryResult() { }
+ };
class StatementNode : public Node {
public:
class ImmediateNumberNode : public NumberNode {
public:
- ImmediateNumberNode(JSValue* v, double d) KJS_FAST_CALL : NumberNode(d), m_value(v) { ASSERT(v == JSImmediate::fromDouble(d)); }
+ ImmediateNumberNode(JSValue* v, double d) KJS_FAST_CALL : NumberNode(d), m_value(v) { ASSERT(v == JSImmediate::from(d)); }
virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
- virtual void setValue(double d) KJS_FAST_CALL { m_double = d; m_value = JSImmediate::fromDouble(d); ASSERT(m_value); }
+ virtual void setValue(double d) KJS_FAST_CALL { m_double = d; m_value = JSImmediate::from(d); ASSERT(m_value); }
private:
JSValue* m_value;
};
ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*);
};
- class ElementNode : public ExpressionNode {
+ class ElementNode : public Node {
public:
ElementNode(int e, ExpressionNode* n) KJS_FAST_CALL : elision(e), node(n) { }
ElementNode(ElementNode* l, int e, ExpressionNode* n) KJS_FAST_CALL
: elision(e), node(n) { l->next = this; }
- virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
- virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
+ virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+ virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
+
PassRefPtr<ElementNode> releaseNext() KJS_FAST_CALL { return next.release(); }
- virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+
+ JSValue* evaluate(ExecState*) KJS_FAST_CALL;
+
private:
friend class ArrayNode;
ListRefPtr<ElementNode> next;
bool opt;
};
- class PropertyNode : public ExpressionNode {
+ class PropertyNode : public Node {
public:
enum Type { Constant, Getter, Setter };
PropertyNode(const Identifier& n, ExpressionNode* a, Type t) KJS_FAST_CALL
: m_name(n), assign(a), type(t) { }
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
- virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
- friend class PropertyListNode;
virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+
+ JSValue* evaluate(ExecState*) KJS_FAST_CALL;
const Identifier& name() const { return m_name; }
+
private:
+ friend class PropertyListNode;
Identifier m_name;
RefPtr<ExpressionNode> assign;
Type type;
};
- class PropertyListNode : public ExpressionNode {
+ class PropertyListNode : public Node {
public:
PropertyListNode(PropertyNode* n) KJS_FAST_CALL
: node(n) { }
PropertyListNode(PropertyNode* n, PropertyListNode* l) KJS_FAST_CALL
: node(n) { l->next = this; }
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
- virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
- PassRefPtr<PropertyListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+
+ JSValue* evaluate(ExecState*) KJS_FAST_CALL;
+ PassRefPtr<PropertyListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
+
private:
friend class ObjectLiteralNode;
RefPtr<PropertyNode> node;
Identifier ident;
};
- class ArgumentListNode : public ExpressionNode {
+ class ArgumentListNode : public Node {
public:
ArgumentListNode(ExpressionNode* e) KJS_FAST_CALL : expr(e) { }
ArgumentListNode(ArgumentListNode* l, ExpressionNode* e) KJS_FAST_CALL
: expr(e) { l->next = this; }
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
- virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
- void evaluateList(ExecState*, List&) KJS_FAST_CALL;
virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
- PassRefPtr<ArgumentListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+
+ void evaluateList(ExecState*, List&) KJS_FAST_CALL;
+ PassRefPtr<ArgumentListNode> releaseNext() KJS_FAST_CALL { return next.release(); }
+
private:
friend class ArgumentsNode;
ListRefPtr<ArgumentListNode> next;
RefPtr<ExpressionNode> expr;
};
- class ArgumentsNode : public ExpressionNode {
+ class ArgumentsNode : public Node {
public:
ArgumentsNode() KJS_FAST_CALL { }
ArgumentsNode(ArgumentListNode* l) KJS_FAST_CALL
: listNode(l) { }
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
- virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
- void evaluateList(ExecState* exec, List& list) KJS_FAST_CALL { if (listNode) listNode->evaluateList(exec, list); }
virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; }
+
+ void evaluateList(ExecState* exec, List& list) KJS_FAST_CALL { if (listNode) listNode->evaluateList(exec, list); }
+
private:
RefPtr<ArgumentListNode> listNode;
};
RefPtr<ArgumentsNode> args;
};
- class PrePostResolveNode : public ExpressionNode {
+ class PrePostResolveNode : public ExpressionNode {
public:
PrePostResolveNode(const Identifier& i) KJS_FAST_CALL : m_ident(i) {}
virtual void optimizeForUnnecessaryResult();
};
- class PostfixBracketNode : public ExpressionNode {
+ class PostfixBracketNode : public ExpressionNode {
public:
PostfixBracketNode(ExpressionNode* b, ExpressionNode* s) KJS_FAST_CALL : m_base(b), m_subscript(s) {}
virtual void optimizeVariableAccess(FunctionBodyNode*, DeclarationStacks::NodeStack&) KJS_FAST_CALL;
ALWAYS_INLINE JSValue* jsNumber(double d)
{
- JSValue *v = JSImmediate::fromDouble(d);
+ JSValue* v = JSImmediate::from(d);
return v ? v : jsNumberCell(d);
}
+ALWAYS_INLINE JSValue* jsNumber(int i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
+ALWAYS_INLINE JSValue* jsNumber(unsigned i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
+ALWAYS_INLINE JSValue* jsNumber(long i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
+ALWAYS_INLINE JSValue* jsNumber(unsigned long i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
+ALWAYS_INLINE JSValue* jsNumber(long long i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
+ALWAYS_INLINE JSValue* jsNumber(unsigned long long i)
+{
+ JSValue* v = JSImmediate::from(i);
+ return v ? v : jsNumberCell(i);
+}
+
ALWAYS_INLINE JSValue* jsNumberFromAnd(ExecState *exec, JSValue* v1, JSValue* v2)
{
if (JSImmediate::areBothImmediateNumbers(v1, v2))