| Index: src/arm/lithium-codegen-arm.cc
|
| ===================================================================
|
| --- src/arm/lithium-codegen-arm.cc (revision 6955)
|
| +++ src/arm/lithium-codegen-arm.cc (working copy)
|
| @@ -54,6 +54,157 @@
|
| };
|
|
|
|
|
| +class LGapNode: public ZoneObject {
|
| + public:
|
| + explicit LGapNode(LOperand* operand)
|
| + : operand_(operand), resolved_(false), visited_id_(-1) { }
|
| +
|
| + LOperand* operand() const { return operand_; }
|
| + bool IsResolved() const { return !IsAssigned() || resolved_; }
|
| + void MarkResolved() {
|
| + ASSERT(!IsResolved());
|
| + resolved_ = true;
|
| + }
|
| + int visited_id() const { return visited_id_; }
|
| + void set_visited_id(int id) {
|
| + ASSERT(id > visited_id_);
|
| + visited_id_ = id;
|
| + }
|
| +
|
| + bool IsAssigned() const { return assigned_from_.is_set(); }
|
| + LGapNode* assigned_from() const { return assigned_from_.get(); }
|
| + void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
|
| +
|
| + private:
|
| + LOperand* operand_;
|
| + SetOncePointer<LGapNode> assigned_from_;
|
| + bool resolved_;
|
| + int visited_id_;
|
| +};
|
| +
|
| +
|
| +LGapResolver::LGapResolver()
|
| + : nodes_(32),
|
| + identified_cycles_(4),
|
| + result_(16),
|
| + next_visited_id_(0) {
|
| +}
|
| +
|
| +
|
| +const ZoneList<LMoveOperands>* LGapResolver::Resolve(
|
| + const ZoneList<LMoveOperands>* moves,
|
| + LOperand* marker_operand) {
|
| + nodes_.Rewind(0);
|
| + identified_cycles_.Rewind(0);
|
| + result_.Rewind(0);
|
| + next_visited_id_ = 0;
|
| +
|
| + for (int i = 0; i < moves->length(); ++i) {
|
| + LMoveOperands move = moves->at(i);
|
| + if (!move.IsRedundant()) RegisterMove(move);
|
| + }
|
| +
|
| + for (int i = 0; i < identified_cycles_.length(); ++i) {
|
| + ResolveCycle(identified_cycles_[i], marker_operand);
|
| + }
|
| +
|
| + int unresolved_nodes;
|
| + do {
|
| + unresolved_nodes = 0;
|
| + for (int j = 0; j < nodes_.length(); j++) {
|
| + LGapNode* node = nodes_[j];
|
| + if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
|
| + AddResultMove(node->assigned_from(), node);
|
| + node->MarkResolved();
|
| + }
|
| + if (!node->IsResolved()) ++unresolved_nodes;
|
| + }
|
| + } while (unresolved_nodes > 0);
|
| + return &result_;
|
| +}
|
| +
|
| +
|
| +void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
|
| + AddResultMove(from->operand(), to->operand());
|
| +}
|
| +
|
| +
|
| +void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
|
| + result_.Add(LMoveOperands(from, to));
|
| +}
|
| +
|
| +
|
| +void LGapResolver::ResolveCycle(LGapNode* start, LOperand* marker_operand) {
|
| + ZoneList<LOperand*> cycle_operands(8);
|
| + cycle_operands.Add(marker_operand);
|
| + LGapNode* cur = start;
|
| + do {
|
| + cur->MarkResolved();
|
| + cycle_operands.Add(cur->operand());
|
| + cur = cur->assigned_from();
|
| + } while (cur != start);
|
| + cycle_operands.Add(marker_operand);
|
| +
|
| + for (int i = cycle_operands.length() - 1; i > 0; --i) {
|
| + LOperand* from = cycle_operands[i];
|
| + LOperand* to = cycle_operands[i - 1];
|
| + AddResultMove(from, to);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
|
| + ASSERT(a != b);
|
| + LGapNode* cur = a;
|
| + while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
|
| + cur->set_visited_id(visited_id);
|
| + cur = cur->assigned_from();
|
| + }
|
| +
|
| + return cur == b;
|
| +}
|
| +
|
| +
|
| +bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
|
| + ASSERT(a != b);
|
| + return CanReach(a, b, next_visited_id_++);
|
| +}
|
| +
|
| +
|
| +void LGapResolver::RegisterMove(LMoveOperands move) {
|
| + if (move.source()->IsConstantOperand()) {
|
| + // Constant moves should be last in the machine code. Therefore add them
|
| + // first to the result set.
|
| + AddResultMove(move.source(), move.destination());
|
| + } else {
|
| + LGapNode* from = LookupNode(move.source());
|
| + LGapNode* to = LookupNode(move.destination());
|
| + if (to->IsAssigned() && to->assigned_from() == from) {
|
| + move.Eliminate();
|
| + return;
|
| + }
|
| + ASSERT(!to->IsAssigned());
|
| + if (CanReach(from, to)) {
|
| + // This introduces a cycle. Save.
|
| + identified_cycles_.Add(from);
|
| + }
|
| + to->set_assigned_from(from);
|
| + }
|
| +}
|
| +
|
| +
|
| +LGapNode* LGapResolver::LookupNode(LOperand* operand) {
|
| + for (int i = 0; i < nodes_.length(); ++i) {
|
| + if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
|
| + }
|
| +
|
| + // No node found => create a new one.
|
| + LGapNode* result = new LGapNode(operand);
|
| + nodes_.Add(result);
|
| + return result;
|
| +}
|
| +
|
| +
|
| #define __ masm()->
|
|
|
| bool LCodeGen::GenerateCode() {
|
| @@ -190,6 +341,11 @@
|
| __ jmp(code->exit());
|
| }
|
|
|
| + // Force constant pool emission at the end of deferred code to make
|
| + // sure that no constant pools are emitted after the official end of
|
| + // the instruction sequence.
|
| + masm()->CheckConstPool(true, false);
|
| +
|
| // Deferred code is the last part of the instruction sequence. Mark
|
| // the generated code as done unless we bailed out.
|
| if (!is_aborted()) status_ = DONE;
|
| @@ -505,7 +661,7 @@
|
| return;
|
| }
|
|
|
| - if (cc == no_condition) {
|
| + if (cc == kNoCondition) {
|
| if (FLAG_trap_on_deopt) __ stop("trap_on_deopt");
|
| __ Jump(entry, RelocInfo::RUNTIME_ENTRY);
|
| } else {
|
| @@ -614,6 +770,27 @@
|
| }
|
|
|
|
|
| +void LCodeGen::RecordSafepointWithRegistersAndDoubles(
|
| + LPointerMap* pointers,
|
| + int arguments,
|
| + int deoptimization_index) {
|
| + const ZoneList<LOperand*>* operands = pointers->operands();
|
| + Safepoint safepoint =
|
| + safepoints_.DefineSafepointWithRegistersAndDoubles(
|
| + masm(), arguments, deoptimization_index);
|
| + for (int i = 0; i < operands->length(); i++) {
|
| + LOperand* pointer = operands->at(i);
|
| + if (pointer->IsStackSlot()) {
|
| + safepoint.DefinePointerSlot(pointer->index());
|
| + } else if (pointer->IsRegister()) {
|
| + safepoint.DefinePointerRegister(ToRegister(pointer));
|
| + }
|
| + }
|
| + // Register cp always contains a pointer to the context.
|
| + safepoint.DefinePointerRegister(cp);
|
| +}
|
| +
|
| +
|
| void LCodeGen::RecordPosition(int position) {
|
| if (!FLAG_debug_info || position == RelocInfo::kNoPosition) return;
|
| masm()->positions_recorder()->RecordPosition(position);
|
| @@ -644,8 +821,8 @@
|
| resolver_.Resolve(move->move_operands(), &marker_operand);
|
| for (int i = moves->length() - 1; i >= 0; --i) {
|
| LMoveOperands move = moves->at(i);
|
| - LOperand* from = move.from();
|
| - LOperand* to = move.to();
|
| + LOperand* from = move.source();
|
| + LOperand* to = move.destination();
|
| ASSERT(!from->IsDoubleRegister() ||
|
| !ToDoubleRegister(from).is(dbl_scratch));
|
| ASSERT(!to->IsDoubleRegister() || !ToDoubleRegister(to).is(dbl_scratch));
|
| @@ -787,7 +964,8 @@
|
| break;
|
| }
|
| case CodeStub::StringCharAt: {
|
| - Abort("StringCharAtStub unimplemented.");
|
| + StringCharAtStub stub;
|
| + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| break;
|
| }
|
| case CodeStub::MathPow: {
|
| @@ -827,23 +1005,198 @@
|
|
|
|
|
| void LCodeGen::DoModI(LModI* instr) {
|
| - Abort("DoModI unimplemented.");
|
| + class DeferredModI: public LDeferredCode {
|
| + public:
|
| + DeferredModI(LCodeGen* codegen, LModI* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() {
|
| + codegen()->DoDeferredGenericBinaryStub(instr_, Token::MOD);
|
| + }
|
| + private:
|
| + LModI* instr_;
|
| + };
|
| + // These registers hold untagged 32 bit values.
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = ToRegister(instr->InputAt(1));
|
| + Register result = ToRegister(instr->result());
|
| + Register scratch = scratch0();
|
| +
|
| + Label deoptimize, done;
|
| + // Check for x % 0.
|
| + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
|
| + __ tst(right, Operand(right));
|
| + __ b(eq, &deoptimize);
|
| + }
|
| +
|
| + // Check for (0 % -x) that will produce negative zero.
|
| + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
|
| + Label ok;
|
| + __ tst(left, Operand(left));
|
| + __ b(ne, &ok);
|
| + __ tst(right, Operand(right));
|
| + __ b(pl, &ok);
|
| + __ b(al, &deoptimize);
|
| + __ bind(&ok);
|
| + }
|
| +
|
| + // Try a few common cases before using the generic stub.
|
| + Label call_stub;
|
| + const int kUnfolds = 3;
|
| + // Skip if either side is negative.
|
| + __ cmp(left, Operand(0));
|
| + __ cmp(right, Operand(0), NegateCondition(mi));
|
| + __ b(mi, &call_stub);
|
| + // If the right hand side is smaller than the (nonnegative)
|
| + // left hand side, it is the result. Else try a few subtractions
|
| + // of the left hand side.
|
| + __ mov(scratch, left);
|
| + for (int i = 0; i < kUnfolds; i++) {
|
| + // Check if the left hand side is less or equal than the
|
| + // the right hand side.
|
| + __ cmp(scratch, right);
|
| + __ mov(result, scratch, LeaveCC, lt);
|
| + __ b(lt, &done);
|
| + // If not, reduce the left hand side by the right hand
|
| + // side and check again.
|
| + if (i < kUnfolds - 1) __ sub(scratch, scratch, right);
|
| + }
|
| +
|
| + // Check for power of two on the right hand side.
|
| + __ sub(scratch, right, Operand(1), SetCC);
|
| + __ b(mi, &call_stub);
|
| + __ tst(scratch, right);
|
| + __ b(ne, &call_stub);
|
| + // Perform modulo operation.
|
| + __ and_(result, scratch, Operand(left));
|
| +
|
| + __ bind(&call_stub);
|
| + // Call the generic stub. The numbers in r0 and r1 have
|
| + // to be tagged to Smis. If that is not possible, deoptimize.
|
| + DeferredModI* deferred = new DeferredModI(this, instr);
|
| + __ TrySmiTag(left, &deoptimize, scratch);
|
| + __ TrySmiTag(right, &deoptimize, scratch);
|
| +
|
| + __ b(al, deferred->entry());
|
| + __ bind(deferred->exit());
|
| +
|
| + // If the result in r0 is a Smi, untag it, else deoptimize.
|
| + __ JumpIfNotSmi(result, &deoptimize);
|
| + __ SmiUntag(result);
|
| +
|
| + __ b(al, &done);
|
| + __ bind(&deoptimize);
|
| + DeoptimizeIf(al, instr->environment());
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| void LCodeGen::DoDivI(LDivI* instr) {
|
| - Abort("DoDivI unimplemented.");
|
| + class DeferredDivI: public LDeferredCode {
|
| + public:
|
| + DeferredDivI(LCodeGen* codegen, LDivI* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() {
|
| + codegen()->DoDeferredGenericBinaryStub(instr_, Token::DIV);
|
| + }
|
| + private:
|
| + LDivI* instr_;
|
| + };
|
| +
|
| + const Register left = ToRegister(instr->InputAt(0));
|
| + const Register right = ToRegister(instr->InputAt(1));
|
| + const Register scratch = scratch0();
|
| + const Register result = ToRegister(instr->result());
|
| +
|
| + // Check for x / 0.
|
| + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
|
| + __ tst(right, right);
|
| + DeoptimizeIf(eq, instr->environment());
|
| + }
|
| +
|
| + // Check for (0 / -x) that will produce negative zero.
|
| + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
|
| + Label left_not_zero;
|
| + __ tst(left, Operand(left));
|
| + __ b(ne, &left_not_zero);
|
| + __ tst(right, Operand(right));
|
| + DeoptimizeIf(mi, instr->environment());
|
| + __ bind(&left_not_zero);
|
| + }
|
| +
|
| + // Check for (-kMinInt / -1).
|
| + if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
|
| + Label left_not_min_int;
|
| + __ cmp(left, Operand(kMinInt));
|
| + __ b(ne, &left_not_min_int);
|
| + __ cmp(right, Operand(-1));
|
| + DeoptimizeIf(eq, instr->environment());
|
| + __ bind(&left_not_min_int);
|
| + }
|
| +
|
| + Label done, deoptimize;
|
| + // Test for a few common cases first.
|
| + __ cmp(right, Operand(1));
|
| + __ mov(result, left, LeaveCC, eq);
|
| + __ b(eq, &done);
|
| +
|
| + __ cmp(right, Operand(2));
|
| + __ tst(left, Operand(1), eq);
|
| + __ mov(result, Operand(left, ASR, 1), LeaveCC, eq);
|
| + __ b(eq, &done);
|
| +
|
| + __ cmp(right, Operand(4));
|
| + __ tst(left, Operand(3), eq);
|
| + __ mov(result, Operand(left, ASR, 2), LeaveCC, eq);
|
| + __ b(eq, &done);
|
| +
|
| + // Call the generic stub. The numbers in r0 and r1 have
|
| + // to be tagged to Smis. If that is not possible, deoptimize.
|
| + DeferredDivI* deferred = new DeferredDivI(this, instr);
|
| +
|
| + __ TrySmiTag(left, &deoptimize, scratch);
|
| + __ TrySmiTag(right, &deoptimize, scratch);
|
| +
|
| + __ b(al, deferred->entry());
|
| + __ bind(deferred->exit());
|
| +
|
| + // If the result in r0 is a Smi, untag it, else deoptimize.
|
| + __ JumpIfNotSmi(result, &deoptimize);
|
| + __ SmiUntag(result);
|
| + __ b(&done);
|
| +
|
| + __ bind(&deoptimize);
|
| + DeoptimizeIf(al, instr->environment());
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| +template<int T>
|
| +void LCodeGen::DoDeferredGenericBinaryStub(LTemplateInstruction<1, 2, T>* instr,
|
| + Token::Value op) {
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = ToRegister(instr->InputAt(1));
|
| +
|
| + __ PushSafepointRegistersAndDoubles();
|
| + GenericBinaryOpStub stub(op, OVERWRITE_LEFT, left, right);
|
| + __ CallStub(&stub);
|
| + RecordSafepointWithRegistersAndDoubles(instr->pointer_map(),
|
| + 0,
|
| + Safepoint::kNoDeoptimizationIndex);
|
| + // Overwrite the stored value of r0 with the result of the stub.
|
| + __ str(r0, MemOperand(sp, DwVfpRegister::kNumAllocatableRegisters *
|
| + kDoubleSize));
|
| + __ PopSafepointRegistersAndDoubles();
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoMulI(LMulI* instr) {
|
| Register scratch = scratch0();
|
| - Register left = ToRegister(instr->left());
|
| - Register right = EmitLoadRegister(instr->right(), scratch);
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = EmitLoadRegister(instr->InputAt(1), scratch);
|
|
|
| if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero) &&
|
| - !instr->right()->IsConstantOperand()) {
|
| - __ orr(ToRegister(instr->temp()), left, right);
|
| + !instr->InputAt(1)->IsConstantOperand()) {
|
| + __ orr(ToRegister(instr->TempAt(0)), left, right);
|
| }
|
|
|
| if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
|
| @@ -861,13 +1214,13 @@
|
| Label done;
|
| __ tst(left, Operand(left));
|
| __ b(ne, &done);
|
| - if (instr->right()->IsConstantOperand()) {
|
| - if (ToInteger32(LConstantOperand::cast(instr->right())) < 0) {
|
| - DeoptimizeIf(no_condition, instr->environment());
|
| + if (instr->InputAt(1)->IsConstantOperand()) {
|
| + if (ToInteger32(LConstantOperand::cast(instr->InputAt(1))) < 0) {
|
| + DeoptimizeIf(kNoCondition, instr->environment());
|
| }
|
| } else {
|
| // Test the non-zero operand for negative sign.
|
| - __ cmp(ToRegister(instr->temp()), Operand(0));
|
| + __ cmp(ToRegister(instr->TempAt(0)), Operand(0));
|
| DeoptimizeIf(mi, instr->environment());
|
| }
|
| __ bind(&done);
|
| @@ -876,8 +1229,8 @@
|
|
|
|
|
| void LCodeGen::DoBitI(LBitI* instr) {
|
| - LOperand* left = instr->left();
|
| - LOperand* right = instr->right();
|
| + LOperand* left = instr->InputAt(0);
|
| + LOperand* right = instr->InputAt(1);
|
| ASSERT(left->Equals(instr->result()));
|
| ASSERT(left->IsRegister());
|
| Register result = ToRegister(left);
|
| @@ -901,8 +1254,8 @@
|
|
|
| void LCodeGen::DoShiftI(LShiftI* instr) {
|
| Register scratch = scratch0();
|
| - LOperand* left = instr->left();
|
| - LOperand* right = instr->right();
|
| + LOperand* left = instr->InputAt(0);
|
| + LOperand* right = instr->InputAt(1);
|
| ASSERT(left->Equals(instr->result()));
|
| ASSERT(left->IsRegister());
|
| Register result = ToRegister(left);
|
| @@ -959,9 +1312,9 @@
|
|
|
|
|
| void LCodeGen::DoSubI(LSubI* instr) {
|
| - Register left = ToRegister(instr->left());
|
| - Register right = EmitLoadRegister(instr->right(), ip);
|
| - ASSERT(instr->left()->Equals(instr->result()));
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = EmitLoadRegister(instr->InputAt(1), ip);
|
| + ASSERT(instr->InputAt(0)->Equals(instr->result()));
|
| __ sub(left, left, right, SetCC);
|
| if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
|
| DeoptimizeIf(vs, instr->environment());
|
| @@ -976,7 +1329,10 @@
|
|
|
|
|
| void LCodeGen::DoConstantD(LConstantD* instr) {
|
| - Abort("DoConstantD unimplemented.");
|
| + ASSERT(instr->result()->IsDoubleRegister());
|
| + DwVfpRegister result = ToDoubleRegister(instr->result());
|
| + double v = instr->value();
|
| + __ vmov(result, v);
|
| }
|
|
|
|
|
| @@ -988,22 +1344,22 @@
|
|
|
| void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
|
| Register result = ToRegister(instr->result());
|
| - Register array = ToRegister(instr->input());
|
| + Register array = ToRegister(instr->InputAt(0));
|
| __ ldr(result, FieldMemOperand(array, JSArray::kLengthOffset));
|
| }
|
|
|
|
|
| void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
|
| Register result = ToRegister(instr->result());
|
| - Register array = ToRegister(instr->input());
|
| + Register array = ToRegister(instr->InputAt(0));
|
| __ ldr(result, FieldMemOperand(array, FixedArray::kLengthOffset));
|
| }
|
|
|
|
|
| void LCodeGen::DoValueOf(LValueOf* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| - Register map = ToRegister(instr->temporary());
|
| + Register map = ToRegister(instr->TempAt(0));
|
| ASSERT(input.is(result));
|
| Label done;
|
|
|
| @@ -1021,14 +1377,14 @@
|
|
|
|
|
| void LCodeGen::DoBitNotI(LBitNotI* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->Equals(instr->result()));
|
| __ mvn(ToRegister(input), Operand(ToRegister(input)));
|
| }
|
|
|
|
|
| void LCodeGen::DoThrow(LThrow* instr) {
|
| - Register input_reg = EmitLoadRegister(instr->input(), ip);
|
| + Register input_reg = EmitLoadRegister(instr->InputAt(0), ip);
|
| __ push(input_reg);
|
| CallRuntime(Runtime::kThrow, 1, instr);
|
|
|
| @@ -1039,8 +1395,8 @@
|
|
|
|
|
| void LCodeGen::DoAddI(LAddI* instr) {
|
| - LOperand* left = instr->left();
|
| - LOperand* right = instr->right();
|
| + LOperand* left = instr->InputAt(0);
|
| + LOperand* right = instr->InputAt(1);
|
| ASSERT(left->Equals(instr->result()));
|
|
|
| Register right_reg = EmitLoadRegister(right, ip);
|
| @@ -1053,8 +1409,8 @@
|
|
|
|
|
| void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
|
| - DoubleRegister left = ToDoubleRegister(instr->left());
|
| - DoubleRegister right = ToDoubleRegister(instr->right());
|
| + DoubleRegister left = ToDoubleRegister(instr->InputAt(0));
|
| + DoubleRegister right = ToDoubleRegister(instr->InputAt(1));
|
| switch (instr->op()) {
|
| case Token::ADD:
|
| __ vadd(left, left, right);
|
| @@ -1080,8 +1436,8 @@
|
|
|
|
|
| void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
|
| - ASSERT(ToRegister(instr->left()).is(r1));
|
| - ASSERT(ToRegister(instr->right()).is(r0));
|
| + ASSERT(ToRegister(instr->InputAt(0)).is(r1));
|
| + ASSERT(ToRegister(instr->InputAt(1)).is(r0));
|
| ASSERT(ToRegister(instr->result()).is(r0));
|
|
|
| // TODO(regis): Implement TypeRecordingBinaryOpStub and replace current
|
| @@ -1125,11 +1481,11 @@
|
|
|
| Representation r = instr->hydrogen()->representation();
|
| if (r.IsInteger32()) {
|
| - Register reg = ToRegister(instr->input());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| __ cmp(reg, Operand(0));
|
| - EmitBranch(true_block, false_block, nz);
|
| + EmitBranch(true_block, false_block, ne);
|
| } else if (r.IsDouble()) {
|
| - DoubleRegister reg = ToDoubleRegister(instr->input());
|
| + DoubleRegister reg = ToDoubleRegister(instr->InputAt(0));
|
| Register scratch = scratch0();
|
|
|
| // Test the double value. Zero and NaN are false.
|
| @@ -1138,7 +1494,7 @@
|
| EmitBranch(true_block, false_block, ne);
|
| } else {
|
| ASSERT(r.IsTagged());
|
| - Register reg = ToRegister(instr->input());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| if (instr->hydrogen()->type().IsBoolean()) {
|
| __ LoadRoot(ip, Heap::kTrueValueRootIndex);
|
| __ cmp(reg, ip);
|
| @@ -1185,7 +1541,7 @@
|
| __ CallStub(&stub);
|
| __ cmp(reg, Operand(0));
|
| __ ldm(ia_w, sp, saved_regs);
|
| - EmitBranch(true_block, false_block, nz);
|
| + EmitBranch(true_block, false_block, ne);
|
| }
|
| }
|
| }
|
| @@ -1237,7 +1593,7 @@
|
|
|
|
|
| Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) {
|
| - Condition cond = no_condition;
|
| + Condition cond = kNoCondition;
|
| switch (op) {
|
| case Token::EQ:
|
| case Token::EQ_STRICT:
|
| @@ -1281,24 +1637,29 @@
|
|
|
|
|
| void LCodeGen::DoCmpJSObjectEq(LCmpJSObjectEq* instr) {
|
| - Register left = ToRegister(instr->left());
|
| - Register right = ToRegister(instr->right());
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = ToRegister(instr->InputAt(1));
|
| Register result = ToRegister(instr->result());
|
|
|
| __ cmp(left, Operand(right));
|
| __ LoadRoot(result, Heap::kTrueValueRootIndex, eq);
|
| __ LoadRoot(result, Heap::kFalseValueRootIndex, ne);
|
| - Abort("DoCmpJSObjectEq untested.");
|
| }
|
|
|
|
|
| void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
|
| - Abort("DoCmpJSObjectEqAndBranch unimplemented.");
|
| + Register left = ToRegister(instr->InputAt(0));
|
| + Register right = ToRegister(instr->InputAt(1));
|
| + int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| + int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| +
|
| + __ cmp(left, Operand(right));
|
| + EmitBranch(true_block, false_block, eq);
|
| }
|
|
|
|
|
| void LCodeGen::DoIsNull(LIsNull* instr) {
|
| - Register reg = ToRegister(instr->input());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
|
|
| __ LoadRoot(ip, Heap::kNullValueRootIndex);
|
| @@ -1333,7 +1694,7 @@
|
|
|
| void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
|
| Register scratch = scratch0();
|
| - Register reg = ToRegister(instr->input());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
|
|
| // TODO(fsc): If the expression is known to be a smi, then it's
|
| // definitely not null. Jump to the false block.
|
| @@ -1369,25 +1730,69 @@
|
| Register temp2,
|
| Label* is_not_object,
|
| Label* is_object) {
|
| - Abort("EmitIsObject unimplemented.");
|
| - return ne;
|
| + __ JumpIfSmi(input, is_not_object);
|
| +
|
| + __ LoadRoot(temp1, Heap::kNullValueRootIndex);
|
| + __ cmp(input, temp1);
|
| + __ b(eq, is_object);
|
| +
|
| + // Load map.
|
| + __ ldr(temp1, FieldMemOperand(input, HeapObject::kMapOffset));
|
| + // Undetectable objects behave like undefined.
|
| + __ ldrb(temp2, FieldMemOperand(temp1, Map::kBitFieldOffset));
|
| + __ tst(temp2, Operand(1 << Map::kIsUndetectable));
|
| + __ b(ne, is_not_object);
|
| +
|
| + // Load instance type and check that it is in object type range.
|
| + __ ldrb(temp2, FieldMemOperand(temp1, Map::kInstanceTypeOffset));
|
| + __ cmp(temp2, Operand(FIRST_JS_OBJECT_TYPE));
|
| + __ b(lt, is_not_object);
|
| + __ cmp(temp2, Operand(LAST_JS_OBJECT_TYPE));
|
| + return le;
|
| }
|
|
|
|
|
| void LCodeGen::DoIsObject(LIsObject* instr) {
|
| - Abort("DoIsObject unimplemented.");
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| + Register result = ToRegister(instr->result());
|
| + Register temp = scratch0();
|
| + Label is_false, is_true, done;
|
| +
|
| + Condition true_cond = EmitIsObject(reg, result, temp, &is_false, &is_true);
|
| + __ b(true_cond, &is_true);
|
| +
|
| + __ bind(&is_false);
|
| + __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| + __ b(&done);
|
| +
|
| + __ bind(&is_true);
|
| + __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| +
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
| - Abort("DoIsObjectAndBranch unimplemented.");
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| + Register temp1 = ToRegister(instr->TempAt(0));
|
| + Register temp2 = scratch0();
|
| +
|
| + int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| + int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| + Label* true_label = chunk_->GetAssemblyLabel(true_block);
|
| + Label* false_label = chunk_->GetAssemblyLabel(false_block);
|
| +
|
| + Condition true_cond =
|
| + EmitIsObject(reg, temp1, temp2, false_label, true_label);
|
| +
|
| + EmitBranch(true_block, false_block, true_cond);
|
| }
|
|
|
|
|
| void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
| ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
| Register result = ToRegister(instr->result());
|
| - Register input_reg = EmitLoadRegister(instr->input(), ip);
|
| + Register input_reg = EmitLoadRegister(instr->InputAt(0), ip);
|
| __ tst(input_reg, Operand(kSmiTagMask));
|
| __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| Label done;
|
| @@ -1401,24 +1806,24 @@
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
|
|
| - Register input_reg = EmitLoadRegister(instr->input(), ip);
|
| + Register input_reg = EmitLoadRegister(instr->InputAt(0), ip);
|
| __ tst(input_reg, Operand(kSmiTagMask));
|
| EmitBranch(true_block, false_block, eq);
|
| }
|
|
|
|
|
| -InstanceType LHasInstanceType::TestType() {
|
| - InstanceType from = hydrogen()->from();
|
| - InstanceType to = hydrogen()->to();
|
| +static InstanceType TestType(HHasInstanceType* instr) {
|
| + InstanceType from = instr->from();
|
| + InstanceType to = instr->to();
|
| if (from == FIRST_TYPE) return to;
|
| ASSERT(from == to || to == LAST_TYPE);
|
| return from;
|
| }
|
|
|
|
|
| -Condition LHasInstanceType::BranchCondition() {
|
| - InstanceType from = hydrogen()->from();
|
| - InstanceType to = hydrogen()->to();
|
| +static Condition BranchCondition(HHasInstanceType* instr) {
|
| + InstanceType from = instr->from();
|
| + InstanceType to = instr->to();
|
| if (from == to) return eq;
|
| if (to == LAST_TYPE) return hs;
|
| if (from == FIRST_TYPE) return ls;
|
| @@ -1428,13 +1833,25 @@
|
|
|
|
|
| void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
|
| - Abort("DoHasInstanceType unimplemented.");
|
| + Register input = ToRegister(instr->InputAt(0));
|
| + Register result = ToRegister(instr->result());
|
| +
|
| + ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
| + Label done;
|
| + __ tst(input, Operand(kSmiTagMask));
|
| + __ LoadRoot(result, Heap::kFalseValueRootIndex, eq);
|
| + __ b(eq, &done);
|
| + __ CompareObjectType(input, result, result, TestType(instr->hydrogen()));
|
| + Condition cond = BranchCondition(instr->hydrogen());
|
| + __ LoadRoot(result, Heap::kTrueValueRootIndex, cond);
|
| + __ LoadRoot(result, Heap::kFalseValueRootIndex, NegateCondition(cond));
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
|
| Register scratch = scratch0();
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
|
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| @@ -1444,8 +1861,8 @@
|
| __ tst(input, Operand(kSmiTagMask));
|
| __ b(eq, false_label);
|
|
|
| - __ CompareObjectType(input, scratch, scratch, instr->TestType());
|
| - EmitBranch(true_block, false_block, instr->BranchCondition());
|
| + __ CompareObjectType(input, scratch, scratch, TestType(instr->hydrogen()));
|
| + EmitBranch(true_block, false_block, BranchCondition(instr->hydrogen()));
|
| }
|
|
|
|
|
| @@ -1518,7 +1935,7 @@
|
|
|
|
|
| void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| ASSERT(input.is(result));
|
| Handle<String> class_name = instr->hydrogen()->class_name();
|
| @@ -1539,9 +1956,9 @@
|
|
|
|
|
| void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| Register temp = scratch0();
|
| - Register temp2 = ToRegister(instr->temporary());
|
| + Register temp2 = ToRegister(instr->TempAt(0));
|
| Handle<String> class_name = instr->hydrogen()->class_name();
|
|
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| @@ -1557,8 +1974,8 @@
|
|
|
|
|
| void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
|
| - Register reg = ToRegister(instr->input());
|
| - Register temp = ToRegister(instr->temp());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| + Register temp = ToRegister(instr->TempAt(0));
|
| int true_block = instr->true_block_id();
|
| int false_block = instr->false_block_id();
|
|
|
| @@ -1569,8 +1986,8 @@
|
|
|
|
|
| void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
|
| - ASSERT(ToRegister(instr->left()).is(r0)); // Object is in r0.
|
| - ASSERT(ToRegister(instr->right()).is(r1)); // Function is in r1.
|
| + ASSERT(ToRegister(instr->InputAt(0)).is(r0)); // Object is in r0.
|
| + ASSERT(ToRegister(instr->InputAt(1)).is(r1)); // Function is in r1.
|
|
|
| InstanceofStub stub(InstanceofStub::kArgsInRegisters);
|
| CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| @@ -1588,10 +2005,122 @@
|
|
|
|
|
| void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
|
| - Abort("DoInstanceOfKnownGlobal unimplemented.");
|
| + class DeferredInstanceOfKnownGlobal: public LDeferredCode {
|
| + public:
|
| + DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
|
| + LInstanceOfKnownGlobal* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() {
|
| + codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_);
|
| + }
|
| +
|
| + Label* map_check() { return &map_check_; }
|
| +
|
| + private:
|
| + LInstanceOfKnownGlobal* instr_;
|
| + Label map_check_;
|
| + };
|
| +
|
| + DeferredInstanceOfKnownGlobal* deferred;
|
| + deferred = new DeferredInstanceOfKnownGlobal(this, instr);
|
| +
|
| + Label done, false_result;
|
| + Register object = ToRegister(instr->InputAt(0));
|
| + Register temp = ToRegister(instr->TempAt(0));
|
| + Register result = ToRegister(instr->result());
|
| +
|
| + ASSERT(object.is(r0));
|
| + ASSERT(result.is(r0));
|
| +
|
| + // A Smi is not instance of anything.
|
| + __ JumpIfSmi(object, &false_result);
|
| +
|
| + // This is the inlined call site instanceof cache. The two occurences of the
|
| + // hole value will be patched to the last map/result pair generated by the
|
| + // instanceof stub.
|
| + Label cache_miss;
|
| + Register map = temp;
|
| + __ ldr(map, FieldMemOperand(object, HeapObject::kMapOffset));
|
| + __ bind(deferred->map_check()); // Label for calculating code patching.
|
| + // We use Factory::the_hole_value() on purpose instead of loading from the
|
| + // root array to force relocation to be able to later patch with
|
| + // the cached map.
|
| + __ mov(ip, Operand(FACTORY->the_hole_value()));
|
| + __ cmp(map, Operand(ip));
|
| + __ b(ne, &cache_miss);
|
| + // We use Factory::the_hole_value() on purpose instead of loading from the
|
| + // root array to force relocation to be able to later patch
|
| + // with true or false.
|
| + __ mov(result, Operand(FACTORY->the_hole_value()));
|
| + __ b(&done);
|
| +
|
| + // The inlined call site cache did not match. Check null and string before
|
| + // calling the deferred code.
|
| + __ bind(&cache_miss);
|
| + // Null is not instance of anything.
|
| + __ LoadRoot(ip, Heap::kNullValueRootIndex);
|
| + __ cmp(object, Operand(ip));
|
| + __ b(eq, &false_result);
|
| +
|
| + // String values is not instance of anything.
|
| + Condition is_string = masm_->IsObjectStringType(object, temp);
|
| + __ b(is_string, &false_result);
|
| +
|
| + // Go to the deferred code.
|
| + __ b(deferred->entry());
|
| +
|
| + __ bind(&false_result);
|
| + __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| +
|
| + // Here result has either true or false. Deferred code also produces true or
|
| + // false object.
|
| + __ bind(deferred->exit());
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| +void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
| + Label* map_check) {
|
| + Register result = ToRegister(instr->result());
|
| + ASSERT(result.is(r0));
|
| +
|
| + InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
|
| + flags = static_cast<InstanceofStub::Flags>(
|
| + flags | InstanceofStub::kArgsInRegisters);
|
| + flags = static_cast<InstanceofStub::Flags>(
|
| + flags | InstanceofStub::kCallSiteInlineCheck);
|
| + flags = static_cast<InstanceofStub::Flags>(
|
| + flags | InstanceofStub::kReturnTrueFalseObject);
|
| + InstanceofStub stub(flags);
|
| +
|
| + __ PushSafepointRegisters();
|
| +
|
| + // Get the temp register reserved by the instruction. This needs to be r4 as
|
| + // its slot of the pushing of safepoint registers is used to communicate the
|
| + // offset to the location of the map check.
|
| + Register temp = ToRegister(instr->TempAt(0));
|
| + ASSERT(temp.is(r4));
|
| + __ mov(InstanceofStub::right(), Operand(instr->function()));
|
| + static const int kAdditionalDelta = 4;
|
| + int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta;
|
| + Label before_push_delta;
|
| + __ bind(&before_push_delta);
|
| + __ BlockConstPoolFor(kAdditionalDelta);
|
| + __ mov(temp, Operand(delta * kPointerSize));
|
| + __ StoreToSafepointRegisterSlot(temp);
|
| + __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
|
| + ASSERT_EQ(kAdditionalDelta,
|
| + masm_->InstructionsGeneratedSince(&before_push_delta));
|
| + RecordSafepointWithRegisters(
|
| + instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
|
| + // Put the result value into the result register slot and
|
| + // restore all registers.
|
| + __ StoreToSafepointRegisterSlot(result);
|
| +
|
| + __ PopSafepointRegisters();
|
| +}
|
| +
|
| +
|
| static Condition ComputeCompareCondition(Token::Value op) {
|
| switch (op) {
|
| case Token::EQ_STRICT:
|
| @@ -1607,7 +2136,7 @@
|
| return ge;
|
| default:
|
| UNREACHABLE();
|
| - return no_condition;
|
| + return kNoCondition;
|
| }
|
| }
|
|
|
| @@ -1665,14 +2194,22 @@
|
|
|
|
|
| void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
| - Register value = ToRegister(instr->input());
|
| + Register value = ToRegister(instr->InputAt(0));
|
| __ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
|
| __ str(value, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
|
| }
|
|
|
|
|
| +void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
| + // TODO(antonm): load a context with a separate instruction.
|
| + Register result = ToRegister(instr->result());
|
| + __ LoadContext(result, instr->context_chain_length());
|
| + __ ldr(result, ContextOperand(result, instr->slot_index()));
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
|
| - Register object = ToRegister(instr->input());
|
| + Register object = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| if (instr->hydrogen()->is_in_object()) {
|
| __ ldr(result, FieldMemOperand(object, instr->hydrogen()->offset()));
|
| @@ -1740,8 +2277,8 @@
|
|
|
|
|
| void LCodeGen::DoLoadElements(LLoadElements* instr) {
|
| - ASSERT(instr->result()->Equals(instr->input()));
|
| - Register reg = ToRegister(instr->input());
|
| + ASSERT(instr->result()->Equals(instr->InputAt(0)));
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| Register scratch = scratch0();
|
|
|
| __ ldr(reg, FieldMemOperand(reg, JSObject::kElementsOffset));
|
| @@ -1823,7 +2360,7 @@
|
|
|
|
|
| void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
|
| - Register elem = ToRegister(instr->input());
|
| + Register elem = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
|
|
| Label done;
|
| @@ -1908,7 +2445,7 @@
|
|
|
|
|
| void LCodeGen::DoPushArgument(LPushArgument* instr) {
|
| - LOperand* argument = instr->input();
|
| + LOperand* argument = instr->InputAt(0);
|
| if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) {
|
| Abort("DoPushArgument not implemented for double type.");
|
| } else {
|
| @@ -1972,20 +2509,121 @@
|
|
|
|
|
| void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
|
| - Abort("DoDeferredMathAbsTaggedHeapNumber unimplemented.");
|
| + Register input = ToRegister(instr->InputAt(0));
|
| + Register scratch = scratch0();
|
| +
|
| + // Deoptimize if not a heap number.
|
| + __ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
|
| + __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
|
| + __ cmp(scratch, Operand(ip));
|
| + DeoptimizeIf(ne, instr->environment());
|
| +
|
| + Label done;
|
| +
|
| + Label negative;
|
| + __ ldr(scratch, FieldMemOperand(input, HeapNumber::kExponentOffset));
|
| + // Check the sign of the argument. If the argument is positive, just
|
| + // return it. We do not need to patch the stack since |input| and
|
| + // |result| are the same register and |input| will be restored
|
| + // unchanged by popping safepoint registers.
|
| + __ tst(scratch, Operand(HeapNumber::kSignMask));
|
| + __ b(ne, &negative);
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&negative);
|
| + // Preserve the value of all registers.
|
| + __ PushSafepointRegisters();
|
| +
|
| + Register tmp = input.is(r0) ? r1 : r0;
|
| + Register tmp2 = input.is(r2) ? r3 : r2;
|
| + Register tmp3 = input.is(r4) ? r5 : r4;
|
| +
|
| + Label allocated, slow;
|
| + __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex);
|
| + __ AllocateHeapNumber(tmp, tmp2, tmp3, scratch, &slow);
|
| + __ b(&allocated);
|
| +
|
| + // Slow case: Call the runtime system to do the number allocation.
|
| + __ bind(&slow);
|
| +
|
| + __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
|
| + RecordSafepointWithRegisters(
|
| + instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
|
| + // Set the pointer to the new heap number in tmp.
|
| + if (!tmp.is(r0)) __ mov(tmp, Operand(r0));
|
| +
|
| + // Restore input_reg after call to runtime.
|
| + MemOperand input_register_slot = masm()->SafepointRegisterSlot(input);
|
| + __ ldr(input, input_register_slot);
|
| +
|
| + __ bind(&allocated);
|
| + __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kExponentOffset));
|
| + __ bic(tmp2, tmp2, Operand(HeapNumber::kSignMask));
|
| + __ str(tmp2, FieldMemOperand(tmp, HeapNumber::kExponentOffset));
|
| + __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
|
| + __ str(tmp2, FieldMemOperand(tmp, HeapNumber::kMantissaOffset));
|
| +
|
| + __ str(tmp, input_register_slot);
|
| + __ PopSafepointRegisters();
|
| +
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| +void LCodeGen::EmitIntegerMathAbs(LUnaryMathOperation* instr) {
|
| + Label is_positive;
|
| + uint32_t kSignMask = 0x80000000u;
|
| + Register input = ToRegister(instr->InputAt(0));
|
| + __ tst(input, Operand(kSignMask));
|
| + __ b(eq, &is_positive);
|
| + __ rsb(input, input, Operand(0), SetCC);
|
| + // Deoptimize on overflow.
|
| + DeoptimizeIf(vs, instr->environment());
|
| + __ bind(&is_positive);
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
|
| - Abort("DoMathAbs unimplemented.");
|
| + // Class for deferred case.
|
| + class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
|
| + public:
|
| + DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen,
|
| + LUnaryMathOperation* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() {
|
| + codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
|
| + }
|
| + private:
|
| + LUnaryMathOperation* instr_;
|
| + };
|
| +
|
| + ASSERT(instr->InputAt(0)->Equals(instr->result()));
|
| + Representation r = instr->hydrogen()->value()->representation();
|
| + if (r.IsDouble()) {
|
| + DwVfpRegister input = ToDoubleRegister(instr->InputAt(0));
|
| + // __ vabs(input, input);
|
| + Abort("Double DoMathAbs unimplemented");
|
| + } else if (r.IsInteger32()) {
|
| + EmitIntegerMathAbs(instr);
|
| + } else {
|
| + // Representation is tagged.
|
| + DeferredMathAbsTaggedHeapNumber* deferred =
|
| + new DeferredMathAbsTaggedHeapNumber(this, instr);
|
| + Register input = ToRegister(instr->InputAt(0));
|
| + // Smi check.
|
| + __ JumpIfNotSmi(input, deferred->entry());
|
| + // If smi, handle it directly.
|
| + EmitIntegerMathAbs(instr);
|
| + __ bind(deferred->exit());
|
| + }
|
| }
|
|
|
|
|
| void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
|
| - DoubleRegister input = ToDoubleRegister(instr->input());
|
| + DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| - Register prev_fpscr = ToRegister(instr->temp());
|
| - SwVfpRegister single_scratch = single_scratch0();
|
| + Register prev_fpscr = ToRegister(instr->TempAt(0));
|
| + SwVfpRegister single_scratch = double_scratch0().low();
|
| Register scratch = scratch0();
|
|
|
| // Set custom FPCSR:
|
| @@ -2013,11 +2651,20 @@
|
|
|
| // Move the result back to general purpose register r0.
|
| __ vmov(result, single_scratch);
|
| +
|
| + // Test for -0.
|
| + Label done;
|
| + __ cmp(result, Operand(0));
|
| + __ b(ne, &done);
|
| + __ vmov(scratch, input.high());
|
| + __ tst(scratch, Operand(HeapNumber::kSignMask));
|
| + DeoptimizeIf(ne, instr->environment());
|
| + __ bind(&done);
|
| }
|
|
|
|
|
| void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
|
| - DoubleRegister input = ToDoubleRegister(instr->input());
|
| + DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
|
| ASSERT(ToDoubleRegister(instr->result()).is(input));
|
| __ vsqrt(input, input);
|
| }
|
| @@ -2098,7 +2745,7 @@
|
|
|
|
|
| void LCodeGen::DoCallNew(LCallNew* instr) {
|
| - ASSERT(ToRegister(instr->input()).is(r1));
|
| + ASSERT(ToRegister(instr->InputAt(0)).is(r1));
|
| ASSERT(ToRegister(instr->result()).is(r0));
|
|
|
| Handle<Code> builtin(Isolate::Current()->builtins()->
|
| @@ -2200,8 +2847,169 @@
|
| }
|
|
|
|
|
| +void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
|
| + class DeferredStringCharCodeAt: public LDeferredCode {
|
| + public:
|
| + DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); }
|
| + private:
|
| + LStringCharCodeAt* instr_;
|
| + };
|
| +
|
| + Register scratch = scratch0();
|
| + Register string = ToRegister(instr->string());
|
| + Register index = no_reg;
|
| + int const_index = -1;
|
| + if (instr->index()->IsConstantOperand()) {
|
| + const_index = ToInteger32(LConstantOperand::cast(instr->index()));
|
| + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
|
| + if (!Smi::IsValid(const_index)) {
|
| + // Guaranteed to be out of bounds because of the assert above.
|
| + // So the bounds check that must dominate this instruction must
|
| + // have deoptimized already.
|
| + if (FLAG_debug_code) {
|
| + __ Abort("StringCharCodeAt: out of bounds index.");
|
| + }
|
| + // No code needs to be generated.
|
| + return;
|
| + }
|
| + } else {
|
| + index = ToRegister(instr->index());
|
| + }
|
| + Register result = ToRegister(instr->result());
|
| +
|
| + DeferredStringCharCodeAt* deferred =
|
| + new DeferredStringCharCodeAt(this, instr);
|
| +
|
| + Label flat_string, ascii_string, done;
|
| +
|
| + // Fetch the instance type of the receiver into result register.
|
| + __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
| + __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
| +
|
| + // We need special handling for non-flat strings.
|
| + STATIC_ASSERT(kSeqStringTag == 0);
|
| + __ tst(result, Operand(kStringRepresentationMask));
|
| + __ b(eq, &flat_string);
|
| +
|
| + // Handle non-flat strings.
|
| + __ tst(result, Operand(kIsConsStringMask));
|
| + __ b(eq, deferred->entry());
|
| +
|
| + // ConsString.
|
| + // Check whether the right hand side is the empty string (i.e. if
|
| + // this is really a flat string in a cons string). If that is not
|
| + // the case we would rather go to the runtime system now to flatten
|
| + // the string.
|
| + __ ldr(scratch, FieldMemOperand(string, ConsString::kSecondOffset));
|
| + __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
|
| + __ cmp(scratch, ip);
|
| + __ b(ne, deferred->entry());
|
| + // Get the first of the two strings and load its instance type.
|
| + __ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
| + __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
| + __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
| + // If the first cons component is also non-flat, then go to runtime.
|
| + STATIC_ASSERT(kSeqStringTag == 0);
|
| + __ tst(result, Operand(kStringRepresentationMask));
|
| + __ b(ne, deferred->entry());
|
| +
|
| + // Check for 1-byte or 2-byte string.
|
| + __ bind(&flat_string);
|
| + STATIC_ASSERT(kAsciiStringTag != 0);
|
| + __ tst(result, Operand(kStringEncodingMask));
|
| + __ b(ne, &ascii_string);
|
| +
|
| + // 2-byte string.
|
| + // Load the 2-byte character code into the result register.
|
| + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
|
| + if (instr->index()->IsConstantOperand()) {
|
| + __ ldrh(result,
|
| + FieldMemOperand(string,
|
| + SeqTwoByteString::kHeaderSize + 2 * const_index));
|
| + } else {
|
| + __ add(scratch,
|
| + string,
|
| + Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + __ ldrh(result, MemOperand(scratch, index, LSL, 1));
|
| + }
|
| + __ jmp(&done);
|
| +
|
| + // ASCII string.
|
| + // Load the byte into the result register.
|
| + __ bind(&ascii_string);
|
| + if (instr->index()->IsConstantOperand()) {
|
| + __ ldrb(result, FieldMemOperand(string,
|
| + SeqAsciiString::kHeaderSize + const_index));
|
| + } else {
|
| + __ add(scratch,
|
| + string,
|
| + Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + __ ldrb(result, MemOperand(scratch, index));
|
| + }
|
| + __ bind(&done);
|
| + __ bind(deferred->exit());
|
| +}
|
| +
|
| +
|
| +void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
|
| + Register string = ToRegister(instr->string());
|
| + Register result = ToRegister(instr->result());
|
| + Register scratch = scratch0();
|
| +
|
| + // TODO(3095996): Get rid of this. For now, we need to make the
|
| + // result register contain a valid pointer because it is already
|
| + // contained in the register pointer map.
|
| + __ mov(result, Operand(0));
|
| +
|
| + __ PushSafepointRegisters();
|
| + __ push(string);
|
| + // Push the index as a smi. This is safe because of the checks in
|
| + // DoStringCharCodeAt above.
|
| + if (instr->index()->IsConstantOperand()) {
|
| + int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
|
| + __ mov(scratch, Operand(Smi::FromInt(const_index)));
|
| + __ push(scratch);
|
| + } else {
|
| + Register index = ToRegister(instr->index());
|
| + __ SmiTag(index);
|
| + __ push(index);
|
| + }
|
| + __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt);
|
| + RecordSafepointWithRegisters(
|
| + instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex);
|
| + if (FLAG_debug_code) {
|
| + __ AbortIfNotSmi(r0);
|
| + }
|
| + __ SmiUntag(r0);
|
| + MemOperand result_stack_slot = masm()->SafepointRegisterSlot(result);
|
| + __ str(r0, result_stack_slot);
|
| + __ PopSafepointRegisters();
|
| +}
|
| +
|
| +
|
| +void LCodeGen::DoStringLength(LStringLength* instr) {
|
| + Register string = ToRegister(instr->InputAt(0));
|
| + Register result = ToRegister(instr->result());
|
| + __ ldr(result, FieldMemOperand(string, String::kLengthOffset));
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
|
| - Abort("DoInteger32ToDouble unimplemented.");
|
| + LOperand* input = instr->InputAt(0);
|
| + ASSERT(input->IsRegister() || input->IsStackSlot());
|
| + LOperand* output = instr->result();
|
| + ASSERT(output->IsDoubleRegister());
|
| + SwVfpRegister single_scratch = double_scratch0().low();
|
| + if (input->IsStackSlot()) {
|
| + Register scratch = scratch0();
|
| + __ ldr(scratch, ToMemOperand(input));
|
| + __ vmov(single_scratch, scratch);
|
| + } else {
|
| + __ vmov(single_scratch, ToRegister(input));
|
| + }
|
| + __ vcvt_f64_s32(ToDoubleRegister(output), single_scratch);
|
| }
|
|
|
|
|
| @@ -2215,7 +3023,7 @@
|
| LNumberTagI* instr_;
|
| };
|
|
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister() && input->Equals(instr->result()));
|
| Register reg = ToRegister(input);
|
|
|
| @@ -2228,7 +3036,7 @@
|
|
|
| void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
|
| Label slow;
|
| - Register reg = ToRegister(instr->input());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| DoubleRegister dbl_scratch = d0;
|
| SwVfpRegister flt_scratch = s0;
|
|
|
| @@ -2285,11 +3093,11 @@
|
| LNumberTagD* instr_;
|
| };
|
|
|
| - DoubleRegister input_reg = ToDoubleRegister(instr->input());
|
| + DoubleRegister input_reg = ToDoubleRegister(instr->InputAt(0));
|
| Register scratch = scratch0();
|
| Register reg = ToRegister(instr->result());
|
| - Register temp1 = ToRegister(instr->temp1());
|
| - Register temp2 = ToRegister(instr->temp2());
|
| + Register temp1 = ToRegister(instr->TempAt(0));
|
| + Register temp2 = ToRegister(instr->TempAt(1));
|
|
|
| DeferredNumberTagD* deferred = new DeferredNumberTagD(this, instr);
|
| if (FLAG_inline_new) {
|
| @@ -2322,7 +3130,7 @@
|
|
|
|
|
| void LCodeGen::DoSmiTag(LSmiTag* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister() && input->Equals(instr->result()));
|
| ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
|
| __ SmiTag(ToRegister(input));
|
| @@ -2330,7 +3138,7 @@
|
|
|
|
|
| void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister() && input->Equals(instr->result()));
|
| if (instr->needs_check()) {
|
| __ tst(ToRegister(input), Operand(kSmiTagMask));
|
| @@ -2397,11 +3205,11 @@
|
|
|
| void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
|
| Label done;
|
| - Register input_reg = ToRegister(instr->input());
|
| + Register input_reg = ToRegister(instr->InputAt(0));
|
| Register scratch = scratch0();
|
| DoubleRegister dbl_scratch = d0;
|
| SwVfpRegister flt_scratch = s0;
|
| - DoubleRegister dbl_tmp = ToDoubleRegister(instr->temp());
|
| + DoubleRegister dbl_tmp = ToDoubleRegister(instr->TempAt(0));
|
|
|
| // Heap number map check.
|
| __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
|
| @@ -2458,7 +3266,7 @@
|
|
|
|
|
| void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister());
|
| ASSERT(input->Equals(instr->result()));
|
|
|
| @@ -2478,7 +3286,7 @@
|
|
|
|
|
| void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister());
|
| LOperand* result = instr->result();
|
| ASSERT(result->IsDoubleRegister());
|
| @@ -2496,7 +3304,7 @@
|
|
|
|
|
| void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister());
|
| __ tst(ToRegister(input), Operand(kSmiTagMask));
|
| DeoptimizeIf(instr->condition(), instr->environment());
|
| @@ -2504,7 +3312,7 @@
|
|
|
|
|
| void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| Register scratch = scratch0();
|
| InstanceType first = instr->hydrogen()->first();
|
| InstanceType last = instr->hydrogen()->last();
|
| @@ -2528,8 +3336,8 @@
|
|
|
|
|
| void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
|
| - ASSERT(instr->input()->IsRegister());
|
| - Register reg = ToRegister(instr->input());
|
| + ASSERT(instr->InputAt(0)->IsRegister());
|
| + Register reg = ToRegister(instr->InputAt(0));
|
| __ cmp(reg, Operand(instr->hydrogen()->target()));
|
| DeoptimizeIf(ne, instr->environment());
|
| }
|
| @@ -2537,7 +3345,7 @@
|
|
|
| void LCodeGen::DoCheckMap(LCheckMap* instr) {
|
| Register scratch = scratch0();
|
| - LOperand* input = instr->input();
|
| + LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister());
|
| Register reg = ToRegister(input);
|
| __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
|
| @@ -2546,28 +3354,28 @@
|
| }
|
|
|
|
|
| -void LCodeGen::LoadPrototype(Register result,
|
| - Handle<JSObject> prototype) {
|
| - if (HEAP->InNewSpace(*prototype)) {
|
| +void LCodeGen::LoadHeapObject(Register result,
|
| + Handle<HeapObject> object) {
|
| + if (HEAP->InNewSpace(*object)) {
|
| Handle<JSGlobalPropertyCell> cell =
|
| - FACTORY->NewJSGlobalPropertyCell(prototype);
|
| + FACTORY->NewJSGlobalPropertyCell(object);
|
| __ mov(result, Operand(cell));
|
| __ ldr(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset));
|
| } else {
|
| - __ mov(result, Operand(prototype));
|
| + __ mov(result, Operand(object));
|
| }
|
| }
|
|
|
|
|
| void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
| - Register temp1 = ToRegister(instr->temp1());
|
| - Register temp2 = ToRegister(instr->temp2());
|
| + Register temp1 = ToRegister(instr->TempAt(0));
|
| + Register temp2 = ToRegister(instr->TempAt(1));
|
|
|
| Handle<JSObject> holder = instr->holder();
|
| Handle<JSObject> current_prototype = instr->prototype();
|
|
|
| // Load prototype object.
|
| - LoadPrototype(temp1, current_prototype);
|
| + LoadHeapObject(temp1, current_prototype);
|
|
|
| // Check prototype maps up to the holder.
|
| while (!current_prototype.is_identical_to(holder)) {
|
| @@ -2577,7 +3385,7 @@
|
| current_prototype =
|
| Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
|
| // Load next prototype object.
|
| - LoadPrototype(temp1, current_prototype);
|
| + LoadHeapObject(temp1, current_prototype);
|
| }
|
|
|
| // Check the holder map.
|
| @@ -2709,14 +3517,14 @@
|
|
|
|
|
| void LCodeGen::DoTypeof(LTypeof* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| __ push(input);
|
| CallRuntime(Runtime::kTypeof, 1, instr);
|
| }
|
|
|
|
|
| void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| Label true_label;
|
| Label false_label;
|
| @@ -2739,7 +3547,7 @@
|
|
|
|
|
| void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
|
| - Register input = ToRegister(instr->input());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| Label* true_label = chunk_->GetAssemblyLabel(true_block);
|
| @@ -2758,7 +3566,7 @@
|
| Label* false_label,
|
| Register input,
|
| Handle<String> type_name) {
|
| - Condition final_branch_condition = no_condition;
|
| + Condition final_branch_condition = kNoCondition;
|
| Register scratch = scratch0();
|
| if (type_name->Equals(HEAP->number_symbol())) {
|
| __ tst(input, Operand(kSmiTagMask));
|
| @@ -2843,7 +3651,7 @@
|
|
|
|
|
| void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
|
| - DeoptimizeIf(no_condition, instr->environment());
|
| + DeoptimizeIf(kNoCondition, instr->environment());
|
| }
|
|
|
|
|
|
|