Skip to content

Commit 3d1377b

Browse files
mcfimeta-codesync[bot]
authored andcommitted
Optimize cvttsd2siq
Summary: This diff improves performance for lowering `cvttsd2siq` for Arm64. The code was found in hot code blocks in production. Old code gen: ``` mrs x0, fpsr and x0, x0, #0xfffffffffffffffe msr fpsr, x0 movz x0, #0x8000000000000000 fcvtzs x1, d0 mrs x2, fpsr tst x2, #0x1 csel x1, x0, x1, ne movz x0, #0x8000000000000000 cmp x1, x0 b.eq slowpath # if equal, branch to slow path ``` New code gen: ``` ubfx x12, x26, #52, #11 # extract the exponent cmp x12, #0x43e # compare against 0x43e, which is 63 if unbiased by subtracting 1023 fmov d24, x26 # mov the value to a NEON register fcvtzs x4, d24 # convert to integer in rounding to zero mode movz x9, #0x8000000000000000 # INT64_MIN csel x4, x9, x4, hs # if dbl is positive, dbl >= 2^63 or is infinity or NaN, impossible to # convert; if dbl is negative, dbl <= INT64_MIN or is infinity or NaN, # impossible to convert except dbl == INT64_MIN. In all cases, set the # the value to INT64_MIN and jump to slow path. cmp x4, x9 b.eq slowpath # if equal, branch to slow path ``` The biggest difference is to not read and write fpsr (floating-point status register), which is slow on Arm64 CPUs. Reviewed By: ottoni Differential Revision: D92241953 fbshipit-source-id: 8448b689d48fc7406fafc85f79924cf3d03c8ea1
1 parent d8d41fd commit 3d1377b

6 files changed

Lines changed: 36 additions & 16 deletions

File tree

