[builtins] Introduce proper Float64Log1p operator.

Import base::ieee754::log1p() from fdlibm and introduce a Float64Log1p
TurboFan operator based on that, similar to what we do for Float64Log.
Rewrite Math.log1p() as TurboFan builtin and use that operator to also
inline Math.log1p() into optimized TurboFan functions.

Also unify the handling of the special IEEE 754 functions somewhat in
the TurboFan backends. At some point we can hopefully express this
completely in the InstructionSelector (once we have an idea what to do
with the ST(0) return issue on IA-32/X87).

Drive-by-fix: Add some more test coverage for the log function.

R=yangguo@chromium.org
BUG=v8:5086,v8:5092

Review-Url: https://codereview.chromium.org/2060743002
Cr-Commit-Position: refs/heads/master@{#36914}
This commit is contained in:
bmeurer 2016-06-12 22:46:38 -07:00 committed by Commit bot
parent b01622c312
commit 7ceed92ac0
49 changed files with 524 additions and 259 deletions

View File

@ -1653,6 +1653,10 @@ ExternalReference ExternalReference::ieee754_log_function(Isolate* isolate) {
Redirect(isolate, FUNCTION_ADDR(base::ieee754::log), BUILTIN_FP_CALL));
}
ExternalReference ExternalReference::ieee754_log1p_function(Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(base::ieee754::log1p), BUILTIN_FP_CALL));
}
ExternalReference ExternalReference::math_exp_constants(int constant_index) {
DCHECK(math_exp_data_initialized);

View File

@ -1037,6 +1037,7 @@ class ExternalReference BASE_EMBEDDED {
// IEEE 754 functions.
static ExternalReference ieee754_log_function(Isolate* isolate);
static ExternalReference ieee754_log1p_function(Isolate* isolate);
static ExternalReference math_exp_constants(int constant_index);
static ExternalReference math_exp_log_table();

View File

@ -163,6 +163,10 @@ typedef union {
(d) = sl_u.value; \
} while (0)
/* Support macro. */
#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval))
} // namespace
/* log(x)
@ -294,6 +298,170 @@ double log(double x) {
}
}
/* double log1p(double x)
*
* Method :
* 1. Argument Reduction: find k and f such that
* 1+x = 2^k * (1+f),
* where sqrt(2)/2 < 1+f < sqrt(2) .
*
* Note. If k=0, then f=x is exact. However, if k!=0, then f
* may not be representable exactly. In that case, a correction
* term is need. Let u=1+x rounded. Let c = (1+x)-u, then
* log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
* and add back the correction term c/u.
* (Note: when x > 2**53, one can simply return log(x))
*
* 2. Approximation of log1p(f).
* Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
* = 2s + 2/3 s**3 + 2/5 s**5 + .....,
* = 2s + s*R
* We use a special Reme algorithm on [0,0.1716] to generate
* a polynomial of degree 14 to approximate R The maximum error
* of this polynomial approximation is bounded by 2**-58.45. In
* other words,
* 2 4 6 8 10 12 14
* R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s
* (the values of Lp1 to Lp7 are listed in the program)
* and
* | 2 14 | -58.45
* | Lp1*s +...+Lp7*s - R(z) | <= 2
* | |
* Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
* In order to guarantee error in log below 1ulp, we compute log
* by
* log1p(f) = f - (hfsq - s*(hfsq+R)).
*
* 3. Finally, log1p(x) = k*ln2 + log1p(f).
* = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
* Here ln2 is split into two floating point number:
* ln2_hi + ln2_lo,
* where n*ln2_hi is always exact for |n| < 2000.
*
* Special cases:
* log1p(x) is NaN with signal if x < -1 (including -INF) ;
* log1p(+INF) is +INF; log1p(-1) is -INF with signal;
* log1p(NaN) is that NaN with no signal.
*
* Accuracy:
* according to an error analysis, the error is always less than
* 1 ulp (unit in the last place).
*
* Constants:
* The hexadecimal values are the intended ones for the following
* constants. The decimal values may be used, provided that the
* compiler will convert from decimal to binary accurately enough
* to produce the hexadecimal values shown.
*
* Note: Assuming log() return accurate answer, the following
* algorithm can be used to compute log1p(x) to within a few ULP:
*
* u = 1+x;
* if(u==1.0) return x ; else
* return log(u)*(x/(u-1.0));
*
* See HP-15C Advanced Functions Handbook, p.193.
*/
double log1p(double x) {
static const double /* -- */
ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */
Lp1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
Lp2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
Lp3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
Lp4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
Lp5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
Lp6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
Lp7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
static const double zero = 0.0;
static volatile double vzero = 0.0;
double hfsq, f, c, s, z, R, u;
int32_t k, hx, hu, ax;
GET_HIGH_WORD(hx, x);
ax = hx & 0x7fffffff;
k = 1;
if (hx < 0x3FDA827A) { /* 1+x < sqrt(2)+ */
if (ax >= 0x3ff00000) { /* x <= -1.0 */
if (x == -1.0)
return -two54 / vzero; /* log1p(-1)=+inf */
else
return (x - x) / (x - x); /* log1p(x<-1)=NaN */
}
if (ax < 0x3e200000) { /* |x| < 2**-29 */
if (two54 + x > zero /* raise inexact */
&& ax < 0x3c900000) /* |x| < 2**-54 */
return x;
else
return x - x * x * 0.5;
}
if (hx > 0 || hx <= static_cast<int32_t>(0xbfd2bec4)) {
k = 0;
f = x;
hu = 1;
} /* sqrt(2)/2- <= 1+x < sqrt(2)+ */
}
if (hx >= 0x7ff00000) return x + x;
if (k != 0) {
if (hx < 0x43400000) {
STRICT_ASSIGN(double, u, 1.0 + x);
GET_HIGH_WORD(hu, u);
k = (hu >> 20) - 1023;
c = (k > 0) ? 1.0 - (u - x) : x - (u - 1.0); /* correction term */
c /= u;
} else {
u = x;
GET_HIGH_WORD(hu, u);
k = (hu >> 20) - 1023;
c = 0;
}
hu &= 0x000fffff;
/*
* The approximation to sqrt(2) used in thresholds is not
* critical. However, the ones used above must give less
* strict bounds than the one here so that the k==0 case is
* never reached from here, since here we have committed to
* using the correction term but don't use it if k==0.
*/
if (hu < 0x6a09e) { /* u ~< sqrt(2) */
SET_HIGH_WORD(u, hu | 0x3ff00000); /* normalize u */
} else {
k += 1;
SET_HIGH_WORD(u, hu | 0x3fe00000); /* normalize u/2 */
hu = (0x00100000 - hu) >> 2;
}
f = u - 1.0;
}
hfsq = 0.5 * f * f;
if (hu == 0) { /* |f| < 2**-20 */
if (f == zero) {
if (k == 0) {
return zero;
} else {
c += k * ln2_lo;
return k * ln2_hi + c;
}
}
R = hfsq * (1.0 - 0.66666666666666666 * f);
if (k == 0)
return f - R;
else
return k * ln2_hi - ((R - (k * ln2_lo + c)) - f);
}
s = f / (2.0 + f);
z = s * s;
R = z * (Lp1 +
z * (Lp2 + z * (Lp3 + z * (Lp4 + z * (Lp5 + z * (Lp6 + z * Lp7))))));
if (k == 0)
return f - (hfsq - s * (hfsq + R));
else
return k * ln2_hi - ((hfsq - (s * (hfsq + R) + (k * ln2_lo + c))) - f);
}
} // namespace ieee754
} // namespace base
} // namespace v8

