Revision: 3833
Author: [email protected]
Date: Thu Feb 11 00:41:19 2010
Log: Simple type tracking in the fast code generator.
Initial implementation of ad hoc must-be-smi tracking in the fast code
generator. Type information is used to avoid the write barrier for
smi property stores and to avoid the smi check for the inputs/output
of bitwise OR.
Review URL: http://codereview.chromium.org/597021
http://code.google.com/p/v8/source/detail?r=3833
Modified:
/branches/bleeding_edge/src/arm/fast-codegen-arm.cc
/branches/bleeding_edge/src/fast-codegen.h
/branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc
/branches/bleeding_edge/src/x64/fast-codegen-x64.cc
=======================================
--- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Tue Feb 9 05:44:43
2010
+++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Thu Feb 11 00:41:19
2010
@@ -62,6 +62,9 @@
__ cmp(destination(), ip);
__ Check(ne, "DontDelete cells can't contain the hole");
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
@@ -75,21 +78,37 @@
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+
// Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ mov(scratch0(), receiver_reg()); // Copy receiver for write
barrier.
+ __ str(accumulator0(), FieldMemOperand(receiver_reg(), offset));
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ mov(scratch0(), receiver_reg());
+ }
} else {
offset += FixedArray::kHeaderSize;
__ ldr(scratch0(),
FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset));
- }
- // Perform the store.
- __ str(accumulator0(), FieldMemOperand(scratch0(), offset));
- __ mov(scratch1(), Operand(offset));
- __ RecordWrite(scratch0(), scratch1(), ip);
+ __ str(accumulator0(), FieldMemOperand(scratch0(), offset));
+ }
+
+ if (needs_write_barrier) {
+ __ mov(scratch1(), Operand(offset));
+ __ RecordWrite(scratch0(), scratch1(), ip);
+ }
+
if (destination().is(accumulator1())) {
__ mov(accumulator1(), accumulator0());
+ if (is_smi(accumulator0())) {
+ set_as_smi(accumulator1());
+ } else {
+ clear_as_smi(accumulator1());
+ }
}
}
@@ -115,30 +134,41 @@
FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset));
__ ldr(destination(), FieldMemOperand(scratch0(), offset));
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
void FastCodeGenerator::EmitBitOr() {
- Register check; // A register is used for the smi check/operation.
- if (destination().is(no_reg)) {
- check = scratch0(); // Do not clobber either operand register.
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to
check
+ // the operands or result. There is no need to perform the operation
in
+ // an effect context.
+ if (!destination().is(no_reg)) {
+ __ orr(destination(), accumulator1(), Operand(accumulator0()));
+ }
+ } else if (destination().is(no_reg)) {
+ // Result is not needed but do not clobber the operands in case of
+ // bailout.
+ __ orr(scratch0(), accumulator1(), Operand(accumulator0()));
+ __ BranchOnNotSmi(scratch0(), bailout());
} else {
- // Preserve whichever operand shares the destination register in case
we
- // have to bail out.
- __ mov(scratch0(), destination());
- check = destination();
- }
- __ orr(check, accumulator1(), Operand(accumulator0()));
- // Restore the clobbered operand if necessary.
- if (destination().is(no_reg)) {
- __ BranchOnNotSmi(check, bailout());
- } else {
+ // Preserve the destination operand in a scratch register in case of
+ // bailout.
Label done;
- __ BranchOnSmi(check, &done);
+ __ mov(scratch0(), destination());
+ __ orr(destination(), accumulator1(), Operand(accumulator0()));
+ __ BranchOnSmi(destination(), &done);
__ mov(destination(), scratch0());
__ jmp(bailout());
__ bind(&done);
}
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known
to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
=======================================
--- /branches/bleeding_edge/src/fast-codegen.h Tue Feb 9 05:44:43 2010
+++ /branches/bleeding_edge/src/fast-codegen.h Thu Feb 11 00:41:19 2010
@@ -66,7 +66,7 @@
class FastCodeGenerator: public AstVisitor {
public:
explicit FastCodeGenerator(MacroAssembler* masm)
- : masm_(masm), info_(NULL), destination_(no_reg) {
+ : masm_(masm), info_(NULL), destination_(no_reg), smi_bits_(0) {
}
static Handle<Code> MakeCode(CompilationInfo* info);
@@ -96,6 +96,21 @@
ASSERT(reg.is(accumulator0()) || reg.is(accumulator1()));
return (reg.is(accumulator0())) ? accumulator1() : accumulator0();
}
+
+ // Flags are true if the respective register is statically known to hold
a
+ // smi. We do not track every register, only the accumulator registers.
+ bool is_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ return (smi_bits_ & reg.bit()) != 0;
+ }
+ void set_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ | reg.bit();
+ }
+ void clear_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ & ~reg.bit();
+ }
// AST node visit functions.
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
@@ -129,6 +144,7 @@
CompilationInfo* info_;
Label bailout_;
Register destination_;
+ uint32_t smi_bits_;
DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
};
=======================================
--- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Tue Feb 9
05:44:43 2010
+++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Thu Feb 11
00:41:19 2010
@@ -61,6 +61,9 @@
__ cmp(destination(), Factory::the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
@@ -74,26 +77,43 @@
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
- // Negative offsets are inobject properties.
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+
+ // Perform the store. Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ mov(scratch0(), receiver_reg()); // Copy receiver for write
barrier.
+ __ mov(FieldOperand(receiver_reg(), offset), accumulator0());
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ mov(scratch0(), receiver_reg());
+ }
} else {
offset += FixedArray::kHeaderSize;
__ mov(scratch0(),
FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
- }
- // Perform the store.
- __ mov(FieldOperand(scratch0(), offset), accumulator0());
- if (destination().is(no_reg)) {
- __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
- } else {
- // Copy the value to the other accumulator to preserve a copy from the
- // write barrier. One of the accumulators is available as a scratch
- // register.
+ __ mov(FieldOperand(scratch0(), offset), accumulator0());
+ }
+
+ if (needs_write_barrier) {
+ if (destination().is(no_reg)) {
+ // After RecordWrite accumulator0 is only accidently a smi, but it is
+ // already marked as not known to be one.
+ __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
+ } else {
+ // Copy the value to the other accumulator to preserve a copy from
the
+ // write barrier. One of the accumulators is available as a scratch
+ // register. Neither is a smi.
+ __ mov(accumulator1(), accumulator0());
+ clear_as_smi(accumulator1());
+ Register value_scratch = other_accumulator(destination());
+ __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ }
+ } else if (destination().is(accumulator1())) {
__ mov(accumulator1(), accumulator0());
- Register value_scratch = other_accumulator(destination());
- __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ // Is a smi because we do not need the write barrier.
+ set_as_smi(accumulator1());
}
}
@@ -119,36 +139,46 @@
FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
__ mov(destination(), FieldOperand(scratch0(), offset));
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
void FastCodeGenerator::EmitBitOr() {
- Register copied; // One operand is copied to a scratch register.
- Register other; // The other is not modified by the operation.
- Register check; // A register is used for the smi check/operation.
- if (destination().is(no_reg)) {
- copied = accumulator1(); // Arbitrary choice of operand to copy.
- other = accumulator0();
- check = scratch0(); // Do not clobber either operand register.
- } else {
- copied = destination();
- other = other_accumulator(destination());
- check = destination();
- }
- __ mov(scratch0(), copied);
- __ or_(check, Operand(other));
- __ test(check, Immediate(kSmiTagMask));
-
- // Restore the clobbered operand if necessary.
- if (destination().is(no_reg)) {
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to
check
+ // the operands or result. There is no need to perform the operation
in
+ // an effect context.
+ if (!destination().is(no_reg)) {
+ // Leave the result in the destination register. Bitwise or is
+ // commutative.
+ __ or_(destination(), Operand(other_accumulator(destination())));
+ }
+ } else if (destination().is(no_reg)) {
+ // Result is not needed but do not clobber the operands in case of
+ // bailout.
+ __ mov(scratch0(), accumulator1());
+ __ or_(scratch0(), Operand(accumulator0()));
+ __ test(scratch0(), Immediate(kSmiTagMask));
__ j(not_zero, bailout(), not_taken);
} else {
+ // Preserve the destination operand in a scratch register in case of
+ // bailout.
Label done;
+ __ mov(scratch0(), destination());
+ __ or_(destination(), Operand(other_accumulator(destination())));
+ __ test(destination(), Immediate(kSmiTagMask));
__ j(zero, &done, taken);
- __ mov(copied, scratch0());
+ __ mov(destination(), scratch0());
__ jmp(bailout());
__ bind(&done);
}
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known
to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
=======================================
--- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Tue Feb 9 05:44:43
2010
+++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Thu Feb 11 00:41:19
2010
@@ -53,6 +53,7 @@
void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
ASSERT(!destination().is(no_reg));
ASSERT(cell->IsJSGlobalPropertyCell());
+
__ Move(destination(), cell);
__ movq(destination(),
FieldOperand(destination(), JSGlobalPropertyCell::kValueOffset));
@@ -60,6 +61,9 @@
__ Cmp(destination(), Factory::the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
@@ -73,26 +77,43 @@
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
- // Negative offsets are inobject properties.
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+
+ // Perform the store. Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ movq(scratch0(), receiver_reg()); // Copy receiver for write
barrier.
+ __ movq(FieldOperand(receiver_reg(), offset), accumulator0());
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ movq(scratch0(), receiver_reg());
+ }
} else {
offset += FixedArray::kHeaderSize;
__ movq(scratch0(),
FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
- }
- // Perform the store.
- __ movq(FieldOperand(scratch0(), offset), accumulator0());
- if (destination().is(no_reg)) {
- __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
- } else {
- // Copy the value to the other accumulator to preserve a copy from the
- // write barrier. One of the accumulators is available as a scratch
- // register.
+ __ movq(FieldOperand(scratch0(), offset), accumulator0());
+ }
+
+ if (needs_write_barrier) {
+ if (destination().is(no_reg)) {
+ // After RecordWrite accumulator0 is only accidently a smi, but it is
+ // already marked as not known to be one.
+ __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
+ } else {
+ // Copy the value to the other accumulator to preserve a copy from
the
+ // write barrier. One of the accumulators is available as a scratch
+ // register. Neither is a smi.
+ __ movq(accumulator1(), accumulator0());
+ clear_as_smi(accumulator1());
+ Register value_scratch = other_accumulator(destination());
+ __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ }
+ } else if (destination().is(accumulator1())) {
__ movq(accumulator1(), accumulator0());
- Register value_scratch = other_accumulator(destination());
- __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ // Is a smi because we do not need the write barrier.
+ set_as_smi(accumulator1());
}
}
@@ -118,34 +139,46 @@
FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
__ movq(destination(), FieldOperand(scratch0(), offset));
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
void FastCodeGenerator::EmitBitOr() {
- Register copied; // One operand is copied to a scratch register.
- Register other; // The other is not modified by the operation.
- Register check; // A register is used for the smi check/operation.
- if (destination().is(no_reg)) {
- copied = accumulator1(); // Arbitrary choice of operand to copy.
- other = accumulator0();
- check = scratch0(); // Do not clobber either operand register.
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to
check
+ // the operands or result.
+ if (destination().is(no_reg)) {
+ __ or_(accumulator1(), accumulator0());
+ } else {
+ // Leave the result in the destination register. Bitwise or is
+ // commutative.
+ __ or_(destination(), other_accumulator(destination()));
+ }
+ } else if (destination().is(no_reg)) {
+ // Result is not needed but do not clobber the operands in case of
+ // bailout.
+ __ movq(scratch0(), accumulator1());
+ __ or_(scratch0(), accumulator0());
+ __ JumpIfNotSmi(scratch0(), bailout());
} else {
- copied = destination();
- other = other_accumulator(destination());
- check = destination();
- }
- __ movq(scratch0(), copied);
- __ or_(check, other);
- // Restore the clobbered operand if necessary.
- if (destination().is(no_reg)) {
- __ JumpIfNotSmi(check, bailout());
- } else {
+ // Preserve the destination operand in a scratch register in case of
+ // bailout.
Label done;
- __ JumpIfSmi(check, &done);
- __ movq(copied, scratch0());
+ __ movq(scratch0(), destination());
+ __ or_(destination(), other_accumulator(destination()));
+ __ JumpIfSmi(destination(), &done);
+ __ movq(destination(), scratch0());
__ jmp(bailout());
__ bind(&done);
}
+
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known
to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev