From f6e95dc928981fb15c26170e0fea317699a3248f Mon Sep 17 00:00:00 2001 From: "rmcilroy@chromium.org" Date: Mon, 17 Feb 2014 15:09:46 +0000 Subject: [PATCH] A64 support for DoubleToIStub (truncating). Added support for truncating DoubleToIStub and reorganize the macro-assembler dToI operations to do the fast-path inline and the slow path by calling the stub. This a port essentially a port of https://codereview.chromium.org/23129003/. R=jacob.bramley@arm.com, ulan@chromium.org Review URL: https://codereview.chromium.org/160423002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19414 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/a64/assembler-a64.cc | 13 +++ src/a64/assembler-a64.h | 7 ++ src/a64/code-stubs-a64.cc | 79 ++++++++++++++++ src/a64/lithium-a64.cc | 4 +- src/a64/lithium-a64.h | 8 +- src/a64/lithium-codegen-a64.cc | 13 +-- src/a64/macro-assembler-a64.cc | 133 +++++++++++--------------- src/a64/macro-assembler-a64.h | 54 +++++------ src/arm/code-stubs-arm.cc | 1 + test/cctest/cctest.gyp | 2 + test/cctest/test-assembler-a64.cc | 101 -------------------- test/cctest/test-code-stubs-a64.cc | 185 +++++++++++++++++++++++++++++++++++++ test/cctest/test-code-stubs.cc | 33 ++++++- 13 files changed, 408 insertions(+), 225 deletions(-) create mode 100644 test/cctest/test-code-stubs-a64.cc diff --git a/src/a64/assembler-a64.cc b/src/a64/assembler-a64.cc index ddf651b..fff902e 100644 --- a/src/a64/assembler-a64.cc +++ b/src/a64/assembler-a64.cc @@ -180,6 +180,19 @@ void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) { } +Register GetAllocatableRegisterThatIsNotOneOf(Register reg1, Register reg2, + Register reg3, Register reg4) { + CPURegList regs(reg1, reg2, reg3, reg4); + for (int i = 0; i < Register::NumAllocatableRegisters(); i++) { + Register candidate = Register::FromAllocationIndex(i); + if (regs.IncludesAliasOf(candidate)) continue; + return candidate; + } + UNREACHABLE(); + return NoReg; +} + + bool AreAliased(const CPURegister& reg1, const CPURegister& reg2, const CPURegister& reg3, const CPURegister& reg4, const CPURegister& reg5, const CPURegister& reg6, diff --git a/src/a64/assembler-a64.h b/src/a64/assembler-a64.h index 18c5b70..a7818b3 100644 --- a/src/a64/assembler-a64.h +++ b/src/a64/assembler-a64.h @@ -383,6 +383,13 @@ ALIAS_REGISTER(FPRegister, fp_scratch, d31); #undef ALIAS_REGISTER + +Register GetAllocatableRegisterThatIsNotOneOf(Register reg1, + Register reg2 = NoReg, + Register reg3 = NoReg, + Register reg4 = NoReg); + + // AreAliased returns true if any of the named registers overlap. Arguments set // to NoReg are ignored. The system stack pointer may be specified. bool AreAliased(const CPURegister& reg1, diff --git a/src/a64/code-stubs-a64.cc b/src/a64/code-stubs-a64.cc index bf6ab32..a314b87 100644 --- a/src/a64/code-stubs-a64.cc +++ b/src/a64/code-stubs-a64.cc @@ -539,6 +539,85 @@ void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) { } +void DoubleToIStub::Generate(MacroAssembler* masm) { + Label done; + Register input = source(); + Register result = destination(); + ASSERT(is_truncating()); + + ASSERT(result.Is64Bits()); + ASSERT(jssp.Is(masm->StackPointer())); + + int double_offset = offset(); + + DoubleRegister double_scratch = d0; // only used if !skip_fastpath() + Register scratch1 = GetAllocatableRegisterThatIsNotOneOf(input, result); + Register scratch2 = + GetAllocatableRegisterThatIsNotOneOf(input, result, scratch1); + + __ Push(scratch1, scratch2); + // Account for saved regs if input is jssp. + if (input.is(jssp)) double_offset += 2 * kPointerSize; + + if (!skip_fastpath()) { + __ Push(double_scratch); + if (input.is(jssp)) double_offset += 1 * kDoubleSize; + __ Ldr(double_scratch, MemOperand(input, double_offset)); + // Try to convert with a FPU convert instruction. This handles all + // non-saturating cases. + __ TryInlineTruncateDoubleToI(result, double_scratch, &done); + __ Fmov(result, double_scratch); + } else { + __ Ldr(result, MemOperand(input, double_offset)); + } + + // If we reach here we need to manually convert the input to an int32. + + // Extract the exponent. + Register exponent = scratch1; + __ Ubfx(exponent, result, HeapNumber::kMantissaBits, + HeapNumber::kExponentBits); + + // It the exponent is >= 84 (kMantissaBits + 32), the result is always 0 since + // the mantissa gets shifted completely out of the int32_t result. + __ Cmp(exponent, HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 32); + __ CzeroX(result, ge); + __ B(ge, &done); + + // The Fcvtzs sequence handles all cases except where the conversion causes + // signed overflow in the int64_t target. Since we've already handled + // exponents >= 84, we can guarantee that 63 <= exponent < 84. + + if (masm->emit_debug_code()) { + __ Cmp(exponent, HeapNumber::kExponentBias + 63); + // Exponents less than this should have been handled by the Fcvt case. + __ Check(ge, kUnexpectedValue); + } + + // Isolate the mantissa bits, and set the implicit '1'. + Register mantissa = scratch2; + __ Ubfx(mantissa, result, 0, HeapNumber::kMantissaBits); + __ Orr(mantissa, mantissa, 1UL << HeapNumber::kMantissaBits); + + // Negate the mantissa if necessary. + __ Tst(result, kXSignMask); + __ Cneg(mantissa, mantissa, ne); + + // Shift the mantissa bits in the correct place. We know that we have to shift + // it left here, because exponent >= 63 >= kMantissaBits. + __ Sub(exponent, exponent, + HeapNumber::kExponentBias + HeapNumber::kMantissaBits); + __ Lsl(result, mantissa, exponent); + + __ Bind(&done); + if (!skip_fastpath()) { + __ Pop(double_scratch); + } + __ Pop(scratch2, scratch1); + __ Ret(); +} + + // See call site for description. static void EmitIdenticalObjectComparison(MacroAssembler* masm, Register left, diff --git a/src/a64/lithium-a64.cc b/src/a64/lithium-a64.cc index e32bb2d..560826b 100644 --- a/src/a64/lithium-a64.cc +++ b/src/a64/lithium-a64.cc @@ -1120,10 +1120,8 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LOperand* value = UseRegister(instr->value()); if (instr->CanTruncateToInt32()) { - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); LTruncateDoubleToIntOrSmi* result = - new(zone()) LTruncateDoubleToIntOrSmi(value, temp1, temp2); + new(zone()) LTruncateDoubleToIntOrSmi(value); return DefineAsRegister(result); } else { LDoubleToIntOrSmi* result = new(zone()) LDoubleToIntOrSmi(value); diff --git a/src/a64/lithium-a64.h b/src/a64/lithium-a64.h index acfdb8f..393b3e5 100644 --- a/src/a64/lithium-a64.h +++ b/src/a64/lithium-a64.h @@ -2680,17 +2680,13 @@ class LTrapAllocationMemento V8_FINAL : public LTemplateInstruction<0, 1, 2> { class LTruncateDoubleToIntOrSmi V8_FINAL - : public LTemplateInstruction<1, 1, 2> { + : public LTemplateInstruction<1, 1, 0> { public: - LTruncateDoubleToIntOrSmi(LOperand* value, LOperand* temp1, LOperand* temp2) { + explicit LTruncateDoubleToIntOrSmi(LOperand* value) { inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; } LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } DECLARE_CONCRETE_INSTRUCTION(TruncateDoubleToIntOrSmi, "truncate-double-to-int-or-smi") diff --git a/src/a64/lithium-codegen-a64.cc b/src/a64/lithium-codegen-a64.cc index 40ef5c6..1f8deea 100644 --- a/src/a64/lithium-codegen-a64.cc +++ b/src/a64/lithium-codegen-a64.cc @@ -5323,8 +5323,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, __ JumpIfNotRoot(scratch1, Heap::kHeapNumberMapRootIndex, &check_bools); // A heap number: load value and convert to int32 using truncating function. - __ Ldr(dbl_scratch1, FieldMemOperand(input, HeapNumber::kValueOffset)); - __ ECMA262ToInt32(output, dbl_scratch1, scratch1, scratch2); + __ TruncateHeapNumberToI(output, input); __ B(&done); __ Bind(&check_bools); @@ -5511,12 +5510,10 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) { void LCodeGen::DoTruncateDoubleToIntOrSmi(LTruncateDoubleToIntOrSmi* instr) { DoubleRegister input = ToDoubleRegister(instr->value()); Register result = ToRegister(instr->result()); - __ ECMA262ToInt32(result, input, - ToRegister(instr->temp1()), - ToRegister(instr->temp2()), - instr->tag_result() - ? MacroAssembler::SMI - : MacroAssembler::INT32_IN_W); + __ TruncateDoubleToI(result, input); + if (instr->tag_result()) { + __ SmiTag(result, result); + } } diff --git a/src/a64/macro-assembler-a64.cc b/src/a64/macro-assembler-a64.cc index 1e8da54..4efd8f5 100644 --- a/src/a64/macro-assembler-a64.cc +++ b/src/a64/macro-assembler-a64.cc @@ -2586,26 +2586,20 @@ void MacroAssembler::InvokeFunction(Handle function, } -void MacroAssembler::ECMA262ToInt32(Register result, - DoubleRegister input, - Register scratch1, - Register scratch2, - ECMA262ToInt32Result format) { - ASSERT(!AreAliased(result, scratch1, scratch2)); - ASSERT(result.Is64Bits() && scratch1.Is64Bits() && scratch2.Is64Bits()); +void MacroAssembler::TryInlineTruncateDoubleToI(Register result, + DoubleRegister double_input, + Label* done) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiValueSize == 32); - Label done, tag, manual_conversion; - - // 1. Try to convert with a FPU convert instruction. It's trivial to compute - // the modulo operation on an integer register so we convert to a 64-bit - // integer, then find the 32-bit result from that. + // Try to convert with a FPU convert instruction. It's trivial to compute + // the modulo operation on an integer register so we convert to a 64-bit + // integer, then find the 32-bit result from that. // // Fcvtzs will saturate to INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) // when the double is out of range. NaNs and infinities will be converted to 0 // (as ECMA-262 requires). - Fcvtzs(result, input); + Fcvtzs(result, double_input); // The values INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) are not // representable using a double, so if the result is one of those then we know @@ -2615,83 +2609,64 @@ void MacroAssembler::ECMA262ToInt32(Register result, // 1 will cause signed overflow. Cmp(result, 1); Ccmp(result, -1, VFlag, vc); - B(vc, &tag); - // 2. Manually convert the input to an int32. - Fmov(result, input); + B(vc, done); +} - // Extract the exponent. - Register exponent = scratch1; - Ubfx(exponent, result, HeapNumber::kMantissaBits, HeapNumber::kExponentBits); - // It the exponent is >= 84 (kMantissaBits + 32), the result is always 0 since - // the mantissa gets shifted completely out of the int32_t result. - Cmp(exponent, HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 32); - CzeroX(result, ge); - B(ge, &done); +void MacroAssembler::TruncateDoubleToI(Register result, + DoubleRegister double_input) { + Label done; + ASSERT(jssp.Is(StackPointer())); - // The Fcvtzs sequence handles all cases except where the conversion causes - // signed overflow in the int64_t target. Since we've already handled - // exponents >= 84, we can guarantee that 63 <= exponent < 84. + TryInlineTruncateDoubleToI(result, double_input, &done); - if (emit_debug_code()) { - Cmp(exponent, HeapNumber::kExponentBias + 63); - // Exponents less than this should have been handled by the Fcvt case. - Check(ge, kUnexpectedValue); - } - - // Isolate the mantissa bits, and set the implicit '1'. - Register mantissa = scratch2; - Ubfx(mantissa, result, 0, HeapNumber::kMantissaBits); - Orr(mantissa, mantissa, 1UL << HeapNumber::kMantissaBits); - - // Negate the mantissa if necessary. - Tst(result, kXSignMask); - Cneg(mantissa, mantissa, ne); - - // Shift the mantissa bits in the correct place. We know that we have to shift - // it left here, because exponent >= 63 >= kMantissaBits. - Sub(exponent, exponent, - HeapNumber::kExponentBias + HeapNumber::kMantissaBits); - Lsl(result, mantissa, exponent); - - Bind(&tag); - switch (format) { - case INT32_IN_W: - // There is nothing to do; the upper 32 bits are undefined. - if (emit_debug_code()) { - __ Mov(scratch1, 0x55555555); - __ Bfi(result, scratch1, 32, 32); - } - break; - case INT32_IN_X: - Sxtw(result, result); - break; - case SMI: - SmiTag(result); - break; - } + // If we fell through then inline version didn't succeed - call stub instead. + Push(lr); + Push(double_input); // Put input on stack. + + DoubleToIStub stub(jssp, + result, + 0, + true, // is_truncating + true); // skip_fastpath + CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber + + Drop(1, kDoubleSize); // Drop the double input on the stack. + Pop(lr); Bind(&done); + + // TODO(rmcilroy): Remove this Sxtw once the following bug is fixed: + // https://code.google.com/p/v8/issues/detail?id=3149 + Sxtw(result, result.W()); } -void MacroAssembler::HeapNumberECMA262ToInt32(Register result, - Register heap_number, - Register scratch1, - Register scratch2, - DoubleRegister double_scratch, - ECMA262ToInt32Result format) { - if (emit_debug_code()) { - // Verify we indeed have a HeapNumber. - Label ok; - JumpIfHeapNumber(heap_number, &ok); - Abort(kExpectedHeapNumber); - Bind(&ok); - } +void MacroAssembler::TruncateHeapNumberToI(Register result, + Register object) { + Label done; + ASSERT(!result.is(object)); + ASSERT(jssp.Is(StackPointer())); + + Ldr(fp_scratch, FieldMemOperand(object, HeapNumber::kValueOffset)); + TryInlineTruncateDoubleToI(result, fp_scratch, &done); + + // If we fell through then inline version didn't succeed - call stub instead. + Push(lr); + DoubleToIStub stub(object, + result, + HeapNumber::kValueOffset - kHeapObjectTag, + true, // is_truncating + true); // skip_fastpath + CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber + Pop(lr); + + Bind(&done); - Ldr(double_scratch, FieldMemOperand(heap_number, HeapNumber::kValueOffset)); - ECMA262ToInt32(result, double_scratch, scratch1, scratch2, format); + // TODO(rmcilroy): Remove this Sxtw once the following bug is fixed: + // https://code.google.com/p/v8/issues/detail?id=3149 + Sxtw(result, result.W()); } diff --git a/src/a64/macro-assembler-a64.h b/src/a64/macro-assembler-a64.h index b711bd1..25bc725 100644 --- a/src/a64/macro-assembler-a64.h +++ b/src/a64/macro-assembler-a64.h @@ -1125,33 +1125,35 @@ class MacroAssembler : public Assembler { // ---- Floating point helpers ---- - enum ECMA262ToInt32Result { - // Provide an untagged int32_t which can be read using result.W(). That is, - // the upper 32 bits of result are undefined. - INT32_IN_W, - // Provide an untagged int32_t which can be read using the 64-bit result - // register. The int32_t result is sign-extended. - INT32_IN_X, - - // Tag the int32_t result as a smi. - SMI - }; - - // Applies ECMA-262 ToInt32 (see section 9.5) to a double value. - void ECMA262ToInt32(Register result, - DoubleRegister input, - Register scratch1, - Register scratch2, - ECMA262ToInt32Result format = INT32_IN_X); - - // As ECMA262ToInt32, but operate on a HeapNumber. - void HeapNumberECMA262ToInt32(Register result, - Register heap_number, - Register scratch1, - Register scratch2, - DoubleRegister double_scratch, - ECMA262ToInt32Result format = INT32_IN_X); + // Performs a truncating conversion of a floating point number as used by + // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it + // succeeds, otherwise falls through if result is saturated. On return + // 'result' either holds answer, or is clobbered on fall through. + // + // Only public for the test code in test-code-stubs-a64.cc. + void TryInlineTruncateDoubleToI(Register result, + DoubleRegister input, + Label* done); + + // Performs a truncating conversion of a floating point number as used by + // the JS bitwise operations. See ECMA-262 9.5: ToInt32. + // Exits with 'result' holding the answer. + void TruncateDoubleToI(Register result, DoubleRegister double_input); + + // Performs a truncating conversion of a heap number as used by + // the JS bitwise operations. See ECMA-262 9.5: ToInt32. 'result' and 'input' + // must be different registers. Exits with 'result' holding the answer. + void TruncateHeapNumberToI(Register result, Register object); + + // Converts the smi or heap number in object to an int32 using the rules + // for ToInt32 as described in ECMAScript 9.5.: the value is truncated + // and brought into the range -2^31 .. +2^31 - 1. 'result' and 'input' must be + // different registers. + void TruncateNumberToI(Register object, + Register result, + Register heap_number_map, + Label* not_int32); // ---- Code generation helpers ---- diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index c9b479e..b3a3acf 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -602,6 +602,7 @@ void DoubleToIStub::Generate(MacroAssembler* masm) { Label out_of_range, only_low, negate, done; Register input_reg = source(); Register result_reg = destination(); + ASSERT(is_truncating()); int double_offset = offset(); // Account for saved regs if input is sp. diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index a0dbd4c..187bdf3 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -156,6 +156,8 @@ 'sources': [ 'test-utils-a64.cc', 'test-assembler-a64.cc', + 'test-code-stubs.cc', + 'test-code-stubs-a64.cc', 'test-disasm-a64.cc', 'test-fuzz-a64.cc', 'test-javascript-a64.cc', diff --git a/test/cctest/test-assembler-a64.cc b/test/cctest/test-assembler-a64.cc index dda47cd..2ec34b3 100644 --- a/test/cctest/test-assembler-a64.cc +++ b/test/cctest/test-assembler-a64.cc @@ -9509,107 +9509,6 @@ TEST(call_no_relocation) { } -static void ECMA262ToInt32Helper(int32_t expected, double input) { - SETUP(); - START(); - - __ Fmov(d0, input); - - __ ECMA262ToInt32(x0, d0, x10, x11, MacroAssembler::INT32_IN_W); - __ ECMA262ToInt32(x1, d0, x10, x11, MacroAssembler::INT32_IN_X); - __ ECMA262ToInt32(x2, d0, x10, x11, MacroAssembler::SMI); - - // The upper bits of INT32_IN_W are undefined, so make sure we don't try to - // test them. - __ Mov(w0, w0); - - END(); - - RUN(); - - int64_t expected64 = expected; - - ASSERT_EQUAL_32(expected, w0); - ASSERT_EQUAL_64(expected64, x1); - ASSERT_EQUAL_64(expected64 << kSmiShift | kSmiTag, x2); - - TEARDOWN(); -} - - -TEST(ecma_262_to_int32) { - INIT_V8(); - // ==== exponent < 64 ==== - - ECMA262ToInt32Helper(0, 0.0); - ECMA262ToInt32Helper(0, -0.0); - ECMA262ToInt32Helper(1, 1.0); - ECMA262ToInt32Helper(-1, -1.0); - - // The largest representable value that is less than 1. - ECMA262ToInt32Helper(0, 0x001fffffffffffff * pow(2.0, -53)); - ECMA262ToInt32Helper(0, 0x001fffffffffffff * -pow(2.0, -53)); - ECMA262ToInt32Helper(0, std::numeric_limits::denorm_min()); - ECMA262ToInt32Helper(0, -std::numeric_limits::denorm_min()); - - // The largest conversion which doesn't require the integer modulo-2^32 step. - ECMA262ToInt32Helper(0x7fffffff, 0x7fffffff); - ECMA262ToInt32Helper(-0x80000000, -0x80000000); - - // The largest simple conversion, requiring module-2^32, but where the fcvt - // does not saturate when converting to int64_t. - ECMA262ToInt32Helper(0xfffffc00, 0x7ffffffffffffc00); - ECMA262ToInt32Helper(-0xfffffc00, 0x7ffffffffffffc00 * -1.0); - - // ==== 64 <= exponent < 84 ==== - - // The smallest conversion where the fcvt saturates. - ECMA262ToInt32Helper(0, 0x8000000000000000); - ECMA262ToInt32Helper(0, 0x8000000000000000 * -1.0); - - // The smallest conversion where the fcvt saturates, and where all the - // mantissa bits are '1' (to check the shift logic). - ECMA262ToInt32Helper(0xfffff800, 0xfffffffffffff800); - ECMA262ToInt32Helper(-0xfffff800, 0xfffffffffffff800 * -1.0); - - // The largest conversion which doesn't produce a zero result. - ECMA262ToInt32Helper(0x80000000, 0x001fffffffffffff * pow(2.0, 31)); - ECMA262ToInt32Helper(-0x80000000, 0x001fffffffffffff * -pow(2.0, 31)); - - // Some large conversions to check the shifting function. - ECMA262ToInt32Helper(0x6789abcd, 0x001123456789abcd); - ECMA262ToInt32Helper(0x12345678, 0x001123456789abcd * pow(2.0, -20)); - ECMA262ToInt32Helper(0x891a2b3c, 0x001123456789abcd * pow(2.0, -21)); - ECMA262ToInt32Helper(0x11234567, 0x001123456789abcd * pow(2.0, -24)); - ECMA262ToInt32Helper(-0x6789abcd, 0x001123456789abcd * -1.0); - ECMA262ToInt32Helper(-0x12345678, 0x001123456789abcd * -pow(2.0, -20)); - ECMA262ToInt32Helper(-0x891a2b3c, 0x001123456789abcd * -pow(2.0, -21)); - ECMA262ToInt32Helper(-0x11234567, 0x001123456789abcd * -pow(2.0, -24)); - - // ==== 84 <= exponent ==== - - // The smallest conversion which produces a zero result by shifting the - // mantissa out of the int32_t range. - ECMA262ToInt32Helper(0, pow(2.0, 32)); - ECMA262ToInt32Helper(0, -pow(2.0, 32)); - - // Some very large conversions. - ECMA262ToInt32Helper(0, 0x001fffffffffffff * pow(2.0, 32)); - ECMA262ToInt32Helper(0, 0x001fffffffffffff * -pow(2.0, 32)); - ECMA262ToInt32Helper(0, DBL_MAX); - ECMA262ToInt32Helper(0, -DBL_MAX); - - // ==== Special values. ==== - - ECMA262ToInt32Helper(0, std::numeric_limits::infinity()); - ECMA262ToInt32Helper(0, -std::numeric_limits::infinity()); - ECMA262ToInt32Helper(0, std::numeric_limits::quiet_NaN()); - ECMA262ToInt32Helper(0, -std::numeric_limits::quiet_NaN()); - ECMA262ToInt32Helper(0, std::numeric_limits::signaling_NaN()); - ECMA262ToInt32Helper(0, -std::numeric_limits::signaling_NaN()); -} - - static void AbsHelperX(int64_t value) { int64_t expected; diff --git a/test/cctest/test-code-stubs-a64.cc b/test/cctest/test-code-stubs-a64.cc new file mode 100644 index 0000000..5d5d370 --- /dev/null +++ b/test/cctest/test-code-stubs-a64.cc @@ -0,0 +1,185 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Rrdistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Rrdistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Rrdistributions 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. + +#include + +#include "v8.h" + +#include "cctest.h" +#include "code-stubs.h" +#include "test-code-stubs.h" +#include "factory.h" +#include "macro-assembler.h" +#include "platform.h" +#include "simulator.h" + +using namespace v8::internal; + +#define __ masm. + +ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate, + Register source_reg, + Register destination_reg, + bool inline_fastpath) { + // Allocate an executable page of memory. + size_t actual_size; + byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, + &actual_size, + true)); + CHECK(buffer); + HandleScope handles(isolate); + MacroAssembler masm(isolate, buffer, static_cast(actual_size)); + DoubleToIStub stub(source_reg, destination_reg, 0, true, inline_fastpath); + + byte* start = stub.GetCode(isolate)->instruction_start(); + Label done; + + __ SetStackPointer(csp); + __ PushCalleeSavedRegisters(); + __ Mov(jssp, csp); + __ SetStackPointer(jssp); + + // Push the double argument. + __ Push(d0); + if (!source_reg.is(jssp)) { + __ Mov(source_reg, jssp); + } + + // Save registers make sure they don't get clobbered. + int source_reg_offset = kDoubleSize; + int reg_num = 0; + for (;reg_num < Register::NumAllocatableRegisters(); ++reg_num) { + Register reg = Register::from_code(reg_num); + if (!reg.is(destination_reg)) { + __ Push(reg); + source_reg_offset += kPointerSize; + } + } + + // Re-push the double argument. + __ Push(d0); + + // Call through to the actual stub + if (inline_fastpath) { + __ Ldr(d0, MemOperand(source_reg)); + __ TryInlineTruncateDoubleToI(destination_reg, d0, &done); + if (destination_reg.is(source_reg)) { + // Restore clobbered source_reg. + __ add(source_reg, jssp, Operand(source_reg_offset)); + } + } + __ Call(start, RelocInfo::EXTERNAL_REFERENCE); + __ bind(&done); + + __ Drop(1, kDoubleSize); + + // // Make sure no registers have been unexpectedly clobbered + for (--reg_num; reg_num >= 0; --reg_num) { + Register reg = Register::from_code(reg_num); + if (!reg.is(destination_reg)) { + __ Pop(ip0); + __ cmp(reg, ip0); + __ Assert(eq, kRegisterWasClobbered); + } + } + + __ Drop(1, kDoubleSize); + + if (!destination_reg.is(x0)) + __ Mov(x0, destination_reg); + + // Restore callee save registers. + __ Mov(csp, jssp); + __ SetStackPointer(csp); + __ PopCalleeSavedRegisters(); + + __ Ret(); + + CodeDesc desc; + masm.GetCode(&desc); + CPU::FlushICache(buffer, actual_size); + return (reinterpret_cast( + reinterpret_cast(buffer))); +} + +#undef __ + + +static Isolate* GetIsolateFrom(LocalContext* context) { + return reinterpret_cast((*context)->GetIsolate()); +} + + +int32_t RunGeneratedCodeCallWrapper(ConvertDToIFunc func, + double from) { +#ifdef USE_SIMULATOR + return Simulator::current(Isolate::Current())->CallInt64( + FUNCTION_ADDR(func), Simulator::CallArgument(from), + Simulator::CallArgument::End()); +#else + return (*func)(from); +#endif +} + + +TEST(ConvertDToI) { + CcTest::InitializeVM(); + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + HandleScope scope(isolate); + +#if DEBUG + // Verify that the tests actually work with the C version. In the release + // code, the compiler optimizes it away because it's all constant, but does it + // wrong, triggering an assert on gcc. + RunAllTruncationTests(&ConvertDToICVersion); +#endif + + Register source_registers[] = {jssp, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, + x10, x11, x12, x13, x14, x15, x18, x19, x20, + x21, x22, x23, x24}; + Register dest_registers[] = {x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, + x12, x13, x14, x15, x18, x19, x20, x21, x22, x23, + x24}; + + for (size_t s = 0; s < sizeof(source_registers) / sizeof(Register); s++) { + for (size_t d = 0; d < sizeof(dest_registers) / sizeof(Register); d++) { + RunAllTruncationTests( + RunGeneratedCodeCallWrapper, + MakeConvertDToIFuncTrampoline(isolate, + source_registers[s], + dest_registers[d], + false)); + RunAllTruncationTests( + RunGeneratedCodeCallWrapper, + MakeConvertDToIFuncTrampoline(isolate, + source_registers[s], + dest_registers[d], + true)); + } + } +} diff --git a/test/cctest/test-code-stubs.cc b/test/cctest/test-code-stubs.cc index db00e9a..999febf 100644 --- a/test/cctest/test-code-stubs.cc +++ b/test/cctest/test-code-stubs.cc @@ -49,6 +49,9 @@ int STDCALL ConvertDToICVersion(double d) { int32_t exponent = (((exponent_bits & shifted_mask) >> (Double::kPhysicalSignificandSize - 32)) - HeapNumber::kExponentBias); + if (exponent < 0) { + return 0; + } uint32_t unsigned_exponent = static_cast(exponent); int result = 0; uint32_t max_exponent = @@ -113,10 +116,27 @@ void RunAllTruncationTests(ConvertDToICallWrapper callWrapper, RunOneTruncationTest(Infinity, 0); RunOneTruncationTest(-NaN, 0); RunOneTruncationTest(-Infinity, 0); - - RunOneTruncationTest(4.5036e+15, 0x1635E000); + RunOneTruncationTest(4.94065645841e-324, 0); + RunOneTruncationTest(-4.94065645841e-324, 0); + + RunOneTruncationTest(0.9999999999999999, 0); + RunOneTruncationTest(-0.9999999999999999, 0); + RunOneTruncationTest(4294967296.0, 0); + RunOneTruncationTest(-4294967296.0, 0); + RunOneTruncationTest(9223372036854775000.0, 4294966272.0); + RunOneTruncationTest(-9223372036854775000.0, -4294966272.0); + RunOneTruncationTest(4.5036e+15, 372629504); RunOneTruncationTest(-4.5036e+15, -372629504); + RunOneTruncationTest(287524199.5377777, 0x11234567); + RunOneTruncationTest(-287524199.5377777, -0x11234567); + RunOneTruncationTest(2300193596.302222, 2300193596.0); + RunOneTruncationTest(-2300193596.302222, -2300193596.0); + RunOneTruncationTest(4600387192.604444, 305419896); + RunOneTruncationTest(-4600387192.604444, -305419896); + RunOneTruncationTest(4823855600872397.0, 1737075661); + RunOneTruncationTest(-4823855600872397.0, -1737075661); + RunOneTruncationTest(4503603922337791.0, -1); RunOneTruncationTest(-4503603922337791.0, 1); RunOneTruncationTest(4503601774854143.0, 2147483647); @@ -134,10 +154,19 @@ void RunAllTruncationTests(ConvertDToICallWrapper callWrapper, RunOneTruncationTest(4.8357078901445341e+24, -1073741824); RunOneTruncationTest(-4.8357078901445341e+24, 1073741824); + RunOneTruncationTest(2147483647.0, 2147483647.0); + RunOneTruncationTest(-2147483648.0, -2147483648.0); RunOneTruncationTest(9.6714111686030497e+24, -2147483648.0); RunOneTruncationTest(-9.6714111686030497e+24, -2147483648.0); RunOneTruncationTest(9.6714157802890681e+24, -2147483648.0); RunOneTruncationTest(-9.6714157802890681e+24, -2147483648.0); + RunOneTruncationTest(1.9342813113834065e+25, 2147483648.0); + RunOneTruncationTest(-1.9342813113834065e+25, 2147483648.0); + + RunOneTruncationTest(3.868562622766813e+25, 0); + RunOneTruncationTest(-3.868562622766813e+25, 0); + RunOneTruncationTest(1.7976931348623157e+308, 0); + RunOneTruncationTest(-1.7976931348623157e+308, 0); } #undef NaN -- 2.7.4