View File

@ -12,6 +12,10 @@ namespace ieee754 {
// Returns the natural logarithm of |x|.
double log(double x);
// Returns a value equivalent to |log(1+x)|, but computed in a way that is
// accurate even if the value of |x| is near zero.
double log1p(double x);
} // namespace ieee754
} // namespace base
} // namespace v8

View File

@ -1682,6 +1682,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> math_log =
SimpleInstallFunction(math, "log", Builtins::kMathLog, 1, true);
native_context()->set_math_log(*math_log);
SimpleInstallFunction(math, "log1p", Builtins::kMathLog1p, 1, true);
SimpleInstallFunction(math, "max", Builtins::kMathMax, 2, false);
SimpleInstallFunction(math, "min", Builtins::kMathMin, 2, false);
SimpleInstallFunction(math, "round", Builtins::kMathRound, 1, true);

View File

@ -2474,6 +2474,18 @@ void Builtins::Generate_MathLog(CodeStubAssembler* assembler) {
assembler->Return(result);
}
// ES6 section 20.2.2.21 Math.log1p ( x )
void Builtins::Generate_MathLog1p(CodeStubAssembler* assembler) {
using compiler::Node;
Node* x = assembler->Parameter(1);
Node* context = assembler->Parameter(4);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
Node* value = assembler->Float64Log1p(x_value);
Node* result = assembler->ChangeFloat64ToTagged(value);
assembler->Return(result);
}
// ES6 section 20.2.2.28 Math.round ( x )
void Builtins::Generate_MathRound(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round);

View File

@ -325,6 +325,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(MathClz32, 2) \
V(MathFloor, 2) \
V(MathLog, 2) \
V(MathLog1p, 2) \
V(MathRound, 2) \
V(MathSqrt, 2) \
V(MathTrunc, 2) \
@ -622,6 +623,8 @@ class Builtins {
static void Generate_MathFloor(CodeStubAssembler* assembler);
// ES6 section 20.2.2.20 Math.log ( x )
static void Generate_MathLog(CodeStubAssembler* assembler);
// ES6 section 20.2.2.21 Math.log ( x )
static void Generate_MathLog1p(CodeStubAssembler* assembler);
enum class MathMaxMinKind { kMax, kMin };
static void Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind);
// ES6 section 20.2.2.24 Math.max ( value1, value2 , ...values )

View File