hphp/runtime/vm/jit/vasm-arm.cpp

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ struct Vgen {
450450
void emit(const mrs& i) { a->Mrs(X(i.r), vixl::SystemRegister(i.s.l())); }
451451
void emit(const msr& i) { a->Msr(vixl::SystemRegister(i.s.l()), X(i.r)); }
452452
void emit(const ubfmli& i) { a->ubfm(W(i.d), W(i.s), i.mr.w(), i.ms.w()); }
453+
void emit(const ubfmliq& i) { a->ubfm(X(i.d), X(i.s), i.mr.l(), i.ms.l()); }
453454
void emit(const sbfizq& i) { a->Sbfiz(X(i.d), X(i.s), i.shift.l(), i.width.l()); }
454455
void emit(const storepair& i) { a->Stp(X(i.s0), X(i.s1), M(i.d)); }
455456
void emit(const storepairl& i) { a->Stp(W(i.s0), W(i.s1), M(i.d)); }
@@ -1857,29 +1858,38 @@ Y(incwm, incw, loadw, storew, m)
18571858

18581859
void lower(const VLS& e, cvttsd2siq& i, Vlabel b, size_t idx) {
18591860
lower_impl(e.unit, b, idx, [&] (Vout& v) {
1860-
// Clear FPSR IOC flag.
1861-
auto const tmp1 = v.makeReg();
1862-
auto const tmp2 = v.makeReg();
1863-
v << mrs{FPSR, tmp1};
1864-
v << andqi{~0x01, tmp1, tmp2, v.makeReg()};
1865-
v << msr{tmp2, FPSR};
1861+
// Move i.s to a GP register verbatim.
1862+
auto const dbl_bits = v.makeReg();
1863+
v << copy{i.s, dbl_bits};
18661864

1867-
// Load error value.
1868-
auto const err = v.makeReg();
1869-
v << ldimmq{0x8000000000000000, err};
1865+
// Extract the exponent of the double value.
1866+
auto const dbl_exp = v.makeReg();
1867+
v << ubfmliq(52, 52 + 11 - 1, dbl_bits, dbl_exp);
1868+
1869+
// Compare against 0x43e, which is 63 if unbiased (0x43e - 1023 = 63).
1870+
auto const sf = v.makeReg();
1871+
v << cmpqi(0x43e, dbl_exp, sf);
18701872

18711873
// Do ARM64's double to signed int64 conversion.
18721874
auto const res = v.makeReg();
18731875
v << fcvtzs{i.s, res};
18741876

1875-
// Check if there was a conversion error.
1876-
auto const fpsr = v.makeReg();
1877-
auto const sf = v.makeReg();
1878-
v << mrs{FPSR, fpsr};
1879-
v << testqi{1, fpsr, sf};
1877+
// Load error value (-2^63)
1878+
auto const err = v.cns(0x8000000000000000L);
18801879

18811880
// Move converted value or error.
1882-
v << cmovq{CC_NZ, sf, res, err, i.d};
1881+
// If exp < 63, the double value is finite and within the range of
1882+
// -2^63 and 2^63 - 1, so fcvtzs always succeeds and the value is
1883+
// chosen as the result.
1884+
// Otherwise, below are all the cases:
1885+
// 1. If dbl is positive, then dbl is >= 2^63, or dbl is an infinity
1886+
// or NaN. Converting those values to int64 certainly fails and
1887+
// we choose the error value.
1888+
// 2. If dbl is negative, then dbl is <= -2^63, or dbl is an infinity
1889+
// or NaN. If dbl is -2^63, the converted integer value is still
1890+
// -2^63, so the error value is actually correct. The other cases
1891+
// will lead to conversion failure, which will pick the error value.
1892+
v << cmovq{CC_AE, sf, res, err, i.d};
18831893
});
18841894
}
18851895

hphp/runtime/vm/jit/vasm-info.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ bool effectsImpl(const Vinstr& inst, bool pure) {
279279
case Vinstr::testw:
280280
case Vinstr::testwi:
281281
case Vinstr::ubfmli:
282+
case Vinstr::ubfmliq:
282283
case Vinstr::ucomisd:
283284
case Vinstr::unpcklpd:
284285
case Vinstr::xorb:

hphp/runtime/vm/jit/vasm-instr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ Width width(Vinstr::Opcode op) {
356356
case Vinstr::sarqi:
357357
case Vinstr::shlqi:
358358
case Vinstr::shrqi:
359+
case Vinstr::ubfmliq:
359360
case Vinstr::sbfizq:
360361
case Vinstr::subq:
361362
case Vinstr::subqi:

hphp/runtime/vm/jit/vasm-instr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ struct Vunit;
362362
O(mrs, I(s), Un, D(r))\
363363
O(msr, I(s), U(r), Dn)\
364364
O(ubfmli, I(mr) I(ms), U(s), D(d))\
365+
O(ubfmliq, I(mr) I(ms), U(s), D(d))\
365366
O(sbfizq, I(shift) I(width), U(s), D(d))\
366367
O(loadpair, Inone, U(s), D(d0) D(d1))\
367368
O(loadpairl, Inone, U(s), D(d0) D(d1))\
@@ -1281,6 +1282,7 @@ struct fcvtzs { VregDbl s; Vreg64 d;};
12811282
struct mrs { Immed s; Vreg64 r; };
12821283
struct msr { Vreg64 r; Immed s; };
12831284
struct ubfmli { Immed mr, ms; Vreg32 s, d; };
1285+
struct ubfmliq { Immed mr, ms; Vreg64 s, d; };
12841286
struct sbfizq { Immed shift, width; Vreg64 s, d; };
12851287
struct loadpair { Vptr128 s; Vreg64 d0, d1; };
12861288
struct loadpairl { Vptr64 s; Vreg32 d0, d1; };

hphp/test/quick/bitwise_not.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?hh
22

33
<<__EntryPoint>> function foo(): void {
4-
$vals = vec[1,5,2,1.54,-123.3,1256.6,null /*throws*/];
4+
$vals = vec[1,5,2,1.54,-123.3,1256.6,NAN,-NAN,INF,-INF,(float)(((1<<52)-1)<<11),(float)PHP_INT_MIN,null /*throws*/];
55
foreach ($vals as $v) {
66
var_dump(~($v is null ? $v : (int)$v));
77
}

hphp/test/quick/bitwise_not.php.expectf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@ int(-3)
44
int(-2)
55
int(122)
66
int(-1257)
7+
int(9223372036854775807)
8+
int(9223372036854775807)
9+
int(-1)
10+
int(9223372036854775807)
11+
int(-9223372036854773761)
12+
int(9223372036854775807)
713

814
Fatal error: Unsupported operand type for ~ in %s on line 6

0 commit comments

Comments
 (0)