Author: [email protected]
Date: Mon Dec 22 06:46:21 2008
New Revision: 1016
Modified:
branches/experimental/toiger/src/codegen-ia32.cc
branches/experimental/toiger/src/codegen-ia32.h
branches/experimental/toiger/src/codegen.cc
branches/experimental/toiger/src/jump-target-ia32.cc
branches/experimental/toiger/src/jump-target.h
branches/experimental/toiger/src/register-allocator-ia32.cc
branches/experimental/toiger/src/register-allocator-ia32.h
branches/experimental/toiger/src/virtual-frame-ia32.cc
branches/experimental/toiger/src/virtual-frame-ia32.h
Log:
Experimental: this is a substantial change to allow the virtual frame
to flow correctly to deferred code.
The same mechanism is used for all labeled blocks (potential merge
points), not just deferred ones: all live mergable values are held on
the frame. At a jump or branch, local results are allowed to be
passed as arguments to the target block, with the semantics of being
pushed on the frame before performing the jump or branch. At a bind,
the arguments to the block are named, with the semantics of performing
the bind and then popping the arguments from the frame.
"Returns" from deferred code are handled the same way (the return
value is an argument to the block labeled with the deferred code exit
label).
There is obviously still some things to clean up here.
Review URL: http://codereview.chromium.org/15079
Modified: branches/experimental/toiger/src/codegen-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/codegen-ia32.cc (original)
+++ branches/experimental/toiger/src/codegen-ia32.cc Mon Dec 22 06:46:21
2008
@@ -713,13 +713,18 @@
void GenerateInlineCode();
virtual void Generate() {
- __ push(ebx);
- __ CallStub(&stub_);
+ // The arguments are is actually passed in ebx and the top of the
stack.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
+ generator()->frame()->EmitPush(ebx);
+ generator()->frame()->CallStub(&stub_, 2);
// We must preserve the eax value here, because it will be written
// to the top-of-stack element when getting back to the fast case
// code. See comment in GenericBinaryOperation where
// deferred->exit() is bound.
- __ push(eax);
+ generator()->frame()->EmitPush(eax);
+ // The result is actually returned on the top of the stack.
+ exit()->Jump();
}
private:
@@ -769,6 +774,10 @@
if (flags == SMI_CODE_INLINED) {
// Create a new deferred code for the slow-case part.
+ //
+ // TODO(): When this code is updated to use the virtual frame, it
+ // has to properly flow to the inline code from this deferred code
+ // stub.
DeferredInlineBinaryOperation* deferred =
new DeferredInlineBinaryOperation(this, op, overwrite_mode, flags);
// Fetch the operands from the stack.
@@ -801,10 +810,15 @@
set_comment("[ DeferredInlinedSmiOperation");
}
virtual void Generate() {
- __ push(eax);
- __ push(Immediate(Smi::FromInt(value_)));
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
+ generator()->frame()->EmitPush(eax);
+ generator()->frame()->EmitPush(Immediate(Smi::FromInt(value_)));
GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -824,10 +838,15 @@
set_comment("[ DeferredInlinedSmiOperationReversed");
}
virtual void Generate() {
- __ push(Immediate(Smi::FromInt(value_)));
- __ push(eax);
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
+ generator()->frame()->EmitPush(Immediate(Smi::FromInt(value_)));
+ generator()->frame()->EmitPush(eax);
GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -846,13 +865,18 @@
}
virtual void Generate() {
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// Undo the optimistic add operation and call the shared stub.
Immediate immediate(Smi::FromInt(value_));
__ sub(Operand(eax), immediate);
- __ push(eax);
- __ push(immediate);
+ generator()->frame()->EmitPush(eax);
+ generator()->frame()->EmitPush(immediate);
GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_,
SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -870,13 +894,18 @@
}
virtual void Generate() {
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// Undo the optimistic add operation and call the shared stub.
Immediate immediate(Smi::FromInt(value_));
__ sub(Operand(eax), immediate);
- __ push(immediate);
- __ push(eax);
+ generator()->frame()->EmitPush(immediate);
+ generator()->frame()->EmitPush(eax);
GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_,
SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -894,13 +923,18 @@
}
virtual void Generate() {
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// Undo the optimistic sub operation and call the shared stub.
Immediate immediate(Smi::FromInt(value_));
__ add(Operand(eax), immediate);
- __ push(eax);
- __ push(immediate);
+ generator()->frame()->EmitPush(eax);
+ generator()->frame()->EmitPush(immediate);
GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_,
SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -921,12 +955,17 @@
}
virtual void Generate() {
+ // The arguments are actually passed in eax and tos_reg.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// Undo the optimistic sub operation and call the shared stub.
__ add(eax, Operand(tos_reg_));
- __ push(eax);
- __ push(tos_reg_);
+ generator()->frame()->EmitPush(eax);
+ generator()->frame()->EmitPush(tos_reg_);
GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_,
SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ generator()->frame()->CallStub(&igostub, 2);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
private:
@@ -1185,18 +1224,16 @@
}
-class SmiComparisonDeferred: public DeferredCode {
+class DeferredSmiComparison: public DeferredCode {
public:
- SmiComparisonDeferred(CodeGenerator* generator,
+ DeferredSmiComparison(CodeGenerator* generator,
Condition cc,
bool strict,
- Register left_side,
- int right_side) :
+ int int_value) :
DeferredCode(generator),
cc_(cc),
strict_(strict),
- left_side_(left_side),
- right_side_(right_side) {
+ int_value_(int_value) {
set_comment("[ ComparisonDeferred");
}
virtual void Generate();
@@ -1204,21 +1241,27 @@
private:
Condition cc_;
bool strict_;
- Register left_side_;
- int right_side_;
+ int int_value_;
};
-void SmiComparisonDeferred::Generate() {
+void DeferredSmiComparison::Generate() {
+ CodeGenerator* cgen = generator();
CompareStub stub(cc_, strict_);
+ Result argument(cgen);
+ enter()->Bind(&argument);
+
// Setup parameters and call stub.
- if (!left_side_.is(edx)) {
- __ mov(edx, Operand(left_side_));
- }
- __ Set(eax, Immediate(Smi::FromInt(right_side_)));
- __ CallStub(&stub);
- __ cmp(eax, 0);
- // "result" is returned in the flags.
+ argument.ToRegister(edx);
+ Result value = cgen->allocator()->Allocate(eax);
+ ASSERT(value.is_valid());
+ __ Set(value.reg(), Immediate(Smi::FromInt(int_value_)));
+ Result result = cgen->frame()->CallStub(&stub, &argument, &value, 0);
+ ASSERT(result.is_register());
+ __ cmp(result.reg(), 0);
+ result.Unuse();
+ // The actual result is returned in the flags.
+ exit()->Jump();
}
@@ -1231,15 +1274,15 @@
int int_value = Smi::cast(*value)->value();
ASSERT(is_intn(int_value, kMaxSmiInlinedBits));
+ DeferredSmiComparison* deferred =
+ new DeferredSmiComparison(this, cc, strict, int_value);
+
Result comparee = frame_->Pop();
comparee.ToRegister();
- Register reg = comparee.reg();
- __ test(reg, Immediate(kSmiTagMask));
- SmiComparisonDeferred* deferred =
- new SmiComparisonDeferred(this, cc, strict, reg, int_value);
- deferred->enter()->Branch(not_zero, not_taken);
+ __ test(comparee.reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, &comparee, not_taken);
// Test smi equality and comparison by signed int comparison.
- __ cmp(Operand(reg), Immediate(value));
+ __ cmp(Operand(comparee.reg()), Immediate(value));
comparee.Unuse();
deferred->exit()->Bind();
cc_reg_ = cc;
@@ -2682,9 +2725,9 @@
}
-class RegExpDeferred: public DeferredCode {
+class DeferredRegExpLiteral: public DeferredCode {
public:
- RegExpDeferred(CodeGenerator* generator, RegExpLiteral* node)
+ DeferredRegExpLiteral(CodeGenerator* generator, RegExpLiteral* node)
: DeferredCode(generator), node_(node) {
set_comment("[ RegExpDeferred");
}
@@ -2694,27 +2737,33 @@
};
-void RegExpDeferred::Generate() {
+void DeferredRegExpLiteral::Generate() {
+ // The argument is actually passed in ecx.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// If the entry is undefined we call the runtime system to compute the
// literal.
// Literal array (0).
- __ push(ecx);
+ generator()->frame()->EmitPush(ecx);
// Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
+ generator()->frame()->EmitPush(
+ Immediate(Smi::FromInt(node_->literal_index())));
// RegExp pattern (2).
- __ push(Immediate(node_->pattern()));
+ generator()->frame()->EmitPush(Immediate(node_->pattern()));
// RegExp flags (3).
- __ push(Immediate(node_->flags()));
- __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ generator()->frame()->EmitPush(Immediate(node_->flags()));
+ generator()->frame()->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
__ mov(ebx, Operand(eax)); // "caller" expects result in ebx
+ // The result is actually returned in ebx.
+ exit()->Jump();
}
void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ RegExp Literal");
- RegExpDeferred* deferred = new RegExpDeferred(this, node);
+ DeferredRegExpLiteral* deferred = new DeferredRegExpLiteral(this, node);
// Retrieve the literal array and check the allocated entry.
@@ -2744,9 +2793,9 @@
// by calling Runtime_CreateObjectLiteral.
// Each created boilerplate is stored in the JSFunction and they are
// therefore context dependent.
-class ObjectLiteralDeferred: public DeferredCode {
+class DeferredObjectLiteral: public DeferredCode {
public:
- ObjectLiteralDeferred(CodeGenerator* generator,
+ DeferredObjectLiteral(CodeGenerator* generator,
ObjectLiteral* node)
: DeferredCode(generator), node_(node) {
set_comment("[ ObjectLiteralDeferred");
@@ -2757,25 +2806,32 @@
};
-void ObjectLiteralDeferred::Generate() {
+void DeferredObjectLiteral::Generate() {
+ // The argument is actually passed in ecx.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
// If the entry is undefined we call the runtime system to compute
// the literal.
// Literal array (0).
- __ push(ecx);
+ generator()->frame()->EmitPush(ecx);
// Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
+ generator()->frame()->EmitPush(
+ Immediate(Smi::FromInt(node_->literal_index())));
// Constant properties (2).
- __ push(Immediate(node_->constant_properties()));
- __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
+ generator()->frame()->EmitPush(Immediate(node_->constant_properties()));
+
generator()->frame()->CallRuntime(Runtime::kCreateObjectLiteralBoilerplate,
+ 3);
__ mov(ebx, Operand(eax));
+ // The result is actually returned in ebx.
+ exit()->Jump();
}
void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ ObjectLiteral");
- ObjectLiteralDeferred* deferred = new ObjectLiteralDeferred(this, node);
+ DeferredObjectLiteral* deferred = new DeferredObjectLiteral(this, node);
// Retrieve the literal array and check the allocated entry.
@@ -3629,9 +3685,9 @@
}
-class CountOperationDeferred: public DeferredCode {
+class DeferredCountOperation: public DeferredCode {
public:
- CountOperationDeferred(CodeGenerator* generator,
+ DeferredCountOperation(CodeGenerator* generator,
bool is_postfix,
bool is_increment,
int result_offset)
@@ -3704,13 +3760,18 @@
};
-void CountOperationDeferred::Generate() {
+void DeferredCountOperation::Generate() {
+ // The argument is actually passed in eax.
+ enter()->Bind();
+ VirtualFrame::SpilledScope spilled_scope(generator());
if (is_postfix_) {
RevertToNumberStub to_number_stub(is_increment_);
- __ CallStub(&to_number_stub);
+ generator()->frame()->CallStub(&to_number_stub, 0);
}
CounterOpStub stub(result_offset_, is_postfix_, is_increment_);
- __ CallStub(&stub);
+ generator()->frame()->CallStub(&stub, 0);
+ // The result is actually returned in eax.
+ exit()->Jump();
}
@@ -3740,8 +3801,8 @@
}
target.GetValueAndSpill(NOT_INSIDE_TYPEOF);
- CountOperationDeferred* deferred =
- new CountOperationDeferred(this, is_postfix, is_increment,
+ DeferredCountOperation* deferred =
+ new DeferredCountOperation(this, is_postfix, is_increment,
target.size() * kPointerSize);
frame_->EmitPop(eax); // Load TOS into eax for calculations below
@@ -4146,6 +4207,17 @@
bool CodeGenerator::IsActualFunctionReturn(JumpTarget* target) {
return (target == &function_return_ && !function_return_is_shadowed_);
}
+
+
+#ifdef DEBUG
+bool CodeGenerator::HasValidEntryRegisters() {
+ return (allocator()->count(eax) - frame()->register_count(eax) == 0)
+ && (allocator()->count(ebx) - frame()->register_count(ebx) == 0)
+ && (allocator()->count(ecx) - frame()->register_count(ecx) == 0)
+ && (allocator()->count(edx) - frame()->register_count(edx) == 0)
+ && (allocator()->count(edi) - frame()->register_count(edi) == 0);
+}
+#endif
#undef __
Modified: branches/experimental/toiger/src/codegen-ia32.h
==============================================================================
--- branches/experimental/toiger/src/codegen-ia32.h (original)
+++ branches/experimental/toiger/src/codegen-ia32.h Mon Dec 22 06:46:21 2008
@@ -448,6 +448,13 @@
bool IsActualFunctionReturn(JumpTarget* target);
bool is_eval_; // Tells whether code is generated for eval.
+
+#ifdef DEBUG
+ // True if the registers are valid for entry to a block. There should be
+ // no frame-external references to eax, ebx, ecx, edx, or edi.
+ bool HasValidEntryRegisters();
+#endif
+
Handle<Script> script_;
List<DeferredCode*> deferred_;
Modified: branches/experimental/toiger/src/codegen.cc
==============================================================================
--- branches/experimental/toiger/src/codegen.cc (original)
+++ branches/experimental/toiger/src/codegen.cc Mon Dec 22 06:46:21 2008
@@ -62,14 +62,10 @@
if (code->position() != RelocInfo::kNoPosition) {
masm->RecordPosition(code->position());
}
- // Bind labels and generate the code.
- code->enter()->Bind();
- frame_->SpillAll();
+ // Generate the code.
Comment cmnt(masm, code->comment());
code->Generate();
- if (code->exit()->is_bound()) {
- code->exit()->Jump();
- }
+ ASSERT(code->enter()->is_bound());
}
}
Modified: branches/experimental/toiger/src/jump-target-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/jump-target-ia32.cc (original)
+++ branches/experimental/toiger/src/jump-target-ia32.cc Mon Dec 22
06:46:21 2008
@@ -39,22 +39,22 @@
JumpTarget::JumpTarget(CodeGenerator* cgen)
: expected_frame_(NULL),
- code_generator_(cgen),
+ cgen_(cgen),
masm_(cgen->masm()) {
}
JumpTarget::JumpTarget()
: expected_frame_(NULL),
- code_generator_(NULL),
+ cgen_(NULL),
masm_(NULL) {
}
void JumpTarget::set_code_generator(CodeGenerator* cgen) {
ASSERT(cgen != NULL);
- ASSERT(code_generator_ == NULL);
- code_generator_ = cgen;
+ ASSERT(cgen_ == NULL);
+ cgen_ = cgen;
masm_ = cgen->masm();
}
@@ -62,28 +62,29 @@
void JumpTarget::Jump() {
// Precondition: there is a current frame. There may or may not be an
// expected frame at the label.
- ASSERT(code_generator_ != NULL);
+ ASSERT(cgen_ != NULL);
- VirtualFrame* current_frame = code_generator_->frame();
+ VirtualFrame* current_frame = cgen_->frame();
ASSERT(current_frame != NULL);
+ ASSERT(cgen_->HasValidEntryRegisters());
if (expected_frame_ == NULL) {
// The frame at the actual function return will always have height
zero.
- if (code_generator_->IsActualFunctionReturn(this)) {
+ if (cgen_->IsActualFunctionReturn(this)) {
current_frame->Forget(current_frame->height());
}
- if (!current_frame->IsMergable()) {
- current_frame->MakeMergable();
- }
+ current_frame->MakeMergable();
expected_frame_ = current_frame;
- code_generator_->SetFrame(NULL);
+ ASSERT(cgen_->HasValidEntryRegisters());
+ cgen_->SetFrame(NULL);
} else {
// No code needs to be emitted to merge to the expected frame at the
// actual function return.
- if (!code_generator_->IsActualFunctionReturn(this)) {
+ if (!cgen_->IsActualFunctionReturn(this)) {
current_frame->MergeTo(expected_frame_);
}
- code_generator_->DeleteFrame();
+ ASSERT(cgen_->HasValidEntryRegisters());
+ cgen_->DeleteFrame();
}
__ jmp(&label_);
@@ -95,16 +96,17 @@
void JumpTarget::Branch(Condition cc, Hint hint) {
// Precondition: there is a current frame. There may or may not be an
// expected frame at the label.
- ASSERT(code_generator_ != NULL);
+ ASSERT(cgen_ != NULL);
ASSERT(masm_ != NULL);
- VirtualFrame* current_frame = code_generator_->frame();
+ VirtualFrame* current_frame = cgen_->frame();
ASSERT(current_frame != NULL);
+ ASSERT(cgen_->HasValidEntryRegisters());
if (expected_frame_ == NULL) {
expected_frame_ = new VirtualFrame(current_frame);
// The frame at the actual function return will always have height
zero.
- if (code_generator_->IsActualFunctionReturn(this)) {
+ if (cgen_->IsActualFunctionReturn(this)) {
expected_frame_->Forget(expected_frame_->height());
}
// For a branch, the frame at the fall-through basic block (not
labeled)
@@ -119,17 +121,17 @@
__ jmp(&label_);
__ bind(&original_fall_through);
} else {
- if (!expected_frame_->IsMergable()) {
- expected_frame_->MakeMergable();
- }
+ expected_frame_->MakeMergable();
+ ASSERT(cgen_->HasValidEntryRegisters());
__ j(cc, &label_, hint);
}
} else {
// No code needs to be emitted to merge to the expected frame at the
// actual function return.
- if (!code_generator_->IsActualFunctionReturn(this)) {
+ if (!cgen_->IsActualFunctionReturn(this)) {
current_frame->MergeTo(expected_frame_);
}
+ ASSERT(cgen_->HasValidEntryRegisters());
__ j(cc, &label_, hint);
}
// Postcondition: there is both a current frame and an expected frame at
@@ -137,24 +139,44 @@
}
+void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) {
+ ASSERT(cgen_ != NULL);
+ ASSERT(cgen_->frame() != NULL);
+
+#ifdef DEBUG
+ // We want register results at the call site to stay in the same
registers
+ // on the fall-through branch.
+ Result::Type arg_type = arg->type();
+ Register arg_reg = arg->is_register() ? arg->reg() : no_reg;
+#endif
+
+ cgen_->frame()->Push(arg);
+ Branch(cc, hint);
+ *arg = cgen_->frame()->Pop();
+
+ ASSERT(arg->type() == arg_type);
+ ASSERT(!arg->is_register() || arg->reg().is(arg_reg));
+}
+
+
void JumpTarget::Call() {
// Precondition: there is a current frame, and there is no expected frame
// at the label.
- ASSERT(code_generator_ != NULL);
+ ASSERT(cgen_ != NULL);
ASSERT(masm_ != NULL);
- ASSERT(!code_generator_->IsActualFunctionReturn(this));
+ ASSERT(!cgen_->IsActualFunctionReturn(this));
- VirtualFrame* current_frame = code_generator_->frame();
+ VirtualFrame* current_frame = cgen_->frame();
ASSERT(current_frame != NULL);
ASSERT(expected_frame_ == NULL);
+ ASSERT(cgen_->HasValidEntryRegisters());
expected_frame_ = new VirtualFrame(current_frame);
- if (!expected_frame_->IsMergable()) {
- expected_frame_->MakeMergable();
- }
+ expected_frame_->MakeMergable();
// Adjust the expected frame's height to account for the return address
// pushed by the call instruction.
expected_frame_->Adjust(1);
+ ASSERT(cgen_->HasValidEntryRegisters());
__ call(&label_);
// Postcondition: there is both a current frame and an expected frame at
@@ -166,37 +188,67 @@
void JumpTarget::Bind() {
// Precondition: there is either a current frame or an expected frame at
// the label (and possibly both). The label is unbound.
- ASSERT(code_generator_ != NULL);
+ ASSERT(cgen_ != NULL);
ASSERT(masm_ != NULL);
- VirtualFrame* current_frame = code_generator_->frame();
+ VirtualFrame* current_frame = cgen_->frame();
ASSERT(current_frame != NULL || expected_frame_ != NULL);
ASSERT(!label_.is_bound());
if (expected_frame_ == NULL) {
+ ASSERT(cgen_->HasValidEntryRegisters());
// When a label is bound the current frame becomes the expected frame
at
// the label. This requires the current frame to be mergable.
// The frame at the actual function return will always have height
zero.
- if (code_generator_->IsActualFunctionReturn(this)) {
+ if (cgen_->IsActualFunctionReturn(this)) {
current_frame->Forget(current_frame->height());
}
- if (!current_frame->IsMergable()) {
- current_frame->MakeMergable();
- }
+ current_frame->MakeMergable();
+ ASSERT(cgen_->HasValidEntryRegisters());
expected_frame_ = new VirtualFrame(current_frame);
} else if (current_frame == NULL) {
- code_generator_->SetFrame(new VirtualFrame(expected_frame_));
+ cgen_->SetFrame(new VirtualFrame(expected_frame_));
+ ASSERT(cgen_->HasValidEntryRegisters());
} else {
+ ASSERT(cgen_->HasValidEntryRegisters());
// No code needs to be emitted to merge to the expected frame at the
// actual function return.
- if (!code_generator_->IsActualFunctionReturn(this)) {
+ if (!cgen_->IsActualFunctionReturn(this)) {
current_frame->MergeTo(expected_frame_);
}
+ ASSERT(cgen_->HasValidEntryRegisters());
}
__ bind(&label_);
// Postcondition: there is both a current frame and an expected frame at
// the label and they match. The label is bound.
+}
+
+
+void JumpTarget::Bind(Result* arg) {
+ ASSERT(cgen_ != NULL);
+
+#ifdef DEBUG
+ // We want register results at the call site to stay in the same
+ // registers.
+ bool had_entry_frame = false;
+ Result::Type arg_type;
+ Register arg_reg;
+#endif
+
+ if (cgen_->frame() != NULL) {
+#ifdef DEBUG
+ had_entry_frame = true;
+ arg_type = arg->type();
+ arg_reg = arg->is_register() ? arg->reg() : no_reg;
+#endif
+ cgen_->frame()->Push(arg);
+ }
+ Bind();
+ *arg = cgen_->frame()->Pop();
+
+ ASSERT(!had_entry_frame || arg->type() == arg_type);
+ ASSERT(!had_entry_frame || !arg->is_register() ||
arg->reg().is(arg_reg));
}
Modified: branches/experimental/toiger/src/jump-target.h
==============================================================================
--- branches/experimental/toiger/src/jump-target.h (original)
+++ branches/experimental/toiger/src/jump-target.h Mon Dec 22 06:46:21 2008
@@ -67,7 +67,7 @@
void set_code_generator(CodeGenerator* cgen);
// Accessors.
- CodeGenerator* code_generator() const { return code_generator_; }
+ CodeGenerator* code_generator() const { return cgen_; }
Label* label() { return &label_; }
@@ -96,10 +96,12 @@
// Emit a conditional branch to the target. If there is no current
frame,
// there must be one expected at the target.
void Branch(Condition cc, Hint hint = no_hint);
+ void Branch(Condition cc, Result* arg, Hint hint = no_hint);
// Bind a jump target. There must be a current frame and no expected
// frame at the target (targets are only bound once).
void Bind();
+ void Bind(Result* arg);
// Emit a call to a jump target. There must be a current frame. The
// frame at the target is the same as the current frame except for an
@@ -115,7 +117,7 @@
private:
// The code generator gives access to the current frame.
- CodeGenerator* code_generator_;
+ CodeGenerator* cgen_;
// Used to emit code.
MacroAssembler* masm_;
Modified: branches/experimental/toiger/src/register-allocator-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/register-allocator-ia32.cc (original)
+++ branches/experimental/toiger/src/register-allocator-ia32.cc Mon Dec 22
06:46:21 2008
@@ -39,7 +39,7 @@
: type_(REGISTER),
cgen_(cgen) {
data_.reg_ = reg;
- ASSERT(!reg.is(no_reg));
+ ASSERT(reg.is_valid());
cgen_->allocator()->Use(reg);
}
@@ -74,14 +74,34 @@
ASSERT(fresh.is_valid());
cgen_->masm()->Set(fresh.reg(), Immediate(handle()));
// This result becomes a copy of the fresh one.
- cgen_->allocator()->Use(fresh.reg());
- type_ = REGISTER;
- data_.reg_ = fresh.reg();
+ *this = fresh;
}
ASSERT(is_register());
}
+void Result::ToRegister(Register target) {
+ ASSERT(is_valid());
+ if (!is_register() || !reg().is(target)) {
+ Result fresh = cgen_->allocator()->Allocate(target);
+ ASSERT(fresh.is_valid());
+ if (is_register()) {
+ cgen_->masm()->mov(fresh.reg(), reg());
+ } else {
+ ASSERT(is_constant());
+ cgen_->masm()->Set(fresh.reg(), Immediate(handle()));
+ }
+ *this = fresh;
+ } else if (is_register() && reg().is(target)) {
+ ASSERT(cgen_->frame() != NULL);
+ cgen_->frame()->Spill(target);
+ ASSERT(cgen_->allocator()->count(target) == 1);
+ }
+ ASSERT(is_register());
+ ASSERT(reg().is(target));
+}
+
+
//
-------------------------------------------------------------------------
// RegisterAllocator implementation.
@@ -113,11 +133,29 @@
ASSERT(cgen_->frame() != NULL);
Register free_reg = cgen_->frame()->SpillAnyRegister();
if (free_reg.is_valid()) {
- ASSERT(!is_used(free_reg.code()));
+ ASSERT(!is_used(free_reg));
return Result(free_reg, cgen_);
}
}
return result;
+}
+
+
+Result RegisterAllocator::Allocate(Register target) {
+ // If the target is not referenced, it can simply be allocated.
+ if (!is_used(target)) {
+ return Result(target, cgen_);
+ }
+ // If the target is only referenced in the frame, it can be spilled and
+ // then allocated.
+ ASSERT(cgen_->frame() != NULL);
+ if (count(target) == cgen_->frame()->register_count(target)) {
+ cgen_->frame()->Spill(target);
+ ASSERT(!is_used(target));
+ return Result(target, cgen_);
+ }
+ // Otherwise (if it's referenced outside the frame) we cannot allocate
it.
+ return Result(cgen_);
}
Modified: branches/experimental/toiger/src/register-allocator-ia32.h
==============================================================================
--- branches/experimental/toiger/src/register-allocator-ia32.h (original)
+++ branches/experimental/toiger/src/register-allocator-ia32.h Mon Dec 22
06:46:21 2008
@@ -41,6 +41,12 @@
class Result BASE_EMBEDDED {
public:
+ enum Type {
+ INVALID,
+ REGISTER,
+ CONSTANT
+ };
+
// Construct an invalid result.
explicit Result(CodeGenerator* cgen) : type_(INVALID), cgen_(cgen) {}
@@ -60,11 +66,11 @@
other.CopyTo(this);
}
- Result& operator=(Result& other) {
+ Result& operator=(const Result& other) {
if (this != &other) {
Unuse();
other.CopyTo(this);
- other.Unuse();
+ // other.Unuse();
}
return *this;
}
@@ -73,6 +79,8 @@
void Unuse();
+ Type type() const { return type_; }
+
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
bool is_constant() const { return type() == CONSTANT; }
@@ -87,18 +95,17 @@
return Handle<Object>(data_.handle_);
}
- // Change a result to a register result. If the result is not already
- // in a register, allocate a register from the code generator, and emit
- // code to move the value into that register.
+ // Move this result to an arbitrary register. The register is not
+ // necessarily spilled from the frame or even singly-referenced outside
+ // it.
void ToRegister();
- private:
- enum Type {
- INVALID,
- REGISTER,
- CONSTANT
- };
+ // Move this result to a specified register. The register is spilled
from
+ // the frame, and the register is singly-referenced (by this result)
+ // outside the frame.
+ void ToRegister(Register reg);
+ private:
Type type_;
union {
@@ -108,8 +115,6 @@
CodeGenerator* cgen_;
- Type type() const { return type_; }
-
void CopyTo(Result* destination) const;
};
@@ -130,11 +135,13 @@
}
}
- // Predicates and accessors for the reference counts. They take a
- // register code rather than a register because they are frequently used
- // in a loop over the register codes.
+ // Predicates and accessors for the reference counts. The versions
+ // that take a register code rather than a register are for
+ // convenience in loops over the register codes.
bool is_used(int reg_code) const { return ref_counts_[reg_code] > 0; }
+ bool is_used(Register reg) const { return is_used(reg.code()); }
int count(int reg_code) const { return ref_counts_[reg_code]; }
+ int count(Register reg) const { return count(reg.code()); }
// Record a use of a register by incrementing its reference count.
void Use(Register reg) {
@@ -169,7 +176,9 @@
// Predicates and accessors for the registers' reference counts.
bool is_used(int reg_code) const { return registers_.is_used(reg_code); }
- int count(int reg_code) { return registers_.count(reg_code); }
+ bool is_used(Register reg) const { return
registers_.is_used(reg.code()); }
+ int count(int reg_code) const { return registers_.count(reg_code); }
+ int count(Register reg) const { return registers_.count(reg.code()); }
// Explicitly record a reference to a register.
void Use(Register reg) { registers_.Use(reg); }
@@ -185,6 +194,10 @@
// Allocate a free register and return a register result if possible or
// fail and return an invalid result.
Result Allocate();
+
+ // Allocate a specific register if possible, spilling it from the frame
if
+ // necessary, or else fail and return an invalid result.
+ Result Allocate(Register target);
// Allocate a free register without spilling any from the current frame
or
// fail and return an invalid result.
Modified: branches/experimental/toiger/src/virtual-frame-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/virtual-frame-ia32.cc (original)
+++ branches/experimental/toiger/src/virtual-frame-ia32.cc Mon Dec 22
06:46:21 2008
@@ -131,7 +131,16 @@
}
-// Spill any register if possible, making its reference count zero.
+void VirtualFrame::Spill(Register target) {
+ for (int i = 0; i < elements_.length(); i++) {
+ if (elements_[i].is_register() && elements_[i].reg().is(target)) {
+ SpillElementAt(i);
+ }
+ }
+}
+
+
+// Spill any register if possible, making its external reference count
zero.
Register VirtualFrame::SpillAnyRegister() {
// Find the leftmost (ordered by register code), least
// internally-referenced register whose internal reference count matches
@@ -148,26 +157,11 @@
}
}
- if (best_register_code != no_reg.code_) {
- // Spill all occurrences of the register. There are min_count
- // occurrences, stop when we have spilled them all to avoid syncing
- // elements unnecessarily.
- int i = 0;
- while (min_count > 0) {
- ASSERT(i < elements_.length());
- if (elements_[i].is_register() &&
- elements_[i].reg().code() == best_register_code) {
- // Found an instance of the best_register being used in the frame.
- // Spill it.
- SpillElementAt(i);
- min_count--;
- }
- i++;
- }
- }
-
- ASSERT(cgen_->allocator()->count(best_register_code) == 0);
Register result = { best_register_code };
+ if (result.is_valid()) {
+ Spill(result);
+ ASSERT(!cgen_->allocator()->is_used(result));
+ }
return result;
}
@@ -277,29 +271,6 @@
}
-bool VirtualFrame::IsMergable() {
- // We cannot merge to a frame that has constants as elements, because an
- // arbitrary frame may not have the same constants at those locations.
We
- // cannot merge to a frame that has registers that are mulitply
referenced
- // in the frame, because an arbitrary frame might not exhibit the same
- // sharing. Thus, a frame is mergable if all elements are in memory or a
- // register and no register is multiply referenced.
- for (int i = 0; i < RegisterFile::kNumRegisters; i++) {
- if (frame_registers_.count(i) > 1) {
- return false;
- }
- }
-
- for (int i = 0; i < elements_.length(); i++) {
- if (!elements_[i].is_memory() && !elements_[i].is_register()) {
- return false;
- }
- }
-
- return true;
-}
-
-
bool VirtualFrame::RequiresMergeCode() {
// A frame requires merge code to be generated in the event that
// there are duplicated non-synched registers or else elements not
@@ -331,13 +302,21 @@
void VirtualFrame::MakeMergable() {
Comment cmnt(masm_, "[ Make frame mergable");
+ // We can call MakeMergable on a frame that is not the code generator's
+ // current frame, which will leave the global register counts out of sync
+ // with the frame. We simply save the current frame and restore it at
the
+ // end of this function. We should find a better way to deal with this.
+ VirtualFrame* original_frame = cgen_->frame();
+ ASSERT(cgen_->HasValidEntryRegisters());
+ cgen_->SetFrame(this);
+ ASSERT(cgen_->HasValidEntryRegisters());
+
// Remove constants from the frame and ensure that no registers are
- // multiply referenced within the frame. Allocate elements to their
- // new locations from the top down so that the topmost elements have
- // a chance to be in registers, then fill them into memory from the
- // bottom up. (NB: Currently when spilling registers that are
- // multiply referenced, it is the lowermost occurrence that gets to
- // stay in the register.)
+ // multiply referenced within the frame. Allocate elements to their new
+ // locations from the top down so that the topmost elements have a chance
+ // to be in registers, then fill them into memory from the bottom up.
+ // (NB: Currently when spilling registers that are multiply referenced,
it
+ // is the lowermost occurrence that gets to stay in the register.)
// The elements of new_elements are initially invalid.
FrameElement* new_elements = new FrameElement[elements_.length()];
@@ -346,7 +325,7 @@
FrameElement element = elements_[i];
if (element.is_constant() ||
(element.is_register() &&
- frame_registers_.count(element.reg().code()) > 1)) {
+ frame_registers_.count(element.reg()) > 1)) {
// A simple strategy is to locate these elements in memory if they
are
// synced (avoiding a spill right now) and otherwise to prefer a
// register for them.
@@ -402,11 +381,18 @@
}
delete[] new_elements;
+ ASSERT(cgen_->HasValidEntryRegisters());
+ cgen_->SetFrame(original_frame);
+ ASSERT(cgen_->HasValidEntryRegisters());
}
void VirtualFrame::MergeTo(VirtualFrame* expected) {
Comment cmnt(masm_, "[ Merge frame");
+ // We should always be merging the code generator's current frame to an
+ // expected frame.
+ ASSERT(cgen_->frame() == this);
+
ASSERT(cgen_ == expected->cgen_);
ASSERT(masm_ == expected->masm_);
ASSERT(elements_.length() == expected->elements_.length());
@@ -481,7 +467,7 @@
elements_[i] = target;
} else {
// We need to move source to target.
- if (frame_registers_.is_used(target.reg().code())) {
+ if (frame_registers_.is_used(target.reg())) {
// The move is blocked because the target contains valid data.
// If we are stuck with only cycles remaining, then we spill
source.
// Otherwise, we just need more iterations.
@@ -733,6 +719,7 @@
void VirtualFrame::PushTryHandler(HandlerType type) {
+ ASSERT(cgen_->HasValidEntryRegisters());
// Grow the expression stack by handler size less two (the return address
// is already pushed by a call instruction, and PushTryHandler from the
// macro assembler will leave the top of stack in the eax register to be
@@ -745,18 +732,34 @@
void VirtualFrame::CallStub(CodeStub* stub, int frame_arg_count) {
+ ASSERT(cgen_->HasValidEntryRegisters());
PrepareForCall(frame_arg_count);
__ CallStub(stub);
}
+Result VirtualFrame::CallStub(CodeStub* stub,
+ Result* arg0,
+ Result* arg1,
+ int frame_arg_count) {
+ arg0->Unuse();
+ arg1->Unuse();
+ CallStub(stub, frame_arg_count);
+ Result result = cgen_->allocator()->Allocate(eax);
+ ASSERT(result.is_valid());
+ return result;
+}
+
+
void VirtualFrame::CallRuntime(Runtime::Function* f, int frame_arg_count) {
+ ASSERT(cgen_->HasValidEntryRegisters());
PrepareForCall(frame_arg_count);
__ CallRuntime(f, frame_arg_count);
}
void VirtualFrame::CallRuntime(Runtime::FunctionId id, int
frame_arg_count) {
+ ASSERT(cgen_->HasValidEntryRegisters());
PrepareForCall(frame_arg_count);
__ CallRuntime(id, frame_arg_count);
}
@@ -765,6 +768,7 @@
void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
int frame_arg_count) {
+ ASSERT(cgen_->HasValidEntryRegisters());
PrepareForCall(frame_arg_count);
__ InvokeBuiltin(id, flag);
}
@@ -773,6 +777,7 @@
void VirtualFrame::CallCodeObject(Handle<Code> code,
RelocInfo::Mode rmode,
int frame_arg_count) {
+ ASSERT(cgen_->HasValidEntryRegisters());
PrepareForCall(frame_arg_count);
__ call(code, rmode);
}
@@ -802,17 +807,6 @@
void VirtualFrame::Drop() { Drop(1); }
-/*
-We need comparison with literal to work.
-It will get Result, is register or constant.
-Pop gives it this, from register, constant, memory, or
-reference to slot.
-
-In comparison to literal, we need register case to work.
-We need non-smi stub to exit and return with a non-spilled frame.
-*/
-
-
Result VirtualFrame::Pop() {
FrameElement popped = elements_.RemoveLast();
bool pop_needed = (stack_pointer_ == elements_.length());
@@ -840,6 +834,7 @@
return temp;
}
}
+
void VirtualFrame::EmitPop(Register reg) {
ASSERT(stack_pointer_ == elements_.length() - 1);
Modified: branches/experimental/toiger/src/virtual-frame-ia32.h
==============================================================================
--- branches/experimental/toiger/src/virtual-frame-ia32.h (original)
+++ branches/experimental/toiger/src/virtual-frame-ia32.h Mon Dec 22
06:46:21 2008
@@ -175,6 +175,10 @@
return elements_.length() - expression_base_index();
}
+ int register_count(Register reg) {
+ return frame_registers_.count(reg);
+ }
+
// Add extra in-memory elements to the top of the frame to match an
actual
// frame (eg, the frame after an exception handler is pushed). No code
is
// emitted.
@@ -187,20 +191,15 @@
// Spill all values from the frame to memory.
void SpillAll();
- // Spill a register if possible. Return the register spilled or no_reg
if
- // it was not possible to spill one.
- Register SpillAnyRegister();
+ // Spill all occurrences of a specific register from the frame.
+ void Spill(Register reg);
- // True if an arbitrary frame of the same size could be merged to this
- // one. Requires all values to be in a unique register or memory
- // location.
- bool IsMergable();
+ // Spill all occurrences of an arbitrary register if possible. Return
the
+ // register spilled or no_reg if it was not possible to free any register
+ // (ie, they all have frame-external references).
+ Register SpillAnyRegister();
// True if making the frame mergable via MakeMergable will generate code.
- // This differs from !IsMergable() because there are some non-mergable
- // frames that can be made mergable simply by changing internal state
(eg,
- // forgetting about constants that are synced to memory) without
- // generating code.
bool RequiresMergeCode();
// Ensure that this frame is in a state where an arbitrary frame of the
@@ -294,6 +293,10 @@
// Call a code stub, given the number of arguments it expects on (and
// removes from) the top of the physical frame.
void CallStub(CodeStub* stub, int frame_arg_count);
+ Result CallStub(CodeStub* stub,
+ Result* arg0,
+ Result* arg1,
+ int frame_arg_count);
// Call the runtime, given the number of arguments expected on (and
// removed from) the top of the physical frame.
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---