@ -407,6 +407,20 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
__ dmb(ISH); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
/* TODO(bmeurer): We should really get rid of this special instruction, */ \
/* and generate a CallAddress instruction instead. */ \
FrameScope scope(masm(), StackFrame::MANUAL); \
__ PrepareCallCFunction(0, 1, kScratchReg); \
__ MovToFloatParameter(i.InputFloat64Register(0)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
0, 1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputFloat64Register()); \
DCHECK_EQ(LeaveCC, i.OutputSBit()); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ LeaveFrame(StackFrame::MANUAL);
}
@ -674,19 +688,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ add(i.OutputRegister(0), base, Operand(offset.offset()));
break;
}
case kIeee754Float64Log: {
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
FrameScope scope(masm(), StackFrame::MANUAL);
__ PrepareCallCFunction(0, 1, kScratchReg);
__ MovToFloatParameter(i.InputFloat64Register(0));
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 0,
1);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputFloat64Register());
DCHECK_EQ(LeaveCC, i.OutputSBit());
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
}
case kArmAdd:
__ add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1),
i.OutputSBit());

View File

@ -1393,13 +1393,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitRR(this, kArmVabsF64, node);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
ArmOperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineAsFixed(node, d0),
g.UseFixed(node->InputAt(0), d0))
->MarkAsCall();
}
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRR(this, kArmVsqrtF32, node);
}
@ -1462,6 +1455,13 @@ void InstructionSelector::VisitFloat64Neg(Node* node) {
VisitRR(this, kArmVnegF64, node);
}
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
ArmOperandGenerator g(this);
Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -512,6 +512,13 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
__ Dmb(InnerShareable, BarrierAll); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
FrameScope scope(masm(), StackFrame::MANUAL); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
0, 1); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
const CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
if (descriptor->IsCFunctionCall() || descriptor->UseNativeStack()) {
@ -792,14 +799,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Add(i.OutputRegister(0), base, Operand(offset.offset()));
break;
}
case kIeee754Float64Log: {
FrameScope scope(masm(), StackFrame::MANUAL);
DCHECK(d0.is(i.InputDoubleRegister(0)));
DCHECK(d0.is(i.OutputDoubleRegister()));
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 0,
1);
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
}
case kArm64Float32RoundDown:
__ Frintm(i.OutputFloat32Register(), i.InputFloat32Register(0));
break;

View File

@ -1687,13 +1687,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitRR(this, kArm64Float64Abs, node);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
Arm64OperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineAsFixed(node, d0),
g.UseFixed(node->InputAt(0), d0))
->MarkAsCall();
}
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRR(this, kArm64Float32Sqrt, node);
}
@ -1756,6 +1749,13 @@ void InstructionSelector::VisitFloat64Neg(Node* node) {
VisitRR(this, kArm64Float64Neg, node);
}
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
Arm64OperandGenerator g(this);
Emit(opcode, g.DefineAsFixed(node, d0), g.UseFixed(node->InputAt(0), d0))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -107,6 +107,7 @@ class Schedule;
#define CODE_ASSEMBLER_UNARY_OP_LIST(V) \
V(Float64Log) \
V(Float64Log1p) \
V(Float64Neg) \
V(Float64Sqrt) \
V(Float64ExtractLowWord32) \

View File

@ -363,6 +363,21 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
/* Pass one double as argument on the stack.*/ \
__ PrepareCallCFunction(2, eax); \
__ movsd(Operand(esp, 0 * kDoubleSize), i.InputDoubleRegister(0)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
2); \
/* Return value is in st(0) on ia32. */ \
/* Store it into the result register. */ \
__ sub(esp, Immediate(kDoubleSize)); \
__ fstp_d(Operand(esp, 0)); \
__ movsd(i.OutputDoubleRegister(), Operand(esp, 0)); \
__ add(esp, Immediate(kDoubleSize)); \
} while (false)
void CodeGenerator::AssembleDeconstructFrame() {
__ mov(esp, ebp);
__ pop(ebp);
@ -617,19 +632,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ lea(i.OutputRegister(), Operand(base, offset.offset()));
break;
}
case kIeee754Float64Log: {
// Pass one double as argument on the stack.
__ PrepareCallCFunction(2, eax);
__ movsd(Operand(esp, 0 * kDoubleSize), i.InputDoubleRegister(0));
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 2);
// Return value is in st(0) on ia32.
// Store it into the result register.
__ sub(esp, Immediate(kDoubleSize));
__ fstp_d(Operand(esp, 0));
__ movsd(i.OutputDoubleRegister(), Operand(esp, 0));
__ add(esp, Immediate(kDoubleSize));
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
}
case kIA32Add:
if (HasImmediateInput(instr, 1)) {
__ add(i.InputOperand(0), i.InputImmediate(1));

View File

@ -1014,13 +1014,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat64Abs, kSSEFloat64Abs);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
IA32OperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineSameAsFirst(node),
g.UseRegister(node->InputAt(0)))
->MarkAsCall();
}
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRO(this, node, kSSEFloat32Sqrt);
}
@ -1079,6 +1072,13 @@ void InstructionSelector::VisitFloat32Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
IA32OperandGenerator g(this);
Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -89,7 +89,8 @@ enum class RecordWriteMode { kValueIsMap, kValueIsPointer, kValueIsAny };
V(AtomicStoreWord8) \
V(AtomicStoreWord16) \
V(AtomicStoreWord32) \
V(Ieee754Float64Log)
V(Ieee754Float64Log) \
V(Ieee754Float64Log1p)
#define ARCH_OPCODE_LIST(V) \
COMMON_ARCH_OPCODE_LIST(V) \

View File

@ -225,6 +225,7 @@ int InstructionScheduler::GetInstructionFlags(const Instruction* instr) const {
case kArchDebugBreak:
case kArchComment:
case kIeee754Float64Log:
case kIeee754Float64Log1p:
return kNoOpcodeFlags;
case kArchStackPointer:

View File

@ -1129,6 +1129,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsFloat64(node), VisitFloat64Abs(node);
case IrOpcode::kFloat64Log:
return MarkAsFloat64(node), VisitFloat64Log(node);
case IrOpcode::kFloat64Log1p:
return MarkAsFloat64(node), VisitFloat64Log1p(node);
case IrOpcode::kFloat64Sqrt:
return MarkAsFloat64(node), VisitFloat64Sqrt(node);
case IrOpcode::kFloat64Equal:
@ -1234,6 +1236,14 @@ void InstructionSelector::VisitLoadParentFramePointer(Node* node) {
Emit(kArchParentFramePointer, g.DefineAsRegister(node));
}
void InstructionSelector::VisitFloat64Log(Node* node) {
VisitFloat64Ieee754Unop(node, kIeee754Float64Log);
}
void InstructionSelector::VisitFloat64Log1p(Node* node) {
VisitFloat64Ieee754Unop(node, kIeee754Float64Log1p);
}
void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw,
InstructionOperand& index_operand) {
OperandGenerator g(this);

View File

@ -244,6 +244,9 @@ class InstructionSelector final {
// Visit the node and generate code, if any.
void VisitNode(Node* node);
// Visit the node and generate code for IEEE 754 functions.
void VisitFloat64Ieee754Unop(Node*, InstructionCode code);
#define DECLARE_GENERATOR(x) void Visit##x(Node* node);
MACHINE_OP_LIST(DECLARE_GENERATOR)
#undef DECLARE_GENERATOR

View File

@ -194,6 +194,17 @@ Reduction JSBuiltinReducer::ReduceMathLog(Node* node) {
return NoChange();
}
// ES6 section 20.2.2.21 Math.log1p ( x )
Reduction JSBuiltinReducer::ReduceMathLog1p(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Math.log1p(a:number) -> NumberLog1p(a)
Node* value = graph()->NewNode(simplified()->NumberLog1p(), r.left());
return Replace(value);
}
return NoChange();
}
// ES6 section 20.2.2.28 Math.round ( x )
Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
JSCallReduction r(node);
@ -267,6 +278,9 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kMathLog:
reduction = ReduceMathLog(node);
break;
case kMathLog1p:
reduction = ReduceMathLog1p(node);
break;
case kMathRound:
reduction = ReduceMathRound(node);
break;

View File

@ -38,6 +38,7 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceMathFloor(Node* node);
Reduction ReduceMathFround(Node* node);
Reduction ReduceMathLog(Node* node);
Reduction ReduceMathLog1p(Node* node);
Reduction ReduceMathRound(Node* node);
Reduction ReduceMathSqrt(Node* node);
Reduction ReduceMathTrunc(Node* node);

View File

@ -388,6 +388,11 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
if (m.HasValue()) return ReplaceFloat64(base::ieee754::log(m.Value()));
break;
}
case IrOpcode::kFloat64Log1p: {
Float64Matcher m(node->InputAt(0));
if (m.HasValue()) return ReplaceFloat64(base::ieee754::log1p(m.Value()));
break;
}
case IrOpcode::kChangeFloat32ToFloat64: {
Float32Matcher m(node->InputAt(0));
if (m.HasValue()) return ReplaceFloat64(m.Value());

View File

@ -155,6 +155,7 @@ MachineRepresentation AtomicStoreRepresentationOf(Operator const* op) {
V(Float32Sqrt, Operator::kNoProperties, 1, 0, 1) \
V(Float64Abs, Operator::kNoProperties, 1, 0, 1) \
V(Float64Log, Operator::kNoProperties, 1, 0, 1) \
V(Float64Log1p, Operator::kNoProperties, 1, 0, 1) \
V(Float64Add, Operator::kCommutative, 2, 0, 1) \
V(Float64Sub, Operator::kNoProperties, 2, 0, 1) \
V(Float64SubPreserveNan, Operator::kNoProperties, 2, 0, 1) \

View File

@ -370,6 +370,7 @@ class MachineOperatorBuilder final : public ZoneObject {
// Floating point logarithm (double-precision).
const Operator* Float64Log();
const Operator* Float64Log1p();
// Floating point bit representation.
const Operator* Float64ExtractLowWord32();

View File

@ -485,6 +485,17 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
__ sync(); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
FrameScope scope(masm(), StackFrame::MANUAL); \
__ PrepareCallCFunction(0, 1, kScratchReg); \
__ MovToFloatParameter(i.InputDoubleRegister(0)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
0, 1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ mov(sp, fp);
__ Pop(ra, fp);
@ -718,18 +729,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
Operand(offset.offset()));
break;
}
case kIeee754Float64Log: {
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
FrameScope scope(masm(), StackFrame::MANUAL);
__ PrepareCallCFunction(0, 1, kScratchReg);
__ MovToFloatParameter(i.InputDoubleRegister(0));
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 0,
1);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputDoubleRegister());
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
}
case kMipsAdd:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;

View File

@ -876,13 +876,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitRR(this, kMipsAbsD, node);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
MipsOperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineAsFixed(node, f0),
g.UseFixed(node->InputAt(0), f12))
->MarkAsCall();
}
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRR(this, kMipsSqrtS, node);
}
@ -941,6 +934,13 @@ void InstructionSelector::VisitFloat32Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
MipsOperandGenerator g(this);
Emit(opcode, g.DefineAsFixed(node, f0), g.UseFixed(node->InputAt(0), f12))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -496,6 +496,17 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
__ sync(); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
FrameScope scope(masm(), StackFrame::MANUAL); \
__ PrepareCallCFunction(0, 1, kScratchReg); \
__ MovToFloatParameter(i.InputDoubleRegister(0)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
0, 1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ mov(sp, fp);
__ Pop(ra, fp);
@ -727,18 +738,12 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
Operand(offset.offset()));
break;
}
case kIeee754Float64Log: {
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
FrameScope scope(masm(), StackFrame::MANUAL);
__ PrepareCallCFunction(0, 1, kScratchReg);
__ MovToFloatParameter(i.InputDoubleRegister(0));
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 0,
1);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputDoubleRegister());
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
}
case kMips64Add:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;

View File

@ -1282,13 +1282,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitRR(this, kMips64AbsD, node);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
Mips64OperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineAsFixed(node, f0),
g.UseFixed(node->InputAt(0), f12))
->MarkAsCall();
}
void InstructionSelector::VisitFloat32Sqrt(Node* node) {
VisitRR(this, kMips64SqrtS, node);
}
@ -1347,6 +1340,13 @@ void InstructionSelector::VisitFloat32Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
Mips64OperandGenerator g(this);
Emit(opcode, g.DefineAsFixed(node, f0), g.UseFixed(node->InputAt(0), f12))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -198,6 +198,7 @@
V(NumberCeil) \
V(NumberFloor) \
V(NumberLog) \
V(NumberLog1p) \
V(NumberRound) \
V(NumberTrunc) \
V(NumberToInt32) \
@ -356,6 +357,7 @@
V(Float64Min) \
V(Float64Abs) \
V(Float64Log) \
V(Float64Log1p) \
V(Float64Sqrt) \
V(Float64RoundDown) \
V(Float32RoundUp) \

View File

@ -461,6 +461,7 @@ class RawMachineAssembler {
Node* Float64Abs(Node* a) { return AddNode(machine()->Float64Abs(), a); }
Node* Float64Neg(Node* a) { return Float64Sub(Float64Constant(-0.0), a); }
Node* Float64Log(Node* a) { return AddNode(machine()->Float64Log(), a); }
Node* Float64Log1p(Node* a) { return AddNode(machine()->Float64Log1p(), a); }
Node* Float64Sqrt(Node* a) { return AddNode(machine()->Float64Sqrt(), a); }
Node* Float64Equal(Node* a, Node* b) {
return AddNode(machine()->Float64Equal(), a, b);

View File

@ -661,6 +661,8 @@ const Operator* RepresentationChanger::Float64OperatorFor(
return machine()->Float64LessThanOrEqual();
case IrOpcode::kNumberLog:
return machine()->Float64Log();
case IrOpcode::kNumberLog1p:
return machine()->Float64Log1p();
default:
UNREACHABLE();
return nullptr;

View File

@ -1393,7 +1393,8 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, lowering->Float64Floor(node));
return;
}
case IrOpcode::kNumberLog: {
case IrOpcode::kNumberLog:
case IrOpcode::kNumberLog1p: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));

View File

@ -205,6 +205,7 @@ BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
V(NumberCeil, Operator::kNoProperties, 1) \
V(NumberFloor, Operator::kNoProperties, 1) \
V(NumberLog, Operator::kNoProperties, 1) \
V(NumberLog1p, Operator::kNoProperties, 1) \
V(NumberRound, Operator::kNoProperties, 1) \
V(NumberTrunc, Operator::kNoProperties, 1) \
V(NumberToInt32, Operator::kNoProperties, 1) \

View File

@ -155,6 +155,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* NumberCeil();
const Operator* NumberFloor();
const Operator* NumberLog();
const Operator* NumberLog1p();
const Operator* NumberRound();
const Operator* NumberTrunc();
const Operator* NumberToInt32();

View File

@ -1787,6 +1787,8 @@ Type* Typer::Visitor::TypeNumberFloor(Node* node) {
Type* Typer::Visitor::TypeNumberLog(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberLog1p(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberRound(Node* node) {
return TypeUnaryOp(node, NumberRound);
}
@ -2504,6 +2506,8 @@ Type* Typer::Visitor::TypeFloat64Abs(Node* node) {
Type* Typer::Visitor::TypeFloat64Log(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeFloat64Log1p(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeFloat64Sqrt(Node* node) { return Type::Number(); }

View File

@ -741,6 +741,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kNumberCeil:
case IrOpcode::kNumberFloor:
case IrOpcode::kNumberLog:
case IrOpcode::kNumberLog1p:
case IrOpcode::kNumberRound:
case IrOpcode::kNumberTrunc:
// Number -> Number
@ -1038,6 +1039,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kFloat64Min:
case IrOpcode::kFloat64Abs:
case IrOpcode::kFloat64Log:
case IrOpcode::kFloat64Log1p:
case IrOpcode::kFloat64Sqrt:
case IrOpcode::kFloat32RoundDown:
case IrOpcode::kFloat64RoundDown:

View File

@ -594,6 +594,13 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} \
} while (false)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
__ PrepareCallCFunction(1); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \
1); \
} while (false)
void CodeGenerator::AssembleDeconstructFrame() {
__ movq(rsp, rbp);
__ popq(rbp);
@ -841,8 +848,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
break;
}
case kIeee754Float64Log:
__ PrepareCallCFunction(1);
__ CallCFunction(ExternalReference::ieee754_log_function(isolate()), 1);
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
case kX64Add32:
ASSEMBLE_BINOP(addl);

View File

@ -1355,13 +1355,6 @@ void InstructionSelector::VisitFloat64Abs(Node* node) {
VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat64Abs, kSSEFloat64Abs);
}
void InstructionSelector::VisitFloat64Log(Node* node) {
X64OperandGenerator g(this);
Emit(kIeee754Float64Log, g.DefineAsFixed(node, xmm0),
g.UseFixed(node->InputAt(0), xmm0))
->MarkAsCall();
}
void InstructionSelector::VisitFloat64Sqrt(Node* node) {
VisitRO(this, node, kSSEFloat64Sqrt);
}
@ -1415,6 +1408,13 @@ void InstructionSelector::VisitFloat32Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Neg(Node* node) { UNREACHABLE(); }
void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
InstructionCode opcode) {
X64OperandGenerator g(this);
Emit(opcode, g.DefineAsFixed(node, xmm0), g.UseFixed(node->InputAt(0), xmm0))
->MarkAsCall();
}
void InstructionSelector::EmitPrepareArguments(
ZoneVector<PushParameter>* arguments, const CallDescriptor* descriptor,
Node* node) {

View File

@ -69,6 +69,8 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
"power_double_int_function");
Add(ExternalReference::ieee754_log_function(isolate).address(),
"base::ieee754::log");
Add(ExternalReference::ieee754_log1p_function(isolate).address(),
"base::ieee754::log1p");
Add(ExternalReference::store_buffer_top(isolate).address(),
"store_buffer_top");
Add(ExternalReference::address_of_the_hole_nan().address(), "the_hole_nan");

View File

