2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/irlower-internal.h"
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/object-data.h"
21 #include "hphp/runtime/base/runtime-option.h"
22 #include "hphp/runtime/base/string-data.h"
23 #include "hphp/runtime/base/type-string.h"
25 #include "hphp/runtime/ext/collections/ext_collections.h"
27 #include "hphp/runtime/vm/jit/arg-group.h"
28 #include "hphp/runtime/vm/jit/call-spec.h"
29 #include "hphp/runtime/vm/jit/code-gen-cf.h"
30 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
31 #include "hphp/runtime/vm/jit/ir-instruction.h"
32 #include "hphp/runtime/vm/jit/ir-opcode.h"
33 #include "hphp/runtime/vm/jit/ssa-tmp.h"
34 #include "hphp/runtime/vm/jit/type.h"
35 #include "hphp/runtime/vm/jit/types.h"
36 #include "hphp/runtime/vm/jit/vasm-gen.h"
37 #include "hphp/runtime/vm/jit/vasm-instr.h"
38 #include "hphp/runtime/vm/jit/vasm-reg.h"
40 #include "hphp/util/asm-x64.h"
41 #include "hphp/util/trace.h"
45 namespace HPHP
{ namespace jit
{ namespace irlower
{
47 TRACE_SET_MOD(irlower
);
49 ///////////////////////////////////////////////////////////////////////////////
52 void cgConvIntToBool(IRLS
& env
, const IRInstruction
* inst
) {
53 auto const dst
= dstLoc(env
, inst
, 0).reg();
54 auto const src
= srcLoc(env
, inst
, 0).reg();
57 auto const sf
= v
.makeReg();
58 v
<< testq
{src
, src
, sf
};
59 v
<< setcc
{CC_NE
, sf
, dst
};
62 void cgConvDblToBool(IRLS
& env
, const IRInstruction
* inst
) {
63 auto const dst
= dstLoc(env
, inst
, 0).reg();
64 auto const src
= srcLoc(env
, inst
, 0).reg();
67 auto const movtdq_res
= v
.makeReg();
68 auto const sf
= v
.makeReg();
69 v
<< movtdq
{src
, movtdq_res
};
70 // 0.0 stays zero and -0.0 is now 0.0
71 v
<< shlqi
{1, movtdq_res
, v
.makeReg(), sf
};
72 // lower byte becomes 1 if dst != 0
73 v
<< setcc
{CC_NE
, sf
, dst
};
76 void cgConvStrToBool(IRLS
& env
, const IRInstruction
* inst
) {
77 auto const dst
= dstLoc(env
, inst
, 0).reg();
78 auto const src
= srcLoc(env
, inst
, 0).reg();
81 auto const sf
= v
.makeReg();
82 v
<< cmplim
{1, src
[StringData::sizeOff()], sf
};
84 unlikelyCond(v
, vcold(env
), CC_E
, sf
, dst
,
86 // Unlikely case is we end up having to check whether the first byte of
87 // the string is equal to '0'.
88 auto const dst
= v
.makeReg();
89 auto const sf
= v
.makeReg();
91 v
<< cmpbim
{'0', src
[sizeof(StringData
)], sf
};
93 auto const sd
= v
.makeReg();
94 v
<< load
{src
[StringData::dataOff()], sd
};
95 v
<< cmpbim
{'0', sd
[0], sf
};
97 v
<< setcc
{CC_NE
, sf
, dst
};
101 // Common case is we have an empty string or a string with size bigger
103 auto const dst
= v
.makeReg();
104 v
<< setcc
{CC_G
, sf
, dst
};
110 void cgConvArrToBool(IRLS
& env
, const IRInstruction
* inst
) {
111 auto const dst
= dstLoc(env
, inst
, 0).reg();
112 auto const src
= srcLoc(env
, inst
, 0).reg();
113 auto& v
= vmain(env
);
115 auto const sf
= v
.makeReg();
116 v
<< cmplim
{0, src
[ArrayData::offsetofSize()], sf
};
118 unlikelyCond(v
, vcold(env
), CC_S
, sf
, dst
,
120 auto const vsize
= v
.makeReg();
121 cgCallHelper(v
, env
, CallSpec::method(&ArrayData::vsize
),
122 callDest(vsize
), SyncOptions::None
,
123 argGroup(env
, inst
).ssa(0));
125 auto const sf
= v
.makeReg();
126 auto const d
= v
.makeReg();
127 v
<< testl
{vsize
, vsize
, sf
};
128 v
<< setcc
{CC_NZ
, sf
, d
};
132 auto const d
= v
.makeReg();
133 v
<< setcc
{CC_NZ
, sf
, d
};
139 void cgConvObjToBool(IRLS
& env
, const IRInstruction
* inst
) {
140 auto const dst
= dstLoc(env
, inst
, 0).reg();
141 auto const src
= srcLoc(env
, inst
, 0).reg();
142 auto& v
= vmain(env
);
144 auto const sf
= v
.makeReg();
145 auto const cls
= v
.makeReg();
146 emitLdObjClass(v
, src
, cls
);
147 v
<< testbim
{Class::CallToImpl
, cls
[Class::RTAttrsOff()], sf
};
149 auto const callToBoolean
= [&] (Vout
& v
) {
150 auto const d
= v
.makeReg();
152 CallSpec::method(&ObjectData::toBoolean
),
153 CallDest
{DestType::Byte
, d
},
155 argGroup(env
, inst
).ssa(0));
159 unlikelyCond(v
, vcold(env
), CC_NZ
, sf
, dst
,
161 if (RuntimeOption::EvalNoticeOnCollectionToBool
) {
162 return callToBoolean(v
);
164 auto const sf
= emitIsCollection(v
, src
);
165 return cond(v
, CC_BE
, sf
, v
.makeReg(),
166 [&] (Vout
& v
) { // src points to native collection
167 auto const d
= v
.makeReg();
168 auto const sf
= v
.makeReg();
169 v
<< cmplim
{0, src
[collections::FAST_SIZE_OFFSET
], sf
};
170 v
<< setcc
{CC_NE
, sf
, d
}; // true iff size not zero
173 [&] (Vout
& v
) { // src is not a native collection
174 return callToBoolean(v
);
177 [&] (Vout
& v
) { return v
.cns(true); }
181 IMPL_OPCODE_CALL(ConvCellToBool
);
183 ///////////////////////////////////////////////////////////////////////////////
186 void cgConvBoolToInt(IRLS
& env
, const IRInstruction
* inst
) {
187 auto const dst
= dstLoc(env
, inst
, 0).reg();
188 auto const src
= srcLoc(env
, inst
, 0).reg();
189 vmain(env
) << movzbq
{src
, dst
};
192 void cgConvDblToInt(IRLS
& env
, const IRInstruction
* inst
) {
193 auto const dst
= dstLoc(env
, inst
, 0).reg();
194 auto const src
= srcLoc(env
, inst
, 0).reg();
195 auto& v
= vmain(env
);
197 auto const indef
= v
.cns(0x8000000000000000L
);
199 auto const d
= v
.makeReg();
200 auto const sf
= v
.makeReg();
201 v
<< cvttsd2siq
{src
, d
};
202 v
<< cmpq
{indef
, d
, sf
};
205 v
, vcold(env
), CC_E
, sf
, dst
,
207 // result > max signed int or unordered
208 auto const sf
= v
.makeReg();
209 v
<< ucomisd
{v
.cns(0.0), src
, sf
};
212 v
, CC_NB
, sf
, v
.makeReg(), [&](Vout
& /*v*/) { return d
; },
214 // src > 0 (CF = 1 -> less than 0 or unordered)
215 return cond(v
, CC_P
, sf
, v
.makeReg(),
217 // PF = 1 -> unordered, i.e., we are doing an int cast of NaN.
218 // PHP5 didn't formally define this, but observationally returns
219 // the truncated value (i.e., what d currently holds). PHP7
220 // formally defines this case to return 0.
221 return RuntimeOption::PHP7_IntSemantics
? v
.cns(0) : d
;
224 constexpr uint64_t ulong_max
=
225 std::numeric_limits
<unsigned long>::max();
227 auto const sf
= v
.makeReg();
228 v
<< ucomisd
{v
.cns(static_cast<double>(ulong_max
)), src
, sf
};
230 return cond(v
, CC_B
, sf
, v
.makeReg(),
231 [&] (Vout
& v
) { return v
.cns(0); }, // src > ULONG_MAX
233 // 0 < src <= ULONG_MAX
235 // We know that LONG_MAX < src <= ULONG_MAX, so therefore:
236 // 0 < src - LONG_MAX <= ULONG_MAX
237 constexpr uint64_t long_max
=
238 std::numeric_limits
<long>::max();
240 auto const tmp_sub
= v
.makeReg();
241 auto const tmp_int
= v
.makeReg();
242 v
<< subsd
{v
.cns(static_cast<double>(long_max
)),
244 v
<< cvttsd2siq
{tmp_sub
, tmp_int
};
246 // We want to simulate integer overflow so we take the
247 // resulting integer and flip its sign bit. (NB: We don't
248 // use orq{} here because it's possible that src == LONG_MAX
249 // in which case cvttsd2siq will yield an indefiniteInteger,
250 // which we would like to make zero.)
251 auto const res
= v
.makeReg();
252 v
<< xorq
{indef
, tmp_int
, res
, v
.makeReg()};
260 [&](Vout
& /*v*/) { return d
; });
263 IMPL_OPCODE_CALL(ConvStrToInt
);
264 IMPL_OPCODE_CALL(ConvObjToInt
);
265 IMPL_OPCODE_CALL(ConvResToInt
);
266 IMPL_OPCODE_CALL(ConvCellToInt
);
268 ///////////////////////////////////////////////////////////////////////////////
273 void implConvBoolOrIntToDbl(IRLS
& env
, const IRInstruction
* inst
) {
274 auto const val
= inst
->src(0);
275 assertx(val
->isA(TBool
) || val
->isA(TInt
));
277 auto const dst
= dstLoc(env
, inst
, 0).reg();
278 auto const src
= srcLoc(env
, inst
, 0).reg();
279 auto& v
= vmain(env
);
281 // cvtsi2sd doesn't modify the high bits of its target, which can cause false
282 // dependencies to prevent register renaming from kicking in. Break the
283 // dependency chain by zeroing out the XMM reg.
284 auto const src_zext
= zeroExtendIfBool(v
, val
->type(), src
);
285 v
<< cvtsi2sd
{src_zext
, dst
};
290 void cgConvBoolToDbl(IRLS
& env
, const IRInstruction
* inst
) {
291 implConvBoolOrIntToDbl(env
, inst
);
294 void cgConvIntToDbl(IRLS
& env
, const IRInstruction
* inst
) {
295 implConvBoolOrIntToDbl(env
, inst
);
298 IMPL_OPCODE_CALL(ConvStrToDbl
);
299 IMPL_OPCODE_CALL(ConvArrToDbl
);
300 IMPL_OPCODE_CALL(ConvObjToDbl
);
301 IMPL_OPCODE_CALL(ConvResToDbl
);
302 IMPL_OPCODE_CALL(ConvCellToDbl
);
304 ///////////////////////////////////////////////////////////////////////////////
307 static ArrayData
* convArrToVArrImpl(ArrayData
* adIn
) {
308 assertx(!RuntimeOption::EvalHackArrDVArrs
);
309 assertx(adIn
->isPHPArray());
310 auto a
= adIn
->toVArray(adIn
->cowCheck());
311 assertx(a
->isPacked());
312 assertx(a
->isVArray());
313 if (a
!= adIn
) decRefArr(adIn
);
317 static ArrayData
* convVecToVArrImpl(ArrayData
* adIn
) {
318 assertx(!RuntimeOption::EvalHackArrDVArrs
);
319 assertx(adIn
->isVecArray());
320 auto a
= PackedArray::ToVArrayVec(adIn
, adIn
->cowCheck());
321 assertx(a
->isPacked());
322 assertx(a
->isVArray());
323 if (a
!= adIn
) decRefArr(adIn
);
327 static ArrayData
* convDictToVArrImpl(ArrayData
* adIn
) {
328 assertx(!RuntimeOption::EvalHackArrDVArrs
);
329 assertx(adIn
->isDict());
330 auto a
= MixedArray::ToVArrayDict(adIn
, adIn
->cowCheck());
332 assertx(a
->isPacked());
333 assertx(a
->isVArray());
338 static ArrayData
* convKeysetToVArrImpl(ArrayData
* adIn
) {
339 assertx(!RuntimeOption::EvalHackArrDVArrs
);
340 assertx(adIn
->isKeyset());
341 auto a
= SetArray::ToVArray(adIn
, adIn
->cowCheck());
343 assertx(a
->isPacked());
344 assertx(a
->isVArray());
349 static ArrayData
* convObjToVArrImpl(ObjectData
* obj
) {
350 assertx(!RuntimeOption::EvalHackArrDVArrs
);
351 auto a
= castObjToVArray(obj
);
352 assertx(a
->isPacked());
353 assertx(a
->isVArray());
360 void convToVArrHelper(IRLS
& env
, const IRInstruction
* inst
,
361 CallSpec call
, bool sync
) {
362 auto const args
= argGroup(env
, inst
).ssa(0);
368 sync
? SyncOptions::Sync
: SyncOptions::None
,
375 void cgConvArrToVArr(IRLS
& env
, const IRInstruction
* inst
) {
376 convToVArrHelper(env
, inst
, CallSpec::direct(convArrToVArrImpl
), false);
379 void cgConvVecToVArr(IRLS
& env
, const IRInstruction
* inst
) {
380 convToVArrHelper(env
, inst
, CallSpec::direct(convVecToVArrImpl
), false);
383 void cgConvDictToVArr(IRLS
& env
, const IRInstruction
* inst
) {
384 convToVArrHelper(env
, inst
, CallSpec::direct(convDictToVArrImpl
), false);
387 void cgConvKeysetToVArr(IRLS
& env
, const IRInstruction
* inst
) {
388 convToVArrHelper(env
, inst
, CallSpec::direct(convKeysetToVArrImpl
), false);
391 void cgConvObjToVArr(IRLS
& env
, const IRInstruction
* inst
) {
392 convToVArrHelper(env
, inst
, CallSpec::direct(convObjToVArrImpl
), true);
395 ///////////////////////////////////////////////////////////////////////////////
398 static ArrayData
* convArrToDArrImpl(ArrayData
* adIn
) {
399 assertx(!RuntimeOption::EvalHackArrDVArrs
);
400 assertx(adIn
->isPHPArray());
401 auto a
= adIn
->toDArray(adIn
->cowCheck());
402 assertx(a
->isMixed());
403 assertx(a
->isDArray());
404 if (a
!= adIn
) decRefArr(adIn
);
408 static ArrayData
* convVecToDArrImpl(ArrayData
* adIn
) {
409 assertx(!RuntimeOption::EvalHackArrDVArrs
);
410 assertx(adIn
->isVecArray());
411 auto a
= PackedArray::ToDArrayVec(adIn
, adIn
->cowCheck());
413 assertx(a
->isMixed());
414 assertx(a
->isDArray());
419 static ArrayData
* convDictToDArrImpl(ArrayData
* adIn
) {
420 assertx(!RuntimeOption::EvalHackArrDVArrs
);
421 assertx(adIn
->isDict());
422 auto a
= MixedArray::ToDArrayDict(adIn
, adIn
->cowCheck());
423 assertx(a
->isMixed());
424 assertx(a
->isDArray());
425 if (a
!= adIn
) decRefArr(adIn
);
429 static ArrayData
* convKeysetToDArrImpl(ArrayData
* adIn
) {
430 assertx(!RuntimeOption::EvalHackArrDVArrs
);
431 assertx(adIn
->isKeyset());
432 auto a
= SetArray::ToDArray(adIn
, adIn
->cowCheck());
434 assertx(a
->isMixed());
435 assertx(a
->isDArray());
440 static ArrayData
* convObjToDArrImpl(ObjectData
* obj
) {
441 assertx(!RuntimeOption::EvalHackArrDVArrs
);
442 auto a
= castObjToDArray(obj
);
443 assertx(a
->isMixed());
444 assertx(a
->isDArray());
451 void convToDArrHelper(IRLS
& env
, const IRInstruction
* inst
,
452 CallSpec call
, bool sync
) {
453 auto const args
= argGroup(env
, inst
).ssa(0);
459 sync
? SyncOptions::Sync
: SyncOptions::None
,
466 void cgConvArrToDArr(IRLS
& env
, const IRInstruction
* inst
) {
467 convToDArrHelper(env
, inst
, CallSpec::direct(convArrToDArrImpl
), false);
470 void cgConvVecToDArr(IRLS
& env
, const IRInstruction
* inst
) {
471 convToDArrHelper(env
, inst
, CallSpec::direct(convVecToDArrImpl
), false);
474 void cgConvDictToDArr(IRLS
& env
, const IRInstruction
* inst
) {
475 // These have to sync because of Hack array compat notices
476 convToDArrHelper(env
, inst
, CallSpec::direct(convDictToDArrImpl
), true);
479 void cgConvKeysetToDArr(IRLS
& env
, const IRInstruction
* inst
) {
480 // These have to sync because of Hack array compat notices
481 convToDArrHelper(env
, inst
, CallSpec::direct(convKeysetToDArrImpl
), true);
484 void cgConvObjToDArr(IRLS
& env
, const IRInstruction
* inst
) {
485 convToDArrHelper(env
, inst
, CallSpec::direct(convObjToDArrImpl
), true);
488 ///////////////////////////////////////////////////////////////////////////////
491 IMPL_OPCODE_CALL(ConvIntToStr
);
492 IMPL_OPCODE_CALL(ConvDblToStr
);
493 IMPL_OPCODE_CALL(ConvObjToStr
);
494 IMPL_OPCODE_CALL(ConvResToStr
);
495 IMPL_OPCODE_CALL(ConvCellToStr
);
497 ///////////////////////////////////////////////////////////////////////////////
499 IMPL_OPCODE_CALL(ConvBoolToArr
);
500 IMPL_OPCODE_CALL(ConvIntToArr
);
501 IMPL_OPCODE_CALL(ConvDblToArr
);
502 IMPL_OPCODE_CALL(ConvStrToArr
);
503 IMPL_OPCODE_CALL(ConvFuncToArr
);
504 IMPL_OPCODE_CALL(ConvVecToArr
);
505 IMPL_OPCODE_CALL(ConvDictToArr
);
506 IMPL_OPCODE_CALL(ConvKeysetToArr
);
507 IMPL_OPCODE_CALL(ConvClsMethToArr
);
508 IMPL_OPCODE_CALL(ConvObjToArr
);
509 IMPL_OPCODE_CALL(ConvCellToArr
);
510 IMPL_OPCODE_CALL(ConvArrToNonDVArr
);
512 IMPL_OPCODE_CALL(ConvClsMethToVArr
);
513 IMPL_OPCODE_CALL(ConvClsMethToDArr
);
515 IMPL_OPCODE_CALL(ConvArrToVec
);
516 IMPL_OPCODE_CALL(ConvDictToVec
);
517 IMPL_OPCODE_CALL(ConvKeysetToVec
);
518 IMPL_OPCODE_CALL(ConvClsMethToVec
);
519 IMPL_OPCODE_CALL(ConvObjToVec
);
521 IMPL_OPCODE_CALL(ConvArrToDict
);
522 IMPL_OPCODE_CALL(ConvVecToDict
);
523 IMPL_OPCODE_CALL(ConvKeysetToDict
);
524 IMPL_OPCODE_CALL(ConvClsMethToDict
);
525 IMPL_OPCODE_CALL(ConvObjToDict
);
527 IMPL_OPCODE_CALL(ConvArrToKeyset
);
528 IMPL_OPCODE_CALL(ConvVecToKeyset
);
529 IMPL_OPCODE_CALL(ConvDictToKeyset
);
530 IMPL_OPCODE_CALL(ConvClsMethToKeyset
);
531 IMPL_OPCODE_CALL(ConvObjToKeyset
);
533 ///////////////////////////////////////////////////////////////////////////////
535 static TypedValue
strictlyIntegerConvImpl(StringData
* str
) {
537 if (str
->isStrictlyInteger(n
)) {
538 return make_tv
<KindOfInt64
>(n
);
541 return make_tv
<KindOfString
>(str
);
544 void cgStrictlyIntegerConv(IRLS
& env
, const IRInstruction
* inst
) {
545 auto const args
= argGroup(env
, inst
).ssa(0);
549 CallSpec::direct(strictlyIntegerConvImpl
),
550 callDestTV(env
, inst
),
556 void cgConvPtrToLval(IRLS
& env
, const IRInstruction
* inst
) {
557 auto& v
= vmain(env
);
558 auto const srcLoc
= irlower::srcLoc(env
, inst
, 0);
559 auto const dstLoc
= irlower::dstLoc(env
, inst
, 0);
561 v
<< copy
{srcLoc
.reg(), dstLoc
.reg(tv_lval::val_idx
)};
563 static_assert(TVOFF(m_data
) == 0, "");
564 v
<< lea
{srcLoc
.reg()[TVOFF(m_type
)], dstLoc
.reg(tv_lval::type_idx
)};
568 ///////////////////////////////////////////////////////////////////////////////
570 void cgDblAsBits(IRLS
& env
, const IRInstruction
* inst
) {
571 auto const dst
= dstLoc(env
, inst
, 0).reg();
572 auto const src
= srcLoc(env
, inst
, 0).reg();
573 auto& v
= vmain(env
);
577 ///////////////////////////////////////////////////////////////////////////////