Clean up return statements in the code generator by explicitly

counting the reference to the return value and passing it to the
return label.  This requires threading it through try/catch and
try/finally.  The return value is loaded into eax more lazily than
before.

Also, perform some related refactoring of jump targets.
Review URL: http://codereview.chromium.org/56172

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1669 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kmillikin@chromium.org 2009-04-02 08:58:18 +00:00
parent 9bc64e657a
commit 06860982a9
9 changed files with 237 additions and 128 deletions

View File

@ -394,6 +394,7 @@ class CodeGenerator: public AstVisitor {
// positions are collected by the assembler and emitted with the relocation
// information.
void CodeForFunctionPosition(FunctionLiteral* fun);
void CodeForReturnPosition(FunctionLiteral* fun);
void CodeForStatementPosition(Node* node);
void CodeForSourcePosition(int pos);

View File

@ -274,25 +274,29 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
if (has_valid_frame()) {
// If there is a valid frame, control flow can fall off the end of
// the body. In that case there is an implicit return statement.
// Compiling a return statement will jump to the return sequence if
// it is already generated or generate it if not.
ASSERT(!function_return_is_shadowed_);
Literal undefined(Factory::undefined_value());
ReturnStatement statement(&undefined);
statement.set_statement_pos(fun->end_position());
VisitReturnStatement(&statement);
CodeForReturnPosition(fun);
frame_->PrepareForReturn();
Result undefined(Factory::undefined_value(), this);
if (function_return_.is_bound()) {
function_return_.Jump(&undefined);
} else {
// Though this is a (possibly) backward block, the frames
// can only differ on their top element.
function_return_.Bind(&undefined, 1);
GenerateReturnSequence(&undefined);
}
} else if (function_return_.is_linked()) {
// If the return target has dangling jumps to it, then we have not
// yet generated the return sequence. This can happen when (a)
// control does not flow off the end of the body so we did not
// compile an artificial return statement just above, and (b) there
// are return statements in the body but (c) they are all shadowed.
//
// There is no valid frame here but it is safe (also necessary) to
// load the return value into eax.
__ mov(eax, Immediate(Factory::undefined_value()));
function_return_.Bind();
GenerateReturnSequence();
Result return_value(this);
// Though this is a (possibly) backward block, the frames can
// only differ on their top element.
function_return_.Bind(&return_value, 1);
GenerateReturnSequence(&return_value);
}
}
}
@ -1943,52 +1947,37 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ReturnStatement");
CodeForStatementPosition(node);
Load(node->expression());
Result return_value = frame_->Pop();
if (function_return_is_shadowed_) {
// If the function return is shadowed, we spill all information
// and just jump to the label.
VirtualFrame::SpilledScope spilled_scope(this);
CodeForStatementPosition(node);
LoadAndSpill(node->expression());
frame_->EmitPop(eax);
function_return_.Jump();
function_return_.Jump(&return_value);
} else {
// Load the returned value.
CodeForStatementPosition(node);
Load(node->expression());
// Pop the result from the frame and prepare the frame for
// returning thus making it easier to merge.
Result result = frame_->Pop();
frame_->PrepareForReturn();
// Move the result into register eax where it belongs.
result.ToRegister(eax);
// TODO(203): Instead of explictly calling Unuse on the result, it
// might be better to pass the result to Jump and Bind below.
result.Unuse();
// If the function return label is already bound, we reuse the
// code by jumping to the return site.
if (function_return_.is_bound()) {
function_return_.Jump();
// If the function return label is already bound we reuse the
// code by jumping to the return site.
function_return_.Jump(&return_value);
} else {
function_return_.Bind();
GenerateReturnSequence();
// Though this is a (possibly) backward block, the frames can
// only differ on their top element.
function_return_.Bind(&return_value, 1);
GenerateReturnSequence(&return_value);
}
}
}
void CodeGenerator::GenerateReturnSequence() {
void CodeGenerator::GenerateReturnSequence(Result* return_value) {
// The return value is a live (but not currently reference counted)
// reference to eax. This is safe because the current frame does not
// contain a reference to eax (it is prepared for the return by spilling
// all registers).
ASSERT(has_valid_frame());
if (FLAG_trace) {
frame_->Push(eax); // Materialize result on the stack.
frame_->CallRuntime(Runtime::kTraceExit, 1);
frame_->Push(return_value);
*return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
}
return_value->ToRegister(eax);
// Add a label for checking the size of the code used for returning.
Label check_exit_codesize;
@ -2921,14 +2910,22 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) {
}
}
// Generate unlink code for the (formerly) shadowing targets that have been
// jumped to. Deallocate each shadow target.
// Generate unlink code for the (formerly) shadowing targets that
// have been jumped to. Deallocate each shadow target.
Result return_value(this);
for (int i = 0; i < shadows.length(); i++) {
if (shadows[i]->is_linked()) {
// Unlink from try chain; be careful not to destroy the TOS.
shadows[i]->Bind();
// Because we can be jumping here (to spilled code) from unspilled
// code, we need to reestablish a spilled frame at this block.
// Unlink from try chain; be careful not to destroy the TOS if
// there is one.
if (i == kReturnShadowIndex) {
shadows[i]->Bind(&return_value);
return_value.ToRegister(eax);
} else {
shadows[i]->Bind();
}
// Because we can be jumping here (to spilled code) from
// unspilled code, we need to reestablish a spilled frame at
// this block.
frame_->SpillAll();
// Reload sp from the top handler, because some statements that we
@ -2943,10 +2940,12 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) {
frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
// next_sp popped.
if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
frame_->PrepareForReturn();
if (i == kReturnShadowIndex) {
if (!function_return_is_shadowed_) frame_->PrepareForReturn();
shadows[i]->other_target()->Jump(&return_value);
} else {
shadows[i]->other_target()->Jump();
}
shadows[i]->other_target()->Jump();
}
delete shadows[i];
}
@ -3043,13 +3042,18 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
for (int i = 0; i < shadows.length(); i++) {
if (shadows[i]->is_linked()) {
// If we have come from the shadowed return, the return value is
// in (a non-refcounted reference to) eax. We must preserve it
// until it is pushed.
//
// on the virtual frame. We must preserve it until it is
// pushed.
if (i == kReturnShadowIndex) {
Result return_value(this);
shadows[i]->Bind(&return_value);
return_value.ToRegister(eax);
} else {
shadows[i]->Bind();
}
// Because we can be jumping here (to spilled code) from
// unspilled code, we need to reestablish a spilled frame at
// this block.
shadows[i]->Bind();
frame_->SpillAll();
// Reload sp from the top handler, because some statements that
@ -3103,14 +3107,23 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
// formerly shadowing targets. Deallocate each shadow target.
for (int i = 0; i < shadows.length(); i++) {
if (has_valid_frame() && shadows[i]->is_bound()) {
JumpTarget* original = shadows[i]->other_target();
BreakTarget* original = shadows[i]->other_target();
__ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
JumpTarget skip(this);
skip.Branch(not_equal);
frame_->PrepareForReturn();
original->Jump();
skip.Bind();
if (i == kReturnShadowIndex) {
// The return value is (already) in eax.
Result return_value = allocator_->Allocate(eax);
ASSERT(return_value.is_valid());
if (function_return_is_shadowed_) {
original->Branch(equal, &return_value);
} else {
// Branch around the preparation for return which may emit
// code.
JumpTarget skip(this);
skip.Branch(not_equal);
frame_->PrepareForReturn();
original->Jump(&return_value);
skip.Bind();
}
} else {
original->Branch(equal);
}

View File

@ -371,11 +371,10 @@ class CodeGenerator: public AstVisitor {
// Main code generation function
void GenCode(FunctionLiteral* fun);
// Generate the return sequence code. Should be called no more than once
// per compiled function (it binds the return target, which can not be
// done more than once). The return value is assumed to be in eax by the
// code generated.
void GenerateReturnSequence();
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
// target (which can not be done more than once).
void GenerateReturnSequence(Result* return_value);
// The following are used by class Reference.
void LoadReference(Reference* ref);
@ -567,6 +566,7 @@ class CodeGenerator: public AstVisitor {
// positions are collected by the assembler and emitted with the relocation
// information.
void CodeForFunctionPosition(FunctionLiteral* fun);
void CodeForReturnPosition(FunctionLiteral* fun);
void CodeForStatementPosition(Node* node);
void CodeForSourcePosition(int pos);

View File

@ -566,12 +566,23 @@ void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
}
void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
if (FLAG_debug_info) {
int pos = fun->start_position();
if (pos != RelocInfo::kNoPosition) {
masm()->RecordStatementPosition(pos);
masm()->RecordPosition(pos);
}
}
}
void CodeGenerator::CodeForStatementPosition(Node* node) {
if (FLAG_debug_info) {
int pos = node->statement_pos();
if (pos != RelocInfo::kNoPosition) {
masm()->RecordStatementPosition(pos);
CodeForSourcePosition(pos);
masm()->RecordPosition(pos);
}
}
}

View File

@ -35,37 +35,41 @@
// Include the declaration of the architecture defined class CodeGenerator.
// The contract to the shared code is that the the CodeGenerator is a subclass
// of Visitor and that the following methods are available publicly:
// CodeGenerator::MakeCode
// CodeGenerator::SetFunctionInfo
// CodeGenerator::masm
// CodeGenerator::frame
// CodeGenerator::has_valid_frame
// CodeGenerator::SetFrame
// CodeGenerator::DeleteFrame
// CodeGenerator::allocator
// CodeGenerator::AddDeferred
// CodeGenerator::in_spilled_code
// CodeGenerator::set_in_spilled_code
// MakeCode
// SetFunctionInfo
// masm
// frame
// has_valid_frame
// SetFrame
// DeleteFrame
// allocator
// AddDeferred
// in_spilled_code
// set_in_spilled_code
//
// These methods are either used privately by the shared code or implemented as
// shared code:
// CodeGenerator::CodeGenerator
// CodeGenerator::~CodeGenerator
// CodeGenerator::ProcessDeferred
// CodeGenerator::ClearDeferred
// CodeGenerator::GenCode
// CodeGenerator::BuildBoilerplate
// CodeGenerator::ComputeCallInitialize
// CodeGenerator::ComputeCallInitializeInLoop
// CodeGenerator::ProcessDeclarations
// CodeGenerator::DeclareGlobals
// CodeGenerator::CheckForInlineRuntimeCall
// CodeGenerator::GenerateFastCaseSwitchStatement
// CodeGenerator::GenerateFastCaseSwitchCases
// CodeGenerator::TryGenerateFastCaseSwitchStatement
// CodeGenerator::GenerateFastCaseSwitchJumpTable
// CodeGenerator::FastCaseSwitchMinCaseCount
// CodeGenerator::FastCaseSwitchMaxOverheadFactor
// CodeGenerator
// ~CodeGenerator
// ProcessDeferred
// ClearDeferred
// GenCode
// BuildBoilerplate
// ComputeCallInitialize
// ComputeCallInitializeInLoop
// ProcessDeclarations
// DeclareGlobals
// CheckForInlineRuntimeCall
// GenerateFastCaseSwitchStatement
// GenerateFastCaseSwitchCases
// TryGenerateFastCaseSwitchStatement
// GenerateFastCaseSwitchJumpTable
// FastCaseSwitchMinCaseCount
// FastCaseSwitchMaxOverheadFactor
// CodeForFunctionPosition
// CodeForReturnPosition
// CodeForStatementPosition
// CodeForSourcePosition
#ifdef ARM
#include "codegen-arm.h"

View File

@ -37,7 +37,7 @@ namespace v8 { namespace internal {
#define __ masm_->
void JumpTarget::Jump() {
void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// Live non-frame registers are not allowed at unconditional jumps
@ -65,7 +65,7 @@ void JumpTarget::Jump() {
}
void JumpTarget::Branch(Condition cc, Hint ignored) {
void JumpTarget::DoBranch(Condition cc, Hint ignored) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
@ -148,7 +148,7 @@ void JumpTarget::Call() {
}
void JumpTarget::Bind(int mergable_elements) {
void JumpTarget::DoBind(int mergable_elements) {
ASSERT(cgen_ != NULL);
ASSERT(!is_bound());

View File

@ -37,7 +37,7 @@ namespace v8 { namespace internal {
#define __ masm_->
void JumpTarget::Jump() {
void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// Live non-frame registers are not allowed at unconditional jumps
@ -65,7 +65,7 @@ void JumpTarget::Jump() {
}
void JumpTarget::Branch(Condition cc, Hint hint) {
void JumpTarget::DoBranch(Condition cc, Hint hint) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
@ -148,7 +148,7 @@ void JumpTarget::Call() {
}
void JumpTarget::Bind(int mergable_elements) {
void JumpTarget::DoBind(int mergable_elements) {
ASSERT(cgen_ != NULL);
ASSERT(!is_bound());

View File

@ -298,12 +298,17 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) {
}
void JumpTarget::Jump() {
DoJump();
}
void JumpTarget::Jump(Result* arg) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
cgen_->frame()->Push(arg);
Jump();
DoJump();
}
@ -313,7 +318,7 @@ void JumpTarget::Jump(Result* arg0, Result* arg1) {
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
Jump();
DoJump();
}
@ -324,7 +329,12 @@ void JumpTarget::Jump(Result* arg0, Result* arg1, Result* arg2) {
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
Jump();
DoJump();
}
void JumpTarget::Branch(Condition cc, Hint hint) {
DoBranch(cc, hint);
}
@ -352,7 +362,7 @@ void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) {
DECLARE_ARGCHECK_VARS(arg);
cgen_->frame()->Push(arg);
Branch(cc, hint);
DoBranch(cc, hint);
*arg = cgen_->frame()->Pop();
ASSERT_ARGCHECK(arg);
@ -370,7 +380,7 @@ void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) {
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
Branch(cc, hint);
DoBranch(cc, hint);
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@ -396,7 +406,7 @@ void JumpTarget::Branch(Condition cc,
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
Branch(cc, hint);
DoBranch(cc, hint);
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@ -427,7 +437,7 @@ void JumpTarget::Branch(Condition cc,
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
cgen_->frame()->Push(arg3);
Branch(cc, hint);
DoBranch(cc, hint);
*arg3 = cgen_->frame()->Pop();
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
@ -439,17 +449,47 @@ void JumpTarget::Branch(Condition cc,
ASSERT_ARGCHECK(arg3);
}
void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
int count = cgen_->frame()->height() - expected_height_;
if (count > 0) {
// We negate and branch here rather than using DoBranch's negate
// and branch. This gives us a hook to remove statement state
// from the frame.
JumpTarget fall_through(cgen_);
// Branch to fall through will not negate, because it is a
// forward-only target.
fall_through.Branch(NegateCondition(cc), NegateHint(hint));
Jump(arg); // May emit merge code here.
fall_through.Bind();
} else {
DECLARE_ARGCHECK_VARS(arg);
cgen_->frame()->Push(arg);
DoBranch(cc, hint);
*arg = cgen_->frame()->Pop();
ASSERT_ARGCHECK(arg);
}
}
#undef DECLARE_ARGCHECK_VARS
#undef ASSERT_ARGCHECK
void JumpTarget::Bind(int mergable_elements) {
DoBind(mergable_elements);
}
void JumpTarget::Bind(Result* arg, int mergable_elements) {
ASSERT(cgen_ != NULL);
if (cgen_->has_valid_frame()) {
cgen_->frame()->Push(arg);
}
Bind(mergable_elements);
DoBind(mergable_elements);
*arg = cgen_->frame()->Pop();
}
@ -461,7 +501,7 @@ void JumpTarget::Bind(Result* arg0, Result* arg1, int mergable_elements) {
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
}
Bind(mergable_elements);
DoBind(mergable_elements);
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
}
@ -478,7 +518,7 @@ void JumpTarget::Bind(Result* arg0,
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
}
Bind(mergable_elements);
DoBind(mergable_elements);
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@ -498,7 +538,7 @@ void JumpTarget::Bind(Result* arg0,
cgen_->frame()->Push(arg2);
cgen_->frame()->Push(arg3);
}
Bind(mergable_elements);
DoBind(mergable_elements);
*arg3 = cgen_->frame()->Pop();
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
@ -548,10 +588,20 @@ void BreakTarget::Jump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// This is a break target so drop leftover statement state from the
// frame before merging.
// Drop leftover statement state from the frame before merging.
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
JumpTarget::Jump();
DoJump();
}
void BreakTarget::Jump(Result* arg) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// Drop leftover statement state from the frame before merging.
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
cgen_->frame()->Push(arg);
DoJump();
}
@ -561,9 +611,9 @@ void BreakTarget::Branch(Condition cc, Hint hint) {
int count = cgen_->frame()->height() - expected_height_;
if (count > 0) {
// We negate and branch here rather than using
// JumpTarget::Branch's negate and branch. This gives us a hook
// to remove statement state from the frame.
// We negate and branch here rather than using DoBranch's negate
// and branch. This gives us a hook to remove statement state
// from the frame.
JumpTarget fall_through(cgen_);
// Branch to fall through will not negate, because it is a
// forward-only target.
@ -571,14 +621,13 @@ void BreakTarget::Branch(Condition cc, Hint hint) {
Jump(); // May emit merge code here.
fall_through.Bind();
} else {
JumpTarget::Branch(cc, hint);
DoBranch(cc, hint);
}
}
void BreakTarget::Bind(int mergable_elements) {
#ifdef DEBUG
ASSERT(mergable_elements == kAllElements);
ASSERT(cgen_ != NULL);
// All the forward-reaching frames should have been adjusted at the
// jumps to this target.
@ -587,13 +636,35 @@ void BreakTarget::Bind(int mergable_elements) {
reaching_frames_[i]->height() == expected_height_);
}
#endif
// This is a break target so we drop leftover statement state from
// the frame before merging, even on the fall through. This is
// because we can bind the return target with state on the frame.
// 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()) {
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
}
JumpTarget::Bind(mergable_elements);
DoBind(mergable_elements);
}
void BreakTarget::Bind(Result* arg, int mergable_elements) {
#ifdef DEBUG
ASSERT(cgen_ != NULL);
// 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()) {
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
cgen_->frame()->Push(arg);
}
DoBind(mergable_elements);
*arg = cgen_->frame()->Pop();
}

View File

@ -105,7 +105,7 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated.
// Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump.
virtual void Jump();
void Jump(Result* arg);
virtual void Jump(Result* arg);
void Jump(Result* arg0, Result* arg1);
void Jump(Result* arg0, Result* arg1, Result* arg2);
@ -113,7 +113,7 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated.
// frame at the branch. The current frame will fall through to the
// code after the branch.
virtual void Branch(Condition cc, Hint hint = no_hint);
void Branch(Condition cc, Result* arg, Hint hint = no_hint);
virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
void Branch(Condition cc, Result* arg0, Result* arg1, Hint hint = no_hint);
void Branch(Condition cc,
Result* arg0,
@ -141,7 +141,7 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated.
// frame elements must be mergable. Mergable elements are ignored
// completely for forward-only jump targets.
virtual void Bind(int mergable_elements = kAllElements);
void Bind(Result* arg, int mergable_elements = kAllElements);
virtual void Bind(Result* arg, int mergable_elements = kAllElements);
void Bind(Result* arg0, Result* arg1, int mergable_elements = kAllElements);
void Bind(Result* arg0,
Result* arg1,
@ -191,6 +191,12 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated.
bool is_bound_;
bool is_linked_;
// Implementations of Jump, Branch, and Bind with all arguments and
// return values using the virtual frame.
void DoJump();
void DoBranch(Condition cc, Hint hint);
void DoBind(int mergable_elements);
private:
// Add a virtual frame reaching this labeled block via a forward
// jump, and a fresh label for its merge code.
@ -243,16 +249,19 @@ class BreakTarget : public JumpTarget {
// Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump.
virtual void Jump();
virtual void Jump(Result* arg);
// Emit a conditional branch to the target. There must be a current
// frame at the branch. The current frame will fall through to the
// code after the branch.
virtual void Branch(Condition cc, Hint hint = no_hint);
virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
// Bind a break target. If there is no current frame at the binding
// site, there must be at least one frame reaching via a forward
// jump.
virtual void Bind(int mergable_elements = kAllElements);
virtual void Bind(Result* arg, int mergable_elements = kAllElements);
// Setter for expected height.
void set_expected_height(int expected) { expected_height_ = expected; }