@ -6587,6 +6587,7 @@ class Script: public Struct {
V(Math, ceil, MathCeil) \
V(Math, abs, MathAbs) \
V(Math, log, MathLog) \
V(Math, log1p, MathLog1p) \
V(Math, exp, MathExp) \
V(Math, sqrt, MathSqrt) \
V(Math, pow, MathPow) \

View File

@ -114,7 +114,7 @@ class CodeEntry {
private:
class TagField : public BitField<Logger::LogEventsAndTags, 0, 8> {};
class BuiltinIdField : public BitField<Builtins::Name, 8, 8> {};
class BuiltinIdField : public BitField<Builtins::Name, 8, 24> {};
uint32_t bit_field_;
const char* name_prefix_;

View File

@ -397,170 +397,12 @@ function MathTan(x) {
return KernelTan(y0, y1, (n & 1) ? -1 : 1);
}
// ES6 draft 09-27-13, section 20.2.2.20.
// Math.log1p
//
// Method :
// 1. Argument Reduction: find k and f such that
// 1+x = 2^k * (1+f),
// where sqrt(2)/2 < 1+f < sqrt(2) .
//
// Note. If k=0, then f=x is exact. However, if k!=0, then f
// may not be representable exactly. In that case, a correction
// term is need. Let u=1+x rounded. Let c = (1+x)-u, then
// log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
// and add back the correction term c/u.
// (Note: when x > 2**53, one can simply return log(x))
//
// 2. Approximation of log1p(f).
// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
// = 2s + s*R
// We use a special Reme algorithm on [0,0.1716] to generate
// a polynomial of degree 14 to approximate R The maximum error
// of this polynomial approximation is bounded by 2**-58.45. In
// other words,
// 2 4 6 8 10 12 14
// R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s
// (the values of Lp1 to Lp7 are listed in the program)
// and
// | 2 14 | -58.45
// | Lp1*s +...+Lp7*s - R(z) | <= 2
// | |
// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
// In order to guarantee error in log below 1ulp, we compute log
// by
// log1p(f) = f - (hfsq - s*(hfsq+R)).
//
// 3. Finally, log1p(x) = k*ln2 + log1p(f).
// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
// Here ln2 is split into two floating point number:
// ln2_hi + ln2_lo,
// where n*ln2_hi is always exact for |n| < 2000.
//
// Special cases:
// log1p(x) is NaN with signal if x < -1 (including -INF) ;
// log1p(+INF) is +INF; log1p(-1) is -INF with signal;
// log1p(NaN) is that NaN with no signal.
//
// Accuracy:
// according to an error analysis, the error is always less than
// 1 ulp (unit in the last place).
//
// Constants:
// Constants are found in fdlibm.cc. We assume the C++ compiler to convert
// from decimal to binary accurately enough to produce the intended values.
//
// Note: Assuming log() return accurate answer, the following
// algorithm can be used to compute log1p(x) to within a few ULP:
//
// u = 1+x;
// if (u==1.0) return x ; else
// return log(u)*(x/(u-1.0));
//
// See HP-15C Advanced Functions Handbook, p.193.
//
define LN2_HI = 6.93147180369123816490e-01;
define LN2_LO = 1.90821492927058770002e-10;
define TWO_THIRD = 6.666666666666666666e-01;
define LP1 = 6.666666666666735130e-01;
define LP2 = 3.999999999940941908e-01;
define LP3 = 2.857142874366239149e-01;
define LP4 = 2.222219843214978396e-01;
define LP5 = 1.818357216161805012e-01;
define LP6 = 1.531383769920937332e-01;
define LP7 = 1.479819860511658591e-01;
// 2^54
define TWO54 = 18014398509481984;
function MathLog1p(x) {
x = x * 1; // Convert to number.
var hx = %_DoubleHi(x);
var ax = hx & 0x7fffffff;
var k = 1;
var f = x;
var hu = 1;
var c = 0;
var u = x;
if (hx < 0x3fda827a) {
// x < 0.41422
if (ax >= 0x3ff00000) { // |x| >= 1
if (x === -1) {
return -INFINITY; // log1p(-1) = -inf
} else {
return NaN; // log1p(x<-1) = NaN
}
} else if (ax < 0x3c900000) {
// For |x| < 2^-54 we can return x.
return x;
} else if (ax < 0x3e200000) {
// For |x| < 2^-29 we can use a simple two-term Taylor series.
return x - x * x * 0.5;
}
if ((hx > 0) || (hx <= -0x402D413D)) { // (int) 0xbfd2bec3 = -0x402d413d
// -.2929 < x < 0.41422
k = 0;
}
}
// Handle Infinity and NaN
if (hx >= 0x7ff00000) return x;
if (k !== 0) {
if (hx < 0x43400000) {
// x < 2^53
u = 1 + x;
hu = %_DoubleHi(u);
k = (hu >> 20) - 1023;
c = (k > 0) ? 1 - (u - x) : x - (u - 1);
c = c / u;
} else {
hu = %_DoubleHi(u);
k = (hu >> 20) - 1023;
}
hu = hu & 0xfffff;
if (hu < 0x6a09e) {
u = %_ConstructDouble(hu | 0x3ff00000, %_DoubleLo(u)); // Normalize u.
} else {
++k;
u = %_ConstructDouble(hu | 0x3fe00000, %_DoubleLo(u)); // Normalize u/2.
hu = (0x00100000 - hu) >> 2;
}
f = u - 1;
}
var hfsq = 0.5 * f * f;
if (hu === 0) {
// |f| < 2^-20;
if (f === 0) {
if (k === 0) {
return 0.0;
} else {
return k * LN2_HI + (c + k * LN2_LO);
}
}
var R = hfsq * (1 - TWO_THIRD * f);
if (k === 0) {
return f - R;
} else {
return k * LN2_HI - ((R - (k * LN2_LO + c)) - f);
}
}
var s = f / (2 + f);
var z = s * s;
var R = z * (LP1 + z * (LP2 + z * (LP3 + z * (LP4 +
z * (LP5 + z * (LP6 + z * LP7))))));
if (k === 0) {
return f - (hfsq - s * (hfsq + R));
} else {
return k * LN2_HI - ((hfsq - (s * (hfsq + R) + (k * LN2_LO + c))) - f);
}
}
// ES6 draft 09-27-13, section 20.2.2.14.
// Math.expm1
// Returns exp(x)-1, the exponential of x minus 1.
@ -1107,7 +949,6 @@ utils.InstallFunctions(GlobalMath, DONT_ENUM, [
"tanh", MathTanh,
"log10", MathLog10,
"log2", MathLog2,
"log1p", MathLog1p,
"expm1", MathExpm1
]);

View File

@ -5511,6 +5511,20 @@ TEST(RunFloat64Log) {
FOR_FLOAT64_INPUTS(i) { CHECK_DOUBLE_EQ(ieee754::log(*i), m.Call(*i)); }
}
TEST(RunFloat64Log1p) {
BufferedRawMachineAssemblerTester<double> m(MachineType::Float64());
m.Return(m.Float64Log1p(m.Parameter(0)));
CHECK(std::isnan(m.Call(std::numeric_limits<double>::quiet_NaN())));
CHECK(std::isnan(m.Call(std::numeric_limits<double>::signaling_NaN())));
CHECK(std::isnan(m.Call(-std::numeric_limits<double>::infinity())));
CHECK_DOUBLE_EQ(-std::numeric_limits<double>::infinity(), m.Call(-1.0));
CHECK_DOUBLE_EQ(0.0, m.Call(0.0));
CHECK_DOUBLE_EQ(-0.0, m.Call(-0.0));
CHECK_DOUBLE_EQ(std::numeric_limits<double>::infinity(),
m.Call(std::numeric_limits<double>::infinity()));
FOR_FLOAT64_INPUTS(i) { CHECK_DOUBLE_EQ(ieee754::log1p(*i), m.Call(*i)); }
}
static double two_30 = 1 << 30; // 2^30 is a smi boundary.
static double two_52 = two_30 * (1 << 22); // 2^52 is a precision boundary.
static double kValues[] = {0.1,

View File

@ -200,6 +200,9 @@ class ValueHelper {
0.375,
0.5,
1.0,
1.17549e-38,
1.56657e-37,
1.0000001,
1.25,
2,
3.1e7,

View File

@ -0,0 +1,57 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include "src/base/ieee754.h"
#include "src/base/macros.h"
#include "testing/gmock-support.h"
#include "testing/gtest-support.h"
using testing::IsNaN;
namespace v8 {
namespace base {
namespace ieee754 {
TEST(Ieee754, Log) {
EXPECT_THAT(log(std::numeric_limits<double>::quiet_NaN()), IsNaN());
EXPECT_THAT(log(std::numeric_limits<double>::signaling_NaN()), IsNaN());
EXPECT_THAT(log(-std::numeric_limits<double>::infinity()), IsNaN());
EXPECT_THAT(log(-1.0), IsNaN());
EXPECT_EQ(-std::numeric_limits<double>::infinity(), log(-0.0));
EXPECT_EQ(-std::numeric_limits<double>::infinity(), log(0.0));
EXPECT_EQ(0.0, log(1.0));
EXPECT_EQ(std::numeric_limits<double>::infinity(),
log(std::numeric_limits<double>::infinity()));
}
TEST(Ieee754, Log1p) {
EXPECT_THAT(log1p(std::numeric_limits<double>::quiet_NaN()), IsNaN());
EXPECT_THAT(log1p(std::numeric_limits<double>::signaling_NaN()), IsNaN());
EXPECT_THAT(log1p(-std::numeric_limits<double>::infinity()), IsNaN());
EXPECT_EQ(-std::numeric_limits<double>::infinity(), log1p(-1.0));
EXPECT_EQ(0.0, log1p(0.0));
EXPECT_EQ(-0.0, log1p(-0.0));
EXPECT_EQ(std::numeric_limits<double>::infinity(),
log1p(std::numeric_limits<double>::infinity()));
EXPECT_EQ(6.9756137364252422e-03, log1p(0.007));
EXPECT_EQ(709.782712893384, log1p(1.7976931348623157e308));
EXPECT_EQ(2.7755575615628914e-17, log1p(2.7755575615628914e-17));
EXPECT_EQ(9.313225741817976e-10, log1p(9.313225746154785e-10));
EXPECT_EQ(-0.2876820724517809, log1p(-0.25));
EXPECT_EQ(0.22314355131420976, log1p(0.25));
EXPECT_EQ(2.3978952727983707, log1p(10));
EXPECT_EQ(36.841361487904734, log1p(10e15));
EXPECT_EQ(37.08337388996168, log1p(12738099905822720));
EXPECT_EQ(37.08336444902049, log1p(12737979646738432));
EXPECT_EQ(1.3862943611198906, log1p(3));
EXPECT_EQ(1.3862945995384413, log1p(3 + 9.5367431640625e-7));
EXPECT_EQ(0.5596157879354227, log1p(0.75));
EXPECT_EQ(0.8109302162163288, log1p(1.25));
}
} // namespace ieee754
} // namespace base
} // namespace v8

View File

@ -202,6 +202,50 @@ TEST_F(JSBuiltinReducerTest, MathFround) {
}
}
// -----------------------------------------------------------------------------
// Math.log
TEST_F(JSBuiltinReducerTest, MathLog) {
Node* function = MathFunction("log");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberLog(p0));
}
}
// -----------------------------------------------------------------------------
// Math.log1p
TEST_F(JSBuiltinReducerTest, MathLog1p) {
Node* function = MathFunction("log1p");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberLog1p(p0));
}
}
// -----------------------------------------------------------------------------
// String.fromCharCode

