| Index: src/codegen-ia32.cc
|
| ===================================================================
|
| --- src/codegen-ia32.cc (revision 408)
|
| +++ src/codegen-ia32.cc (working copy)
|
| @@ -732,69 +732,6 @@
|
| }
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| -Operand Ia32CodeGenerator::SlotOperand(CodeGenerator* cgen,
|
| - Slot* slot,
|
| - Register tmp) {
|
| - // Currently, this assertion will fail if we try to assign to
|
| - // a constant variable that is constant because it is read-only
|
| - // (such as the variable referring to a named function expression).
|
| - // We need to implement assignments to read-only variables.
|
| - // Ideally, we should do this during AST generation (by converting
|
| - // such assignments into expression statements); however, in general
|
| - // we may not be able to make the decision until past AST generation,
|
| - // that is when the entire program is known.
|
| - ASSERT(slot != NULL);
|
| - int index = slot->index();
|
| - switch (slot->type()) {
|
| - case Slot::PARAMETER: return ParameterOperand(cgen, index);
|
| -
|
| - case Slot::LOCAL: {
|
| - ASSERT(0 <= index && index < cgen->scope()->num_stack_slots());
|
| - const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
| - return Operand(ebp, kLocal0Offset - index * kPointerSize);
|
| - }
|
| -
|
| - case Slot::CONTEXT: {
|
| - MacroAssembler* masm = cgen->masm();
|
| - // Follow the context chain if necessary.
|
| - ASSERT(!tmp.is(esi)); // do not overwrite context register
|
| - Register context = esi;
|
| - int chain_length =
|
| - cgen->scope()->ContextChainLength(slot->var()->scope());
|
| - for (int i = chain_length; i-- > 0;) {
|
| - // Load the closure.
|
| - // (All contexts, even 'with' contexts, have a closure,
|
| - // and it is the same for all contexts inside a function.
|
| - // There is no need to go to the function context first.)
|
| - __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
|
| - // Load the function context (which is the incoming, outer context).
|
| - __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
|
| - context = tmp;
|
| - }
|
| - // We may have a 'with' context now. Get the function context.
|
| - // (In fact this mov may never be the needed, since the scope analysis
|
| - // may not permit a direct context access in this case and thus we are
|
| - // always at a function context. However it is safe to dereference be-
|
| - // cause the function context of a function context is itself. Before
|
| - // deleting this mov we should try to create a counter-example first,
|
| - // though...)
|
| - __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
|
| - return ContextOperand(tmp, index);
|
| - }
|
| -
|
| - default:
|
| - UNREACHABLE();
|
| - return Operand(eax);
|
| - }
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| // Loads a value on TOS. If it is a boolean value, the result may have been
|
| // (partially) translated into branches, or it may have set the condition code
|
| // register. If force_cc is set, the value is forced to set the condition code
|
| @@ -959,115 +896,6 @@
|
| }
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| -void Property::GenerateStoreCode(CodeGenerator* cgen,
|
| - Reference* ref,
|
| - InitState init_state) {
|
| - MacroAssembler* masm = cgen->masm();
|
| - Comment cmnt(masm, "[ Store to Property");
|
| - __ RecordPosition(position());
|
| - Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key());
|
| -}
|
| -
|
| -
|
| -void VariableProxy::GenerateStoreCode(CodeGenerator* cgen,
|
| - Reference* ref,
|
| - InitState init_state) {
|
| - MacroAssembler* masm = cgen->masm();
|
| - Comment cmnt(masm, "[ Store to VariableProxy");
|
| - Variable* node = var();
|
| -
|
| - Expression* expr = node->rewrite();
|
| - if (expr != NULL) {
|
| - expr->GenerateStoreCode(cgen, ref, init_state);
|
| - } else {
|
| - ASSERT(node->is_global());
|
| - if (node->AsProperty() != NULL) {
|
| - __ RecordPosition(node->AsProperty()->position());
|
| - }
|
| - Expression* key = new Literal(node->name());
|
| - Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key);
|
| - }
|
| -}
|
| -
|
| -
|
| -void Slot::GenerateStoreCode(CodeGenerator* cgen,
|
| - Reference* ref,
|
| - InitState init_state) {
|
| - MacroAssembler* masm = cgen->masm();
|
| - Comment cmnt(masm, "[ Store to Slot");
|
| -
|
| - if (type() == Slot::LOOKUP) {
|
| - ASSERT(var()->mode() == Variable::DYNAMIC);
|
| -
|
| - // For now, just do a runtime call.
|
| - __ push(esi);
|
| - __ push(Immediate(var()->name()));
|
| -
|
| - if (init_state == CONST_INIT) {
|
| - // Same as the case for a normal store, but ignores attribute
|
| - // (e.g. READ_ONLY) of context slot so that we can initialize const
|
| - // properties (introduced via eval("const foo = (some expr);")). Also,
|
| - // uses the current function context instead of the top context.
|
| - //
|
| - // Note that we must declare the foo upon entry of eval(), via a
|
| - // context slot declaration, but we cannot initialize it at the same
|
| - // time, because the const declaration may be at the end of the eval
|
| - // code (sigh...) and the const variable may have been used before
|
| - // (where its value is 'undefined'). Thus, we can only do the
|
| - // initialization when we actually encounter the expression and when
|
| - // the expression operands are defined and valid, and thus we need the
|
| - // split into 2 operations: declaration of the context slot followed
|
| - // by initialization.
|
| - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
| - } else {
|
| - __ CallRuntime(Runtime::kStoreContextSlot, 3);
|
| - }
|
| - // Storing a variable must keep the (new) value on the expression
|
| - // stack. This is necessary for compiling assignment expressions.
|
| - __ push(eax);
|
| -
|
| - } else {
|
| - ASSERT(var()->mode() != Variable::DYNAMIC);
|
| -
|
| - Label exit;
|
| - if (init_state == CONST_INIT) {
|
| - ASSERT(var()->mode() == Variable::CONST);
|
| - // Only the first const initialization must be executed (the slot
|
| - // still contains 'the hole' value). When the assignment is executed,
|
| - // the code is identical to a normal store (see below).
|
| - Comment cmnt(masm, "[ Init const");
|
| - __ mov(eax, Ia32CodeGenerator::SlotOperand(cgen, this, ecx));
|
| - __ cmp(eax, Factory::the_hole_value());
|
| - __ j(not_equal, &exit);
|
| - }
|
| -
|
| - // We must execute the store.
|
| - // Storing a variable must keep the (new) value on the stack. This is
|
| - // necessary for compiling assignment expressions. ecx may be loaded
|
| - // with context; used below in RecordWrite.
|
| - //
|
| - // Note: We will reach here even with node->var()->mode() ==
|
| - // Variable::CONST because of const declarations which will initialize
|
| - // consts to 'the hole' value and by doing so, end up calling this
|
| - // code.
|
| - __ pop(eax);
|
| - __ mov(Ia32CodeGenerator::SlotOperand(cgen, this, ecx), eax);
|
| - __ push(eax); // RecordWrite may destroy the value in eax.
|
| - if (type() == Slot::CONTEXT) {
|
| - // ecx is loaded with context when calling SlotOperand above.
|
| - int offset = FixedArray::kHeaderSize + index() * kPointerSize;
|
| - __ RecordWrite(ecx, offset, eax, ebx);
|
| - }
|
| - // If we definitely did not jump over the assignment, we do not need to
|
| - // bind the exit label. Doing so can defeat peephole optimization.
|
| - if (init_state == CONST_INIT) __ bind(&exit);
|
| - }
|
| -}
|
| -
|
| -
|
| class ToBooleanStub: public CodeStub {
|
| public:
|
| ToBooleanStub() { }
|
| @@ -1090,66 +918,6 @@
|
| };
|
|
|
|
|
| -// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
|
| -void ToBooleanStub::Generate(MacroAssembler* masm) {
|
| - Label false_result, true_result, not_string;
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| -
|
| - // 'null' => false.
|
| - __ cmp(eax, Factory::null_value());
|
| - __ j(equal, &false_result);
|
| -
|
| - // Get the map and type of the heap object.
|
| - __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
|
| - __ movzx_b(ecx, FieldOperand(edx, Map::kInstanceTypeOffset));
|
| -
|
| - // Undetectable => false.
|
| - __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
|
| - __ and_(ebx, 1 << Map::kIsUndetectable);
|
| - __ j(not_zero, &false_result);
|
| -
|
| - // JavaScript object => true.
|
| - __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
|
| - __ j(above_equal, &true_result);
|
| -
|
| - // String value => false iff empty.
|
| - __ cmp(ecx, FIRST_NONSTRING_TYPE);
|
| - __ j(above_equal, ¬_string);
|
| - __ and_(ecx, kStringSizeMask);
|
| - __ cmp(ecx, kShortStringTag);
|
| - __ j(not_equal, &true_result); // Empty string is always short.
|
| - __ mov(edx, FieldOperand(eax, String::kLengthOffset));
|
| - __ shr(edx, String::kShortLengthShift);
|
| - __ j(zero, &false_result);
|
| - __ jmp(&true_result);
|
| -
|
| - __ bind(¬_string);
|
| - // HeapNumber => false iff +0, -0, or NaN.
|
| - __ cmp(edx, Factory::heap_number_map());
|
| - __ j(not_equal, &true_result);
|
| - __ fldz();
|
| - __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| - __ fucompp();
|
| - __ push(eax);
|
| - __ fnstsw_ax();
|
| - __ sahf();
|
| - __ pop(eax);
|
| - __ j(zero, &false_result);
|
| - // Fall through to |true_result|.
|
| -
|
| - // Return 1/0 for true/false in eax.
|
| - __ bind(&true_result);
|
| - __ mov(eax, 1);
|
| - __ ret(1 * kPointerSize);
|
| - __ bind(&false_result);
|
| - __ mov(eax, 0);
|
| - __ ret(1 * kPointerSize);
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| // ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
|
| // convert it to a boolean in the condition code register or jump to
|
| // 'false_target'/'true_target' as appropriate.
|
| @@ -1234,41 +1002,6 @@
|
| }
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| -void Ia32CodeGenerator::SetReferenceProperty(CodeGenerator* cgen,
|
| - Reference* ref,
|
| - Expression* key) {
|
| - ASSERT(!ref->is_illegal());
|
| - MacroAssembler* masm = cgen->masm();
|
| -
|
| - if (ref->type() == Reference::NAMED) {
|
| - // Compute the name of the property.
|
| - Literal* literal = key->AsLiteral();
|
| - Handle<String> name(String::cast(*literal->handle()));
|
| -
|
| - // Call the appropriate IC code.
|
| - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| - // TODO(1222589): Make the IC grab the values from the stack.
|
| - __ pop(eax);
|
| - // Setup the name register.
|
| - __ Set(ecx, Immediate(name));
|
| - __ call(ic, RelocInfo::CODE_TARGET);
|
| - } else {
|
| - // Access keyed property.
|
| - ASSERT(ref->type() == Reference::KEYED);
|
| -
|
| - // Call IC code.
|
| - Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| - // TODO(1222589): Make the IC grab the values from the stack.
|
| - __ pop(eax);
|
| - __ call(ic, RelocInfo::CODE_TARGET);
|
| - }
|
| - __ push(eax); // IC call leaves result in eax, push it out
|
| -}
|
| -
|
| -
|
| class FloatingPointHelper : public AllStatic {
|
| public:
|
| // Code pattern for loading floating point values. Input values must
|
| @@ -1341,499 +1074,6 @@
|
| }
|
|
|
|
|
| -void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
| - Label call_runtime;
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize)); // Get y.
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize)); // Get x.
|
| -
|
| - // 1. Smi case.
|
| - switch (op_) {
|
| - case Token::ADD: {
|
| - // eax: y.
|
| - // edx: x.
|
| - Label revert;
|
| - __ mov(ecx, Operand(eax));
|
| - __ or_(ecx, Operand(edx)); // ecx = x | y.
|
| - __ add(eax, Operand(edx)); // Add y optimistically.
|
| - // Go slow-path in case of overflow.
|
| - __ j(overflow, &revert, not_taken);
|
| - // Go slow-path in case of non-smi operands.
|
| - ASSERT(kSmiTag == 0); // adjust code below
|
| - __ test(ecx, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &revert, not_taken);
|
| - __ ret(2 * kPointerSize); // Remove all operands.
|
| -
|
| - // Revert optimistic add.
|
| - __ bind(&revert);
|
| - __ sub(eax, Operand(edx));
|
| - break;
|
| - }
|
| - case Token::SUB: {
|
| - // eax: y.
|
| - // edx: x.
|
| - Label revert;
|
| - __ mov(ecx, Operand(edx));
|
| - __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| - __ sub(edx, Operand(eax)); // Subtract y optimistically.
|
| - // Go slow-path in case of overflow.
|
| - __ j(overflow, &revert, not_taken);
|
| - // Go slow-path in case of non-smi operands.
|
| - ASSERT(kSmiTag == 0); // adjust code below
|
| - __ test(ecx, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &revert, not_taken);
|
| - __ mov(eax, Operand(edx));
|
| - __ ret(2 * kPointerSize); // Remove all operands.
|
| -
|
| - // Revert optimistic sub.
|
| - __ bind(&revert);
|
| - __ add(edx, Operand(eax));
|
| - break;
|
| - }
|
| - case Token::MUL: {
|
| - // eax: y
|
| - // edx: x
|
| - // a) both operands smi and result fits into a smi -> return.
|
| - // b) at least one of operands non-smi -> non_smi_operands.
|
| - // c) result does not fit in a smi -> non_smi_result.
|
| - Label non_smi_operands, non_smi_result;
|
| - // Tag check.
|
| - __ mov(ecx, Operand(edx));
|
| - __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| - ASSERT(kSmiTag == 0); // Adjust code below.
|
| - __ test(ecx, Immediate(kSmiTagMask));
|
| - // Jump if not both smi; check if float numbers.
|
| - __ j(not_zero, &non_smi_operands, not_taken);
|
| -
|
| - // Get copies of operands.
|
| - __ mov(ebx, Operand(eax));
|
| - __ mov(ecx, Operand(edx));
|
| - // If the smi tag is 0 we can just leave the tag on one operand.
|
| - ASSERT(kSmiTag == 0); // adjust code below
|
| - // Remove tag from one of the operands (but keep sign).
|
| - __ sar(ecx, kSmiTagSize);
|
| - // Do multiplication.
|
| - __ imul(eax, Operand(ecx)); // Multiplication of Smis; result in eax.
|
| - // Go slow on overflows.
|
| - __ j(overflow, &non_smi_result, not_taken);
|
| - // ...but operands OK for float arithmetic.
|
| -
|
| - // If the result is +0 we may need to check if the result should
|
| - // really be -0. Welcome to the -0 fan club.
|
| - __ NegativeZeroTest(eax, ebx, edx, ecx, &non_smi_result);
|
| -
|
| - __ ret(2 * kPointerSize);
|
| -
|
| - __ bind(&non_smi_result);
|
| - // TODO(1243132): Do not check float operands here.
|
| - __ bind(&non_smi_operands);
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| - break;
|
| - }
|
| - case Token::DIV: {
|
| - // eax: y
|
| - // edx: x
|
| - Label non_smi_operands, non_smi_result, division_by_zero;
|
| - __ mov(ebx, Operand(eax)); // Get y
|
| - __ mov(eax, Operand(edx)); // Get x
|
| -
|
| - __ cdq(); // Sign extend eax into edx:eax.
|
| - // Tag check.
|
| - __ mov(ecx, Operand(ebx));
|
| - __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| - ASSERT(kSmiTag == 0); // Adjust code below.
|
| - __ test(ecx, Immediate(kSmiTagMask));
|
| - // Jump if not both smi; check if float numbers.
|
| - __ j(not_zero, &non_smi_operands, not_taken);
|
| - __ test(ebx, Operand(ebx)); // Check for 0 divisor.
|
| - __ j(zero, &division_by_zero, not_taken);
|
| -
|
| - __ idiv(ebx);
|
| - // Check for the corner case of dividing the most negative smi by -1.
|
| - // (We cannot use the overflow flag, since it is not set by idiv.)
|
| - ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
|
| - __ cmp(eax, 0x40000000);
|
| - __ j(equal, &non_smi_result);
|
| - // If the result is +0 we may need to check if the result should
|
| - // really be -0. Welcome to the -0 fan club.
|
| - __ NegativeZeroTest(eax, ecx, &non_smi_result); // Use ecx = x | y.
|
| - __ test(edx, Operand(edx));
|
| - // Use floats if there's a remainder.
|
| - __ j(not_zero, &non_smi_result, not_taken);
|
| - __ shl(eax, kSmiTagSize);
|
| - __ ret(2 * kPointerSize); // Remove all operands.
|
| -
|
| - __ bind(&division_by_zero);
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| - __ jmp(&call_runtime); // Division by zero must go through runtime.
|
| -
|
| - __ bind(&non_smi_result);
|
| - // TODO(1243132): Do not check float operands here.
|
| - __ bind(&non_smi_operands);
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| - break;
|
| - }
|
| - case Token::MOD: {
|
| - Label slow;
|
| - __ mov(ebx, Operand(eax)); // get y
|
| - __ mov(eax, Operand(edx)); // get x
|
| - __ cdq(); // sign extend eax into edx:eax
|
| - // tag check
|
| - __ mov(ecx, Operand(ebx));
|
| - __ or_(ecx, Operand(eax)); // ecx = x | y;
|
| - ASSERT(kSmiTag == 0); // adjust code below
|
| - __ test(ecx, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &slow, not_taken);
|
| - __ test(ebx, Operand(ebx)); // test for y == 0
|
| - __ j(zero, &slow);
|
| -
|
| - // Fast case: Do integer division and use remainder.
|
| - __ idiv(ebx);
|
| - __ NegativeZeroTest(edx, ecx, &slow); // use ecx = x | y
|
| - __ mov(eax, Operand(edx));
|
| - __ ret(2 * kPointerSize);
|
| -
|
| - // Slow case: Call runtime operator implementation.
|
| - __ bind(&slow);
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| - // Fall through to |call_runtime|.
|
| - break;
|
| - }
|
| - case Token::BIT_OR:
|
| - case Token::BIT_AND:
|
| - case Token::BIT_XOR:
|
| - case Token::SAR:
|
| - case Token::SHL:
|
| - case Token::SHR: {
|
| - // Smi-case for bitops should already have been inlined.
|
| - break;
|
| - }
|
| - default: {
|
| - UNREACHABLE();
|
| - }
|
| - }
|
| -
|
| - // 2. Floating point case.
|
| - switch (op_) {
|
| - case Token::ADD:
|
| - case Token::SUB:
|
| - case Token::MUL:
|
| - case Token::DIV: {
|
| - // eax: y
|
| - // edx: x
|
| - FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
|
| - // Fast-case: Both operands are numbers.
|
| - // Allocate a heap number, if needed.
|
| - Label skip_allocation;
|
| - switch (mode_) {
|
| - case OVERWRITE_LEFT:
|
| - __ mov(eax, Operand(edx));
|
| - // Fall through!
|
| - case OVERWRITE_RIGHT:
|
| - // If the argument in eax is already an object, we skip the
|
| - // allocation of a heap number.
|
| - __ test(eax, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &skip_allocation, not_taken);
|
| - // Fall through!
|
| - case NO_OVERWRITE:
|
| - FloatingPointHelper::AllocateHeapNumber(masm,
|
| - &call_runtime,
|
| - ecx,
|
| - edx);
|
| - __ bind(&skip_allocation);
|
| - break;
|
| - default: UNREACHABLE();
|
| - }
|
| - FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| -
|
| - switch (op_) {
|
| - case Token::ADD: __ faddp(1); break;
|
| - case Token::SUB: __ fsubp(1); break;
|
| - case Token::MUL: __ fmulp(1); break;
|
| - case Token::DIV: __ fdivp(1); break;
|
| - default: UNREACHABLE();
|
| - }
|
| - __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| - __ ret(2 * kPointerSize);
|
| - }
|
| - case Token::MOD: {
|
| - // For MOD we go directly to runtime in the non-smi case.
|
| - break;
|
| - }
|
| - case Token::BIT_OR:
|
| - case Token::BIT_AND:
|
| - case Token::BIT_XOR:
|
| - case Token::SAR:
|
| - case Token::SHL:
|
| - case Token::SHR: {
|
| - FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
|
| - FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| -
|
| - Label non_int32_operands, non_smi_result, skip_allocation;
|
| - // Reserve space for converted numbers.
|
| - __ sub(Operand(esp), Immediate(2 * kPointerSize));
|
| -
|
| - // Check if right operand is int32.
|
| - __ fist_s(Operand(esp, 1 * kPointerSize));
|
| - __ fild_s(Operand(esp, 1 * kPointerSize));
|
| - __ fucompp();
|
| - __ fnstsw_ax();
|
| - __ sahf();
|
| - __ j(not_zero, &non_int32_operands);
|
| - __ j(parity_even, &non_int32_operands);
|
| -
|
| - // Check if left operand is int32.
|
| - __ fist_s(Operand(esp, 0 * kPointerSize));
|
| - __ fild_s(Operand(esp, 0 * kPointerSize));
|
| - __ fucompp();
|
| - __ fnstsw_ax();
|
| - __ sahf();
|
| - __ j(not_zero, &non_int32_operands);
|
| - __ j(parity_even, &non_int32_operands);
|
| -
|
| - // Get int32 operands and perform bitop.
|
| - __ pop(eax);
|
| - __ pop(ecx);
|
| - switch (op_) {
|
| - case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
|
| - case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
|
| - case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
|
| - case Token::SAR: __ sar(eax); break;
|
| - case Token::SHL: __ shl(eax); break;
|
| - case Token::SHR: __ shr(eax); break;
|
| - default: UNREACHABLE();
|
| - }
|
| -
|
| - // Check if result is non-negative and fits in a smi.
|
| - __ test(eax, Immediate(0xc0000000));
|
| - __ j(not_zero, &non_smi_result);
|
| -
|
| - // Tag smi result and return.
|
| - ASSERT(kSmiTagSize == times_2); // adjust code if not the case
|
| - __ lea(eax, Operand(eax, times_2, kSmiTag));
|
| - __ ret(2 * kPointerSize);
|
| -
|
| - // All ops except SHR return a signed int32 that we load in a HeapNumber.
|
| - if (op_ != Token::SHR) {
|
| - __ bind(&non_smi_result);
|
| - // Allocate a heap number if needed.
|
| - __ mov(ebx, Operand(eax)); // ebx: result
|
| - switch (mode_) {
|
| - case OVERWRITE_LEFT:
|
| - case OVERWRITE_RIGHT:
|
| - // If the operand was an object, we skip the
|
| - // allocation of a heap number.
|
| - __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
|
| - 1 * kPointerSize : 2 * kPointerSize));
|
| - __ test(eax, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &skip_allocation, not_taken);
|
| - // Fall through!
|
| - case NO_OVERWRITE:
|
| - FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime,
|
| - ecx, edx);
|
| - __ bind(&skip_allocation);
|
| - break;
|
| - default: UNREACHABLE();
|
| - }
|
| - // Store the result in the HeapNumber and return.
|
| - __ mov(Operand(esp, 1 * kPointerSize), ebx);
|
| - __ fild_s(Operand(esp, 1 * kPointerSize));
|
| - __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| - __ ret(2 * kPointerSize);
|
| - }
|
| - __ bind(&non_int32_operands);
|
| - // Restore stacks and operands before calling runtime.
|
| - __ ffree(0);
|
| - __ add(Operand(esp), Immediate(2 * kPointerSize));
|
| -
|
| - // SHR should return uint32 - go to runtime for non-smi/negative result.
|
| - if (op_ == Token::SHR) __ bind(&non_smi_result);
|
| - __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| - __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| - break;
|
| - }
|
| - default: UNREACHABLE(); break;
|
| - }
|
| -
|
| - // 3. If all else fails, use the runtime system to get the correct result.
|
| - __ bind(&call_runtime);
|
| - switch (op_) {
|
| - case Token::ADD:
|
| - __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
|
| - break;
|
| - case Token::SUB:
|
| - __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
|
| - break;
|
| - case Token::MUL:
|
| - __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
|
| - break;
|
| - case Token::DIV:
|
| - __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
|
| - break;
|
| - case Token::MOD:
|
| - __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
|
| - break;
|
| - case Token::BIT_OR:
|
| - __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
|
| - break;
|
| - case Token::BIT_AND:
|
| - __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
|
| - break;
|
| - case Token::BIT_XOR:
|
| - __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
|
| - break;
|
| - case Token::SAR:
|
| - __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
|
| - break;
|
| - case Token::SHL:
|
| - __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
|
| - break;
|
| - case Token::SHR:
|
| - __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
|
| - break;
|
| - default:
|
| - UNREACHABLE();
|
| - }
|
| -}
|
| -
|
| -
|
| -void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
|
| - Label* need_gc,
|
| - Register scratch1,
|
| - Register scratch2) {
|
| - ExternalReference allocation_top =
|
| - ExternalReference::new_space_allocation_top_address();
|
| - ExternalReference allocation_limit =
|
| - ExternalReference::new_space_allocation_limit_address();
|
| - __ mov(Operand(scratch1), Immediate(allocation_top));
|
| - __ mov(eax, Operand(scratch1, 0));
|
| - __ lea(scratch2, Operand(eax, HeapNumber::kSize)); // scratch2: new top
|
| - __ cmp(scratch2, Operand::StaticVariable(allocation_limit));
|
| - __ j(above, need_gc, not_taken);
|
| -
|
| - __ mov(Operand(scratch1, 0), scratch2); // store new top
|
| - __ mov(Operand(eax, HeapObject::kMapOffset),
|
| - Immediate(Factory::heap_number_map()));
|
| - // Tag old top and use as result.
|
| - __ add(Operand(eax), Immediate(kHeapObjectTag));
|
| -}
|
| -
|
| -
|
| -void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
|
| - Register scratch) {
|
| - Label load_smi_1, load_smi_2, done_load_1, done;
|
| - __ mov(scratch, Operand(esp, 2 * kPointerSize));
|
| - __ test(scratch, Immediate(kSmiTagMask));
|
| - __ j(zero, &load_smi_1, not_taken);
|
| - __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
|
| - __ bind(&done_load_1);
|
| -
|
| - __ mov(scratch, Operand(esp, 1 * kPointerSize));
|
| - __ test(scratch, Immediate(kSmiTagMask));
|
| - __ j(zero, &load_smi_2, not_taken);
|
| - __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
|
| - __ jmp(&done);
|
| -
|
| - __ bind(&load_smi_1);
|
| - __ sar(scratch, kSmiTagSize);
|
| - __ push(scratch);
|
| - __ fild_s(Operand(esp, 0));
|
| - __ pop(scratch);
|
| - __ jmp(&done_load_1);
|
| -
|
| - __ bind(&load_smi_2);
|
| - __ sar(scratch, kSmiTagSize);
|
| - __ push(scratch);
|
| - __ fild_s(Operand(esp, 0));
|
| - __ pop(scratch);
|
| -
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| -void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
|
| - Label* non_float,
|
| - Register scratch) {
|
| - Label test_other, done;
|
| - // Test if both operands are floats or smi -> scratch=k_is_float;
|
| - // Otherwise scratch = k_not_float.
|
| - __ test(edx, Immediate(kSmiTagMask));
|
| - __ j(zero, &test_other, not_taken); // argument in edx is OK
|
| - __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
|
| - __ cmp(scratch, Factory::heap_number_map());
|
| - __ j(not_equal, non_float); // argument in edx is not a number -> NaN
|
| -
|
| - __ bind(&test_other);
|
| - __ test(eax, Immediate(kSmiTagMask));
|
| - __ j(zero, &done); // argument in eax is OK
|
| - __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
|
| - __ cmp(scratch, Factory::heap_number_map());
|
| - __ j(not_equal, non_float); // argument in eax is not a number -> NaN
|
| -
|
| - // Fall-through: Both operands are numbers.
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| -void UnarySubStub::Generate(MacroAssembler* masm) {
|
| - Label undo;
|
| - Label slow;
|
| - Label done;
|
| - Label try_float;
|
| -
|
| - // Check whether the value is a smi.
|
| - __ test(eax, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &try_float, not_taken);
|
| -
|
| - // Enter runtime system if the value of the expression is zero
|
| - // to make sure that we switch between 0 and -0.
|
| - __ test(eax, Operand(eax));
|
| - __ j(zero, &slow, not_taken);
|
| -
|
| - // The value of the expression is a smi that is not zero. Try
|
| - // optimistic subtraction '0 - value'.
|
| - __ mov(edx, Operand(eax));
|
| - __ Set(eax, Immediate(0));
|
| - __ sub(eax, Operand(edx));
|
| - __ j(overflow, &undo, not_taken);
|
| -
|
| - // If result is a smi we are done.
|
| - __ test(eax, Immediate(kSmiTagMask));
|
| - __ j(zero, &done, taken);
|
| -
|
| - // Restore eax and enter runtime system.
|
| - __ bind(&undo);
|
| - __ mov(eax, Operand(edx));
|
| -
|
| - // Enter runtime system.
|
| - __ bind(&slow);
|
| - __ pop(ecx); // pop return address
|
| - __ push(eax);
|
| - __ push(ecx); // push return address
|
| - __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
|
| -
|
| - // Try floating point case.
|
| - __ bind(&try_float);
|
| - __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
|
| - __ cmp(edx, Factory::heap_number_map());
|
| - __ j(not_equal, &slow);
|
| - __ mov(edx, Operand(eax));
|
| - // edx: operand
|
| - FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx);
|
| - // eax: allocated 'empty' number
|
| - __ fld_d(FieldOperand(edx, HeapNumber::kValueOffset));
|
| - __ fchs();
|
| - __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| -
|
| - __ bind(&done);
|
| -
|
| - __ StubReturn(1);
|
| -}
|
| -
|
| -
|
| class ArgumentsAccessStub: public CodeStub {
|
| public:
|
| explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { }
|
| @@ -1856,81 +1096,6 @@
|
| };
|
|
|
|
|
| -void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
|
| - // Check that the key is a smi for non-length access.
|
| - Label slow;
|
| - if (!is_length_) {
|
| - __ mov(ebx, Operand(esp, 1 * kPointerSize)); // skip return address
|
| - __ test(ebx, Immediate(kSmiTagMask));
|
| - __ j(not_zero, &slow, not_taken);
|
| - }
|
| -
|
| - // Check if the calling frame is an arguments adaptor frame.
|
| - Label adaptor;
|
| - __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
|
| - __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
|
| - __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
|
| - __ j(equal, &adaptor);
|
| -
|
| - // The displacement is used for skipping the return address on the
|
| - // stack. It is the offset of the last parameter (if any) relative
|
| - // to the frame pointer.
|
| - static const int kDisplacement = 1 * kPointerSize;
|
| - ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
|
| -
|
| - if (is_length_) {
|
| - // Do nothing. The length is already in register eax.
|
| - } else {
|
| - // Check index against formal parameters count limit passed in
|
| - // through register eax. Use unsigned comparison to get negative
|
| - // check for free.
|
| - __ cmp(ebx, Operand(eax));
|
| - __ j(above_equal, &slow, not_taken);
|
| -
|
| - // Read the argument from the stack.
|
| - __ lea(edx, Operand(ebp, eax, times_2, 0));
|
| - __ neg(ebx);
|
| - __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
|
| - }
|
| -
|
| - // Return the length or the argument.
|
| - __ ret(0);
|
| -
|
| - // Arguments adaptor case: Find the length or the actual argument in
|
| - // the calling frame.
|
| - __ bind(&adaptor);
|
| - if (is_length_) {
|
| - // Read the arguments length from the adaptor frame.
|
| - __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
| - } else {
|
| - // Check index against actual arguments limit found in the
|
| - // arguments adaptor frame. Use unsigned comparison to get
|
| - // negative check for free.
|
| - __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
| - __ cmp(ebx, Operand(ecx));
|
| - __ j(above_equal, &slow, not_taken);
|
| -
|
| - // Read the argument from the stack.
|
| - __ lea(edx, Operand(edx, ecx, times_2, 0));
|
| - __ neg(ebx);
|
| - __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
|
| - }
|
| -
|
| - // Return the length or the argument.
|
| - __ ret(0);
|
| -
|
| - // Slow-case: Handle non-smi or out-of-bounds access to arguments
|
| - // by calling the runtime system.
|
| - if (!is_length_) {
|
| - __ bind(&slow);
|
| - __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
|
| - }
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| void Ia32CodeGenerator::GenericBinaryOperation(Token::Value op,
|
| OverwriteMode overwrite_mode) {
|
| Comment cmnt(masm_, "[ BinaryOperation");
|
| @@ -2366,9 +1531,6 @@
|
| }
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| class CompareStub: public CodeStub {
|
| public:
|
| CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { }
|
| @@ -2399,92 +1561,6 @@
|
| };
|
|
|
|
|
| -void CompareStub::Generate(MacroAssembler* masm) {
|
| - Label call_builtin, done;
|
| - // Save the return address (and get it off the stack).
|
| - __ pop(ecx);
|
| -
|
| - // Push arguments.
|
| - __ push(eax);
|
| - __ push(edx);
|
| - __ push(ecx);
|
| -
|
| - // Inlined floating point compare.
|
| - // Call builtin if operands are not floating point or smi.
|
| - FloatingPointHelper::CheckFloatOperands(masm, &call_builtin, ebx);
|
| - FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| - __ FCmp();
|
| -
|
| - // Jump to builtin for NaN.
|
| - __ j(parity_even, &call_builtin, not_taken);
|
| -
|
| - // 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, not_taken);
|
| - __ j(above, &above_lbl, not_taken);
|
| -
|
| - __ xor_(eax, Operand(eax)); // equal
|
| - __ ret(2 * kPointerSize);
|
| -
|
| - __ bind(&below_lbl);
|
| - __ mov(eax, -1);
|
| - __ ret(2 * kPointerSize);
|
| -
|
| - __ bind(&above_lbl);
|
| - __ mov(eax, 1);
|
| - __ ret(2 * kPointerSize); // eax, edx were pushed
|
| -
|
| - __ bind(&call_builtin);
|
| - // must swap argument order
|
| - __ pop(ecx);
|
| - __ pop(edx);
|
| - __ pop(eax);
|
| - __ push(edx);
|
| - __ push(eax);
|
| -
|
| - // 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(ecx);
|
| -
|
| - // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
|
| - // tagged as a small integer.
|
| - __ InvokeBuiltin(builtin, JUMP_FUNCTION);
|
| -}
|
| -
|
| -
|
| -void StackCheckStub::Generate(MacroAssembler* masm) {
|
| - // Because builtins always remove the receiver from the stack, we
|
| - // have to fake one to avoid underflowing the stack. The receiver
|
| - // must be inserted below the return address on the stack so we
|
| - // temporarily store that in a register.
|
| - __ pop(eax);
|
| - __ push(Immediate(Smi::FromInt(0)));
|
| - __ push(eax);
|
| -
|
| - // Do tail-call to runtime routine.
|
| - __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| void Ia32CodeGenerator::Comparison(Condition cc, bool strict) {
|
| // Strict only makes sense for equality comparisons.
|
| ASSERT(!strict || cc == equal);
|
| @@ -2593,42 +1669,6 @@
|
| };
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| -void CallFunctionStub::Generate(MacroAssembler* masm) {
|
| - Label slow;
|
| -
|
| - // Get the function to call from the stack.
|
| - // +2 ~ receiver, return address
|
| - __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
|
| -
|
| - // Check that the function really is a JavaScript function.
|
| - __ test(edi, Immediate(kSmiTagMask));
|
| - __ j(zero, &slow, not_taken);
|
| - // Get the map.
|
| - __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
|
| - __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
| - __ cmp(ecx, JS_FUNCTION_TYPE);
|
| - __ j(not_equal, &slow, not_taken);
|
| -
|
| - // Fast-case: Just invoke the function.
|
| - ParameterCount actual(argc_);
|
| - __ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
| -
|
| - // Slow-case: Non-function called.
|
| - __ bind(&slow);
|
| - __ Set(eax, Immediate(argc_));
|
| - __ Set(ebx, Immediate(0));
|
| - __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
|
| - Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
|
| - __ jmp(adaptor, RelocInfo::CODE_TARGET);
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| // Call the function just below TOS on the stack with the given
|
| // arguments. The receiver is the TOS.
|
| void Ia32CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
|
| @@ -4634,9 +3674,6 @@
|
| };
|
|
|
|
|
| -#undef __
|
| -#define __ masm->
|
| -
|
| class RevertToNumberStub: public CodeStub {
|
| public:
|
| explicit RevertToNumberStub(bool is_increment)
|
| @@ -4660,22 +3697,6 @@
|
| };
|
|
|
|
|
| -void RevertToNumberStub::Generate(MacroAssembler* masm) {
|
| - // Revert optimistic increment/decrement.
|
| - if (is_increment_) {
|
| - __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
|
| - } else {
|
| - __ add(Operand(eax), Immediate(Smi::FromInt(1)));
|
| - }
|
| -
|
| - __ pop(ecx);
|
| - __ push(eax);
|
| - __ push(ecx);
|
| - __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
|
| - // Code never returns due to JUMP_FUNCTION.
|
| -}
|
| -
|
| -
|
| class CounterOpStub: public CodeStub {
|
| public:
|
| CounterOpStub(int result_offset, bool is_postfix, bool is_increment)
|
| @@ -4710,37 +3731,6 @@
|
| };
|
|
|
|
|
| -void CounterOpStub::Generate(MacroAssembler* masm) {
|
| - // Store to the result on the stack (skip return address) before
|
| - // performing the count operation.
|
| - if (is_postfix_) {
|
| - __ mov(Operand(esp, result_offset_ + kPointerSize), eax);
|
| - }
|
| -
|
| - // Revert optimistic increment/decrement but only for prefix
|
| - // counts. For postfix counts it has already been reverted before
|
| - // the conversion to numbers.
|
| - if (!is_postfix_) {
|
| - if (is_increment_) {
|
| - __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
|
| - } else {
|
| - __ add(Operand(eax), Immediate(Smi::FromInt(1)));
|
| - }
|
| - }
|
| -
|
| - // Compute the new value by calling the right JavaScript native.
|
| - __ pop(ecx);
|
| - __ push(eax);
|
| - __ push(ecx);
|
| - Builtins::JavaScript builtin = is_increment_ ? Builtins::INC : Builtins::DEC;
|
| - __ InvokeBuiltin(builtin, JUMP_FUNCTION);
|
| - // Code never returns due to JUMP_FUNCTION.
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ masm_->
|
| -
|
| void CountOperationDeferred::Generate() {
|
| if (is_postfix_) {
|
| RevertToNumberStub to_number_stub(is_increment_);
|
| @@ -5178,6 +4168,980 @@
|
| #undef __
|
| #define __ masm->
|
|
|
| +Operand Ia32CodeGenerator::SlotOperand(CodeGenerator* cgen,
|
| + Slot* slot,
|
| + Register tmp) {
|
| + // Currently, this assertion will fail if we try to assign to
|
| + // a constant variable that is constant because it is read-only
|
| + // (such as the variable referring to a named function expression).
|
| + // We need to implement assignments to read-only variables.
|
| + // Ideally, we should do this during AST generation (by converting
|
| + // such assignments into expression statements); however, in general
|
| + // we may not be able to make the decision until past AST generation,
|
| + // that is when the entire program is known.
|
| + ASSERT(slot != NULL);
|
| + int index = slot->index();
|
| + switch (slot->type()) {
|
| + case Slot::PARAMETER: return ParameterOperand(cgen, index);
|
| +
|
| + case Slot::LOCAL: {
|
| + ASSERT(0 <= index && index < cgen->scope()->num_stack_slots());
|
| + const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
| + return Operand(ebp, kLocal0Offset - index * kPointerSize);
|
| + }
|
| +
|
| + case Slot::CONTEXT: {
|
| + MacroAssembler* masm = cgen->masm();
|
| + // Follow the context chain if necessary.
|
| + ASSERT(!tmp.is(esi)); // do not overwrite context register
|
| + Register context = esi;
|
| + int chain_length =
|
| + cgen->scope()->ContextChainLength(slot->var()->scope());
|
| + for (int i = chain_length; i-- > 0;) {
|
| + // Load the closure.
|
| + // (All contexts, even 'with' contexts, have a closure,
|
| + // and it is the same for all contexts inside a function.
|
| + // There is no need to go to the function context first.)
|
| + __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
|
| + // Load the function context (which is the incoming, outer context).
|
| + __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
|
| + context = tmp;
|
| + }
|
| + // We may have a 'with' context now. Get the function context.
|
| + // (In fact this mov may never be the needed, since the scope analysis
|
| + // may not permit a direct context access in this case and thus we are
|
| + // always at a function context. However it is safe to dereference be-
|
| + // cause the function context of a function context is itself. Before
|
| + // deleting this mov we should try to create a counter-example first,
|
| + // though...)
|
| + __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
|
| + return ContextOperand(tmp, index);
|
| + }
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + return Operand(eax);
|
| + }
|
| +}
|
| +
|
| +
|
| +void Property::GenerateStoreCode(CodeGenerator* cgen,
|
| + Reference* ref,
|
| + InitState init_state) {
|
| + MacroAssembler* masm = cgen->masm();
|
| + Comment cmnt(masm, "[ Store to Property");
|
| + __ RecordPosition(position());
|
| + Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key());
|
| +}
|
| +
|
| +
|
| +void VariableProxy::GenerateStoreCode(CodeGenerator* cgen,
|
| + Reference* ref,
|
| + InitState init_state) {
|
| + MacroAssembler* masm = cgen->masm();
|
| + Comment cmnt(masm, "[ Store to VariableProxy");
|
| + Variable* node = var();
|
| +
|
| + Expression* expr = node->rewrite();
|
| + if (expr != NULL) {
|
| + expr->GenerateStoreCode(cgen, ref, init_state);
|
| + } else {
|
| + ASSERT(node->is_global());
|
| + if (node->AsProperty() != NULL) {
|
| + __ RecordPosition(node->AsProperty()->position());
|
| + }
|
| + Expression* key = new Literal(node->name());
|
| + Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key);
|
| + }
|
| +}
|
| +
|
| +
|
| +void Slot::GenerateStoreCode(CodeGenerator* cgen,
|
| + Reference* ref,
|
| + InitState init_state) {
|
| + MacroAssembler* masm = cgen->masm();
|
| + Comment cmnt(masm, "[ Store to Slot");
|
| +
|
| + if (type() == Slot::LOOKUP) {
|
| + ASSERT(var()->mode() == Variable::DYNAMIC);
|
| +
|
| + // For now, just do a runtime call.
|
| + __ push(esi);
|
| + __ push(Immediate(var()->name()));
|
| +
|
| + if (init_state == CONST_INIT) {
|
| + // Same as the case for a normal store, but ignores attribute
|
| + // (e.g. READ_ONLY) of context slot so that we can initialize const
|
| + // properties (introduced via eval("const foo = (some expr);")). Also,
|
| + // uses the current function context instead of the top context.
|
| + //
|
| + // Note that we must declare the foo upon entry of eval(), via a
|
| + // context slot declaration, but we cannot initialize it at the same
|
| + // time, because the const declaration may be at the end of the eval
|
| + // code (sigh...) and the const variable may have been used before
|
| + // (where its value is 'undefined'). Thus, we can only do the
|
| + // initialization when we actually encounter the expression and when
|
| + // the expression operands are defined and valid, and thus we need the
|
| + // split into 2 operations: declaration of the context slot followed
|
| + // by initialization.
|
| + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
| + } else {
|
| + __ CallRuntime(Runtime::kStoreContextSlot, 3);
|
| + }
|
| + // Storing a variable must keep the (new) value on the expression
|
| + // stack. This is necessary for compiling assignment expressions.
|
| + __ push(eax);
|
| +
|
| + } else {
|
| + ASSERT(var()->mode() != Variable::DYNAMIC);
|
| +
|
| + Label exit;
|
| + if (init_state == CONST_INIT) {
|
| + ASSERT(var()->mode() == Variable::CONST);
|
| + // Only the first const initialization must be executed (the slot
|
| + // still contains 'the hole' value). When the assignment is executed,
|
| + // the code is identical to a normal store (see below).
|
| + Comment cmnt(masm, "[ Init const");
|
| + __ mov(eax, Ia32CodeGenerator::SlotOperand(cgen, this, ecx));
|
| + __ cmp(eax, Factory::the_hole_value());
|
| + __ j(not_equal, &exit);
|
| + }
|
| +
|
| + // We must execute the store.
|
| + // Storing a variable must keep the (new) value on the stack. This is
|
| + // necessary for compiling assignment expressions. ecx may be loaded
|
| + // with context; used below in RecordWrite.
|
| + //
|
| + // Note: We will reach here even with node->var()->mode() ==
|
| + // Variable::CONST because of const declarations which will initialize
|
| + // consts to 'the hole' value and by doing so, end up calling this
|
| + // code.
|
| + __ pop(eax);
|
| + __ mov(Ia32CodeGenerator::SlotOperand(cgen, this, ecx), eax);
|
| + __ push(eax); // RecordWrite may destroy the value in eax.
|
| + if (type() == Slot::CONTEXT) {
|
| + // ecx is loaded with context when calling SlotOperand above.
|
| + int offset = FixedArray::kHeaderSize + index() * kPointerSize;
|
| + __ RecordWrite(ecx, offset, eax, ebx);
|
| + }
|
| + // If we definitely did not jump over the assignment, we do not need to
|
| + // bind the exit label. Doing so can defeat peephole optimization.
|
| + if (init_state == CONST_INIT) __ bind(&exit);
|
| + }
|
| +}
|
| +
|
| +
|
| +// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
|
| +void ToBooleanStub::Generate(MacroAssembler* masm) {
|
| + Label false_result, true_result, not_string;
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| +
|
| + // 'null' => false.
|
| + __ cmp(eax, Factory::null_value());
|
| + __ j(equal, &false_result);
|
| +
|
| + // Get the map and type of the heap object.
|
| + __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
|
| + __ movzx_b(ecx, FieldOperand(edx, Map::kInstanceTypeOffset));
|
| +
|
| + // Undetectable => false.
|
| + __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
|
| + __ and_(ebx, 1 << Map::kIsUndetectable);
|
| + __ j(not_zero, &false_result);
|
| +
|
| + // JavaScript object => true.
|
| + __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
|
| + __ j(above_equal, &true_result);
|
| +
|
| + // String value => false iff empty.
|
| + __ cmp(ecx, FIRST_NONSTRING_TYPE);
|
| + __ j(above_equal, ¬_string);
|
| + __ and_(ecx, kStringSizeMask);
|
| + __ cmp(ecx, kShortStringTag);
|
| + __ j(not_equal, &true_result); // Empty string is always short.
|
| + __ mov(edx, FieldOperand(eax, String::kLengthOffset));
|
| + __ shr(edx, String::kShortLengthShift);
|
| + __ j(zero, &false_result);
|
| + __ jmp(&true_result);
|
| +
|
| + __ bind(¬_string);
|
| + // HeapNumber => false iff +0, -0, or NaN.
|
| + __ cmp(edx, Factory::heap_number_map());
|
| + __ j(not_equal, &true_result);
|
| + __ fldz();
|
| + __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| + __ fucompp();
|
| + __ push(eax);
|
| + __ fnstsw_ax();
|
| + __ sahf();
|
| + __ pop(eax);
|
| + __ j(zero, &false_result);
|
| + // Fall through to |true_result|.
|
| +
|
| + // Return 1/0 for true/false in eax.
|
| + __ bind(&true_result);
|
| + __ mov(eax, 1);
|
| + __ ret(1 * kPointerSize);
|
| + __ bind(&false_result);
|
| + __ mov(eax, 0);
|
| + __ ret(1 * kPointerSize);
|
| +}
|
| +
|
| +
|
| +void Ia32CodeGenerator::SetReferenceProperty(CodeGenerator* cgen,
|
| + Reference* ref,
|
| + Expression* key) {
|
| + ASSERT(!ref->is_illegal());
|
| + MacroAssembler* masm = cgen->masm();
|
| +
|
| + if (ref->type() == Reference::NAMED) {
|
| + // Compute the name of the property.
|
| + Literal* literal = key->AsLiteral();
|
| + Handle<String> name(String::cast(*literal->handle()));
|
| +
|
| + // Call the appropriate IC code.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| + // TODO(1222589): Make the IC grab the values from the stack.
|
| + __ pop(eax);
|
| + // Setup the name register.
|
| + __ Set(ecx, Immediate(name));
|
| + __ call(ic, RelocInfo::CODE_TARGET);
|
| + } else {
|
| + // Access keyed property.
|
| + ASSERT(ref->type() == Reference::KEYED);
|
| +
|
| + // Call IC code.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| + // TODO(1222589): Make the IC grab the values from the stack.
|
| + __ pop(eax);
|
| + __ call(ic, RelocInfo::CODE_TARGET);
|
| + }
|
| + __ push(eax); // IC call leaves result in eax, push it out
|
| +}
|
| +
|
| +
|
| +void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
| + Label call_runtime;
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize)); // Get y.
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize)); // Get x.
|
| +
|
| + // 1. Smi case.
|
| + switch (op_) {
|
| + case Token::ADD: {
|
| + // eax: y.
|
| + // edx: x.
|
| + Label revert;
|
| + __ mov(ecx, Operand(eax));
|
| + __ or_(ecx, Operand(edx)); // ecx = x | y.
|
| + __ add(eax, Operand(edx)); // Add y optimistically.
|
| + // Go slow-path in case of overflow.
|
| + __ j(overflow, &revert, not_taken);
|
| + // Go slow-path in case of non-smi operands.
|
| + ASSERT(kSmiTag == 0); // adjust code below
|
| + __ test(ecx, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &revert, not_taken);
|
| + __ ret(2 * kPointerSize); // Remove all operands.
|
| +
|
| + // Revert optimistic add.
|
| + __ bind(&revert);
|
| + __ sub(eax, Operand(edx));
|
| + break;
|
| + }
|
| + case Token::SUB: {
|
| + // eax: y.
|
| + // edx: x.
|
| + Label revert;
|
| + __ mov(ecx, Operand(edx));
|
| + __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| + __ sub(edx, Operand(eax)); // Subtract y optimistically.
|
| + // Go slow-path in case of overflow.
|
| + __ j(overflow, &revert, not_taken);
|
| + // Go slow-path in case of non-smi operands.
|
| + ASSERT(kSmiTag == 0); // adjust code below
|
| + __ test(ecx, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &revert, not_taken);
|
| + __ mov(eax, Operand(edx));
|
| + __ ret(2 * kPointerSize); // Remove all operands.
|
| +
|
| + // Revert optimistic sub.
|
| + __ bind(&revert);
|
| + __ add(edx, Operand(eax));
|
| + break;
|
| + }
|
| + case Token::MUL: {
|
| + // eax: y
|
| + // edx: x
|
| + // a) both operands smi and result fits into a smi -> return.
|
| + // b) at least one of operands non-smi -> non_smi_operands.
|
| + // c) result does not fit in a smi -> non_smi_result.
|
| + Label non_smi_operands, non_smi_result;
|
| + // Tag check.
|
| + __ mov(ecx, Operand(edx));
|
| + __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| + ASSERT(kSmiTag == 0); // Adjust code below.
|
| + __ test(ecx, Immediate(kSmiTagMask));
|
| + // Jump if not both smi; check if float numbers.
|
| + __ j(not_zero, &non_smi_operands, not_taken);
|
| +
|
| + // Get copies of operands.
|
| + __ mov(ebx, Operand(eax));
|
| + __ mov(ecx, Operand(edx));
|
| + // If the smi tag is 0 we can just leave the tag on one operand.
|
| + ASSERT(kSmiTag == 0); // adjust code below
|
| + // Remove tag from one of the operands (but keep sign).
|
| + __ sar(ecx, kSmiTagSize);
|
| + // Do multiplication.
|
| + __ imul(eax, Operand(ecx)); // Multiplication of Smis; result in eax.
|
| + // Go slow on overflows.
|
| + __ j(overflow, &non_smi_result, not_taken);
|
| + // ...but operands OK for float arithmetic.
|
| +
|
| + // If the result is +0 we may need to check if the result should
|
| + // really be -0. Welcome to the -0 fan club.
|
| + __ NegativeZeroTest(eax, ebx, edx, ecx, &non_smi_result);
|
| +
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + __ bind(&non_smi_result);
|
| + // TODO(1243132): Do not check float operands here.
|
| + __ bind(&non_smi_operands);
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| + break;
|
| + }
|
| + case Token::DIV: {
|
| + // eax: y
|
| + // edx: x
|
| + Label non_smi_operands, non_smi_result, division_by_zero;
|
| + __ mov(ebx, Operand(eax)); // Get y
|
| + __ mov(eax, Operand(edx)); // Get x
|
| +
|
| + __ cdq(); // Sign extend eax into edx:eax.
|
| + // Tag check.
|
| + __ mov(ecx, Operand(ebx));
|
| + __ or_(ecx, Operand(eax)); // ecx = x | y.
|
| + ASSERT(kSmiTag == 0); // Adjust code below.
|
| + __ test(ecx, Immediate(kSmiTagMask));
|
| + // Jump if not both smi; check if float numbers.
|
| + __ j(not_zero, &non_smi_operands, not_taken);
|
| + __ test(ebx, Operand(ebx)); // Check for 0 divisor.
|
| + __ j(zero, &division_by_zero, not_taken);
|
| +
|
| + __ idiv(ebx);
|
| + // Check for the corner case of dividing the most negative smi by -1.
|
| + // (We cannot use the overflow flag, since it is not set by idiv.)
|
| + ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
|
| + __ cmp(eax, 0x40000000);
|
| + __ j(equal, &non_smi_result);
|
| + // If the result is +0 we may need to check if the result should
|
| + // really be -0. Welcome to the -0 fan club.
|
| + __ NegativeZeroTest(eax, ecx, &non_smi_result); // Use ecx = x | y.
|
| + __ test(edx, Operand(edx));
|
| + // Use floats if there's a remainder.
|
| + __ j(not_zero, &non_smi_result, not_taken);
|
| + __ shl(eax, kSmiTagSize);
|
| + __ ret(2 * kPointerSize); // Remove all operands.
|
| +
|
| + __ bind(&division_by_zero);
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| + __ jmp(&call_runtime); // Division by zero must go through runtime.
|
| +
|
| + __ bind(&non_smi_result);
|
| + // TODO(1243132): Do not check float operands here.
|
| + __ bind(&non_smi_operands);
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| + break;
|
| + }
|
| + case Token::MOD: {
|
| + Label slow;
|
| + __ mov(ebx, Operand(eax)); // get y
|
| + __ mov(eax, Operand(edx)); // get x
|
| + __ cdq(); // sign extend eax into edx:eax
|
| + // tag check
|
| + __ mov(ecx, Operand(ebx));
|
| + __ or_(ecx, Operand(eax)); // ecx = x | y;
|
| + ASSERT(kSmiTag == 0); // adjust code below
|
| + __ test(ecx, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &slow, not_taken);
|
| + __ test(ebx, Operand(ebx)); // test for y == 0
|
| + __ j(zero, &slow);
|
| +
|
| + // Fast case: Do integer division and use remainder.
|
| + __ idiv(ebx);
|
| + __ NegativeZeroTest(edx, ecx, &slow); // use ecx = x | y
|
| + __ mov(eax, Operand(edx));
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + // Slow case: Call runtime operator implementation.
|
| + __ bind(&slow);
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| + // Fall through to |call_runtime|.
|
| + break;
|
| + }
|
| + case Token::BIT_OR:
|
| + case Token::BIT_AND:
|
| + case Token::BIT_XOR:
|
| + case Token::SAR:
|
| + case Token::SHL:
|
| + case Token::SHR: {
|
| + // Smi-case for bitops should already have been inlined.
|
| + break;
|
| + }
|
| + default: {
|
| + UNREACHABLE();
|
| + }
|
| + }
|
| +
|
| + // 2. Floating point case.
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + case Token::SUB:
|
| + case Token::MUL:
|
| + case Token::DIV: {
|
| + // eax: y
|
| + // edx: x
|
| + FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
|
| + // Fast-case: Both operands are numbers.
|
| + // Allocate a heap number, if needed.
|
| + Label skip_allocation;
|
| + switch (mode_) {
|
| + case OVERWRITE_LEFT:
|
| + __ mov(eax, Operand(edx));
|
| + // Fall through!
|
| + case OVERWRITE_RIGHT:
|
| + // If the argument in eax is already an object, we skip the
|
| + // allocation of a heap number.
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &skip_allocation, not_taken);
|
| + // Fall through!
|
| + case NO_OVERWRITE:
|
| + FloatingPointHelper::AllocateHeapNumber(masm,
|
| + &call_runtime,
|
| + ecx,
|
| + edx);
|
| + __ bind(&skip_allocation);
|
| + break;
|
| + default: UNREACHABLE();
|
| + }
|
| + FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| +
|
| + switch (op_) {
|
| + case Token::ADD: __ faddp(1); break;
|
| + case Token::SUB: __ fsubp(1); break;
|
| + case Token::MUL: __ fmulp(1); break;
|
| + case Token::DIV: __ fdivp(1); break;
|
| + default: UNREACHABLE();
|
| + }
|
| + __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| + __ ret(2 * kPointerSize);
|
| + }
|
| + case Token::MOD: {
|
| + // For MOD we go directly to runtime in the non-smi case.
|
| + break;
|
| + }
|
| + case Token::BIT_OR:
|
| + case Token::BIT_AND:
|
| + case Token::BIT_XOR:
|
| + case Token::SAR:
|
| + case Token::SHL:
|
| + case Token::SHR: {
|
| + FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
|
| + FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| +
|
| + Label non_int32_operands, non_smi_result, skip_allocation;
|
| + // Reserve space for converted numbers.
|
| + __ sub(Operand(esp), Immediate(2 * kPointerSize));
|
| +
|
| + // Check if right operand is int32.
|
| + __ fist_s(Operand(esp, 1 * kPointerSize));
|
| + __ fild_s(Operand(esp, 1 * kPointerSize));
|
| + __ fucompp();
|
| + __ fnstsw_ax();
|
| + __ sahf();
|
| + __ j(not_zero, &non_int32_operands);
|
| + __ j(parity_even, &non_int32_operands);
|
| +
|
| + // Check if left operand is int32.
|
| + __ fist_s(Operand(esp, 0 * kPointerSize));
|
| + __ fild_s(Operand(esp, 0 * kPointerSize));
|
| + __ fucompp();
|
| + __ fnstsw_ax();
|
| + __ sahf();
|
| + __ j(not_zero, &non_int32_operands);
|
| + __ j(parity_even, &non_int32_operands);
|
| +
|
| + // Get int32 operands and perform bitop.
|
| + __ pop(eax);
|
| + __ pop(ecx);
|
| + switch (op_) {
|
| + case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
|
| + case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
|
| + case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
|
| + case Token::SAR: __ sar(eax); break;
|
| + case Token::SHL: __ shl(eax); break;
|
| + case Token::SHR: __ shr(eax); break;
|
| + default: UNREACHABLE();
|
| + }
|
| +
|
| + // Check if result is non-negative and fits in a smi.
|
| + __ test(eax, Immediate(0xc0000000));
|
| + __ j(not_zero, &non_smi_result);
|
| +
|
| + // Tag smi result and return.
|
| + ASSERT(kSmiTagSize == times_2); // adjust code if not the case
|
| + __ lea(eax, Operand(eax, times_2, kSmiTag));
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + // All ops except SHR return a signed int32 that we load in a HeapNumber.
|
| + if (op_ != Token::SHR) {
|
| + __ bind(&non_smi_result);
|
| + // Allocate a heap number if needed.
|
| + __ mov(ebx, Operand(eax)); // ebx: result
|
| + switch (mode_) {
|
| + case OVERWRITE_LEFT:
|
| + case OVERWRITE_RIGHT:
|
| + // If the operand was an object, we skip the
|
| + // allocation of a heap number.
|
| + __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
|
| + 1 * kPointerSize : 2 * kPointerSize));
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &skip_allocation, not_taken);
|
| + // Fall through!
|
| + case NO_OVERWRITE:
|
| + FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime,
|
| + ecx, edx);
|
| + __ bind(&skip_allocation);
|
| + break;
|
| + default: UNREACHABLE();
|
| + }
|
| + // Store the result in the HeapNumber and return.
|
| + __ mov(Operand(esp, 1 * kPointerSize), ebx);
|
| + __ fild_s(Operand(esp, 1 * kPointerSize));
|
| + __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| + __ ret(2 * kPointerSize);
|
| + }
|
| + __ bind(&non_int32_operands);
|
| + // Restore stacks and operands before calling runtime.
|
| + __ ffree(0);
|
| + __ add(Operand(esp), Immediate(2 * kPointerSize));
|
| +
|
| + // SHR should return uint32 - go to runtime for non-smi/negative result.
|
| + if (op_ == Token::SHR) __ bind(&non_smi_result);
|
| + __ mov(eax, Operand(esp, 1 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
| + break;
|
| + }
|
| + default: UNREACHABLE(); break;
|
| + }
|
| +
|
| + // 3. If all else fails, use the runtime system to get the correct result.
|
| + __ bind(&call_runtime);
|
| + switch (op_) {
|
| + case Token::ADD:
|
| + __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SUB:
|
| + __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
|
| + break;
|
| + case Token::MUL:
|
| + __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
|
| + break;
|
| + case Token::DIV:
|
| + __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
|
| + break;
|
| + case Token::MOD:
|
| + __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_OR:
|
| + __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_AND:
|
| + __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
|
| + break;
|
| + case Token::BIT_XOR:
|
| + __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SAR:
|
| + __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SHL:
|
| + __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
|
| + break;
|
| + case Token::SHR:
|
| + __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
|
| + Label* need_gc,
|
| + Register scratch1,
|
| + Register scratch2) {
|
| + ExternalReference allocation_top =
|
| + ExternalReference::new_space_allocation_top_address();
|
| + ExternalReference allocation_limit =
|
| + ExternalReference::new_space_allocation_limit_address();
|
| + __ mov(Operand(scratch1), Immediate(allocation_top));
|
| + __ mov(eax, Operand(scratch1, 0));
|
| + __ lea(scratch2, Operand(eax, HeapNumber::kSize)); // scratch2: new top
|
| + __ cmp(scratch2, Operand::StaticVariable(allocation_limit));
|
| + __ j(above, need_gc, not_taken);
|
| +
|
| + __ mov(Operand(scratch1, 0), scratch2); // store new top
|
| + __ mov(Operand(eax, HeapObject::kMapOffset),
|
| + Immediate(Factory::heap_number_map()));
|
| + // Tag old top and use as result.
|
| + __ add(Operand(eax), Immediate(kHeapObjectTag));
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
|
| + Register scratch) {
|
| + Label load_smi_1, load_smi_2, done_load_1, done;
|
| + __ mov(scratch, Operand(esp, 2 * kPointerSize));
|
| + __ test(scratch, Immediate(kSmiTagMask));
|
| + __ j(zero, &load_smi_1, not_taken);
|
| + __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
|
| + __ bind(&done_load_1);
|
| +
|
| + __ mov(scratch, Operand(esp, 1 * kPointerSize));
|
| + __ test(scratch, Immediate(kSmiTagMask));
|
| + __ j(zero, &load_smi_2, not_taken);
|
| + __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&load_smi_1);
|
| + __ sar(scratch, kSmiTagSize);
|
| + __ push(scratch);
|
| + __ fild_s(Operand(esp, 0));
|
| + __ pop(scratch);
|
| + __ jmp(&done_load_1);
|
| +
|
| + __ bind(&load_smi_2);
|
| + __ sar(scratch, kSmiTagSize);
|
| + __ push(scratch);
|
| + __ fild_s(Operand(esp, 0));
|
| + __ pop(scratch);
|
| +
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| +void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
|
| + Label* non_float,
|
| + Register scratch) {
|
| + Label test_other, done;
|
| + // Test if both operands are floats or smi -> scratch=k_is_float;
|
| + // Otherwise scratch = k_not_float.
|
| + __ test(edx, Immediate(kSmiTagMask));
|
| + __ j(zero, &test_other, not_taken); // argument in edx is OK
|
| + __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
|
| + __ cmp(scratch, Factory::heap_number_map());
|
| + __ j(not_equal, non_float); // argument in edx is not a number -> NaN
|
| +
|
| + __ bind(&test_other);
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| + __ j(zero, &done); // argument in eax is OK
|
| + __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
|
| + __ cmp(scratch, Factory::heap_number_map());
|
| + __ j(not_equal, non_float); // argument in eax is not a number -> NaN
|
| +
|
| + // Fall-through: Both operands are numbers.
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| +void UnarySubStub::Generate(MacroAssembler* masm) {
|
| + Label undo;
|
| + Label slow;
|
| + Label done;
|
| + Label try_float;
|
| +
|
| + // Check whether the value is a smi.
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &try_float, not_taken);
|
| +
|
| + // Enter runtime system if the value of the expression is zero
|
| + // to make sure that we switch between 0 and -0.
|
| + __ test(eax, Operand(eax));
|
| + __ j(zero, &slow, not_taken);
|
| +
|
| + // The value of the expression is a smi that is not zero. Try
|
| + // optimistic subtraction '0 - value'.
|
| + __ mov(edx, Operand(eax));
|
| + __ Set(eax, Immediate(0));
|
| + __ sub(eax, Operand(edx));
|
| + __ j(overflow, &undo, not_taken);
|
| +
|
| + // If result is a smi we are done.
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| + __ j(zero, &done, taken);
|
| +
|
| + // Restore eax and enter runtime system.
|
| + __ bind(&undo);
|
| + __ mov(eax, Operand(edx));
|
| +
|
| + // Enter runtime system.
|
| + __ bind(&slow);
|
| + __ pop(ecx); // pop return address
|
| + __ push(eax);
|
| + __ push(ecx); // push return address
|
| + __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
|
| +
|
| + // Try floating point case.
|
| + __ bind(&try_float);
|
| + __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
|
| + __ cmp(edx, Factory::heap_number_map());
|
| + __ j(not_equal, &slow);
|
| + __ mov(edx, Operand(eax));
|
| + // edx: operand
|
| + FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx);
|
| + // eax: allocated 'empty' number
|
| + __ fld_d(FieldOperand(edx, HeapNumber::kValueOffset));
|
| + __ fchs();
|
| + __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
| +
|
| + __ bind(&done);
|
| +
|
| + __ StubReturn(1);
|
| +}
|
| +
|
| +
|
| +void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
|
| + // Check that the key is a smi for non-length access.
|
| + Label slow;
|
| + if (!is_length_) {
|
| + __ mov(ebx, Operand(esp, 1 * kPointerSize)); // skip return address
|
| + __ test(ebx, Immediate(kSmiTagMask));
|
| + __ j(not_zero, &slow, not_taken);
|
| + }
|
| +
|
| + // Check if the calling frame is an arguments adaptor frame.
|
| + Label adaptor;
|
| + __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
|
| + __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
|
| + __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
|
| + __ j(equal, &adaptor);
|
| +
|
| + // The displacement is used for skipping the return address on the
|
| + // stack. It is the offset of the last parameter (if any) relative
|
| + // to the frame pointer.
|
| + static const int kDisplacement = 1 * kPointerSize;
|
| + ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
|
| +
|
| + if (is_length_) {
|
| + // Do nothing. The length is already in register eax.
|
| + } else {
|
| + // Check index against formal parameters count limit passed in
|
| + // through register eax. Use unsigned comparison to get negative
|
| + // check for free.
|
| + __ cmp(ebx, Operand(eax));
|
| + __ j(above_equal, &slow, not_taken);
|
| +
|
| + // Read the argument from the stack.
|
| + __ lea(edx, Operand(ebp, eax, times_2, 0));
|
| + __ neg(ebx);
|
| + __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
|
| + }
|
| +
|
| + // Return the length or the argument.
|
| + __ ret(0);
|
| +
|
| + // Arguments adaptor case: Find the length or the actual argument in
|
| + // the calling frame.
|
| + __ bind(&adaptor);
|
| + if (is_length_) {
|
| + // Read the arguments length from the adaptor frame.
|
| + __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
| + } else {
|
| + // Check index against actual arguments limit found in the
|
| + // arguments adaptor frame. Use unsigned comparison to get
|
| + // negative check for free.
|
| + __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
| + __ cmp(ebx, Operand(ecx));
|
| + __ j(above_equal, &slow, not_taken);
|
| +
|
| + // Read the argument from the stack.
|
| + __ lea(edx, Operand(edx, ecx, times_2, 0));
|
| + __ neg(ebx);
|
| + __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
|
| + }
|
| +
|
| + // Return the length or the argument.
|
| + __ ret(0);
|
| +
|
| + // Slow-case: Handle non-smi or out-of-bounds access to arguments
|
| + // by calling the runtime system.
|
| + if (!is_length_) {
|
| + __ bind(&slow);
|
| + __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
|
| + }
|
| +}
|
| +
|
| +
|
| +void CompareStub::Generate(MacroAssembler* masm) {
|
| + Label call_builtin, done;
|
| + // Save the return address (and get it off the stack).
|
| + __ pop(ecx);
|
| +
|
| + // Push arguments.
|
| + __ push(eax);
|
| + __ push(edx);
|
| + __ push(ecx);
|
| +
|
| + // Inlined floating point compare.
|
| + // Call builtin if operands are not floating point or smi.
|
| + FloatingPointHelper::CheckFloatOperands(masm, &call_builtin, ebx);
|
| + FloatingPointHelper::LoadFloatOperands(masm, ecx);
|
| + __ FCmp();
|
| +
|
| + // Jump to builtin for NaN.
|
| + __ j(parity_even, &call_builtin, not_taken);
|
| +
|
| + // 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, not_taken);
|
| + __ j(above, &above_lbl, not_taken);
|
| +
|
| + __ xor_(eax, Operand(eax)); // equal
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + __ bind(&below_lbl);
|
| + __ mov(eax, -1);
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + __ bind(&above_lbl);
|
| + __ mov(eax, 1);
|
| + __ ret(2 * kPointerSize); // eax, edx were pushed
|
| +
|
| + __ bind(&call_builtin);
|
| + // must swap argument order
|
| + __ pop(ecx);
|
| + __ pop(edx);
|
| + __ pop(eax);
|
| + __ push(edx);
|
| + __ push(eax);
|
| +
|
| + // 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(ecx);
|
| +
|
| + // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
|
| + // tagged as a small integer.
|
| + __ InvokeBuiltin(builtin, JUMP_FUNCTION);
|
| +}
|
| +
|
| +
|
| +void StackCheckStub::Generate(MacroAssembler* masm) {
|
| + // Because builtins always remove the receiver from the stack, we
|
| + // have to fake one to avoid underflowing the stack. The receiver
|
| + // must be inserted below the return address on the stack so we
|
| + // temporarily store that in a register.
|
| + __ pop(eax);
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(eax);
|
| +
|
| + // Do tail-call to runtime routine.
|
| + __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
|
| +}
|
| +
|
| +
|
| +void CallFunctionStub::Generate(MacroAssembler* masm) {
|
| + Label slow;
|
| +
|
| + // Get the function to call from the stack.
|
| + // +2 ~ receiver, return address
|
| + __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
|
| +
|
| + // Check that the function really is a JavaScript function.
|
| + __ test(edi, Immediate(kSmiTagMask));
|
| + __ j(zero, &slow, not_taken);
|
| + // Get the map.
|
| + __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
|
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
| + __ cmp(ecx, JS_FUNCTION_TYPE);
|
| + __ j(not_equal, &slow, not_taken);
|
| +
|
| + // Fast-case: Just invoke the function.
|
| + ParameterCount actual(argc_);
|
| + __ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
| +
|
| + // Slow-case: Non-function called.
|
| + __ bind(&slow);
|
| + __ Set(eax, Immediate(argc_));
|
| + __ Set(ebx, Immediate(0));
|
| + __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
|
| + Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
|
| + __ jmp(adaptor, RelocInfo::CODE_TARGET);
|
| +}
|
| +
|
| +
|
| +void RevertToNumberStub::Generate(MacroAssembler* masm) {
|
| + // Revert optimistic increment/decrement.
|
| + if (is_increment_) {
|
| + __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
|
| + } else {
|
| + __ add(Operand(eax), Immediate(Smi::FromInt(1)));
|
| + }
|
| +
|
| + __ pop(ecx);
|
| + __ push(eax);
|
| + __ push(ecx);
|
| + __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
|
| + // Code never returns due to JUMP_FUNCTION.
|
| +}
|
| +
|
| +
|
| +void CounterOpStub::Generate(MacroAssembler* masm) {
|
| + // Store to the result on the stack (skip return address) before
|
| + // performing the count operation.
|
| + if (is_postfix_) {
|
| + __ mov(Operand(esp, result_offset_ + kPointerSize), eax);
|
| + }
|
| +
|
| + // Revert optimistic increment/decrement but only for prefix
|
| + // counts. For postfix counts it has already been reverted before
|
| + // the conversion to numbers.
|
| + if (!is_postfix_) {
|
| + if (is_increment_) {
|
| + __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
|
| + } else {
|
| + __ add(Operand(eax), Immediate(Smi::FromInt(1)));
|
| + }
|
| + }
|
| +
|
| + // Compute the new value by calling the right JavaScript native.
|
| + __ pop(ecx);
|
| + __ push(eax);
|
| + __ push(ecx);
|
| + Builtins::JavaScript builtin = is_increment_ ? Builtins::INC : Builtins::DEC;
|
| + __ InvokeBuiltin(builtin, JUMP_FUNCTION);
|
| + // Code never returns due to JUMP_FUNCTION.
|
| +}
|
| +
|
| +
|
| void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
| ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
|
| ExternalReference handler_address(Top::k_handler_address);
|
| @@ -5465,7 +5429,6 @@
|
|
|
| #undef __
|
|
|
| -
|
| // -----------------------------------------------------------------------------
|
| // CodeGenerator interfaces
|
|
|
|
|