// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "codegen-inl.h" #include "jump-target-inl.h" #include "register-allocator-inl.h" namespace v8 { namespace internal { // ------------------------------------------------------------------------- // JumpTarget implementation. #define __ ACCESS_MASM(cgen()->masm()) void JumpTarget::DoJump() { ASSERT(cgen()->has_valid_frame()); // Live non-frame registers are not allowed at unconditional jumps // because we have no way of invalidating the corresponding results // which are still live in the C++ code. ASSERT(cgen()->HasValidEntryRegisters()); if (is_bound()) { // Backward jump. There is an expected frame to merge to. ASSERT(direction_ == BIDIRECTIONAL); cgen()->frame()->PrepareMergeTo(entry_frame_); cgen()->frame()->MergeTo(entry_frame_); cgen()->DeleteFrame(); __ jmp(&entry_label_); } else if (entry_frame_ != NULL) { // Forward jump with a preconfigured entry frame. Assert the // current frame matches the expected one and jump to the block. ASSERT(cgen()->frame()->Equals(entry_frame_)); cgen()->DeleteFrame(); __ jmp(&entry_label_); } else { // Forward jump. Remember the current frame and emit a jump to // its merge code. AddReachingFrame(cgen()->frame()); RegisterFile empty; cgen()->SetFrame(NULL, &empty); __ jmp(&merge_labels_.last()); } } void JumpTarget::DoBranch(Condition cc, Hint hint) { ASSERT(cgen() != NULL); ASSERT(cgen()->has_valid_frame()); if (is_bound()) { ASSERT(direction_ == BIDIRECTIONAL); // Backward branch. We have an expected frame to merge to on the // backward edge. // Swap the current frame for a copy (we do the swapping to get // the off-frame registers off the fall through) to use for the // branch. VirtualFrame* fall_through_frame = cgen()->frame(); VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame); RegisterFile non_frame_registers; cgen()->SetFrame(branch_frame, &non_frame_registers); // Check if we can avoid merge code. cgen()->frame()->PrepareMergeTo(entry_frame_); if (cgen()->frame()->Equals(entry_frame_)) { // Branch right in to the block. cgen()->DeleteFrame(); __ j(cc, &entry_label_, hint); cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } // Check if we can reuse existing merge code. for (int i = 0; i < reaching_frames_.length(); i++) { if (reaching_frames_[i] != NULL && cgen()->frame()->Equals(reaching_frames_[i])) { // Branch to the merge code. cgen()->DeleteFrame(); __ j(cc, &merge_labels_[i], hint); cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } } // To emit the merge code here, we negate the condition and branch // around the merge code on the fall through path. Label original_fall_through; __ j(NegateCondition(cc), &original_fall_through, NegateHint(hint)); cgen()->frame()->MergeTo(entry_frame_); cgen()->DeleteFrame(); __ jmp(&entry_label_); cgen()->SetFrame(fall_through_frame, &non_frame_registers); __ bind(&original_fall_through); } else if (entry_frame_ != NULL) { // Forward branch with a preconfigured entry frame. Assert the // current frame matches the expected one and branch to the block. ASSERT(cgen()->frame()->Equals(entry_frame_)); // Explicitly use the macro assembler instead of __ as forward // branches are expected to be a fixed size (no inserted // coverage-checking instructions please). This is used in // Reference::GetValue. cgen()->masm()->j(cc, &entry_label_, hint); } else { // Forward branch. A copy of the current frame is remembered and // a branch to the merge code is emitted. Explicitly use the // macro assembler instead of __ as forward branches are expected // to be a fixed size (no inserted coverage-checking instructions // please). This is used in Reference::GetValue. AddReachingFrame(new VirtualFrame(cgen()->frame())); cgen()->masm()->j(cc, &merge_labels_.last(), hint); } } void JumpTarget::Call() { // Call is used to push the address of the catch block on the stack as // a return address when compiling try/catch and try/finally. We // fully spill the frame before making the call. The expected frame // at the label (which should be the only one) is the spilled current // frame plus an in-memory return address. The "fall-through" frame // at the return site is the spilled current frame. ASSERT(cgen() != NULL); ASSERT(cgen()->has_valid_frame()); // There are no non-frame references across the call. ASSERT(cgen()->HasValidEntryRegisters()); ASSERT(!is_linked()); cgen()->frame()->SpillAll(); VirtualFrame* target_frame = new VirtualFrame(cgen()->frame()); target_frame->Adjust(1); // We do not expect a call with a preconfigured entry frame. ASSERT(entry_frame_ == NULL); AddReachingFrame(target_frame); __ call(&merge_labels_.last()); } void JumpTarget::DoBind() { ASSERT(cgen() != NULL); ASSERT(!is_bound()); // Live non-frame registers are not allowed at the start of a basic // block. ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters()); // Fast case: the jump target was manually configured with an entry // frame to use. if (entry_frame_ != NULL) { // Assert no reaching frames to deal with. ASSERT(reaching_frames_.is_empty()); ASSERT(!cgen()->has_valid_frame()); RegisterFile empty; if (direction_ == BIDIRECTIONAL) { // Copy the entry frame so the original can be used for a // possible backward jump. cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); } else { // Take ownership of the entry frame. cgen()->SetFrame(entry_frame_, &empty); entry_frame_ = NULL; } __ bind(&entry_label_); return; } if (!is_linked()) { ASSERT(cgen()->has_valid_frame()); if (direction_ == FORWARD_ONLY) { // Fast case: no forward jumps and no possible backward jumps. // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. VirtualFrame* frame = cgen()->frame(); int difference = frame->stack_pointer_ - (frame->element_count() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(Operand(esp), Immediate(difference * kPointerSize)); } } else { ASSERT(direction_ == BIDIRECTIONAL); // Fast case: no forward jumps, possible backward ones. Remove // constants and copies above the watermark on the fall-through // frame and use it as the entry frame. cgen()->frame()->MakeMergable(); entry_frame_ = new VirtualFrame(cgen()->frame()); } __ bind(&entry_label_); return; } if (direction_ == FORWARD_ONLY && !cgen()->has_valid_frame() && reaching_frames_.length() == 1) { // Fast case: no fall-through, a single forward jump, and no // possible backward jumps. Pick up the only reaching frame, take // ownership of it, and use it for the block about to be emitted. VirtualFrame* frame = reaching_frames_[0]; RegisterFile empty; cgen()->SetFrame(frame, &empty); reaching_frames_[0] = NULL; __ bind(&merge_labels_[0]); // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. int difference = frame->stack_pointer_ - (frame->element_count() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(Operand(esp), Immediate(difference * kPointerSize)); } __ bind(&entry_label_); return; } // If there is a current frame, record it as the fall-through. It // is owned by the reaching frames for now. bool had_fall_through = false; if (cgen()->has_valid_frame()) { had_fall_through = true; AddReachingFrame(cgen()->frame()); // Return value ignored. RegisterFile empty; cgen()->SetFrame(NULL, &empty); } // Compute the frame to use for entry to the block. ComputeEntryFrame(); // Some moves required to merge to an expected frame require purely // frame state changes, and do not require any code generation. // Perform those first to increase the possibility of finding equal // frames below. for (int i = 0; i < reaching_frames_.length(); i++) { if (reaching_frames_[i] != NULL) { reaching_frames_[i]->PrepareMergeTo(entry_frame_); } } if (is_linked()) { // There were forward jumps. Handle merging the reaching frames // to the entry frame. // Loop over the (non-null) reaching frames and process any that // need merge code. Iterate backwards through the list to handle // the fall-through frame first. Set frames that will be // processed after 'i' to NULL if we want to avoid processing // them. for (int i = reaching_frames_.length() - 1; i >= 0; i--) { VirtualFrame* frame = reaching_frames_[i]; if (frame != NULL) { // Does the frame (probably) need merge code? if (!frame->Equals(entry_frame_)) { // We could have a valid frame as the fall through to the // binding site or as the fall through from a previous merge // code block. Jump around the code we are about to // generate. if (cgen()->has_valid_frame()) { cgen()->DeleteFrame(); __ jmp(&entry_label_); } // Pick up the frame for this block. Assume ownership if // there cannot be backward jumps. RegisterFile empty; if (direction_ == BIDIRECTIONAL) { cgen()->SetFrame(new VirtualFrame(frame), &empty); } else { cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } __ bind(&merge_labels_[i]); // Loop over the remaining (non-null) reaching frames, // looking for any that can share merge code with this one. for (int j = 0; j < i; j++) { VirtualFrame* other = reaching_frames_[j]; if (other != NULL && other->Equals(cgen()->frame())) { // Set the reaching frame element to null to avoid // processing it later, and then bind its entry label. reaching_frames_[j] = NULL; __ bind(&merge_labels_[j]); } } // Emit the merge code. cgen()->frame()->MergeTo(entry_frame_); } else if (i == reaching_frames_.length() - 1 && had_fall_through) { // If this is the fall through frame, and it didn't need // merge code, we need to pick up the frame so we can jump // around subsequent merge blocks if necessary. RegisterFile empty; cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } } } // The code generator may not have a current frame if there was no // fall through and none of the reaching frames needed merging. // In that case, clone the entry frame as the current frame. if (!cgen()->has_valid_frame()) { RegisterFile empty; cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); } // There may be unprocessed reaching frames that did not need // merge code. They will have unbound merge labels. Bind their // merge labels to be the same as the entry label and deallocate // them. for (int i = 0; i < reaching_frames_.length(); i++) { if (!merge_labels_[i].is_bound()) { reaching_frames_[i] = NULL; __ bind(&merge_labels_[i]); } } // There are non-NULL reaching frames with bound labels for each // merge block, but only on backward targets. } else { // There were no forward jumps. There must be a current frame and // this must be a bidirectional target. ASSERT(reaching_frames_.length() == 1); ASSERT(reaching_frames_[0] != NULL); ASSERT(direction_ == BIDIRECTIONAL); // Use a copy of the reaching frame so the original can be saved // for possible reuse as a backward merge block. RegisterFile empty; cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty); __ bind(&merge_labels_[0]); cgen()->frame()->MergeTo(entry_frame_); } __ bind(&entry_label_); } void BreakTarget::Jump() { // Drop leftover statement state from the frame before merging, without // emitting code. ASSERT(cgen()->has_valid_frame()); int count = cgen()->frame()->height() - expected_height_; cgen()->frame()->ForgetElements(count); DoJump(); } void BreakTarget::Jump(Result* arg) { // Drop leftover statement state from the frame before merging, without // emitting code. ASSERT(cgen()->has_valid_frame()); int count = cgen()->frame()->height() - expected_height_; cgen()->frame()->ForgetElements(count); cgen()->frame()->Push(arg); DoJump(); } void BreakTarget::Bind() { #ifdef DEBUG // All the forward-reaching frames should have been adjusted at the // jumps to this target. for (int i = 0; i < reaching_frames_.length(); i++) { ASSERT(reaching_frames_[i] == NULL || reaching_frames_[i]->height() == expected_height_); } #endif // Drop leftover statement state from the frame before merging, even on // the fall through. This is so we can bind the return target with state // on the frame. if (cgen()->has_valid_frame()) { int count = cgen()->frame()->height() - expected_height_; cgen()->frame()->ForgetElements(count); } DoBind(); } void BreakTarget::Bind(Result* arg) { #ifdef DEBUG // All the forward-reaching frames should have been adjusted at the // jumps to this target. for (int i = 0; i < reaching_frames_.length(); i++) { ASSERT(reaching_frames_[i] == NULL || reaching_frames_[i]->height() == expected_height_ + 1); } #endif // Drop leftover statement state from the frame before merging, even on // the fall through. This is so we can bind the return target with state // on the frame. if (cgen()->has_valid_frame()) { int count = cgen()->frame()->height() - expected_height_; cgen()->frame()->ForgetElements(count); cgen()->frame()->Push(arg); } DoBind(); *arg = cgen()->frame()->Pop(); } #undef __ } } // namespace v8::internal