Author: [email protected]
Date: Thu Apr 16 02:30:23 2009
New Revision: 1720
Modified:
branches/bleeding_edge/src/assembler.cc
branches/bleeding_edge/src/assembler.h
branches/bleeding_edge/src/builtins-arm.cc
branches/bleeding_edge/src/codegen-arm.cc
branches/bleeding_edge/src/codegen-arm.h
branches/bleeding_edge/src/codegen-ia32.cc
branches/bleeding_edge/src/codegen-ia32.h
branches/bleeding_edge/src/codegen.h
branches/bleeding_edge/src/constants-arm.h
branches/bleeding_edge/src/debug-arm.cc
branches/bleeding_edge/src/disasm-arm.cc
branches/bleeding_edge/src/globals.h
branches/bleeding_edge/src/ic-arm.cc
branches/bleeding_edge/src/macro-assembler-arm.cc
branches/bleeding_edge/src/macro-assembler-arm.h
branches/bleeding_edge/src/runtime.cc
branches/bleeding_edge/src/serialize.cc
branches/bleeding_edge/src/simulator-arm.cc
branches/bleeding_edge/src/simulator-arm.h
branches/bleeding_edge/src/stub-cache-arm.cc
branches/bleeding_edge/src/virtual-frame-arm.cc
branches/bleeding_edge/test/mjsunit/negate.js
branches/bleeding_edge/test/mjsunit/number-limits.js
branches/bleeding_edge/test/mjsunit/smi-ops.js
Log:
Avoid a call to the runtime system when doing binary fp ops on ARM
(at the moment only if we do not need to allocate a heap number).
Find a few more oportunities to avoid heap number allocation on IA32.
Add some infrastructure to test coverage of generated ARM code in our
tests.
Review URL: http://codereview.chromium.org/67163
Modified: branches/bleeding_edge/src/assembler.cc
==============================================================================
--- branches/bleeding_edge/src/assembler.cc (original)
+++ branches/bleeding_edge/src/assembler.cc Thu Apr 16 02:30:23 2009
@@ -582,4 +582,40 @@
return ExternalReference(Debug::step_in_fp_addr());
}
+
+static double add_two_doubles(double x, double y) {
+ return x + y;
+}
+
+
+static double sub_two_doubles(double x, double y) {
+ return x - y;
+}
+
+
+static double mul_two_doubles(double x, double y) {
+ return x * y;
+}
+
+
+ExternalReference ExternalReference::double_fp_operation(
+ Token::Value operation) {
+ typedef double BinaryFPOperation(double x, double y);
+ BinaryFPOperation* function = NULL;
+ switch (operation) {
+ case Token::ADD:
+ function = &add_two_doubles;
+ break;
+ case Token::SUB:
+ function = &sub_two_doubles;
+ break;
+ case Token::MUL:
+ function = &mul_two_doubles;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return ExternalReference(FUNCTION_ADDR(function));
+}
+
} } // namespace v8::internal
Modified: branches/bleeding_edge/src/assembler.h
==============================================================================
--- branches/bleeding_edge/src/assembler.h (original)
+++ branches/bleeding_edge/src/assembler.h Thu Apr 16 02:30:23 2009
@@ -38,6 +38,7 @@
#include "runtime.h"
#include "top.h"
#include "zone-inl.h"
+#include "token.h"
namespace v8 { namespace internal {
@@ -416,6 +417,8 @@
// Used to check if single stepping is enabled in generated code.
static ExternalReference debug_step_in_fp_address();
+
+ static ExternalReference double_fp_operation(Token::Value operation);
Address address() const {return address_;}
Modified: branches/bleeding_edge/src/builtins-arm.cc
==============================================================================
--- branches/bleeding_edge/src/builtins-arm.cc (original)
+++ branches/bleeding_edge/src/builtins-arm.cc Thu Apr 16 02:30:23 2009
@@ -34,7 +34,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ DEFINE_MASM(masm)
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
@@ -218,8 +218,9 @@
__ mov(r5, Operand(r4));
__ mov(r6, Operand(r4));
__ mov(r7, Operand(r4));
- if (kR9Available == 1)
+ if (kR9Available == 1) {
__ mov(r9, Operand(r4));
+ }
// Invoke the code and pass argc as r0.
__ mov(r0, Operand(r3));
Modified: branches/bleeding_edge/src/codegen-arm.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-arm.cc (original)
+++ branches/bleeding_edge/src/codegen-arm.cc Thu Apr 16 02:30:23 2009
@@ -38,7 +38,8 @@
namespace v8 { namespace internal {
-#define __ masm_->
+#define __ DEFINE_MASM(masm_)
+
//
-------------------------------------------------------------------------
// CodeGenState implementation.
@@ -677,13 +678,25 @@
class GenericBinaryOpStub : public CodeStub {
public:
- explicit GenericBinaryOpStub(Token::Value op) : op_(op) { }
+ GenericBinaryOpStub(Token::Value op,
+ OverwriteMode mode)
+ : op_(op), mode_(mode) { }
private:
Token::Value op_;
+ OverwriteMode mode_;
+
+ // Minor key encoding in 16 bits.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 14> {};
Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() { return static_cast<int>(op_); }
+ int MinorKey() {
+ // Encode the parameters in a unique 16 bit value.
+ return OpBits::encode(op_)
+ | ModeBits::encode(mode_);
+ }
+
void Generate(MacroAssembler* masm);
const char* GetName() {
@@ -708,7 +721,8 @@
};
-void CodeGenerator::GenericBinaryOperation(Token::Value op) {
+void CodeGenerator::GenericBinaryOperation(Token::Value op,
+ OverwriteMode overwrite_mode) {
VirtualFrame::SpilledScope spilled_scope(this);
// sp[0] : y
// sp[1] : x
@@ -727,7 +741,7 @@
case Token::SAR: {
frame_->EmitPop(r0); // r0 : y
frame_->EmitPop(r1); // r1 : x
- GenericBinaryOpStub stub(op);
+ GenericBinaryOpStub stub(op, overwrite_mode);
frame_->CallStub(&stub, 0);
break;
}
@@ -767,11 +781,13 @@
DeferredInlineSmiOperation(CodeGenerator* generator,
Token::Value op,
int value,
- bool reversed)
+ bool reversed,
+ OverwriteMode overwrite_mode)
: DeferredCode(generator),
op_(op),
value_(value),
- reversed_(reversed) {
+ reversed_(reversed),
+ overwrite_mode_(overwrite_mode) {
set_comment("[ DeferredInlinedSmiOperation");
}
@@ -781,6 +797,7 @@
Token::Value op_;
int value_;
bool reversed_;
+ OverwriteMode overwrite_mode_;
};
@@ -844,7 +861,7 @@
break;
}
- GenericBinaryOpStub igostub(op_);
+ GenericBinaryOpStub igostub(op_, overwrite_mode_);
Result arg0 = generator()->allocator()->Allocate(r1);
ASSERT(arg0.is_valid());
Result arg1 = generator()->allocator()->Allocate(r0);
@@ -856,7 +873,8 @@
void CodeGenerator::SmiOperation(Token::Value op,
Handle<Object> value,
- bool reversed) {
+ bool reversed,
+ OverwriteMode mode) {
VirtualFrame::SpilledScope spilled_scope(this);
// NOTE: This is an attempt to inline (a bit) more of the code for
// some possible smi operations (like + and -) when (at least) one
@@ -875,7 +893,7 @@
switch (op) {
case Token::ADD: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed,
mode);
__ add(r0, r0, Operand(value), SetCC);
deferred->enter()->Branch(vs);
@@ -887,7 +905,7 @@
case Token::SUB: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed,
mode);
if (!reversed) {
__ sub(r0, r0, Operand(value), SetCC);
@@ -905,7 +923,7 @@
case Token::BIT_XOR:
case Token::BIT_AND: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed,
mode);
__ tst(r0, Operand(kSmiTagMask));
deferred->enter()->Branch(ne);
switch (op) {
@@ -925,12 +943,12 @@
__ mov(ip, Operand(value));
frame_->EmitPush(ip);
frame_->EmitPush(r0);
- GenericBinaryOperation(op);
+ GenericBinaryOperation(op, mode);
} else {
int shift_value = int_value & 0x1f; // least significant 5 bits
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, shift_value, false);
+ new DeferredInlineSmiOperation(this, op, shift_value, false,
mode);
__ tst(r0, Operand(kSmiTagMask));
deferred->enter()->Branch(ne);
__ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags
@@ -982,7 +1000,7 @@
frame_->EmitPush(ip);
frame_->EmitPush(r0);
}
- GenericBinaryOperation(op);
+ GenericBinaryOperation(op, mode);
break;
}
@@ -1487,8 +1505,8 @@
// Test for a Smi value in a HeapNumber.
__ tst(r0, Operand(kSmiTagMask));
is_smi.Branch(eq);
- __ ldr(r1, MemOperand(r0, HeapObject::kMapOffset - kHeapObjectTag));
- __ ldrb(r1, MemOperand(r1, Map::kInstanceTypeOffset - kHeapObjectTag));
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
__ cmp(r1, Operand(HEAP_NUMBER_TYPE));
default_target->Branch(ne);
frame_->EmitPush(r0);
@@ -2523,7 +2541,9 @@
if (s->is_eval_scope()) {
Label next, fast;
- if (!context.is(tmp)) __ mov(tmp, Operand(context));
+ if (!context.is(tmp)) {
+ __ mov(tmp, Operand(context));
+ }
__ bind(&next);
// Terminate at global context.
__ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
@@ -2934,15 +2954,24 @@
LoadAndSpill(node->value());
} else {
+ // +=, *= and similar binary assignments.
+ // Get the old value of the lhs.
target.GetValueAndSpill(NOT_INSIDE_TYPEOF);
Literal* literal = node->value()->AsLiteral();
+ bool overwrite =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
if (literal != NULL && literal->handle()->IsSmi()) {
- SmiOperation(node->binary_op(), literal->handle(), false);
+ SmiOperation(node->binary_op(),
+ literal->handle(),
+ false,
+ overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE);
frame_->EmitPush(r0);
} else {
LoadAndSpill(node->value());
- GenericBinaryOperation(node->binary_op());
+ GenericBinaryOperation(node->binary_op(),
+ overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE);
frame_->EmitPush(r0);
}
}
@@ -3822,19 +3851,39 @@
// is a literal small integer.
Literal* lliteral = node->left()->AsLiteral();
Literal* rliteral = node->right()->AsLiteral();
+ // NOTE: The code below assumes that the slow cases (calls to runtime)
+ // never return a constant/immutable object.
+ bool overwrite_left =
+ (node->left()->AsBinaryOperation() != NULL &&
+ node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
+ bool overwrite_right =
+ (node->right()->AsBinaryOperation() != NULL &&
+ node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
if (rliteral != NULL && rliteral->handle()->IsSmi()) {
LoadAndSpill(node->left());
- SmiOperation(node->op(), rliteral->handle(), false);
+ SmiOperation(node->op(),
+ rliteral->handle(),
+ false,
+ overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
LoadAndSpill(node->right());
- SmiOperation(node->op(), lliteral->handle(), true);
+ SmiOperation(node->op(),
+ lliteral->handle(),
+ true,
+ overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
} else {
+ OverwriteMode overwrite_mode = NO_OVERWRITE;
+ if (overwrite_left) {
+ overwrite_mode = OVERWRITE_LEFT;
+ } else if (overwrite_right) {
+ overwrite_mode = OVERWRITE_RIGHT;
+ }
LoadAndSpill(node->left());
LoadAndSpill(node->right());
- GenericBinaryOperation(node->op());
+ GenericBinaryOperation(node->op(), overwrite_mode);
}
frame_->EmitPush(r0);
}
@@ -4067,7 +4116,8 @@
#undef __
-#define __ masm->
+#define __ DEFINE_MASM(masm)
+
Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED);
@@ -4469,94 +4519,157 @@
}
+static void HandleBinaryOpSlowCases(MacroAssembler* masm,
+ Label* not_smi,
+ const Builtins::JavaScript& builtin,
+ Token::Value operation,
+ int swi_number,
+ OverwriteMode mode) {
+ Label slow;
+ if (mode == NO_OVERWRITE) {
+ __ bind(not_smi);
+ }
+ __ bind(&slow);
+ __ push(r1);
+ __ push(r0);
+ __ mov(r0, Operand(1)); // Set number of arguments.
+ __ InvokeBuiltin(builtin, JUMP_JS); // Tail call.
+
+ // Could it be a double-double op? If we already have a place to put
+ // the answer then we can do the op and skip the builtin and runtime
call.
+ if (mode != NO_OVERWRITE) {
+ __ bind(not_smi);
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &slow); // We can't handle a Smi-double combination yet.
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &slow); // We can't handle a Smi-double combination yet.
+ // Get map of r0 into r2.
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+ // Get type of r0 into r3.
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+ __ cmp(r3, Operand(HEAP_NUMBER_TYPE));
+ __ b(ne, &slow);
+ // Get type of r1 into r3.
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+ // Check they are both the same map (heap number map).
+ __ cmp(r2, r3);
+ __ b(ne, &slow);
+ // Both are doubles.
+ // Calling convention says that second double is in r2 and r3.
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset +
kPointerSize));
+ __ push(lr);
+ if (mode == OVERWRITE_LEFT) {
+ __ push(r1);
+ } else {
+ __ push(r0);
+ }
+ // Calling convention says that first double is in r0 and r1.
+ __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset));
+ __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset +
kPointerSize));
+ // Call C routine that may not cause GC or other trouble.
+ __ mov(r5, Operand(ExternalReference::double_fp_operation(operation)));
+#if !defined(__arm__)
+ // Notify the simulator that we are calling an add routine in C.
+ __ swi(swi_number);
+#else
+ // Actually call the add routine written in C.
+ __ blx(r5);
+#endif
+ // Store answer in the overwritable heap number.
+ __ pop(r4);
+ __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset));
+ __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset +
kPointerSize));
+ __ mov(r0, Operand(r4));
+ // And we are done.
+ __ pop(pc);
+ }
+}
+
+
void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// r1 : x
// r0 : y
// result : r0
+ // All ops need to know whether we are dealing with two Smis. Set up r2
to
+ // tell us that.
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+
switch (op_) {
case Token::ADD: {
- Label slow, exit;
- // fast path
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
- __ add(r0, r1, Operand(r0), SetCC); // add y optimistically
- // go slow-path in case of overflow
- __ b(vs, &slow);
- // go slow-path in case of non-smi operands
- ASSERT(kSmiTag == 0); // adjust code below
+ Label not_smi;
+ // Fast path.
+ ASSERT(kSmiTag == 0); // Adjust code below.
__ tst(r2, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // slow path
- __ bind(&slow);
- __ sub(r0, r0, Operand(r1)); // revert optimistic add
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::ADD, JUMP_JS);
- // done
- __ bind(&exit);
+ __ b(ne, ¬_smi);
+ __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
+ // Return if no overflow.
+ __ Ret(vc);
+ __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::ADD,
+ Token::ADD,
+ assembler::arm::simulator_fp_add,
+ mode_);
break;
}
case Token::SUB: {
- Label slow, exit;
- // fast path
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
- __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically
- // go slow-path in case of overflow
- __ b(vs, &slow);
- // go slow-path in case of non-smi operands
- ASSERT(kSmiTag == 0); // adjust code below
+ Label not_smi;
+ // Fast path.
+ ASSERT(kSmiTag == 0); // Adjust code below.
__ tst(r2, Operand(kSmiTagMask));
- __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to
result
- __ b(eq, &exit);
- // slow path
- __ bind(&slow);
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::SUB, JUMP_JS);
- // done
- __ bind(&exit);
+ __ b(ne, ¬_smi);
+ __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
+ // Return if no overflow.
+ __ Ret(vc);
+ __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::SUB,
+ Token::SUB,
+ assembler::arm::simulator_fp_sub,
+ mode_);
break;
}
case Token::MUL: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label not_smi, slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
- __ b(ne, &slow);
- // remove tag from one operand (but keep sign), so that result is smi
+ __ b(ne, ¬_smi);
+ // Remove tag from one operand (but keep sign), so that result is
Smi.
__ mov(ip, Operand(r0, ASR, kSmiTagSize));
- // do multiplication
- __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1
- // go slow on overflows (overflow bit is not set)
+ // Do multiplication
+ __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1.
+ // Go slow on overflows (overflow bit is not set).
__ mov(ip, Operand(r3, ASR, 31));
__ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are
identical
__ b(ne, &slow);
- // go slow on zero result to handle -0
+ // Go slow on zero result to handle -0.
__ tst(r3, Operand(r3));
__ mov(r0, Operand(r3), LeaveCC, ne);
- __ b(ne, &exit);
- // slow case
+ __ Ret(ne);
+ // Slow case.
__ bind(&slow);
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::MUL, JUMP_JS);
- // done
- __ bind(&exit);
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::MUL,
+ Token::MUL,
+ assembler::arm::simulator_fp_mul,
+ mode_);
break;
}
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
__ b(ne, &slow);
@@ -4566,7 +4679,7 @@
case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break;
default: UNREACHABLE();
}
- __ b(&exit);
+ __ Ret();
__ bind(&slow);
__ push(r1); // restore stack
__ push(r0);
@@ -4584,16 +4697,13 @@
default:
UNREACHABLE();
}
- __ bind(&exit);
break;
}
case Token::SHL:
case Token::SHR:
case Token::SAR: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
__ b(ne, &slow);
@@ -4633,7 +4743,7 @@
// tag result and store it in r0
ASSERT(kSmiTag == 0); // adjust code below
__ mov(r0, Operand(r3, LSL, kSmiTagSize));
- __ b(&exit);
+ __ Ret();
// slow case
__ bind(&slow);
__ push(r1); // restore stack
@@ -4645,13 +4755,13 @@
case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break;
default: UNREACHABLE();
}
- __ bind(&exit);
break;
}
default: UNREACHABLE();
}
- __ Ret();
+ // This code should be unreachable.
+ __ stop("Unreachable");
}
@@ -4721,7 +4831,9 @@
__ mov(cp, Operand(0), LeaveCC, eq);
// Restore cp otherwise.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
+ if (kDebug && FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
__ pop(pc);
}
@@ -4784,7 +4896,9 @@
__ mov(cp, Operand(0), LeaveCC, eq);
// Restore cp otherwise.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
+ if (kDebug && FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
__ pop(pc);
}
@@ -5043,9 +5157,11 @@
}
__ ldr(ip, MemOperand(ip)); // deref address
- // Branch and link to JSEntryTrampoline
+ // Branch and link to JSEntryTrampoline. We don't use the double
underscore
+ // macro for the add instruction because we don't want the coverage tool
+ // inserting instructions here after we read the pc.
__ mov(lr, Operand(pc));
- __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
+ masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
// Unlink this frame from the handler chain. When reading the
// address of the next handler, there is no need to use the address
@@ -5057,6 +5173,7 @@
// No need to restore registers
__ add(sp, sp, Operand(StackHandlerConstants::kSize));
+
__ bind(&exit); // r0 holds result
// Restore the top frame descriptors from the stack.
__ pop(r3);
@@ -5068,7 +5185,9 @@
// Restore callee-saved registers and return.
#ifdef DEBUG
- if (FLAG_debug_code) __ mov(lr, Operand(pc));
+ if (FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
#endif
__ ldm(ia_w, sp, kCalleeSaved | pc.bit());
}
Modified: branches/bleeding_edge/src/codegen-arm.h
==============================================================================
--- branches/bleeding_edge/src/codegen-arm.h (original)
+++ branches/bleeding_edge/src/codegen-arm.h Thu Apr 16 02:30:23 2009
@@ -35,9 +35,6 @@
class RegisterAllocator;
class RegisterFile;
-// Mode to overwrite BinaryExpression values.
-enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
-
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
@@ -292,10 +289,13 @@
void ToBoolean(JumpTarget* true_target, JumpTarget* false_target);
- void GenericBinaryOperation(Token::Value op);
+ void GenericBinaryOperation(Token::Value op, OverwriteMode
overwrite_mode);
void Comparison(Condition cc, bool strict = false);
- void SmiOperation(Token::Value op, Handle<Object> value, bool reversed);
+ void SmiOperation(Token::Value op,
+ Handle<Object> value,
+ bool reversed,
+ OverwriteMode mode);
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
Modified: branches/bleeding_edge/src/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/codegen-ia32.cc Thu Apr 16 02:30:23 2009
@@ -3933,6 +3933,9 @@
} else {
Literal* literal = node->value()->AsLiteral();
+ bool overwrite_value =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
// There are two cases where the target is not read in the right hand
// side, that are easy to test for: the right hand side is a literal,
@@ -3945,7 +3948,9 @@
target.GetValue(NOT_INSIDE_TYPEOF);
}
Load(node->value());
- GenericBinaryOperation(node->binary_op(), node->type());
+ GenericBinaryOperation(node->binary_op(),
+ node->type(),
+ overwrite_value ? OVERWRITE_RIGHT :
NO_OVERWRITE);
}
if (var != NULL &&
Modified: branches/bleeding_edge/src/codegen-ia32.h
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.h (original)
+++ branches/bleeding_edge/src/codegen-ia32.h Thu Apr 16 02:30:23 2009
@@ -35,9 +35,6 @@
class RegisterAllocator;
class RegisterFile;
-// Mode to overwrite BinaryExpression values.
-enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
-
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
@@ -435,7 +432,7 @@
void GenericBinaryOperation(
Token::Value op,
SmiAnalysis* type,
- const OverwriteMode overwrite_mode = NO_OVERWRITE);
+ OverwriteMode overwrite_mode);
// If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time.
Modified: branches/bleeding_edge/src/codegen.h
==============================================================================
--- branches/bleeding_edge/src/codegen.h (original)
+++ branches/bleeding_edge/src/codegen.h Thu Apr 16 02:30:23 2009
@@ -71,6 +71,11 @@
// CodeForStatementPosition
// CodeForSourcePosition
+
+// Mode to overwrite BinaryExpression values.
+enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
+
+
#ifdef ARM
#include "codegen-arm.h"
#else
Modified: branches/bleeding_edge/src/constants-arm.h
==============================================================================
--- branches/bleeding_edge/src/constants-arm.h (original)
+++ branches/bleeding_edge/src/constants-arm.h Thu Apr 16 02:30:23 2009
@@ -106,7 +106,12 @@
call_rt_r5 = 0x10,
call_rt_r2 = 0x11,
// break point
- break_point = 0x20
+ break_point = 0x20,
+ // FP operations. These simulate calling into C for a moment to do fp
ops.
+ // They should trash all caller-save registers.
+ simulator_fp_add = 0x21,
+ simulator_fp_sub = 0x22,
+ simulator_fp_mul = 0x23
};
Modified: branches/bleeding_edge/src/debug-arm.cc
==============================================================================
--- branches/bleeding_edge/src/debug-arm.cc (original)
+++ branches/bleeding_edge/src/debug-arm.cc Thu Apr 16 02:30:23 2009
@@ -58,7 +58,7 @@
}
-#define __ masm->
+#define __ DEFINE_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
Modified: branches/bleeding_edge/src/disasm-arm.cc
==============================================================================
--- branches/bleeding_edge/src/disasm-arm.cc (original)
+++ branches/bleeding_edge/src/disasm-arm.cc Thu Apr 16 02:30:23 2009
@@ -261,6 +261,15 @@
case break_point:
Print("break_point");
return;
+ case simulator_fp_add:
+ Print("simulator_fp_add");
+ return;
+ case simulator_fp_mul:
+ Print("simulator_fp_mul");
+ return;
+ case simulator_fp_sub:
+ Print("simulator_fp_sub");
+ return;
default:
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d",
Modified: branches/bleeding_edge/src/globals.h
==============================================================================
--- branches/bleeding_edge/src/globals.h (original)
+++ branches/bleeding_edge/src/globals.h Thu Apr 16 02:30:23 2009
@@ -514,6 +514,16 @@
}
+#ifdef ARM_GENERATED_CODE_COVERAGE
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define DEFINE_MASM(masm) masm->stop(__FILE_LINE__); masm->
+#else
+#define DEFINE_MASM(masm) masm->
+#endif
+
+
} } // namespace v8::internal
#endif // V8_GLOBALS_H_
Modified: branches/bleeding_edge/src/ic-arm.cc
==============================================================================
--- branches/bleeding_edge/src/ic-arm.cc (original)
+++ branches/bleeding_edge/src/ic-arm.cc Thu Apr 16 02:30:23 2009
@@ -39,7 +39,7 @@
// Static IC stub generators.
//
-#define __ masm->
+#define __ DEFINE_MASM(masm)
// Helper function used from LoadIC/CallIC GenerateNormal.
@@ -96,7 +96,9 @@
// Compute the masked index: (hash + i + i * i) & mask.
__ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
__ mov(t1, Operand(t1, LSR, String::kHashShift));
- if (i > 0) __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
+ if (i > 0) {
+ __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
+ }
__ and_(t1, t1, Operand(r3));
// Scale the index by multiplying by the element size.
Modified: branches/bleeding_edge/src/macro-assembler-arm.cc
==============================================================================
--- branches/bleeding_edge/src/macro-assembler-arm.cc (original)
+++ branches/bleeding_edge/src/macro-assembler-arm.cc Thu Apr 16 02:30:23
2009
@@ -168,11 +168,11 @@
}
-void MacroAssembler::Ret() {
+void MacroAssembler::Ret(Condition cond) {
#if USE_BX
- bx(lr);
+ bx(lr, cond);
#else
- mov(pc, Operand(lr));
+ mov(pc, Operand(lr), LeaveCC, cond);
#endif
}
Modified: branches/bleeding_edge/src/macro-assembler-arm.h
==============================================================================
--- branches/bleeding_edge/src/macro-assembler-arm.h (original)
+++ branches/bleeding_edge/src/macro-assembler-arm.h Thu Apr 16 02:30:23
2009
@@ -86,7 +86,7 @@
void Call(Register target, Condition cond = al);
void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
- void Ret();
+ void Ret(Condition cond = al);
// Jumps to the label at the index given by the Smi in "index".
void SmiJumpTable(Register index, Vector<Label*> targets);
Modified: branches/bleeding_edge/src/runtime.cc
==============================================================================
--- branches/bleeding_edge/src/runtime.cc (original)
+++ branches/bleeding_edge/src/runtime.cc Thu Apr 16 02:30:23 2009
@@ -3482,6 +3482,7 @@
return Heap::nan_value();
}
+
static Object* Runtime_NumberAdd(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
Modified: branches/bleeding_edge/src/serialize.cc
==============================================================================
--- branches/bleeding_edge/src/serialize.cc (original)
+++ branches/bleeding_edge/src/serialize.cc Thu Apr 16 02:30:23 2009
@@ -683,6 +683,18 @@
UNCLASSIFIED,
10,
"Debug::step_in_fp_addr()");
+ Add(ExternalReference::double_fp_operation(Token::ADD).address(),
+ UNCLASSIFIED,
+ 11,
+ "add_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::SUB).address(),
+ UNCLASSIFIED,
+ 12,
+ "sub_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::MUL).address(),
+ UNCLASSIFIED,
+ 13,
+ "mul_two_doubles");
}
Modified: branches/bleeding_edge/src/simulator-arm.cc
==============================================================================
--- branches/bleeding_edge/src/simulator-arm.cc (original)
+++ branches/bleeding_edge/src/simulator-arm.cc Thu Apr 16 02:30:23 2009
@@ -90,12 +90,44 @@
}
+
+#ifdef ARM_GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitializeCoverage() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void Debugger::Stop(Instr* instr) {
+ char* str = reinterpret_cast<char*>(instr->InstructionBits() &
0x0fffffff);
+ if (strlen(str) > 0) {
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "Simulator hit %s\n", str);
+ fflush(coverage_log);
+ }
+ instr->SetInstructionBits(0xe1a00000); // Overwrite with nop.
+ }
+ sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+}
+
+#else // ndef ARM_GENERATED_CODE_COVERAGE
+
+static void InitializeCoverage() {
+}
+
+
void Debugger::Stop(Instr* instr) {
const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff);
PrintF("Simulator hit %s\n", str);
sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
Debug();
}
+#endif
static const char* reg_names[] = { "r0", "r1", "r2", "r3",
@@ -375,6 +407,7 @@
// access violation if the simulator ever tries to execute it.
registers_[pc] = bad_lr;
registers_[lr] = bad_lr;
+ InitializeCoverage();
}
@@ -427,6 +460,37 @@
}
+// For use in calls that take two double values, constructed from r0, r1,
r2
+// and r3.
+void Simulator::GetFpArgs(double* x, double* y) {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[2 * sizeof(registers_[0])];
+ // Registers 0 and 1 -> x.
+ memcpy(buffer, registers_, sizeof(buffer));
+ memcpy(x, buffer, sizeof(buffer));
+ // Registers 2 and 3 -> y.
+ memcpy(buffer, registers_ + 2, sizeof(buffer));
+ memcpy(y, buffer, sizeof(buffer));
+}
+
+
+void Simulator::SetFpResult(const double& result) {
+ char buffer[2 * sizeof(registers_[0])];
+ memcpy(buffer, &result, sizeof(buffer));
+ // result -> registers 0 and 1.
+ memcpy(registers_, buffer, sizeof(buffer));
+}
+
+
+void Simulator::TrashCallerSaveRegisters() {
+ // We don't trash the registers with the return value.
+ registers_[2] = 0x50Bad4U;
+ registers_[3] = 0x50Bad4U;
+ registers_[12] = 0x50Bad4U;
+}
+
+
// The ARM cannot do unaligned reads and writes. On some ARM platforms an
// interrupt is caused. On others it does a funky rotation thing. For
now we
// simply disallow unaligned reads, but at some point we may want to move
to
@@ -862,7 +926,8 @@
// Software interrupt instructions are used by the simulator to call into
the
// C-based V8 runtime.
void Simulator::SoftwareInterrupt(Instr* instr) {
- switch (instr->SwiField()) {
+ int swi = instr->SwiField();
+ switch (swi) {
case call_rt_r5: {
SimulatorRuntimeCall target =
reinterpret_cast<SimulatorRuntimeCall>(get_register(r5));
@@ -892,6 +957,30 @@
case break_point: {
Debugger dbg(this);
dbg.Debug();
+ break;
+ }
+ {
+ double x, y, z;
+ case simulator_fp_add:
+ GetFpArgs(&x, &y);
+ z = x + y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
+ break;
+ case simulator_fp_sub:
+ GetFpArgs(&x, &y);
+ z = x - y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
+ break;
+ case simulator_fp_mul:
+ GetFpArgs(&x, &y);
+ z = x * y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
break;
}
default: {
Modified: branches/bleeding_edge/src/simulator-arm.h
==============================================================================
--- branches/bleeding_edge/src/simulator-arm.h (original)
+++ branches/bleeding_edge/src/simulator-arm.h Thu Apr 16 02:30:23 2009
@@ -174,6 +174,12 @@
// Executes one instruction.
void InstructionDecode(Instr* instr);
+ // For use in calls that take two double values, constructed from r0,
r1, r2
+ // and r3.
+ void GetFpArgs(double* x, double* y);
+ void SetFpResult(const double& result);
+ void TrashCallerSaveRegisters();
+
// architecture state
int32_t registers_[16];
bool n_flag_;
Modified: branches/bleeding_edge/src/stub-cache-arm.cc
==============================================================================
--- branches/bleeding_edge/src/stub-cache-arm.cc (original)
+++ branches/bleeding_edge/src/stub-cache-arm.cc Thu Apr 16 02:30:23 2009
@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ DEFINE_MASM(masm)
static void ProbeTable(MacroAssembler* masm,
@@ -183,7 +183,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
GenerateFastPropertyLoad(masm, r0, reg, holder, index);
__ Ret();
}
@@ -203,7 +203,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
// Return the constant value.
__ mov(r0, Operand(Handle<Object>(value)));
@@ -226,7 +226,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
@@ -256,7 +256,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2,
miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
@@ -456,8 +456,7 @@
#undef __
-
-#define __ masm()->
+#define __ DEFINE_MASM(masm())
Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
@@ -511,7 +510,7 @@
// Do the right check and compute the holder register.
Register reg =
- __ CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
+ masm()->CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
// Check that the function really is a function.
Modified: branches/bleeding_edge/src/virtual-frame-arm.cc
==============================================================================
--- branches/bleeding_edge/src/virtual-frame-arm.cc (original)
+++ branches/bleeding_edge/src/virtual-frame-arm.cc Thu Apr 16 02:30:23 2009
@@ -36,7 +36,8 @@
//
-------------------------------------------------------------------------
// VirtualFrame implementation.
-#define __ masm_->
+#define __ DEFINE_MASM(masm_)
+
// On entry to a function, the virtual frame already contains the
// receiver and the parameters. All initial frame elements are in
Modified: branches/bleeding_edge/test/mjsunit/negate.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/negate.js (original)
+++ branches/bleeding_edge/test/mjsunit/negate.js Thu Apr 16 02:30:23 2009
@@ -30,9 +30,9 @@
function testmulneg(a, b) {
var base = a * b;
- assertEquals(-base, a * -b);
- assertEquals(-base, -a * b);
- assertEquals(base, -a * -b);
+ assertEquals(-base, a * -b, "a * -b where a = " + a + ", b = " + b);
+ assertEquals(-base, -a * b, "-a * b where a = " + a + ", b = " + b);
+ assertEquals(base, -a * -b, "*-a * -b where a = " + a + ", b = " + b);
}
testmulneg(2, 3);
Modified: branches/bleeding_edge/test/mjsunit/number-limits.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/number-limits.js (original)
+++ branches/bleeding_edge/test/mjsunit/number-limits.js Thu Apr 16
02:30:23 2009
@@ -33,10 +33,14 @@
var addAboveMax = Number.MAX_VALUE + 1/eps;
var mulBelowMin = Number.MIN_VALUE * (1 - eps);
var addBelowMin = Number.MIN_VALUE - eps;
- assertTrue(mulAboveMax == Number.MAX_VALUE || mulAboveMax == Infinity);
- assertTrue(addAboveMax == Number.MAX_VALUE || addAboveMax == Infinity);
- assertTrue(mulBelowMin == Number.MIN_VALUE || mulBelowMin <= 0);
- assertTrue(addBelowMin == Number.MIN_VALUE || addBelowMin <= 0);
+ assertTrue(mulAboveMax == Number.MAX_VALUE ||
+ mulAboveMax == Infinity, "mul" + i);
+ assertTrue(addAboveMax == Number.MAX_VALUE ||
+ addAboveMax == Infinity, "add" + i);
+ assertTrue(mulBelowMin == Number.MIN_VALUE ||
+ mulBelowMin <= 0, "mul2" + i);
+ assertTrue(addBelowMin == Number.MIN_VALUE ||
+ addBelowMin <= 0, "add2" + i);
}
}
Modified: branches/bleeding_edge/test/mjsunit/smi-ops.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/smi-ops.js (original)
+++ branches/bleeding_edge/test/mjsunit/smi-ops.js Thu Apr 16 02:30:23 2009
@@ -100,3 +100,98 @@
assertEquals(ONE_HUNDRED - SMI_MIN, Sub100Reversed(SMI_MIN)); // overflow
assertEquals(42 - ONE_HUNDRED, Sub100(OBJ_42)); // non-smi
assertEquals(ONE_HUNDRED - 42, Sub100Reversed(OBJ_42)); // non-smi
+
+
+function Shr1(x) {
+ return x >>> 1;
+}
+
+function Shr100(x) {
+ return x >>> 100;
+}
+
+function Shr1Reversed(x) {
+ return 1 >>> x;
+}
+
+function Shr100Reversed(x) {
+ return 100 >>> x;
+}
+
+function Sar1(x) {
+ return x >> 1;
+}
+
+function Sar100(x) {
+ return x >> 100;
+}
+
+function Sar1Reversed(x) {
+ return 1 >> x;
+}
+
+function Sar100Reversed(x) {
+ return 100 >> x;
+}
+
+
+assertEquals(0, Shr1(1));
+assertEquals(0, Sar1(1));
+assertEquals(0, Shr1Reversed(2));
+assertEquals(0, Sar1Reversed(2));
+assertEquals(1610612736, Shr1(SMI_MIN));
+assertEquals(-536870912, Sar1(SMI_MIN));
+assertEquals(1, Shr1Reversed(SMI_MIN));
+assertEquals(1, Sar1Reversed(SMI_MIN));
+assertEquals(21, Shr1(OBJ_42));
+assertEquals(21, Sar1(OBJ_42));
+assertEquals(0, Shr1Reversed(OBJ_42));
+assertEquals(0, Sar1Reversed(OBJ_42));
+
+assertEquals(6, Shr100(100));
+assertEquals(6, Sar100(100));
+assertEquals(12, Shr100Reversed(99));
+assertEquals(12, Sar100Reversed(99));
+assertEquals(201326592, Shr100(SMI_MIN));
+assertEquals(-67108864, Sar100(SMI_MIN));
+assertEquals(100, Shr100Reversed(SMI_MIN));
+assertEquals(100, Sar100Reversed(SMI_MIN));
+assertEquals(2, Shr100(OBJ_42));
+assertEquals(2, Sar100(OBJ_42));
+assertEquals(0, Shr100Reversed(OBJ_42));
+assertEquals(0, Sar100Reversed(OBJ_42));
+
+
+function Xor1(x) {
+ return x ^ 1;
+}
+
+function Xor100(x) {
+ return x ^ 100;
+}
+
+function Xor1Reversed(x) {
+ return 1 ^ x;
+}
+
+function Xor100Reversed(x) {
+ return 100 ^ x;
+}
+
+
+assertEquals(0, Xor1(1));
+assertEquals(3, Xor1Reversed(2));
+assertEquals(SMI_MIN + 1, Xor1(SMI_MIN));
+assertEquals(SMI_MIN + 1, Xor1Reversed(SMI_MIN));
+assertEquals(43, Xor1(OBJ_42));
+assertEquals(43, Xor1Reversed(OBJ_42));
+
+assertEquals(0, Xor100(100));
+assertEquals(7, Xor100Reversed(99));
+assertEquals(-1073741724, Xor100(SMI_MIN));
+assertEquals(-1073741724, Xor100Reversed(SMI_MIN));
+assertEquals(78, Xor100(OBJ_42));
+assertEquals(78, Xor100Reversed(OBJ_42));
+
+var x = 0x23; var y = 0x35;
+assertEquals(0x16, x ^ y);
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---