// 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 "register-allocator-inl.h" namespace v8 { namespace internal { // ------------------------------------------------------------------------- // JumpTarget implementation. #define __ ACCESS_MASM(masm_) void JumpTarget::DoJump() { ASSERT(cgen_ != NULL); 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()->MergeTo(entry_frame_); cgen_->DeleteFrame(); __ jmp(&entry_label_); } else { // Forward jump. The current frame is added to the end of the list // of frames reaching the target block and a jump to the merge code // is emitted. AddReachingFrame(cgen_->frame()); RegisterFile empty; cgen_->SetFrame(NULL, &empty); __ jmp(&merge_labels_.last()); } is_linked_ = !is_bound_; } 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 = RegisterAllocator::Reserved(); 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 { // Forward branch. A copy of the current frame is added to the end of the // list of frames reaching the target block and a branch to the merge code // is emitted. Use masm_-> 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())); masm_->j(cc, &merge_labels_.last(), hint); is_linked_ = true; } } 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); AddReachingFrame(target_frame); __ call(&merge_labels_.last()); is_linked_ = !is_bound_; } void JumpTarget::DoBind(int mergable_elements) { 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()); if (direction_ == FORWARD_ONLY) { // A simple case: no forward jumps and no possible backward jumps. if (!is_linked()) { // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. ASSERT(cgen_->has_valid_frame()); VirtualFrame* frame = cgen_->frame(); int difference = frame->stack_pointer_ - (frame->elements_.length() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(Operand(esp), Immediate(difference * kPointerSize)); } is_bound_ = true; return; } // Another simple case: no fall through, a single forward jump, // and no possible backward jumps. if (!cgen_->has_valid_frame() && reaching_frames_.length() == 1) { // 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 reserved = RegisterAllocator::Reserved(); cgen_->SetFrame(frame, &reserved); 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->elements_.length() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(Operand(esp), Immediate(difference * kPointerSize)); } is_linked_ = false; is_bound_ = true; 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()); RegisterFile empty; cgen_->SetFrame(NULL, &empty); } // Compute the frame to use for entry to the block. ComputeEntryFrame(mergable_elements); // 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 reserved = RegisterAllocator::Reserved(); if (direction_ == BIDIRECTIONAL) { cgen_->SetFrame(new VirtualFrame(frame), &reserved); } else { cgen_->SetFrame(frame, &reserved); 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. delete other; 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 reserved = RegisterAllocator::Reserved(); cgen_->SetFrame(frame, &reserved); 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 reserved_registers = RegisterAllocator::Reserved(); cgen_->SetFrame(new VirtualFrame(entry_frame_), &reserved_registers); } // There is certainly a current frame equal to the entry frame. // Bind the entry frame label. __ bind(&entry_label_); // 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()) { delete reaching_frames_[i]; 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 reserved = RegisterAllocator::Reserved(); cgen_->SetFrame(new VirtualFrame(reaching_frames_[0]), &reserved); __ bind(&merge_labels_[0]); cgen_->frame()->MergeTo(entry_frame_); __ bind(&entry_label_); } is_linked_ = false; is_bound_ = true; } #undef __ } } // namespace v8::internal