Commit 78df13d0 authored by olivf@chromium.org's avatar olivf@chromium.org

Move ToI conversions to the MacroAssembler

+ Replace DeferredTaggedToINoSSE2 by DoubleToIStub and a fpu version.

+ Prevent truncating TaggedToI from bailing out.

BUG=
R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/22290005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16464 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f4e16f24
......@@ -947,6 +947,11 @@ class HValue : public ZoneObject {
return type().ToStringOrToNumberCanBeObserved(representation());
}
MinusZeroMode GetMinusZeroMode() {
return CheckFlag(kBailoutOnMinusZero)
? FAIL_ON_MINUS_ZERO : TREAT_MINUS_ZERO_AS_ZERO;
}
protected:
// This function must be overridden for instructions with flag kUseGVN, to
// compare the non-Operand parts of the instruction.
......
......@@ -183,6 +183,7 @@ const IntelDoubleRegister double_register_4 = { 4 };
const IntelDoubleRegister double_register_5 = { 5 };
const IntelDoubleRegister double_register_6 = { 6 };
const IntelDoubleRegister double_register_7 = { 7 };
const IntelDoubleRegister no_double_reg = { -1 };
struct XMMRegister : IntelDoubleRegister {
......@@ -227,6 +228,7 @@ struct XMMRegister : IntelDoubleRegister {
#define xmm5 (static_cast<const XMMRegister&>(double_register_5))
#define xmm6 (static_cast<const XMMRegister&>(double_register_6))
#define xmm7 (static_cast<const XMMRegister&>(double_register_7))
#define no_xmm_reg (static_cast<const XMMRegister&>(no_double_reg))
struct X87Register : IntelDoubleRegister {
......
......@@ -658,18 +658,6 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
}
// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
// |conversion_failure| if the heap number did not contain an int32 value.
// Result is in ecx. Trashes ebx, xmm0, and xmm1.
static void ConvertHeapNumberToInt32(MacroAssembler* masm,
Register source,
Label* conversion_failure) {
__ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
FloatingPointHelper::CheckSSE2OperandIsInt32(
masm, conversion_failure, xmm0, ecx, ebx, xmm1);
}
void BinaryOpStub::Initialize() {
platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
}
......@@ -2270,16 +2258,7 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
__ cmp(ebx, factory->heap_number_map());
__ j(not_equal, &check_undefined_arg1);
// Get the untagged integer version of the edx heap number in ecx.
if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope use_sse2(masm, SSE2);
ConvertHeapNumberToInt32(masm, edx, conversion_failure);
} else {
DoubleToIStub stub(edx, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
true);
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
}
__ mov(edx, ecx);
__ TruncateHeapNumberToI(edx, edx);
// Here edx has the untagged integer, eax has a Smi or a heap number.
__ bind(&load_arg2);
......@@ -2308,14 +2287,7 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
__ j(not_equal, &check_undefined_arg2);
// Get the untagged integer version of the eax heap number in ecx.
if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope use_sse2(masm, SSE2);
ConvertHeapNumberToInt32(masm, eax, conversion_failure);
} else {
DoubleToIStub stub(eax, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
true);
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
}
__ TruncateHeapNumberToI(ecx, eax);
__ bind(&done);
__ mov(eax, edx);
......@@ -2542,16 +2514,16 @@ void MathPowStub::Generate(MacroAssembler* masm) {
}
if (exponent_type_ != INTEGER) {
Label fast_power;
// Detect integer exponents stored as double.
__ cvttsd2si(exponent, Operand(double_exponent));
Label fast_power, try_arithmetic_simplification;
__ DoubleToI(exponent, double_exponent, double_scratch,
TREAT_MINUS_ZERO_AS_ZERO, &try_arithmetic_simplification);
__ jmp(&int_exponent);
__ bind(&try_arithmetic_simplification);
// Skip to runtime if possibly NaN (indicated by the indefinite integer).
__ cvttsd2si(exponent, Operand(double_exponent));
__ cmp(exponent, Immediate(0x80000000u));
__ j(equal, &call_runtime);
__ cvtsi2sd(double_scratch, exponent);
// Already ruled out NaNs for exponent.
__ ucomisd(double_exponent, double_scratch);
__ j(equal, &int_exponent);
if (exponent_type_ == ON_STACK) {
// Detect square root case. Crankshaft detects constant +/-0.5 at
......
This diff is collapsed.
......@@ -163,8 +163,7 @@ class LCodeGen V8_FINAL BASE_EMBEDDED {
LOperand* value,
IntegerSignedness signedness);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr);
void DoDeferredTaggedToI(LTaggedToI* instr, Label* done);
void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
......@@ -549,6 +548,7 @@ class LDeferredCode : public ZoneObject {
void SetExit(Label* exit) { external_exit_ = exit; }
Label* entry() { return &entry_; }
Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; }
Label* done() { return &done_; }
int instruction_index() const { return instruction_index_; }
const LCodeGen::X87Stack& x87_stack() const { return x87_stack_; }
......@@ -561,6 +561,7 @@ class LDeferredCode : public ZoneObject {
Label entry_;
Label exit_;
Label* external_exit_;
Label done_;
int instruction_index_;
LCodeGen::X87Stack x87_stack_;
};
......
......@@ -1944,21 +1944,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
return DefineSameAsFirst(new(zone()) LSmiUntag(value, false));
} else {
bool truncating = instr->CanTruncateToInt32();
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
LOperand* value = UseRegister(val);
LOperand* xmm_temp =
(truncating && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
LOperand* value = UseFixed(val, ecx);
LTaggedToINoSSE2* res =
new(zone()) LTaggedToINoSSE2(value, TempRegister(),
TempRegister(), TempRegister());
return AssignEnvironment(DefineFixed(res, ecx));
}
LOperand* xmm_temp =
(CpuFeatures::IsSafeForSnapshot(SSE2) && !truncating)
? FixedTemp(xmm1) : NULL;
LTaggedToI* res = new(zone()) LTaggedToI(UseRegister(val), xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
}
}
} else if (from.IsDouble()) {
......@@ -1978,7 +1968,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
} else {
ASSERT(to.IsInteger32());
bool truncating = instr->CanTruncateToInt32();
bool needs_temp = truncating && !CpuFeatures::IsSupported(SSE3);
bool needs_temp = CpuFeatures::IsSafeForSnapshot(SSE2) && !truncating;
LOperand* value = needs_temp ?
UseTempRegister(instr->value()) : UseRegister(instr->value());
LOperand* temp = needs_temp ? TempRegister() : NULL;
......
......@@ -175,7 +175,6 @@ class LCodeGen;
V(StringCompareAndBranch) \
V(SubI) \
V(TaggedToI) \
V(TaggedToINoSSE2) \
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
......@@ -2171,31 +2170,6 @@ class LTaggedToI V8_FINAL : public LTemplateInstruction<1, 1, 1> {
};
// Truncating conversion from a tagged value to an int32.
class LTaggedToINoSSE2 V8_FINAL : public LTemplateInstruction<1, 1, 3> {
public:
LTaggedToINoSSE2(LOperand* value,
LOperand* temp1,
LOperand* temp2,
LOperand* temp3) {
inputs_[0] = value;
temps_[0] = temp1;
temps_[1] = temp2;
temps_[2] = temp3;
}
LOperand* value() { return inputs_[0]; }
LOperand* scratch() { return temps_[0]; }
LOperand* scratch2() { return temps_[1]; }
LOperand* scratch3() { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(TaggedToINoSSE2, "tagged-to-i-nosse2")
DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
class LSmiTag V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
explicit LSmiTag(LOperand* value) {
......
......@@ -215,6 +215,231 @@ void MacroAssembler::ClampUint8(Register reg) {
}
void MacroAssembler::SlowTruncateToI(Register result_reg,
Register input_reg,
int offset) {
DoubleToIStub stub(input_reg, result_reg, offset, true);
call(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
}
void MacroAssembler::TruncateDoubleToI(Register result_reg,
XMMRegister input_reg) {
Label done;
cvttsd2si(result_reg, Operand(input_reg));
cmp(result_reg, 0x80000000u);
j(not_equal, &done, Label::kNear);
sub(esp, Immediate(kDoubleSize));
movdbl(MemOperand(esp, 0), input_reg);
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
bind(&done);
}
void MacroAssembler::TruncateX87TOSToI(Register result_reg) {
sub(esp, Immediate(kDoubleSize));
fst_d(MemOperand(esp, 0));
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
}
void MacroAssembler::X87TOSToI(Register result_reg,
MinusZeroMode minus_zero_mode,
Label* conversion_failed,
Label::Distance dst) {
Label done;
sub(esp, Immediate(kPointerSize));
fist_s(MemOperand(esp, 0));
fld(0);
fild_s(MemOperand(esp, 0));
pop(result_reg);
FCmp();
j(not_equal, conversion_failed, dst);
j(parity_even, conversion_failed, dst);
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
sub(esp, Immediate(kPointerSize));
fst_s(MemOperand(esp, 0));
pop(result_reg);
test(result_reg, Operand(result_reg));
j(not_zero, conversion_failed, dst);
}
bind(&done);
}
void MacroAssembler::DoubleToI(Register result_reg,
XMMRegister input_reg,
XMMRegister scratch,
MinusZeroMode minus_zero_mode,
Label* conversion_failed,
Label::Distance dst) {
ASSERT(!input_reg.is(scratch));
Label done;
cvttsd2si(result_reg, Operand(input_reg));
cvtsi2sd(scratch, Operand(result_reg));
ucomisd(scratch, input_reg);
j(not_equal, conversion_failed, dst);
j(parity_even, conversion_failed, dst); // NaN.
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
movmskpd(result_reg, input_reg);
and_(result_reg, 1);
j(not_zero, conversion_failed, dst);
}
bind(&done);
}
void MacroAssembler::TruncateHeapNumberToI(Register result_reg,
Register input_reg) {
Label done, slow_case;
if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatureScope scope(this, SSE3);
Label convert;
// Use more powerful conversion when sse3 is available.
// Load x87 register with heap number.
fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
// Get exponent alone and check for too-big exponent.
mov(result_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
and_(result_reg, HeapNumber::kExponentMask);
const uint32_t kTooBigExponent =
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
cmp(Operand(result_reg), Immediate(kTooBigExponent));
j(greater_equal, &slow_case, Label::kNear);
// Reserve space for 64 bit answer.
sub(Operand(esp), Immediate(kDoubleSize));
// Do conversion, which cannot fail because we checked the exponent.
fisttp_d(Operand(esp, 0));
mov(result_reg, Operand(esp, 0)); // Low word of answer is the result.
add(Operand(esp), Immediate(kDoubleSize));
jmp(&done, Label::kNear);
// Slow case.
bind(&slow_case);
if (input_reg.is(result_reg)) {
// Input is clobbered. Restore number from fpu stack
sub(Operand(esp), Immediate(kDoubleSize));
fstp_d(Operand(esp, 0));
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
} else {
fstp(0);
SlowTruncateToI(result_reg, input_reg);
}
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(this, SSE2);
movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
cvttsd2si(result_reg, Operand(xmm0));
cmp(result_reg, 0x80000000u);
j(not_equal, &done, Label::kNear);
// Check if the input was 0x8000000 (kMinInt).
// If no, then we got an overflow and we deoptimize.
ExternalReference min_int = ExternalReference::address_of_min_int();
ucomisd(xmm0, Operand::StaticVariable(min_int));
j(not_equal, &slow_case, Label::kNear);
j(parity_even, &slow_case, Label::kNear); // NaN.
jmp(&done, Label::kNear);
// Slow case.
bind(&slow_case);
if (input_reg.is(result_reg)) {
// Input is clobbered. Restore number from double scratch.
sub(esp, Immediate(kDoubleSize));
movdbl(MemOperand(esp, 0), xmm0);
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
} else {
SlowTruncateToI(result_reg, input_reg);
}
} else {
SlowTruncateToI(result_reg, input_reg);
}
bind(&done);
}
void MacroAssembler::TaggedToI(Register result_reg,
Register input_reg,
XMMRegister temp,
MinusZeroMode minus_zero_mode,
Label* lost_precision) {
Label done;
ASSERT(!temp.is(xmm0));
cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
isolate()->factory()->heap_number_map());
j(not_equal, lost_precision, Label::kNear);
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
ASSERT(!temp.is(no_xmm_reg));
CpuFeatureScope scope(this, SSE2);
movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
cvttsd2si(result_reg, Operand(xmm0));
cvtsi2sd(temp, Operand(result_reg));
ucomisd(xmm0, temp);
RecordComment("Deferred TaggedToI: lost precision");
j(not_equal, lost_precision, Label::kNear);
RecordComment("Deferred TaggedToI: NaN");
j(parity_even, lost_precision, Label::kNear);
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
movmskpd(result_reg, xmm0);
and_(result_reg, 1);
RecordComment("Deferred TaggedToI: minus zero");
j(not_zero, lost_precision, Label::kNear);
}
} else {
// TODO(olivf) Converting a number on the fpu is actually quite slow. We
// should first try a fast conversion and then bailout to this slow case.
Label lost_precision_pop, zero_check;
Label* lost_precision_int = (minus_zero_mode == FAIL_ON_MINUS_ZERO)
? &lost_precision_pop : lost_precision;
sub(esp, Immediate(kPointerSize));
fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) fld(0);
fist_s(MemOperand(esp, 0));
fild_s(MemOperand(esp, 0));
FCmp();
pop(result_reg);
j(not_equal, lost_precision_int, Label::kNear);
j(parity_even, lost_precision_int, Label::kNear); // NaN.
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(zero, &zero_check, Label::kNear);
fstp(0);
jmp(&done, Label::kNear);
bind(&zero_check);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
sub(esp, Immediate(kPointerSize));
fstp_s(Operand(esp, 0));
pop(result_reg);
test(result_reg, Operand(result_reg));
j(zero, &done, Label::kNear);
jmp(lost_precision, Label::kNear);
bind(&lost_precision_pop);
fstp(0);
jmp(lost_precision, Label::kNear);
}
}
bind(&done);
}
static double kUint32Bias =
static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1;
......
......@@ -474,6 +474,21 @@ class MacroAssembler: public Assembler {
XMMRegister scratch_reg,
Register result_reg);
void SlowTruncateToI(Register result_reg, Register input_reg,
int offset = HeapNumber::kValueOffset - kHeapObjectTag);
void TruncateHeapNumberToI(Register result_reg, Register input_reg);
void TruncateDoubleToI(Register result_reg, XMMRegister input_reg);
void TruncateX87TOSToI(Register result_reg);
void DoubleToI(Register result_reg, XMMRegister input_reg,
XMMRegister scratch, MinusZeroMode minus_zero_mode,
Label* conversion_failed, Label::Distance dst = Label::kFar);
void X87TOSToI(Register result_reg, MinusZeroMode minus_zero_mode,
Label* conversion_failed, Label::Distance dst = Label::kFar);
void TaggedToI(Register result_reg, Register input_reg, XMMRegister temp,
MinusZeroMode minus_zero_mode, Label* lost_precision);
// Smi tagging support.
void SmiTag(Register reg) {
......
......@@ -565,6 +565,11 @@ enum ClearExceptionFlag {
};
enum MinusZeroMode {
TREAT_MINUS_ZERO_AS_ZERO,
FAIL_ON_MINUS_ZERO
};
} } // namespace v8::internal
#endif // V8_V8GLOBALS_H_
......@@ -679,3 +679,37 @@ boo(built_in_array, 0, 2.5);
assertEquals(2.5, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
// Check all int range edge cases
function checkRange() {
var e32 = Math.pow(2,32); var e31 = Math.pow(2,31);
var e16 = Math.pow(2,16); var e15 = Math.pow(2,15);
var e8 = Math.pow(2,8); var e7 = Math.pow(2,7);
var a7 = new Uint32Array(2); var a71 = new Int32Array(2);
var a72 = new Uint16Array(2); var a73 = new Int16Array(2);
var a74 = new Uint8Array(2); var a75 = new Int8Array(2);
for (i = 1; i <= Math.pow(2,33); i *= 2) {
var j = i-1;
a7[0] = i; a71[0] = i; a72[0] = i; a73[0] = i; a74[0] = i; a75[0] = i;
a7[1] = j; a71[1] = j; a72[1] = j; a73[1] = j; a74[1] = j; a75[1] = j;
if (i < e32) { assertEquals(a7[0], i); } else { assertEquals(a7[0], 0); }
if (j < e32) { assertEquals(a7[1], j); } else { assertEquals(a7[1],e32-1); }
if (i < e31) { assertEquals(a71[0], i); } else {
assertEquals(a71[0], (i < e32) ? -e31 : 0 ); }
if (j < e31) { assertEquals(a71[1], j); } else { assertEquals(a71[1], -1); }
if (i < e16) { assertEquals(a72[0], i); } else { assertEquals(a72[0], 0); }
if (j < e16) { assertEquals(a72[1], j); } else { assertEquals(a72[1], e16-1); }
if (i < e15) { assertEquals(a73[0], i); } else {
assertEquals(a73[0], (i < e16) ? -e15 : 0 ); }
if (j < e15) { assertEquals(a73[1], j); } else { assertEquals(a73[1], -1); }
if (i < e8) { assertEquals(a74[0], i); } else { assertEquals(a74[0], 0); }
if (j < e8) { assertEquals(a74[1], j); } else { assertEquals(a74[1], e8-1); }
if (i < e7) { assertEquals(a75[0], i); } else {
assertEquals(a75[0], (i < e8) ? -e7 : 0); }
if (j < e7) { assertEquals(a75[1], j); } else { assertEquals(a75[1], -1); }
}
}
checkRange();
......@@ -678,3 +678,37 @@ boo(built_in_array, 0, 2.5);
assertEquals(2.5, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
// Check all int range edge cases
function checkRange() {
var e32 = Math.pow(2,32); var e31 = Math.pow(2,31);
var e16 = Math.pow(2,16); var e15 = Math.pow(2,15);
var e8 = Math.pow(2,8); var e7 = Math.pow(2,7);
var a7 = new Uint32Array(2); var a71 = new Int32Array(2);
var a72 = new Uint16Array(2); var a73 = new Int16Array(2);
var a74 = new Uint8Array(2); var a75 = new Int8Array(2);
for (i = 1; i <= Math.pow(2,33); i *= 2) {
var j = i-1;
a7[0] = i; a71[0] = i; a72[0] = i; a73[0] = i; a74[0] = i; a75[0] = i;
a7[1] = j; a71[1] = j; a72[1] = j; a73[1] = j; a74[1] = j; a75[1] = j;
if (i < e32) { assertEquals(a7[0], i); } else { assertEquals(a7[0], 0); }
if (j < e32) { assertEquals(a7[1], j); } else { assertEquals(a7[1],e32-1); }
if (i < e31) { assertEquals(a71[0], i); } else {
assertEquals(a71[0], (i < e32) ? -e31 : 0 ); }
if (j < e31) { assertEquals(a71[1], j); } else { assertEquals(a71[1], -1); }
if (i < e16) { assertEquals(a72[0], i); } else { assertEquals(a72[0], 0); }
if (j < e16) { assertEquals(a72[1], j); } else { assertEquals(a72[1], e16-1); }
if (i < e15) { assertEquals(a73[0], i); } else {
assertEquals(a73[0], (i < e16) ? -e15 : 0 ); }
if (j < e15) { assertEquals(a73[1], j); } else { assertEquals(a73[1], -1); }
if (i < e8) { assertEquals(a74[0], i); } else { assertEquals(a74[0], 0); }
if (j < e8) { assertEquals(a74[1], j); } else { assertEquals(a74[1], e8-1); }
if (i < e7) { assertEquals(a75[0], i); } else {
assertEquals(a75[0], (i < e8) ? -e7 : 0); }
if (j < e7) { assertEquals(a75[1], j); } else { assertEquals(a75[1], -1); }
}
}
checkRange();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment