Inline Math.sqrt().
authorricow@chromium.org <ricow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 8 Mar 2010 13:23:54 +0000 (13:23 +0000)
committerricow@chromium.org <ricow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 8 Mar 2010 13:23:54 +0000 (13:23 +0000)
Also changed name of GeneratePow and the %_ call name to follow convention based on MathSin and MathCos. Moved GeneratePow down to the other methods.

Review URL: http://codereview.chromium.org/661179

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4054 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/codegen.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/math.js
test/mjsunit/math-sqrt.js [new file with mode: 0644]

index 9257ed44669cef3c35ee951270f5a9434ac8d5c9..6841c21612b9b4066389dda653a36b7760c90a92 100644 (file)
@@ -381,9 +381,10 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateStringCompare, "_StringCompare"},
   {&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
   {&CodeGenerator::GenerateNumberToString, "_NumberToString"},
-  {&CodeGenerator::GeneratePow, "_Pow"},
+  {&CodeGenerator::GenerateMathPow, "_Math_pow"},
   {&CodeGenerator::GenerateMathSin, "_Math_sin"},
   {&CodeGenerator::GenerateMathCos, "_Math_cos"},
+  {&CodeGenerator::GenerateMathSqrt, "_Math_sqrt"},
 };
 
 
index dfa48bd07d09750aee8a92a62f7bc161bcea8d33..74de706f8ce3ef6a20825817b01203b240e1a538 100644 (file)
@@ -5445,182 +5445,6 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
 }
 
 
-// Generates the Math.pow method - only handles special cases and branches to
-// the runtime system if not. Uses eax to store result and as temporary reg.
-void CodeGenerator::GeneratePow(ZoneList<Expression*>* args) {
-  ASSERT(args->length() == 2);
-  if (CpuFeatures::IsSupported(SSE2)) {
-    CpuFeatures::Scope use_sse2(SSE2);
-    Load(args->at(0));
-    Load(args->at(1));
-    Label go_runtime;
-    Label return_preg;
-    Result p = allocator()->Allocate(eax);
-    Result y = frame_->Pop();
-    Result x= frame_->Pop();
-    if (p.is_valid() && p.reg().is(eax)) {
-      x.ToRegister();
-      y.ToRegister();
-      frame_->Spill(x.reg());
-      frame_->Spill(y.reg());
-      ASSERT(x.is_valid());
-      ASSERT(y.is_valid());
-
-      // Save 1 in xmm3 - we need this several times later on
-      __ mov(p.reg(), Immediate(1));
-      __ cvtsi2sd(xmm3, Operand(p.reg()));
-
-      Label y_nonsmi;
-      Label x_is_double;
-      // If y is a heap number go to that specific case.
-      __ test(y.reg(), Immediate(kSmiTagMask));
-      __ j(not_zero, &y_nonsmi);
-      __ test(x.reg(), Immediate(kSmiTagMask));
-      __ j(not_zero, &x_is_double);
-
-      // Bot numbers are smis.
-      Label powi;
-      __ SmiUntag(x.reg());
-      __ cvtsi2sd(xmm0, Operand(x.reg()));
-      __ jmp(&powi);
-      // Y is smi and x is a double.
-      __ bind(&x_is_double);
-      __ cmp(FieldOperand(x.reg(), HeapObject::kMapOffset),
-             Factory::heap_number_map());
-      __ j(not_equal, &go_runtime);
-      __ movdbl(xmm0, FieldOperand(x.reg(), HeapNumber::kValueOffset));
-
-      __ bind(&powi);
-      __ SmiUntag(y.reg());
-
-      // Save y in x as we need to check if y is negative later.
-      __ mov(x.reg(), y.reg());
-
-
-      // Get absolute value of y.
-      Label no_neg;
-      __ cmp(y.reg(), 0);
-      __ j(greater_equal, &no_neg);
-      __ neg(y.reg());
-      __ bind(&no_neg);
-
-      // Optimized version of pow if y is an integer.
-      // Load xmm1 with 1.
-      __ movsd(xmm1, xmm3);
-      Label while_true;
-      Label no_multiply;
-      Label powi_done;
-      Label allocate_and_return;
-      __ bind(&while_true);
-      __ shr(y.reg(), 1);
-      __ j(not_carry, &no_multiply);
-      __ mulsd(xmm1, xmm0);
-      __ bind(&no_multiply);
-      __ test(y.reg(), Operand(y.reg()));
-      __ mulsd(xmm0, xmm0);
-      __ j(not_zero, &while_true);
-
-      __ bind(&powi_done);
-      // x has the original value of y - if y is negative return 1/result.
-      __ test(x.reg(), Operand(x.reg()));
-      __ j(positive, &allocate_and_return);
-      // Special case if xmm1 has reached infinity
-      __ mov(p.reg(), Immediate(0x7FB00000));
-      __ movd(xmm0, Operand(p.reg()));
-      __ cvtss2sd(xmm0, xmm0);
-      __ ucomisd(xmm0, xmm1);
-      __ j(equal, &go_runtime);
-      __ divsd(xmm3, xmm1);
-      __ movsd(xmm1, xmm3);
-      __ jmp(&allocate_and_return);
-
-      // y (or both) is a double - no matter what we should now work
-      // on doubles.
-      __ bind(&y_nonsmi);
-      __ cmp(FieldOperand(y.reg(), HeapObject::kMapOffset),
-             Factory::heap_number_map());
-      __ j(not_equal, &go_runtime);
-      // Y must be a double.
-      __ movdbl(xmm1, FieldOperand(y.reg(), HeapNumber::kValueOffset));
-      // Test if y is nan.
-      __ ucomisd(xmm1, xmm1);
-      __ j(parity_even, &go_runtime);
-
-      Label x_not_smi;
-      Label handle_special_cases;
-      __ test(x.reg(), Immediate(kSmiTagMask));
-      __ j(not_zero, &x_not_smi);
-      __ SmiUntag(x.reg());
-      __ cvtsi2sd(xmm0, Operand(x.reg()));
-      __ jmp(&handle_special_cases);
-      __ bind(&x_not_smi);
-      __ cmp(FieldOperand(x.reg(), HeapObject::kMapOffset),
-             Factory::heap_number_map());
-      __ j(not_equal, &go_runtime);
-      __ mov(p.reg(), FieldOperand(x.reg(), HeapNumber::kExponentOffset));
-      __ and_(p.reg(), HeapNumber::kExponentMask);
-      __ cmp(Operand(p.reg()), Immediate(HeapNumber::kExponentMask));
-      // x is NaN or +/-Infinity
-      __ j(greater_equal, &go_runtime);
-      __ movdbl(xmm0, FieldOperand(x.reg(), HeapNumber::kValueOffset));
-
-      // x is in xmm0 and y is in xmm1.
-      __ bind(&handle_special_cases);
-      Label not_minus_half;
-      // Test for -0.5.
-      // Load xmm2 with -0.5.
-      __ mov(p.reg(), Immediate(0xBF000000));
-      __ movd(xmm2, Operand(p.reg()));
-      __ cvtss2sd(xmm2, xmm2);
-      // xmm2 now has -0.5.
-      __ ucomisd(xmm2, xmm1);
-      __ j(not_equal, &not_minus_half);
-
-      // Calculates reciprocal of square root.
-      // Note that 1/sqrt(x) = sqrt(1/x))
-      __ divsd(xmm3, xmm0);
-      __ movsd(xmm1, xmm3);
-      __ sqrtsd(xmm1, xmm1);
-      __ jmp(&allocate_and_return);
-
-      // Test for 0.5.
-      __ bind(&not_minus_half);
-      // Load xmm2 with 0.5.
-      // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 = xmm3
-      __ addsd(xmm2, xmm3);
-      // xmm2 now has 0.5.
-      __ ucomisd(xmm2, xmm1);
-      __ j(not_equal, &go_runtime);
-      // Calculates square root.
-      __ movsd(xmm1, xmm0);
-      __ sqrtsd(xmm1, xmm1);
-
-      __ bind(&allocate_and_return);
-      __ AllocateHeapNumber(p.reg(), y.reg(), x.reg(), &go_runtime);
-      __ movdbl(FieldOperand(p.reg(), HeapNumber::kValueOffset), xmm1);
-      __ jmp(&return_preg);
-    }
-    __ bind(&go_runtime);
-    x.Unuse();
-    y.Unuse();
-    p.Unuse();
-    Load(args->at(0));
-    Load(args->at(1));
-    frame_->CallRuntime(Runtime::kMath_pow_cfunction, 2);
-
-    // Since we store the result in p.reg() which is eax - return this value.
-    // If we called runtime the result is also in eax.
-    __ bind(&return_preg);
-    frame_->Push(eax);
-  } else {  // Simply call runtime.
-      Load(args->at(0));
-      Load(args->at(1));
-      Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
-      frame_->Push(&res);
-  }
-}
-
-
 // This generates code that performs a charCodeAt() call or returns
 // undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
 // It can handle flat, 8 and 16 bit characters and cons strings where the
@@ -6209,6 +6033,194 @@ void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
 }
 
 
+// Generates the Math.pow method - only handles special cases and branches to
+// the runtime system if not.Please note - this function assumes that
+// the callsite has executed ToNumber on both arguments and that the
+// arguments are not the same identifier.
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+  ASSERT(args->length() == 2);
+  Load(args->at(0));
+  Load(args->at(1));
+  if (!CpuFeatures::IsSupported(SSE2)) {
+    Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
+    frame_->Push(&res);
+  } else {
+    CpuFeatures::Scope use_sse2(SSE2);
+    Label allocate_return;
+    // Load the two operands while leaving the values on the frame.
+    frame()->Dup();
+    Result exponent = frame()->Pop();
+    exponent.ToRegister();
+    frame()->Spill(exponent.reg());
+    frame()->PushElementAt(1);
+    Result base = frame()->Pop();
+    base.ToRegister();
+    frame()->Spill(base.reg());
+
+    Result answer = allocator()->Allocate();
+    ASSERT(answer.is_valid());
+    // We can safely assume that the base and exponent is not in the same
+    // register since we only call this from one callsite (math.js).
+    ASSERT(!exponent.reg().is(base.reg()));
+    JumpTarget call_runtime;
+
+    // Save 1 in xmm3 - we need this several times later on.
+    __ mov(answer.reg(), Immediate(1));
+    __ cvtsi2sd(xmm3, Operand(answer.reg()));
+
+    Label exponent_nonsmi;
+    Label base_nonsmi;
+    // If the exponent is a heap number go to that specific case.
+    __ test(exponent.reg(), Immediate(kSmiTagMask));
+    __ j(not_zero, &exponent_nonsmi);
+    __ test(base.reg(), Immediate(kSmiTagMask));
+    __ j(not_zero, &base_nonsmi);
+
+    // Optimized version when y is an integer.
+    Label powi;
+    __ SmiUntag(base.reg());
+    __ cvtsi2sd(xmm0, Operand(base.reg()));
+    __ jmp(&powi);
+    // exponent is smi and base is a heapnumber.
+    __ bind(&base_nonsmi);
+    __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+           Factory::heap_number_map());
+    call_runtime.Branch(not_equal);
+
+    __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+    // Optimized version of pow if y is an integer.
+    __ bind(&powi);
+    __ SmiUntag(exponent.reg());
+
+    // Save exponent in base as we need to check if exponent is negative later.
+    // We know that base and exponent are in different registers.
+    __ mov(base.reg(), exponent.reg());
+
+    // Get absolute value of exponent.
+    Label no_neg;
+    __ cmp(exponent.reg(), 0);
+    __ j(greater_equal, &no_neg);
+    __ neg(exponent.reg());
+    __ bind(&no_neg);
+
+    // Load xmm1 with 1.
+    __ movsd(xmm1, xmm3);
+    Label while_true;
+    Label no_multiply;
+
+    //  Label allocate_and_return;
+    __ bind(&while_true);
+    __ shr(exponent.reg(), 1);
+    __ j(not_carry, &no_multiply);
+    __ mulsd(xmm1, xmm0);
+    __ bind(&no_multiply);
+    __ test(exponent.reg(), Operand(exponent.reg()));
+    __ mulsd(xmm0, xmm0);
+    __ j(not_zero, &while_true);
+
+    // x has the original value of y - if y is negative return 1/result.
+    __ test(base.reg(), Operand(base.reg()));
+    __ j(positive, &allocate_return);
+    // Special case if xmm1 has reached infinity.
+    __ mov(answer.reg(), Immediate(0x7FB00000));
+    __ movd(xmm0, Operand(answer.reg()));
+    __ cvtss2sd(xmm0, xmm0);
+    __ ucomisd(xmm0, xmm1);
+    call_runtime.Branch(equal);
+    __ divsd(xmm3, xmm1);
+    __ movsd(xmm1, xmm3);
+    __ jmp(&allocate_return);
+
+    // exponent (or both) is a heapnumber - no matter what we should now work
+    // on doubles.
+    __ bind(&exponent_nonsmi);
+    __ cmp(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
+           Factory::heap_number_map());
+    call_runtime.Branch(not_equal);
+    __ movdbl(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
+    // Test if exponent is nan.
+    __ ucomisd(xmm1, xmm1);
+    call_runtime.Branch(parity_even);
+
+    Label base_not_smi;
+    Label handle_special_cases;
+    __ test(base.reg(), Immediate(kSmiTagMask));
+    __ j(not_zero, &base_not_smi);
+    __ SmiUntag(base.reg());
+    __ cvtsi2sd(xmm0, Operand(base.reg()));
+    __ jmp(&handle_special_cases);
+    __ bind(&base_not_smi);
+    __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+           Factory::heap_number_map());
+    call_runtime.Branch(not_equal);
+    __ mov(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
+    __ and_(answer.reg(), HeapNumber::kExponentMask);
+    __ cmp(Operand(answer.reg()), Immediate(HeapNumber::kExponentMask));
+    // base is NaN or +/-Infinity
+    call_runtime.Branch(greater_equal);
+    __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+    // base is in xmm0 and exponent is in xmm1.
+    __ bind(&handle_special_cases);
+    Label not_minus_half;
+    // Test for -0.5.
+    // Load xmm2 with -0.5.
+    __ mov(answer.reg(), Immediate(0xBF000000));
+    __ movd(xmm2, Operand(answer.reg()));
+    __ cvtss2sd(xmm2, xmm2);
+    // xmm2 now has -0.5.
+    __ ucomisd(xmm2, xmm1);
+    __ j(not_equal, &not_minus_half);
+
+    // Calculates reciprocal of square root.
+    // Note that 1/sqrt(x) = sqrt(1/x))
+    __ divsd(xmm3, xmm0);
+    __ movsd(xmm1, xmm3);
+    __ sqrtsd(xmm1, xmm1);
+    __ jmp(&allocate_return);
+
+    // Test for 0.5.
+    __ bind(&not_minus_half);
+    // Load xmm2 with 0.5.
+    // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
+    __ addsd(xmm2, xmm3);
+    // xmm2 now has 0.5.
+    __ comisd(xmm2, xmm1);
+    call_runtime.Branch(not_equal);
+    // Calculates square root.
+    __ movsd(xmm1, xmm0);
+    __ sqrtsd(xmm1, xmm1);
+
+    JumpTarget done;
+    Label failure, success;
+    __ bind(&allocate_return);
+    // Make a copy of the frame to enable us to handle allocation
+    // failure after the JumpTarget jump.
+    VirtualFrame* clone = new VirtualFrame(frame());
+    __ AllocateHeapNumber(answer.reg(), exponent.reg(),
+                          base.reg(), &failure);
+    __ movdbl(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
+    // Remove the two original values from the frame - we only need those
+    // in the case where we branch to runtime.
+    frame()->Drop(2);
+    exponent.Unuse();
+    base.Unuse();
+    done.Jump(&answer);
+    // Use the copy of the original frame as our current frame.
+    RegisterFile empty_regs;
+    SetFrame(clone, &empty_regs);
+    // If we experience an allocation failure we branch to runtime.
+    __ bind(&failure);
+    call_runtime.Bind();
+    answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
+
+    done.Bind(&answer);
+    frame()->Push(&answer);
+  }
+}
+
+
 void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
   ASSERT_EQ(args->length(), 1);
   Load(args->at(0));
@@ -6227,6 +6239,63 @@ void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
 }
 
 
+// Generates the Math.sqrt method. Please note - this function assumes that
+// the callsite has executed ToNumber on the argument.
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+  ASSERT_EQ(args->length(), 1);
+  Load(args->at(0));
+
+  if (!CpuFeatures::IsSupported(SSE2)) {
+    Result result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+    frame()->Push(&result);
+  } else {
+    CpuFeatures::Scope use_sse2(SSE2);
+    // Leave original value on the frame if we need to call runtime.
+    frame()->Dup();
+    Result result = frame()->Pop();
+    result.ToRegister();
+    frame()->Spill(result.reg());
+    Label runtime;
+    Label non_smi;
+    Label load_done;
+    JumpTarget end;
+
+    __ test(result.reg(), Immediate(kSmiTagMask));
+    __ j(not_zero, &non_smi);
+    __ SmiUntag(result.reg());
+    __ cvtsi2sd(xmm0, Operand(result.reg()));
+    __ jmp(&load_done);
+    __ bind(&non_smi);
+    __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
+           Factory::heap_number_map());
+    __ j(not_equal, &runtime);
+    __ movdbl(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
+
+    __ bind(&load_done);
+    __ sqrtsd(xmm0, xmm0);
+    // A copy of the virtual frame to allow us to go to runtime after the
+    // JumpTarget jump.
+    Result scratch = allocator()->Allocate();
+    VirtualFrame* clone = new VirtualFrame(frame());
+    __ AllocateHeapNumber(result.reg(), scratch.reg(), no_reg, &runtime);
+
+    __ movdbl(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
+    frame()->Drop(1);
+    scratch.Unuse();
+    end.Jump(&result);
+    // We only branch to runtime if we have an allocation error.
+    // Use the copy of the original frame as our current frame.
+    RegisterFile empty_regs;
+    SetFrame(clone, &empty_regs);
+    __ bind(&runtime);
+    result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+
+    end.Bind(&result);
+    frame()->Push(&result);
+  }
+}
+
+
 void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
   if (CheckForInlineRuntimeCall(node)) {
     return;
index 36c975b64e9b25f8d4d8980be8839586262babe9..79cad7268532219c8c97874a985f8bc9e73b0fc7 100644 (file)
@@ -594,12 +594,15 @@ class CodeGenerator: public AstVisitor {
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
   // Fast support for Math.pow().
-  void GeneratePow(ZoneList<Expression*>* args);
+  void GenerateMathPow(ZoneList<Expression*>* args);
 
   // Fast call to transcendental functions.
   void GenerateMathSin(ZoneList<Expression*>* args);
   void GenerateMathCos(ZoneList<Expression*>* args);
 
+  // Fast case for sqrt
+  void GenerateMathSqrt(ZoneList<Expression*>* args);
+
   // Simple condition analysis.
   enum ConditionAnalysis {
     ALWAYS_TRUE,
index 380d687f9eec61568dd11c9829f0bca0dcfae071..034f32d7fc47cff1fb2045365a166c2b687f16f4 100644 (file)
@@ -159,7 +159,7 @@ function MathMin(arg1, arg2) {  // length == 2
 function MathPow(x, y) {
   if (!IS_NUMBER(x)) x = ToNumber(x);
   if (!IS_NUMBER(y)) y = ToNumber(y);
-  return %_Pow(x, y);
+  return %_Math_pow(x, y);
 }
 
 // ECMA 262 - 15.8.2.14
@@ -182,7 +182,7 @@ function MathSin(x) {
 // ECMA 262 - 15.8.2.17
 function MathSqrt(x) {
   if (!IS_NUMBER(x)) x = ToNumber(x);
-  return %Math_sqrt(x);
+  return %_Math_sqrt(x);
 }
 
 // ECMA 262 - 15.8.2.18
diff --git a/test/mjsunit/math-sqrt.js b/test/mjsunit/math-sqrt.js
new file mode 100644 (file)
index 0000000..ae29b74
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2010 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.
+
+// Tests the special cases specified by ES 15.8.2.17
+
+// Simple sanity check
+assertEquals(2, Math.sqrt(4));
+assertEquals(0.1, Math.sqrt(0.01));
+
+// Spec tests
+assertEquals(NaN, Math.sqrt(NaN));
+assertEquals(NaN, Math.sqrt(-1));
+assertEquals(+0, Math.sqrt(+0));
+assertEquals(-0, Math.sqrt(-0));
+assertEquals(Infinity, Math.sqrt(Infinity));
+// -Infinity is smaller than 0 so it should return NaN
+assertEquals(NaN, Math.sqrt(-Infinity));
+
+
+