Skip to content

Commit 50eaeec

Browse files
committed
[MERGE #5686 @nhat-nguyen] Float type specialization for comparison instructions
Merge pull request #5686 from nhat-nguyen:floats Perform type specialization and update destination value to boolean for comparison instructions when either source is float
2 parents cb291c2 + 1cd7462 commit 50eaeec

File tree

4 files changed

+125
-9
lines changed

4 files changed

+125
-9
lines changed

lib/Backend/GlobOpt.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10473,6 +10473,7 @@ GlobOpt::TypeSpecializeFloatBinary(IR::Instr *instr, Value *src1Val, Value *src2
1047310473
bool skipSrc1 = false;
1047410474
bool skipSrc2 = false;
1047510475
bool skipDst = false;
10476+
bool convertDstToBool = false;
1047610477

1047710478
if (!this->DoFloatTypeSpec())
1047810479
{
@@ -10544,6 +10545,19 @@ GlobOpt::TypeSpecializeFloatBinary(IR::Instr *instr, Value *src1Val, Value *src2
1054410545
skipDst = true;
1054510546
break;
1054610547

10548+
case Js::OpCode::CmEq_A:
10549+
case Js::OpCode::CmSrEq_A:
10550+
case Js::OpCode::CmNeq_A:
10551+
case Js::OpCode::CmSrNeq_A:
10552+
case Js::OpCode::CmLe_A:
10553+
case Js::OpCode::CmLt_A:
10554+
case Js::OpCode::CmGe_A:
10555+
case Js::OpCode::CmGt_A:
10556+
{
10557+
convertDstToBool = true;
10558+
break;
10559+
}
10560+
1054710561
default:
1054810562
return false;
1054910563
}
@@ -10589,13 +10603,19 @@ GlobOpt::TypeSpecializeFloatBinary(IR::Instr *instr, Value *src1Val, Value *src2
1058910603
if (!skipDst)
1059010604
{
1059110605
dst = instr->GetDst();
10592-
1059310606
if (dst)
1059410607
{
10595-
*pDstVal = CreateDstUntransferredValue(ValueType::Float, instr, src1Val, src2Val);
10596-
10597-
AssertMsg(dst->IsRegOpnd(), "What else?");
10598-
this->ToFloat64Dst(instr, dst->AsRegOpnd(), this->currentBlock);
10608+
if (convertDstToBool)
10609+
{
10610+
*pDstVal = CreateDstUntransferredValue(ValueType::Boolean, instr, src1Val, src2Val);
10611+
ToVarRegOpnd(dst->AsRegOpnd(), currentBlock);
10612+
}
10613+
else
10614+
{
10615+
*pDstVal = CreateDstUntransferredValue(ValueType::Float, instr, src1Val, src2Val);
10616+
AssertMsg(dst->IsRegOpnd(), "What else?");
10617+
this->ToFloat64Dst(instr, dst->AsRegOpnd(), this->currentBlock);
10618+
}
1059910619
}
1060010620
}
1060110621

lib/Backend/LowerMDShared.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2653,9 +2653,7 @@ void LowererMD::GenerateFastCmXx(IR::Instr *instr)
26532653
bool isFloatSrc = src1->IsFloat();
26542654
bool isInt64Src = src1->IsInt64();
26552655
Assert(!isFloatSrc || src2->IsFloat());
2656-
Assert(!isFloatSrc || isIntDst);
26572656
Assert(!isInt64Src || src2->IsInt64());
2658-
Assert(!isInt64Src || isIntDst);
26592657
Assert(!isFloatSrc || AutoSystemInfo::Data.SSE2Available());
26602658
IR::Opnd *opnd;
26612659
IR::Instr *newInstr;
@@ -2684,16 +2682,19 @@ void LowererMD::GenerateFastCmXx(IR::Instr *instr)
26842682
done = instr;
26852683
}
26862684

2685+
bool isNegOpt = instr->m_opcode == Js::OpCode::CmNeq_A || instr->m_opcode == Js::OpCode::CmSrNeq_A;
2686+
bool initDstToFalse = true;
26872687
if (isIntDst)
26882688
{
2689+
// Fast path for int src with destination type specialized to int
26892690
// reg = MOV 0 will get peeped to XOR reg, reg which sets the flags.
26902691
// Put the MOV before the CMP, but use a tmp if dst == src1/src2
26912692
if (dst->IsEqual(src1) || dst->IsEqual(src2))
26922693
{
26932694
tmp = IR::RegOpnd::New(dst->GetType(), this->m_func);
26942695
}
26952696
// dst = MOV 0
2696-
if (isFloatSrc && instr->m_opcode == Js::OpCode::CmNeq_A)
2697+
if (isFloatSrc && isNegOpt)
26972698
{
26982699
opnd = IR::IntConstOpnd::New(1, TyInt32, this->m_func);
26992700
}
@@ -2703,6 +2704,22 @@ void LowererMD::GenerateFastCmXx(IR::Instr *instr)
27032704
}
27042705
m_lowerer->InsertMove(tmp, opnd, done);
27052706
}
2707+
else if (isFloatSrc)
2708+
{
2709+
// Fast path for float src when destination is a var
2710+
// Assign default value for destination in case either src is NaN
2711+
Assert(dst->IsVar());
2712+
if (isNegOpt)
2713+
{
2714+
opnd = this->m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue);
2715+
}
2716+
else
2717+
{
2718+
opnd = this->m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueFalse);
2719+
initDstToFalse = false;
2720+
}
2721+
Lowerer::InsertMove(tmp, opnd, done);
2722+
}
27062723

