X64 implementation: comparison operations.

Review URL: http://codereview.chromium.org/146082

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2264 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
whesse@chromium.org 2009-06-24 13:46:07 +00:00
parent ecfd1f1017
commit a367522a1a
7 changed files with 664 additions and 37 deletions

View File

@ -509,13 +509,29 @@ void Assembler::immediate_arithmetic_op_8(byte subcode,
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_optional_rex_32(dst);
ASSERT(is_int8(src.value_));
ASSERT(is_int8(src.value_) || is_uint8(src.value_));
emit(0x80);
emit_operand(subcode, dst);
emit(src.value_);
}
void Assembler::immediate_arithmetic_op_8(byte subcode,
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
if (dst.code() > 3) {
// Use 64-bit mode byte registers.
emit_rex_64(dst);
}
ASSERT(is_int8(src.value_) || is_uint8(src.value_));
emit(0x80);
emit_modrm(subcode, dst);
emit(src.value_);
}
void Assembler::shift(Register dst, Immediate shift_amount, int subcode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;

View File

@ -544,6 +544,10 @@ class Assembler : public Malloced {
immediate_arithmetic_op_32(0x0, dst, src);
}
void cmpb(Register dst, Immediate src) {
immediate_arithmetic_op_8(0x7, dst, src);
}
void cmpb(const Operand& dst, Immediate src) {
immediate_arithmetic_op_8(0x7, dst, src);
}
@ -1077,10 +1081,13 @@ class Assembler : public Malloced {
void immediate_arithmetic_op_32(byte subcode,
Register dst,
Immediate src);
// Operate on a byte in memory.
// Operate on a byte in memory or register.
void immediate_arithmetic_op_8(byte subcode,
const Operand& dst,
Immediate src);
void immediate_arithmetic_op_8(byte subcode,
Register dst,
Immediate src);
// Emit machine code for a shift operation.
void shift(Register dst, Immediate shift_amount, int subcode);
// Shift dst by cl % 64 bits.

View File

@ -1604,8 +1604,153 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
void CodeGenerator::VisitCompareOperation(CompareOperation* a) {
UNIMPLEMENTED();
void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
Comment cmnt(masm_, "[ CompareOperation");
// Get the expressions from the node.
Expression* left = node->left();
Expression* right = node->right();
Token::Value op = node->op();
// To make typeof testing for natives implemented in JavaScript really
// efficient, we generate special code for expressions of the form:
// 'typeof <expression> == <string>'.
UnaryOperation* operation = left->AsUnaryOperation();
if ((op == Token::EQ || op == Token::EQ_STRICT) &&
(operation != NULL && operation->op() == Token::TYPEOF) &&
(right->AsLiteral() != NULL &&
right->AsLiteral()->handle()->IsString())) {
Handle<String> check(String::cast(*right->AsLiteral()->handle()));
// Load the operand and move it to a register.
LoadTypeofExpression(operation->expression());
Result answer = frame_->Pop();
answer.ToRegister();
if (check->Equals(Heap::number_symbol())) {
__ testl(answer.reg(), Immediate(kSmiTagMask));
destination()->true_target()->Branch(zero);
frame_->Spill(answer.reg());
__ movq(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
__ Cmp(answer.reg(), Factory::heap_number_map());
answer.Unuse();
destination()->Split(equal);
} else if (check->Equals(Heap::string_symbol())) {
__ testl(answer.reg(), Immediate(kSmiTagMask));
destination()->false_target()->Branch(zero);
// It can be an undetectable string object.
__ movq(kScratchRegister,
FieldOperand(answer.reg(), HeapObject::kMapOffset));
__ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
destination()->false_target()->Branch(not_zero);
__ CmpInstanceType(kScratchRegister, FIRST_NONSTRING_TYPE);
answer.Unuse();
destination()->Split(below); // Unsigned byte comparison needed.
} else if (check->Equals(Heap::boolean_symbol())) {
__ Cmp(answer.reg(), Factory::true_value());
destination()->true_target()->Branch(equal);
__ Cmp(answer.reg(), Factory::false_value());
answer.Unuse();
destination()->Split(equal);
} else if (check->Equals(Heap::undefined_symbol())) {
__ Cmp(answer.reg(), Factory::undefined_value());
destination()->true_target()->Branch(equal);
__ testl(answer.reg(), Immediate(kSmiTagMask));
destination()->false_target()->Branch(zero);
// It can be an undetectable object.
__ movq(kScratchRegister,
FieldOperand(answer.reg(), HeapObject::kMapOffset));
__ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
answer.Unuse();
destination()->Split(not_zero);
} else if (check->Equals(Heap::function_symbol())) {
__ testl(answer.reg(), Immediate(kSmiTagMask));
destination()->false_target()->Branch(zero);
frame_->Spill(answer.reg());
__ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
answer.Unuse();
destination()->Split(equal);
} else if (check->Equals(Heap::object_symbol())) {
__ testl(answer.reg(), Immediate(kSmiTagMask));
destination()->false_target()->Branch(zero);
__ Cmp(answer.reg(), Factory::null_value());
destination()->true_target()->Branch(equal);
// It can be an undetectable object.
__ movq(kScratchRegister,
FieldOperand(answer.reg(), HeapObject::kMapOffset));
__ movb(kScratchRegister,
FieldOperand(kScratchRegister, Map::kBitFieldOffset));
__ testb(kScratchRegister, Immediate(1 << Map::kIsUndetectable));
destination()->false_target()->Branch(not_zero);
__ cmpb(kScratchRegister, Immediate(FIRST_JS_OBJECT_TYPE));
destination()->false_target()->Branch(below);
__ cmpb(kScratchRegister, Immediate(LAST_JS_OBJECT_TYPE));
answer.Unuse();
destination()->Split(below_equal);
} else {
// Uncommon case: typeof testing against a string literal that is
// never returned from the typeof operator.
answer.Unuse();
destination()->Goto(false);
}
return;
}
Condition cc = no_condition;
bool strict = false;
switch (op) {
case Token::EQ_STRICT:
strict = true;
// Fall through
case Token::EQ:
cc = equal;
break;
case Token::LT:
cc = less;
break;
case Token::GT:
cc = greater;
break;
case Token::LTE:
cc = less_equal;
break;
case Token::GTE:
cc = greater_equal;
break;
case Token::IN: {
Load(left);
Load(right);
Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
frame_->Push(&answer); // push the result
return;
}
case Token::INSTANCEOF: {
Load(left);
Load(right);
InstanceofStub stub;
Result answer = frame_->CallStub(&stub, 2);
answer.ToRegister();
__ testq(answer.reg(), answer.reg());
answer.Unuse();
destination()->Split(zero);
return;
}
default:
UNREACHABLE();
}
Load(left);
Load(right);
Comparison(cc, strict, destination());
}
@ -2198,6 +2343,248 @@ void CodeGenerator::LoadGlobalReceiver() {
}
// TODO(1241834): Get rid of this function in favor of just using Load, now
// that we have the INSIDE_TYPEOF typeof state. => Need to handle global
// variables w/o reference errors elsewhere.
void CodeGenerator::LoadTypeofExpression(Expression* x) {
Variable* variable = x->AsVariableProxy()->AsVariable();
if (variable != NULL && !variable->is_this() && variable->is_global()) {
// NOTE: This is somewhat nasty. We force the compiler to load
// the variable as if through '<global>.<variable>' to make sure we
// do not get reference errors.
Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
Literal key(variable->name());
// TODO(1241834): Fetch the position from the variable instead of using
// no position.
Property property(&global, &key, RelocInfo::kNoPosition);
Load(&property);
} else {
Load(x, INSIDE_TYPEOF);
}
}
class CompareStub: public CodeStub {
public:
CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { }
void Generate(MacroAssembler* masm);
private:
Condition cc_;
bool strict_;
Major MajorKey() { return Compare; }
int MinorKey() {
// Encode the three parameters in a unique 16 bit value.
ASSERT(static_cast<int>(cc_) < (1 << 15));
return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0);
}
// Branch to the label if the given object isn't a symbol.
void BranchIfNonSymbol(MacroAssembler* masm,
Label* label,
Register object);
#ifdef DEBUG
void Print() {
PrintF("CompareStub (cc %d), (strict %s)\n",
static_cast<int>(cc_),
strict_ ? "true" : "false");
}
#endif
};
void CodeGenerator::Comparison(Condition cc,
bool strict,
ControlDestination* dest) {
// Strict only makes sense for equality comparisons.
ASSERT(!strict || cc == equal);
Result left_side;
Result right_side;
// Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
if (cc == greater || cc == less_equal) {
cc = ReverseCondition(cc);
left_side = frame_->Pop();
right_side = frame_->Pop();
} else {
right_side = frame_->Pop();
left_side = frame_->Pop();
}
ASSERT(cc == less || cc == equal || cc == greater_equal);
// If either side is a constant smi, optimize the comparison.
bool left_side_constant_smi =
left_side.is_constant() && left_side.handle()->IsSmi();
bool right_side_constant_smi =
right_side.is_constant() && right_side.handle()->IsSmi();
bool left_side_constant_null =
left_side.is_constant() && left_side.handle()->IsNull();
bool right_side_constant_null =
right_side.is_constant() && right_side.handle()->IsNull();
if (left_side_constant_smi || right_side_constant_smi) {
if (left_side_constant_smi && right_side_constant_smi) {
// Trivial case, comparing two constants.
int left_value = Smi::cast(*left_side.handle())->value();
int right_value = Smi::cast(*right_side.handle())->value();
switch (cc) {
case less:
dest->Goto(left_value < right_value);
break;
case equal:
dest->Goto(left_value == right_value);
break;
case greater_equal:
dest->Goto(left_value >= right_value);
break;
default:
UNREACHABLE();
}
} else { // Only one side is a constant Smi.
// If left side is a constant Smi, reverse the operands.
// Since one side is a constant Smi, conversion order does not matter.
if (left_side_constant_smi) {
Result temp = left_side;
left_side = right_side;
right_side = temp;
cc = ReverseCondition(cc);
// This may reintroduce greater or less_equal as the value of cc.
// CompareStub and the inline code both support all values of cc.
}
// Implement comparison against a constant Smi, inlining the case
// where both sides are Smis.
left_side.ToRegister();
// Here we split control flow to the stub call and inlined cases
// before finally splitting it to the control destination. We use
// a jump target and branching to duplicate the virtual frame at
// the first split. We manually handle the off-frame references
// by reconstituting them on the non-fall-through path.
JumpTarget is_smi;
Register left_reg = left_side.reg();
Handle<Object> right_val = right_side.handle();
__ testl(left_side.reg(), Immediate(kSmiTagMask));
is_smi.Branch(zero, taken);
// Setup and call the compare stub.
CompareStub stub(cc, strict);
Result result = frame_->CallStub(&stub, &left_side, &right_side);
result.ToRegister();
__ cmpq(result.reg(), Immediate(0));
result.Unuse();
dest->true_target()->Branch(cc);
dest->false_target()->Jump();
is_smi.Bind();
left_side = Result(left_reg);
right_side = Result(right_val);
// Test smi equality and comparison by signed int comparison.
if (IsUnsafeSmi(right_side.handle())) {
right_side.ToRegister();
__ cmpq(left_side.reg(), right_side.reg());
} else {
__ Cmp(left_side.reg(), right_side.handle());
}
left_side.Unuse();
right_side.Unuse();
dest->Split(cc);
}
} else if (cc == equal &&
(left_side_constant_null || right_side_constant_null)) {
// To make null checks efficient, we check if either the left side or
// the right side is the constant 'null'.
// If so, we optimize the code by inlining a null check instead of
// calling the (very) general runtime routine for checking equality.
Result operand = left_side_constant_null ? right_side : left_side;
right_side.Unuse();
left_side.Unuse();
operand.ToRegister();
__ Cmp(operand.reg(), Factory::null_value());
if (strict) {
operand.Unuse();
dest->Split(equal);
} else {
// The 'null' value is only equal to 'undefined' if using non-strict
// comparisons.
dest->true_target()->Branch(equal);
__ Cmp(operand.reg(), Factory::undefined_value());
dest->true_target()->Branch(equal);
__ testl(operand.reg(), Immediate(kSmiTagMask));
dest->false_target()->Branch(equal);
// It can be an undetectable object.
// Use a scratch register in preference to spilling operand.reg().
Result temp = allocator()->Allocate();
ASSERT(temp.is_valid());
__ movq(temp.reg(),
FieldOperand(operand.reg(), HeapObject::kMapOffset));
__ testb(FieldOperand(temp.reg(), Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
temp.Unuse();
operand.Unuse();
dest->Split(not_zero);
}
} else { // Neither side is a constant Smi or null.
// If either side is a non-smi constant, skip the smi check.
bool known_non_smi =
(left_side.is_constant() && !left_side.handle()->IsSmi()) ||
(right_side.is_constant() && !right_side.handle()->IsSmi());
left_side.ToRegister();
right_side.ToRegister();
if (known_non_smi) {
// When non-smi, call out to the compare stub.
CompareStub stub(cc, strict);
Result answer = frame_->CallStub(&stub, &left_side, &right_side);
if (cc == equal) {
__ testq(answer.reg(), answer.reg());
} else {
__ cmpq(answer.reg(), Immediate(0));
}
answer.Unuse();
dest->Split(cc);
} else {
// Here we split control flow to the stub call and inlined cases
// before finally splitting it to the control destination. We use
// a jump target and branching to duplicate the virtual frame at
// the first split. We manually handle the off-frame references
// by reconstituting them on the non-fall-through path.
JumpTarget is_smi;
Register left_reg = left_side.reg();
Register right_reg = right_side.reg();
__ movq(kScratchRegister, left_side.reg());
__ or_(kScratchRegister, right_side.reg());
__ testl(kScratchRegister, Immediate(kSmiTagMask));
is_smi.Branch(zero, taken);
// When non-smi, call out to the compare stub.
CompareStub stub(cc, strict);
Result answer = frame_->CallStub(&stub, &left_side, &right_side);
if (cc == equal) {
__ testq(answer.reg(), answer.reg());
} else {
__ cmpq(answer.reg(), Immediate(0));
}
answer.Unuse();
dest->true_target()->Branch(cc);
dest->false_target()->Jump();
is_smi.Bind();
left_side = Result(left_reg);
right_side = Result(right_reg);
__ cmpq(left_side.reg(), right_side.reg());
right_side.Unuse();
left_side.Unuse();
dest->Split(cc);
}
}
}
// Flag that indicates whether or not the code that handles smi arguments
// should be placed in the stub, inlined, or omitted entirely.
enum GenericBinaryFlags {
@ -3313,35 +3700,212 @@ void UnarySubStub::Generate(MacroAssembler* masm) {
UNIMPLEMENTED();
}
class CompareStub: public CodeStub {
public:
CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { }
void Generate(MacroAssembler* masm);
private:
Condition cc_;
bool strict_;
Major MajorKey() { return Compare; }
int MinorKey() {
// Encode the three parameters in a unique 16 bit value.
ASSERT(static_cast<int>(cc_) < (1 << 15));
return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0);
}
#ifdef DEBUG
void Print() {
PrintF("CompareStub (cc %d), (strict %s)\n",
static_cast<int>(cc_),
strict_ ? "true" : "false");
}
#endif
};
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;
// NOTICE! This code is only reached after a smi-fast-case check, so
// it is certain that at least one operand isn't a smi.
if (cc_ == equal) { // Both strict and non-strict.
Label slow; // Fallthrough label.
// Equality is almost reflexive (everything but NaN), so start by testing
// for "identity and not NaN".
{
Label not_identical;
__ cmpq(rax, rdx);
__ j(not_equal, &not_identical);
// Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
// so we do the second best thing - test it ourselves.
Label return_equal;
Label heap_number;
// If it's not a heap number, then return equal.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
Factory::heap_number_map());
__ j(equal, &heap_number);
__ bind(&return_equal);
__ xor_(rax, rax);
__ ret(0);
__ bind(&heap_number);
// It is a heap number, so return non-equal if it's NaN and equal if it's
// not NaN.
// The representation of NaN values has all exponent bits (52..62) set,
// and not all mantissa bits (0..51) clear.
// Read double representation into rax.
__ movq(rbx, 0x7ff0000000000000, RelocInfo::NONE);
__ movq(rax, FieldOperand(rdx, HeapNumber::kValueOffset));
// Test that exponent bits are all set.
__ or_(rbx, rax);
__ cmpq(rbx, rax);
__ j(not_equal, &return_equal);
// Shift out flag and all exponent bits, retaining only mantissa.
__ shl(rax, Immediate(12));
// If all bits in the mantissa are zero the number is Infinity, and
// we return zero. Otherwise it is a NaN, and we return non-zero.
// So just return rax.
__ ret(0);
__ bind(&not_identical);
}
// If we're doing a strict equality comparison, we don't have to do
// type conversion, so we generate code to do fast comparison for objects
// and oddballs. Non-smi numbers and strings still go through the usual
// slow-case code.
if (strict_) {
// If either is a Smi (we know that not both are), then they can only
// be equal if the other is a HeapNumber. If so, use the slow case.
{
Label not_smis;
ASSERT_EQ(0, kSmiTag);
ASSERT_EQ(0, Smi::FromInt(0));
__ movq(rcx, Immediate(kSmiTagMask));
__ and_(rcx, rax);
__ testq(rcx, rdx);
__ j(not_zero, &not_smis);
// One operand is a smi.
// Check whether the non-smi is a heap number.
ASSERT_EQ(1, kSmiTagMask);
// rcx still holds rax & kSmiTag, which is either zero or one.
__ decq(rcx); // If rax is a smi, all 1s, else all 0s.
__ movq(rbx, rdx);
__ xor_(rbx, rax);
__ and_(rbx, rcx); // rbx holds either 0 or rax ^ rdx.
__ xor_(rbx, rax);
// if rax was smi, rbx is now rdx, else rax.
// Check if the non-smi operand is a heap number.
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
Factory::heap_number_map());
// If heap number, handle it in the slow case.
__ j(equal, &slow);
// Return non-equal (ebx is not zero)
__ movq(rax, rbx);
__ ret(0);
__ bind(&not_smis);
}
// If either operand is a JSObject or an oddball value, then they are not
// equal since their pointers are different
// There is no test for undetectability in strict equality.
// If the first object is a JS object, we have done pointer comparison.
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
Label first_non_object;
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
__ j(below, &first_non_object);
// Return non-zero (rax is not zero)
Label return_not_equal;
ASSERT(kHeapObjectTag != 0);
__ bind(&return_not_equal);
__ ret(0);
__ bind(&first_non_object);
// Check for oddballs: true, false, null, undefined.
__ CmpInstanceType(rcx, ODDBALL_TYPE);
__ j(equal, &return_not_equal);
__ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
__ j(above_equal, &return_not_equal);
// Check for oddballs: true, false, null, undefined.
__ CmpInstanceType(rcx, ODDBALL_TYPE);
__ j(equal, &return_not_equal);
// Fall through to the general case.
}
__ bind(&slow);
}
// Push arguments below the return address.
__ pop(rcx);
__ push(rax);
__ push(rdx);
__ push(rcx);
// Inlined floating point compare.
// Call builtin if operands are not floating point or smi.
Label check_for_symbols;
// TODO(X64): Implement floating point comparisons;
__ int3();
// TODO(1243847): Use cmov below once CpuFeatures are properly hooked up.
Label below_lbl, above_lbl;
// use edx, eax to convert unsigned to signed comparison
__ j(below, &below_lbl);
__ j(above, &above_lbl);
__ xor_(rax, rax); // equal
__ ret(2 * kPointerSize);
__ bind(&below_lbl);
__ movq(rax, Immediate(-1));
__ ret(2 * kPointerSize);
__ bind(&above_lbl);
__ movq(rax, Immediate(1));
__ ret(2 * kPointerSize); // eax, edx were pushed
// Fast negative check for symbol-to-symbol equality.
__ bind(&check_for_symbols);
if (cc_ == equal) {
BranchIfNonSymbol(masm, &call_builtin, rax);
BranchIfNonSymbol(masm, &call_builtin, rdx);
// We've already checked for object identity, so if both operands
// are symbols they aren't equal. Register eax already holds a
// non-zero value, which indicates not equal, so just return.
__ ret(2 * kPointerSize);
}
__ bind(&call_builtin);
// must swap argument order
__ pop(rcx);
__ pop(rdx);
__ pop(rax);
__ push(rdx);
__ push(rax);
// Figure out which native to call and setup the arguments.
Builtins::JavaScript builtin;
if (cc_ == equal) {
builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
builtin = Builtins::COMPARE;
int ncr; // NaN compare result
if (cc_ == less || cc_ == less_equal) {
ncr = GREATER;
} else {
ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
ncr = LESS;
}
__ push(Immediate(Smi::FromInt(ncr)));
}
// Restore return address on the stack.
__ push(rcx);
// Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
__ InvokeBuiltin(builtin, JUMP_FUNCTION);
}
void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
Label* label,
Register object) {
__ testl(object, Immediate(kSmiTagMask));
__ j(zero, label);
__ movq(kScratchRegister, FieldOperand(object, HeapObject::kMapOffset));
__ movzxbq(kScratchRegister,
FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
__ and_(kScratchRegister, Immediate(kIsSymbolMask | kIsNotStringMask));
__ cmpb(kScratchRegister, Immediate(kSymbolTag | kStringTag));
__ j(not_equal, label);
}

View File

@ -309,6 +309,12 @@ void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
}
void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
Move(kScratchRegister, source);
cmpq(dst, kScratchRegister);
}
void MacroAssembler::Push(Handle<Object> source) {
Move(kScratchRegister, source);
push(kScratchRegister);

View File

@ -172,6 +172,7 @@ class MacroAssembler: public Assembler {
void Move(Register dst, Handle<Object> source);
void Move(const Operand& dst, Handle<Object> source);
void Cmp(Register dst, Handle<Object> source);
void Cmp(const Operand& dst, Handle<Object> source);
void Push(Handle<Object> source);
// Control Flow

View File

@ -702,6 +702,39 @@ Result VirtualFrame::RawCallStub(CodeStub* stub) {
}
Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) {
PrepareForCall(0, 0);
arg->ToRegister(rax);
arg->Unuse();
return RawCallStub(stub);
}
Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) {
PrepareForCall(0, 0);
if (arg0->is_register() && arg0->reg().is(rax)) {
if (arg1->is_register() && arg1->reg().is(rdx)) {
// Wrong registers.
__ xchg(rax, rdx);
} else {
// Register rdx is free for arg0, which frees rax for arg1.
arg0->ToRegister(rdx);
arg1->ToRegister(rax);
}
} else {
// Register rax is free for arg1, which guarantees rdx is free for
// arg0.
arg1->ToRegister(rax);
arg0->ToRegister(rdx);
}
arg0->Unuse();
arg1->Unuse();
return RawCallStub(stub);
}
void VirtualFrame::SyncElementBelowStackPointer(int index) {
// Emit code to write elements below the stack pointer to their
// (already allocated) stack address.

View File

@ -307,8 +307,8 @@ class VirtualFrame : public ZoneObject {
// even a register. The argument is consumed by the call.
Result CallStub(CodeStub* stub, Result* arg);
// Call stub that takes a pair of arguments passed in edx (arg0) and
// eax (arg1). The arguments are given as results which do not have
// Call stub that takes a pair of arguments passed in edx (arg0, rdx) and
// eax (arg1, rax). The arguments are given as results which do not have
// to be in the proper registers or even in registers. The
// arguments are consumed by the call.
Result CallStub(CodeStub* stub, Result* arg0, Result* arg1);