View File

@ -1413,6 +1413,20 @@ TEST_F(MachineOperatorReducerTest, Float64LogWithConstant) {
}
}
// -----------------------------------------------------------------------------
// Float64Log1p
TEST_F(MachineOperatorReducerTest, Float64Log1pWithConstant) {
TRACED_FOREACH(double, x, kFloat64Values) {
Reduction const r =
Reduce(graph()->NewNode(machine()->Float64Log1p(), Float64Constant(x)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsFloat64Constant(NanSensitiveDoubleEq(base::ieee754::log1p(x))));
}
}
// -----------------------------------------------------------------------------
// Float64InsertLowWord32

View File

@ -2309,6 +2309,8 @@ IS_UNOP_MATCHER(Float64RoundTruncate)
IS_UNOP_MATCHER(Float64RoundTiesAway)
IS_UNOP_MATCHER(Float64ExtractLowWord32)
IS_UNOP_MATCHER(Float64ExtractHighWord32)
IS_UNOP_MATCHER(NumberLog)
IS_UNOP_MATCHER(NumberLog1p)
IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(PlainPrimitiveToNumber)

View File

@ -224,6 +224,8 @@ Matcher<Node*> IsNumberShiftRightLogical(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberImul(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberLog(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsNumberLog1p(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsStringFromCharCode(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsAllocate(const Matcher<Node*>& size_matcher,
const Matcher<Node*>& effect_matcher,

View File

@ -31,6 +31,7 @@
'base/division-by-constant-unittest.cc',
'base/flags-unittest.cc',
'base/functional-unittest.cc',
'base/ieee754-unittest.cc',
'base/logging-unittest.cc',
'base/iterator-unittest.cc',
'base/platform/condition-variable-unittest.cc',