27072724
Js::OpCode cmpOp;
27082725
if (isFloatSrc)
@@ -2733,7 +2750,9 @@ void LowererMD::GenerateFastCmXx(IR::Instr *instr)
27332750
done->InsertBefore(newInstr);
27342751
}
27352752

2736-
if (!isIntDst)
2753+
// For all cases where the operator is a comparison, we do not want to emit False value
2754+
// since it has already been generated in the if block before.
2755+
if (!isIntDst && initDstToFalse)
27372756
{
27382757
opnd = this->m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueFalse);
27392758
Lowerer::InsertMove(tmp, opnd, done);
@@ -2744,11 +2763,13 @@ void LowererMD::GenerateFastCmXx(IR::Instr *instr)
27442763
{
27452764
case Js::OpCode::CmEq_I4:
27462765
case Js::OpCode::CmEq_A:
2766+
case Js::OpCode::CmSrEq_A:
27472767
useCC = isIntDst ? Js::OpCode::SETE : Js::OpCode::CMOVE;
27482768
break;
27492769

27502770
case Js::OpCode::CmNeq_I4:
27512771
case Js::OpCode::CmNeq_A:
2772+
case Js::OpCode::CmSrNeq_A:
27522773
useCC = isIntDst ? Js::OpCode::SETNE : Js::OpCode::CMOVNE;
27532774
break;
27542775

test/Basics/FloatComparison.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
// This tests the fast path for cmxx where either src is type specialized to float
9+
var tests = [
10+
{
11+
name: "NaN equality test",
12+
body: function() {
13+
assert.isTrue(NaN !== NaN);
14+
assert.isTrue(NaN !== 0.5);
15+
assert.isTrue(0.5 !== NaN);
16+
17+
assert.isTrue(NaN != NaN);
18+
assert.isTrue(NaN != 0.5);
19+
assert.isTrue(0.5 != NaN);
20+
21+
assert.isFalse(NaN === NaN);
22+
assert.isFalse(NaN === 0.5);
23+
assert.isFalse(0.5 === NaN);
24+
25+
assert.isFalse(NaN == NaN);
26+
assert.isFalse(NaN == 0.5);
27+
assert.isFalse(0.5 == NaN);
28+
29+
assert.isFalse(NaN > 0.5);
30+
assert.isFalse(NaN >= 0.5);
31+
assert.isFalse(NaN < 0.5);
32+
assert.isFalse(NaN <= 0.5);
33+
}
34+
},
35+
{
36+
name: "Type coercion test",
37+
body: function() {
38+
assert.isTrue('0.5' == 0.5);
39+
assert.isFalse('0.5' === 0.5);
40+
assert.isFalse('NaN' == NaN);
41+
assert.isTrue('NaN' != NaN);
42+
}
43+
},
44+
{
45+
name: "int vs. float",
46+
body: function() {
47+
assert.isFalse(5 == 0.5);
48+
assert.isTrue(5 != 0.5);
49+
assert.isFalse(5 === 0.5);
50+
assert.isTrue(5 !== 0.5);
51+
}
52+
},
53+
{
54+
name: "object vs. float",
55+
body: function() {
56+
assert.isFalse({} == 0.5);
57+
assert.isFalse({} === 0.5);
58+
assert.isTrue({} != 0.5);
59+
assert.isTrue({} !== 0.5);
60+
61+
assert.isFalse({} > 0.5);
62+
assert.isFalse({} >= 0.5);
63+
assert.isFalse({} < 0.5);
64+
assert.isFalse({} <= 0.5);
65+
}
66+
}
67+
];
68+
69+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/Basics/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,10 @@
337337
<tags>exclude_test,exclude_jshost</tags>
338338
</default>
339339
</test>
340+
<test>
341+
<default>
342+
<files>FloatComparison.js</files>
343+
<compile-flags>-args summary -endargs</compile-flags>
344+
</default>
345+
</test>
340346
</regress-exe>

0 commit comments

Comments
 (0)