From 308e69755b2c36529c3fdf367ec302c8d2b81995 Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Thu, 23 May 2013 08:32:07 +0000 Subject: [PATCH] Implement HChange support for Smis and use it in Load/StoreNameField BUG= R=verwaest@chromium.org Review URL: https://chromiumcodereview.appspot.com/15303004 Patch from Daniel Clifford . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14765 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 27 ++++++++++++++- src/arm/lithium-arm.h | 36 ++++++++++++++++++- src/arm/lithium-codegen-arm.cc | 70 +++++++++++++++++++++++++++++++------ src/arm/lithium-codegen-arm.h | 1 + src/hydrogen-instructions.cc | 6 ++-- src/hydrogen-instructions.h | 4 +-- src/hydrogen.cc | 4 ++- src/ia32/lithium-codegen-ia32.cc | 69 ++++++++++++++++++++++++++++-------- src/ia32/lithium-codegen-ia32.h | 1 + src/ia32/lithium-ia32.cc | 29 ++++++++++++++-- src/ia32/lithium-ia32.h | 30 +++++++++++++++- src/lithium-allocator.cc | 4 +-- src/lithium.h | 2 +- src/property-details.h | 1 + src/x64/lithium-codegen-x64.cc | 71 ++++++++++++++++++++++++++++++++------ src/x64/lithium-codegen-x64.h | 1 + src/x64/lithium-x64.cc | 32 +++++++++++++++-- src/x64/lithium-x64.h | 30 +++++++++++++++- test/mjsunit/smi-representation.js | 68 ++++++++++++++++++++++++++++++++++++ 19 files changed, 431 insertions(+), 55 deletions(-) create mode 100644 test/mjsunit/smi-representation.js diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 54efacb..57d7525 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1887,12 +1887,24 @@ LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) { LInstruction* LChunkBuilder::DoChange(HChange* instr) { Representation from = instr->from(); Representation to = instr->to(); + if (from.IsSmi()) { + if (to.IsTagged()) { + LOperand* value = UseRegister(instr->value()); + return DefineSameAsFirst(new(zone()) LDummyUse(value)); + } + from = Representation::Tagged(); + } if (from.IsTagged()) { if (to.IsDouble()) { info()->MarkAsDeferredCalling(); LOperand* value = UseRegister(instr->value()); LNumberUntagD* res = new(zone()) LNumberUntagD(value); return AssignEnvironment(DefineAsRegister(res)); + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegisterAtStart(val); + return AssignEnvironment( + DefineSameAsFirst(new(zone()) LCheckSmi(value))); } else { ASSERT(to.IsInteger32()); LOperand* value = NULL; @@ -1934,6 +1946,10 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LNumberTagD* result = new(zone()) LNumberTagD(value, temp1, temp2); Define(result, result_temp); return AssignPointerMap(result); + } else if (to.IsSmi()) { + LOperand* value = UseRegister(instr->value()); + return AssignEnvironment(DefineAsRegister(new(zone()) LDoubleToSmi(value, + TempRegister(), TempRegister()))); } else { ASSERT(to.IsInteger32()); LOperand* value = UseRegister(instr->value()); @@ -1956,6 +1972,15 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LNumberTagI* result = new(zone()) LNumberTagI(value); return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); } + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegister(val); + LInstruction* result = + DefineSameAsFirst(new(zone()) LInteger32ToSmi(value)); + if (val->HasRange() && val->range()->IsInSmiRange()) { + return result; + } + return AssignEnvironment(result); } else { ASSERT(to.IsDouble()); if (instr->value()->CheckFlag(HInstruction::kUint32)) { @@ -2049,7 +2074,7 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { return DefineAsRegister(new(zone()) LConstantI); } else if (r.IsDouble()) { return DefineAsRegister(new(zone()) LConstantD); - } else if (r.IsTagged()) { + } else if (r.IsTagged() || r.IsSmi()) { return DefineAsRegister(new(zone()) LConstantT); } else { UNREACHABLE(); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index c9d61b9..68e8748 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -95,6 +95,7 @@ class LCodeGen; V(Deoptimize) \ V(DivI) \ V(DoubleToI) \ + V(DoubleToSmi) \ V(DummyUse) \ V(ElementsKind) \ V(FixedArrayBaseLength) \ @@ -111,6 +112,7 @@ class LCodeGen; V(InstanceSize) \ V(InstructionGap) \ V(Integer32ToDouble) \ + V(Integer32ToSmi) \ V(Uint32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ @@ -1954,6 +1956,19 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> { }; +class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> { + public: + explicit LInteger32ToSmi(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; + + class LUint32ToDouble: public LTemplateInstruction<1, 1, 0> { public: explicit LUint32ToDouble(LOperand* value) { @@ -2007,6 +2022,25 @@ class LNumberTagD: public LTemplateInstruction<1, 1, 2> { }; +class LDoubleToSmi: public LTemplateInstruction<1, 1, 2> { + public: + LDoubleToSmi(LOperand* value, LOperand* temp, LOperand* temp2) { + inputs_[0] = value; + temps_[0] = temp; + temps_[1] = temp2; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + LOperand* temp2() { return temps_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi") + DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) + + bool truncating() { return hydrogen()->CanTruncateToInt32(); } +}; + + // Sometimes truncating conversion from a tagged value to an int32. class LDoubleToI: public LTemplateInstruction<1, 1, 2> { public: @@ -2353,7 +2387,7 @@ class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 2> { }; -class LCheckSmi: public LTemplateInstruction<0, 1, 0> { +class LCheckSmi: public LTemplateInstruction<1, 1, 0> { public: explicit LCheckSmi(LOperand* value) { inputs_[0] = value; diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index e12fcb7..e616c4a 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -518,7 +518,7 @@ DwVfpRegister LCodeGen::EmitLoadDoubleRegister(LOperand* op, Handle LCodeGen::ToHandle(LConstantOperand* op) const { HConstant* constant = chunk_->LookupConstant(op); - ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged()); return constant->handle(); } @@ -528,6 +528,11 @@ bool LCodeGen::IsInteger32(LConstantOperand* op) const { } +bool LCodeGen::IsSmi(LConstantOperand* op) const { + return chunk_->LookupLiteralRepresentation(op).IsSmi(); +} + + int LCodeGen::ToInteger32(LConstantOperand* op) const { HConstant* constant = chunk_->LookupConstant(op); return constant->Integer32Value(); @@ -2207,7 +2212,7 @@ void LCodeGen::DoBranch(LBranch* instr) { int false_block = chunk_->LookupDestination(instr->false_block_id()); Representation r = instr->hydrogen()->value()->representation(); - if (r.IsInteger32()) { + if (r.IsInteger32() || r.IsSmi()) { Register reg = ToRegister(instr->value()); __ cmp(reg, Operand::Zero()); EmitBranch(true_block, false_block, ne); @@ -4210,13 +4215,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { Handle transition = instr->transition(); - if (FLAG_track_fields && representation.IsSmi()) { - Register value = ToRegister(instr->value()); - __ SmiTag(value, value, SetCC); - if (!instr->hydrogen()->value()->range()->IsInSmiRange()) { - DeoptimizeIf(vs, instr->environment()); - } - } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { + if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { Register value = ToRegister(instr->value()); if (!instr->hydrogen()->value()->type().IsHeapObject()) { __ SmiTst(value); @@ -4702,6 +4701,19 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { } +void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) { + LOperand* input = instr->value(); + ASSERT(input->IsRegister()); + LOperand* output = instr->result(); + ASSERT(output->IsRegister()); + __ SmiTag(ToRegister(output), ToRegister(input), SetCC); + if (!instr->hydrogen()->value()->HasRange() || + !instr->hydrogen()->value()->range()->IsInSmiRange()) { + DeoptimizeIf(vs, instr->environment()); + } +} + + void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) { LOperand* input = instr->value(); LOperand* output = instr->result(); @@ -5139,7 +5151,33 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { DwVfpRegister double_input = ToDoubleRegister(instr->value()); DwVfpRegister double_scratch = double_scratch0(); - Label done; + if (instr->truncating()) { + Register scratch3 = ToRegister(instr->temp2()); + __ ECMAToInt32(result_reg, double_input, + scratch1, scratch2, scratch3, double_scratch); + } else { + __ TryDoubleToInt32Exact(result_reg, double_input, double_scratch); + // Deoptimize if the input wasn't a int32 (inside a double). + DeoptimizeIf(ne, instr->environment()); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + Label done; + __ cmp(result_reg, Operand::Zero()); + __ b(ne, &done); + __ vmov(scratch1, double_input.high()); + __ tst(scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment()); + __ bind(&done); + } + } +} + + +void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) { + Register result_reg = ToRegister(instr->result()); + Register scratch1 = scratch0(); + Register scratch2 = ToRegister(instr->temp()); + DwVfpRegister double_input = ToDoubleRegister(instr->value()); + DwVfpRegister double_scratch = double_scratch0(); if (instr->truncating()) { Register scratch3 = ToRegister(instr->temp2()); @@ -5149,8 +5187,18 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { __ TryDoubleToInt32Exact(result_reg, double_input, double_scratch); // Deoptimize if the input wasn't a int32 (inside a double). DeoptimizeIf(ne, instr->environment()); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + Label done; + __ cmp(result_reg, Operand::Zero()); + __ b(ne, &done); + __ vmov(scratch1, double_input.high()); + __ tst(scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment()); + __ bind(&done); + } } - __ bind(&done); + __ SmiTag(result_reg, SetCC); + DeoptimizeIf(vs, instr->environment()); } diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index 1a34169..b4dca22 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -126,6 +126,7 @@ class LCodeGen BASE_EMBEDDED { MemOperand ToHighMemOperand(LOperand* op) const; bool IsInteger32(LConstantOperand* op) const; + bool IsSmi(LConstantOperand* op) const; Handle ToHandle(LConstantOperand* op) const; // Try to generate code for the entire chunk, but it may fail if the diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index b747387..4a0aeff 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1687,7 +1687,7 @@ Range* HValue::InferRange(Zone* zone) { Range* HChange::InferRange(Zone* zone) { Range* input_range = value()->range(); if (from().IsInteger32() && - to().IsTagged() && + to().IsSmiOrTagged() && !value()->CheckFlag(HInstruction::kUint32) && input_range != NULL && input_range->IsInSmiRange()) { set_type(HType::Smi()); @@ -3563,7 +3563,7 @@ Representation HPhi::RepresentationFromInputs() { HPhi* hint_value = HUnknownOSRValue::cast(value)->incoming_value(); if (hint_value != NULL) { Representation hint = hint_value->representation(); - if (hint.IsTagged()) return hint; + if (hint.IsSmiOrTagged()) return hint; if (hint.IsDouble()) double_occurred = true; if (hint.IsInteger32()) int32_occurred = true; } @@ -3571,7 +3571,7 @@ Representation HPhi::RepresentationFromInputs() { } if (value->representation().IsDouble()) double_occurred = true; if (value->representation().IsInteger32()) int32_occurred = true; - if (value->representation().IsTagged()) { + if (value->representation().IsSmiOrTagged()) { if (value->IsConstant()) { HConstant* constant = HConstant::cast(value); if (constant->IsConvertibleToInteger()) { diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index f16efe6..90fea07 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -5209,7 +5209,7 @@ class HLoadNamedField: public HTemplateInstruction<2> { if (FLAG_track_fields && field_representation.IsSmi()) { set_type(HType::Smi()); - set_representation(Representation::Tagged()); + set_representation(field_representation); } else if (FLAG_track_double_fields && field_representation.IsDouble()) { set_representation(field_representation); } else if (FLAG_track_heap_object_fields && @@ -5604,7 +5604,7 @@ class HStoreNamedField: public HTemplateInstruction<2> { return field_representation_; } else if (FLAG_track_fields && index == 1 && field_representation_.IsSmi()) { - return Representation::Integer32(); + return field_representation_; } return Representation::Tagged(); } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index f25efbf..fe639e2 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -4399,7 +4399,9 @@ void HGraph::ComputeMinusZeroChecks() { Representation from = change->value()->representation(); ASSERT(from.Equals(change->from())); if (from.IsInteger32()) { - ASSERT(change->to().IsTagged() || change->to().IsDouble()); + ASSERT(change->to().IsTagged() || + change->to().IsDouble() || + change->to().IsSmi()); ASSERT(visited.IsEmpty()); PropagateMinusZeroChecks(change->value(), &visited); visited.Clear(); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index bf014da..7cc8e4a 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -590,7 +590,7 @@ int LCodeGen::ToInteger32(LConstantOperand* op) const { Handle LCodeGen::ToHandle(LConstantOperand* op) const { HConstant* constant = chunk_->LookupConstant(op); - ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged()); return constant->handle(); } @@ -607,6 +607,11 @@ bool LCodeGen::IsInteger32(LConstantOperand* op) const { } +bool LCodeGen::IsSmi(LConstantOperand* op) const { + return chunk_->LookupLiteralRepresentation(op).IsSmi(); +} + + Operand LCodeGen::ToOperand(LOperand* op) const { if (op->IsRegister()) return Operand(ToRegister(op)); if (op->IsDoubleRegister()) return Operand(ToDoubleRegister(op)); @@ -2101,7 +2106,7 @@ void LCodeGen::DoBranch(LBranch* instr) { CpuFeatureScope scope(masm(), SSE2); Representation r = instr->hydrogen()->value()->representation(); - if (r.IsInteger32()) { + if (r.IsInteger32() || r.IsSmi()) { Register reg = ToRegister(instr->value()); __ test(reg, Operand(reg)); EmitBranch(true_block, false_block, not_zero); @@ -4220,15 +4225,9 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (FLAG_track_fields && representation.IsSmi()) { if (instr->value()->IsConstantOperand()) { LConstantOperand* operand_value = LConstantOperand::cast(instr->value()); - if (!IsInteger32(operand_value)) { + if (!IsSmi(operand_value)) { DeoptimizeIf(no_condition, instr->environment()); } - } else { - Register value = ToRegister(instr->value()); - __ SmiTag(value); - if (!instr->hydrogen()->value()->range()->IsInSmiRange()) { - DeoptimizeIf(overflow, instr->environment()); - } } } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { if (instr->value()->IsConstantOperand()) { @@ -4293,12 +4292,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (instr->value()->IsConstantOperand()) { LConstantOperand* operand_value = LConstantOperand::cast(instr->value()); - if (IsInteger32(operand_value)) { - // In lithium register preparation, we made sure that the constant integer - // operand fits into smi range. - Smi* smi_value = Smi::FromInt(ToInteger32(operand_value)); - __ mov(FieldOperand(write_register, offset), Immediate(smi_value)); - } else if (operand_value->IsRegister()) { + if (operand_value->IsRegister()) { __ mov(FieldOperand(write_register, offset), ToRegister(operand_value)); } else { Handle handle_value = ToHandle(operand_value); @@ -4769,6 +4763,16 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { } +void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) { + Register input = ToRegister(instr->value()); + __ SmiTag(input); + if (!instr->hydrogen()->value()->HasRange() || + !instr->hydrogen()->value()->range()->IsInSmiRange()) { + DeoptimizeIf(overflow, instr->environment()); + } +} + + void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) { CpuFeatureScope scope(masm(), SSE2); LOperand* input = instr->value(); @@ -5637,6 +5641,41 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { } +void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) { + LOperand* input = instr->value(); + ASSERT(input->IsDoubleRegister()); + LOperand* result = instr->result(); + ASSERT(result->IsRegister()); + CpuFeatureScope scope(masm(), SSE2); + + XMMRegister input_reg = ToDoubleRegister(input); + Register result_reg = ToRegister(result); + + Label done; + __ cvttsd2si(result_reg, Operand(input_reg)); + __ cvtsi2sd(xmm0, Operand(result_reg)); + __ ucomisd(xmm0, input_reg); + DeoptimizeIf(not_equal, instr->environment()); + DeoptimizeIf(parity_even, instr->environment()); // NaN. + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // The integer converted back is equal to the original. We + // only have to test if we got -0 as an input. + __ test(result_reg, Operand(result_reg)); + __ j(not_zero, &done, Label::kNear); + __ movmskpd(result_reg, input_reg); + // Bit 0 contains the sign of the double in input_reg. + // If input was positive, we are ok and return 0, otherwise + // deoptimize. + __ and_(result_reg, 1); + DeoptimizeIf(not_zero, instr->environment()); + __ bind(&done); + } + __ SmiTag(result_reg); + DeoptimizeIf(overflow, instr->environment()); +} + + void LCodeGen::DoCheckSmi(LCheckSmi* instr) { LOperand* input = instr->value(); __ test(ToOperand(input), Immediate(kSmiTagMask)); diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index 9f8d4fd..7de3103 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -111,6 +111,7 @@ class LCodeGen BASE_EMBEDDED { bool IsX87TopOfStack(LOperand* op) const; bool IsInteger32(LConstantOperand* op) const; + bool IsSmi(LConstantOperand* op) const; Immediate ToInteger32Immediate(LOperand* op) const { return Immediate(ToInteger32(LConstantOperand::cast(op))); } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 8179753..0a098cb 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1908,6 +1908,13 @@ LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) { LInstruction* LChunkBuilder::DoChange(HChange* instr) { Representation from = instr->from(); Representation to = instr->to(); + if (from.IsSmi()) { + if (to.IsTagged()) { + LOperand* value = UseRegister(instr->value()); + return DefineSameAsFirst(new(zone()) LDummyUse(value)); + } + from = Representation::Tagged(); + } // Only mark conversions that might need to allocate as calling rather than // all changes. This makes simple, non-allocating conversion not have to force // building a stack frame. @@ -1925,6 +1932,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { } else { return AssignEnvironment(DefineX87TOS(res)); } + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegisterAtStart(val); + return AssignEnvironment( + DefineSameAsFirst(new(zone()) LCheckSmi(value))); } else { ASSERT(to.IsInteger32()); if (instr->value()->type().IsSmi()) { @@ -1970,6 +1982,10 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LUnallocated* result_temp = TempRegister(); LNumberTagD* result = new(zone()) LNumberTagD(value, temp); return AssignPointerMap(Define(result, result_temp)); + } else if (to.IsSmi()) { + LOperand* value = UseRegister(instr->value()); + return AssignEnvironment( + DefineAsRegister(new(zone()) LDoubleToSmi(value))); } else { ASSERT(to.IsInteger32()); bool truncating = instr->CanTruncateToInt32(); @@ -1994,6 +2010,15 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LNumberTagI* result = new(zone()) LNumberTagI(value); return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); } + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegister(val); + LInstruction* result = + DefineSameAsFirst(new(zone()) LInteger32ToSmi(value)); + if (val->HasRange() && val->range()->IsInSmiRange()) { + return result; + } + return AssignEnvironment(result); } else { ASSERT(to.IsDouble()); if (instr->value()->CheckFlag(HInstruction::kUint32)) { @@ -2034,13 +2059,13 @@ LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) { LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) { LOperand* value = UseAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckSmi(value)); + return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value))); } LInstruction* LChunkBuilder::DoCheckSmiOrInt32(HCheckSmiOrInt32* instr) { LOperand* value = UseAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckSmi(value)); + return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value))); } diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index a7d9fb4..e10bee5 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -90,6 +90,7 @@ class LCodeGen; V(Deoptimize) \ V(DivI) \ V(DoubleToI) \ + V(DoubleToSmi) \ V(DummyUse) \ V(ElementsKind) \ V(FixedArrayBaseLength) \ @@ -106,6 +107,7 @@ class LCodeGen; V(InstanceSize) \ V(InstructionGap) \ V(Integer32ToDouble) \ + V(Integer32ToSmi) \ V(Uint32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ @@ -1998,6 +2000,19 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> { }; +class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> { + public: + explicit LInteger32ToSmi(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; + + class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> { public: explicit LUint32ToDouble(LOperand* value, LOperand* temp) { @@ -2069,6 +2084,19 @@ class LDoubleToI: public LTemplateInstruction<1, 1, 1> { }; +class LDoubleToSmi: public LTemplateInstruction<1, 1, 0> { + public: + explicit LDoubleToSmi(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi") + DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) +}; + + // Truncating conversion from a tagged value to an int32. class LTaggedToI: public LTemplateInstruction<1, 1, 1> { public: @@ -2433,7 +2461,7 @@ class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 1> { }; -class LCheckSmi: public LTemplateInstruction<0, 1, 0> { +class LCheckSmi: public LTemplateInstruction<1, 1, 0> { public: explicit LCheckSmi(LOperand* value) { inputs_[0] = value; diff --git a/src/lithium-allocator.cc b/src/lithium-allocator.cc index 74132b3..04113e1 100644 --- a/src/lithium-allocator.cc +++ b/src/lithium-allocator.cc @@ -1057,7 +1057,7 @@ void LAllocator::ResolvePhis(HBasicBlock* block) { LInstruction* branch = InstructionAt(cur_block->last_instruction_index()); if (branch->HasPointerMap()) { - if (phi->representation().IsTagged()) { + if (phi->representation().IsSmiOrTagged()) { branch->pointer_map()->RecordPointer(phi_operand, zone()); } else if (!phi->representation().IsDouble()) { branch->pointer_map()->RecordUntagged(phi_operand, zone()); @@ -1640,7 +1640,7 @@ void LAllocator::TraceAlloc(const char* msg, ...) { bool LAllocator::HasTaggedValue(int virtual_register) const { HValue* value = graph_->LookupValue(virtual_register); if (value == NULL) return false; - return value->representation().IsTagged(); + return value->representation().IsSmiOrTagged(); } diff --git a/src/lithium.h b/src/lithium.h index 388f565..85df95a 100644 --- a/src/lithium.h +++ b/src/lithium.h @@ -558,7 +558,7 @@ class LEnvironment: public ZoneObject { Representation representation, bool is_uint32) { values_.Add(operand, zone()); - if (representation.IsTagged()) { + if (representation.IsSmiOrTagged()) { ASSERT(!is_uint32); is_tagged_.Add(values_.length() - 1); } diff --git a/src/property-details.h b/src/property-details.h index 46ca4f2..ac7ade4 100644 --- a/src/property-details.h +++ b/src/property-details.h @@ -131,6 +131,7 @@ class Representation { bool IsNone() const { return kind_ == kNone; } bool IsTagged() const { return kind_ == kTagged; } bool IsSmi() const { return kind_ == kSmi; } + bool IsSmiOrTagged() const { return IsSmi() || IsTagged(); } bool IsInteger32() const { return kind_ == kInteger32; } bool IsDouble() const { return kind_ == kDouble; } bool IsHeapObject() const { return kind_ == kHeapObject; } diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 42c6e91..8d43410 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -435,6 +435,12 @@ bool LCodeGen::IsInteger32Constant(LConstantOperand* op) const { } +bool LCodeGen::IsSmiConstant(LConstantOperand* op) const { + return op->IsConstantOperand() && + chunk_->LookupLiteralRepresentation(op).IsSmi(); +} + + bool LCodeGen::IsTaggedConstant(LConstantOperand* op) const { return op->IsConstantOperand() && chunk_->LookupLiteralRepresentation(op).IsTagged(); @@ -456,7 +462,7 @@ double LCodeGen::ToDouble(LConstantOperand* op) const { Handle LCodeGen::ToHandle(LConstantOperand* op) const { HConstant* constant = chunk_->LookupConstant(op); - ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged()); return constant->handle(); } @@ -1877,6 +1883,10 @@ void LCodeGen::DoBranch(LBranch* instr) { Register reg = ToRegister(instr->value()); __ testl(reg, reg); EmitBranch(true_block, false_block, not_zero); + } else if (r.IsSmi()) { + Register reg = ToRegister(instr->value()); + __ testq(reg, reg); + EmitBranch(true_block, false_block, not_zero); } else if (r.IsDouble()) { XMMRegister reg = ToDoubleRegister(instr->value()); __ xorps(xmm0, xmm0); @@ -3905,12 +3915,9 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (FLAG_track_fields && representation.IsSmi()) { if (instr->value()->IsConstantOperand()) { LConstantOperand* operand_value = LConstantOperand::cast(instr->value()); - if (!IsInteger32Constant(operand_value)) { + if (!IsSmiConstant(operand_value)) { DeoptimizeIf(no_condition, instr->environment()); } - } else { - Register value = ToRegister(instr->value()); - __ Integer32ToSmi(value, value); } } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { if (instr->value()->IsConstantOperand()) { @@ -3968,12 +3975,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (instr->value()->IsConstantOperand()) { LConstantOperand* operand_value = LConstantOperand::cast(instr->value()); - if (IsInteger32Constant(operand_value)) { - // In lithium register preparation, we made sure that the constant integer - // operand fits into smi range. - Smi* smi_value = Smi::FromInt(ToInteger32(operand_value)); - __ Move(FieldOperand(write_register, offset), smi_value); - } else if (operand_value->IsRegister()) { + if (operand_value->IsRegister()) { __ movq(FieldOperand(write_register, offset), ToRegister(operand_value)); } else { @@ -4434,6 +4436,18 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { } +void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) { + LOperand* input = instr->value(); + ASSERT(input->IsRegister()); + LOperand* output = instr->result(); + __ Integer32ToSmi(ToRegister(output), ToRegister(input)); + if (!instr->hydrogen()->value()->HasRange() || + !instr->hydrogen()->value()->range()->IsInSmiRange()) { + DeoptimizeIf(overflow, instr->environment()); + } +} + + void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) { LOperand* input = instr->value(); LOperand* output = instr->result(); @@ -4841,6 +4855,41 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { } +void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) { + LOperand* input = instr->value(); + ASSERT(input->IsDoubleRegister()); + LOperand* result = instr->result(); + ASSERT(result->IsRegister()); + CpuFeatureScope scope(masm(), SSE2); + + XMMRegister input_reg = ToDoubleRegister(input); + Register result_reg = ToRegister(result); + + Label done; + __ cvttsd2si(result_reg, input_reg); + __ cvtlsi2sd(xmm0, result_reg); + __ ucomisd(xmm0, input_reg); + DeoptimizeIf(not_equal, instr->environment()); + DeoptimizeIf(parity_even, instr->environment()); // NaN. + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // The integer converted back is equal to the original. We + // only have to test if we got -0 as an input. + __ testl(result_reg, result_reg); + __ j(not_zero, &done, Label::kNear); + __ movmskpd(result_reg, input_reg); + // Bit 0 contains the sign of the double in input_reg. + // If input was positive, we are ok and return 0, otherwise + // deoptimize. + __ andl(result_reg, Immediate(1)); + DeoptimizeIf(not_zero, instr->environment()); + __ bind(&done); + } + __ Integer32ToSmi(result_reg, result_reg); + DeoptimizeIf(overflow, instr->environment()); +} + + void LCodeGen::DoCheckSmi(LCheckSmi* instr) { LOperand* input = instr->value(); Condition cc = masm()->CheckSmi(ToRegister(input)); diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index d0dd90e..5f9ae2c 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -104,6 +104,7 @@ class LCodeGen BASE_EMBEDDED { Register ToRegister(LOperand* op) const; XMMRegister ToDoubleRegister(LOperand* op) const; bool IsInteger32Constant(LConstantOperand* op) const; + bool IsSmiConstant(LConstantOperand* op) const; int ToInteger32(LConstantOperand* op) const; double ToDouble(LConstantOperand* op) const; bool IsTaggedConstant(LConstantOperand* op) const; diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index cde4e86..135c4cf 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1812,6 +1812,13 @@ LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) { LInstruction* LChunkBuilder::DoChange(HChange* instr) { Representation from = instr->from(); Representation to = instr->to(); + if (from.IsSmi()) { + if (to.IsTagged()) { + LOperand* value = UseRegister(instr->value()); + return DefineSameAsFirst(new(zone()) LDummyUse(value)); + } + from = Representation::Tagged(); + } // Only mark conversions that might need to allocate as calling rather than // all changes. This makes simple, non-allocating conversion not have to force // building a stack frame. @@ -1821,6 +1828,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LOperand* value = UseRegister(instr->value()); LNumberUntagD* res = new(zone()) LNumberUntagD(value); return AssignEnvironment(DefineAsRegister(res)); + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegisterAtStart(val); + return AssignEnvironment( + DefineSameAsFirst(new(zone()) LCheckSmi(value))); } else { ASSERT(to.IsInteger32()); LOperand* value = UseRegister(instr->value()); @@ -1852,10 +1864,15 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LUnallocated* result_temp = TempRegister(); LNumberTagD* result = new(zone()) LNumberTagD(value, temp); return AssignPointerMap(Define(result, result_temp)); + } else if (to.IsSmi()) { + LOperand* value = UseRegister(instr->value()); + return AssignEnvironment( + DefineAsRegister(new(zone()) LDoubleToSmi(value))); } else { ASSERT(to.IsInteger32()); LOperand* value = UseRegister(instr->value()); - return AssignEnvironment(DefineAsRegister(new(zone()) LDoubleToI(value))); + return AssignEnvironment( + DefineAsRegister(new(zone()) LDoubleToI(value))); } } else if (from.IsInteger32()) { info()->MarkAsDeferredCalling(); @@ -1872,6 +1889,15 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LNumberTagI* result = new(zone()) LNumberTagI(value); return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); } + } else if (to.IsSmi()) { + HValue* val = instr->value(); + LOperand* value = UseRegister(val); + LInstruction* result = + DefineAsRegister(new(zone()) LInteger32ToSmi(value)); + if (val->HasRange() && val->range()->IsInSmiRange()) { + return result; + } + return AssignEnvironment(result); } else { if (instr->value()->CheckFlag(HInstruction::kUint32)) { LOperand* temp = FixedTemp(xmm1); @@ -1911,13 +1937,13 @@ LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) { LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) { LOperand* value = UseRegisterAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckSmi(value)); + return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value))); } LInstruction* LChunkBuilder::DoCheckSmiOrInt32(HCheckSmiOrInt32* instr) { LOperand* value = UseRegisterAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckSmi(value)); + return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value))); } diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 9926fc3..18f64f8 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -95,6 +95,7 @@ class LCodeGen; V(Deoptimize) \ V(DivI) \ V(DoubleToI) \ + V(DoubleToSmi) \ V(DummyUse) \ V(ElementsKind) \ V(FixedArrayBaseLength) \ @@ -112,6 +113,7 @@ class LCodeGen; V(InstanceSize) \ V(InstructionGap) \ V(Integer32ToDouble) \ + V(Integer32ToSmi) \ V(Uint32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ @@ -1887,6 +1889,19 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> { }; +class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> { + public: + explicit LInteger32ToSmi(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; + + class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> { public: explicit LUint32ToDouble(LOperand* value, LOperand* temp) { @@ -1958,6 +1973,19 @@ class LDoubleToI: public LTemplateInstruction<1, 1, 0> { }; +class LDoubleToSmi: public LTemplateInstruction<1, 1, 0> { + public: + explicit LDoubleToSmi(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi") + DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) +}; + + // Truncating conversion from a tagged value to an int32. class LTaggedToI: public LTemplateInstruction<1, 1, 1> { public: @@ -2266,7 +2294,7 @@ class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 1> { }; -class LCheckSmi: public LTemplateInstruction<0, 1, 0> { +class LCheckSmi: public LTemplateInstruction<1, 1, 0> { public: explicit LCheckSmi(LOperand* value) { inputs_[0] = value; diff --git a/test/mjsunit/smi-representation.js b/test/mjsunit/smi-representation.js new file mode 100644 index 0000000..882b5b9 --- /dev/null +++ b/test/mjsunit/smi-representation.js @@ -0,0 +1,68 @@ +// Copyright 2013 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. + +// Flags: --track-fields --track-double-fields --allow-natives-syntax + +function smi_field() { + return {"smi":0}; +} + +function check_smi_repr(o, d1, d2) { + var s = o.smi; + var d = d1 - d2; + s = s + d; + o.smi = s; + return o; +} + +var test = smi_field(); +check_smi_repr(smi_field(), 5, 3); +check_smi_repr(smi_field(), 6, 2); +%OptimizeFunctionOnNextCall(check_smi_repr); +var val = check_smi_repr(smi_field(), 8, 1); +assertTrue(%HaveSameMap(val, test)); + +function tagged_smi_field() { + var o = {"tag":false}; + o.tag = 10; + return o; +} + +function check_smi_repr_from_tagged(o, o2) { + var t = o2.tag; + o.smi = t; + return o; +} + +check_smi_repr_from_tagged(smi_field(), tagged_smi_field()); +check_smi_repr_from_tagged(smi_field(), tagged_smi_field()); +%OptimizeFunctionOnNextCall(check_smi_repr_from_tagged); +var val = check_smi_repr_from_tagged(smi_field(), tagged_smi_field()); +assertTrue(%HaveSameMap(val, test)); +var overflow = tagged_smi_field(); +overflow.tag = 0x80000000; +var val = check_smi_repr_from_tagged(smi_field(), overflow); -- 2.7.4