Remove SpillFrame, merge its memory effects into CallEffects and InlineEnterEffects
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-conv.cpp
blob106e6cff59857161ab9844bbb8b85fc9645eeaac
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
43 #include <limits>
45 namespace HPHP { namespace jit { namespace irlower {
47 TRACE_SET_MOD(irlower);
49 ///////////////////////////////////////////////////////////////////////////////
50 // ConvToBool
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();
55 auto& v = vmain(env);
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();
65 auto& v = vmain(env);
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();
79 auto& v = vmain(env);
81 auto const sf = v.makeReg();
82 v << cmplim{1, src[StringData::sizeOff()], sf};
84 unlikelyCond(v, vcold(env), CC_E, sf, dst,
85 [&] (Vout& v) {
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();
90 #ifdef NO_M_DATA
91 v << cmpbim{'0', src[sizeof(StringData)], sf};
92 #else
93 auto const sd = v.makeReg();
94 v << load{src[StringData::dataOff()], sd};
95 v << cmpbim{'0', sd[0], sf};
96 #endif
97 v << setcc{CC_NE, sf, dst};
98 return dst;
100 [&] (Vout& v) {
101 // Common case is we have an empty string or a string with size bigger
102 // than one.
103 auto const dst = v.makeReg();
104 v << setcc{CC_G, sf, dst};
105 return 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,
119 [&] (Vout& v) {
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};
129 return d;
131 [&] (Vout& v) {
132 auto const d = v.makeReg();
133 v << setcc{CC_NZ, sf, d};
134 return 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();
151 cgCallHelper(v, env,
152 CallSpec::method(&ObjectData::toBoolean),
153 CallDest{DestType::Byte, d},
154 SyncOptions::Sync,
155 argGroup(env, inst).ssa(0));
156 return d;
159 unlikelyCond(v, vcold(env), CC_NZ, sf, dst,
160 [&] (Vout& v) {
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
171 return d;
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 ///////////////////////////////////////////////////////////////////////////////
184 // ConvToInt
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};
204 unlikelyCond(
205 v, vcold(env), CC_E, sf, dst,
206 [&](Vout& v) {
207 // result > max signed int or unordered
208 auto const sf = v.makeReg();
209 v << ucomisd{v.cns(0.0), src, sf};
211 return cond(
212 v, CC_NB, sf, v.makeReg(), [&](Vout& /*v*/) { return d; },
213 [&](Vout& v) {
214 // src > 0 (CF = 1 -> less than 0 or unordered)
215 return cond(v, CC_P, sf, v.makeReg(),
216 [&] (Vout& v) {
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;
223 [&] (Vout& v) {
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
232 [&] (Vout& v) {
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)),
243 src, tmp_sub};
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()};
253 return res;
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 ///////////////////////////////////////////////////////////////////////////////
269 // ConvToDbl
271 namespace {
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 ///////////////////////////////////////////////////////////////////////////////
305 // ConvToVArray
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);
314 return a;
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);
324 return a;
327 static ArrayData* convDictToVArrImpl(ArrayData* adIn) {
328 assertx(!RuntimeOption::EvalHackArrDVArrs);
329 assertx(adIn->isDict());
330 auto a = MixedArray::ToVArrayDict(adIn, adIn->cowCheck());
331 assertx(a != adIn);
332 assertx(a->isPacked());
333 assertx(a->isVArray());
334 decRefArr(adIn);
335 return a;
338 static ArrayData* convKeysetToVArrImpl(ArrayData* adIn) {
339 assertx(!RuntimeOption::EvalHackArrDVArrs);
340 assertx(adIn->isKeyset());
341 auto a = SetArray::ToVArray(adIn, adIn->cowCheck());
342 assertx(a != adIn);
343 assertx(a->isPacked());
344 assertx(a->isVArray());
345 decRefArr(adIn);
346 return a;
349 static ArrayData* convObjToVArrImpl(ObjectData* obj) {
350 assertx(!RuntimeOption::EvalHackArrDVArrs);
351 auto a = castObjToVArray(obj);
352 assertx(a->isPacked());
353 assertx(a->isVArray());
354 decRefObj(obj);
355 return a;
358 namespace {
360 void convToVArrHelper(IRLS& env, const IRInstruction* inst,
361 CallSpec call, bool sync) {
362 auto const args = argGroup(env, inst).ssa(0);
363 cgCallHelper(
364 vmain(env),
365 env,
366 call,
367 callDest(env, inst),
368 sync ? SyncOptions::Sync : SyncOptions::None,
369 args
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 ///////////////////////////////////////////////////////////////////////////////
396 // ConvToDArray
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);
405 return a;
408 static ArrayData* convVecToDArrImpl(ArrayData* adIn) {
409 assertx(!RuntimeOption::EvalHackArrDVArrs);
410 assertx(adIn->isVecArray());
411 auto a = PackedArray::ToDArrayVec(adIn, adIn->cowCheck());
412 assertx(a != adIn);
413 assertx(a->isMixed());
414 assertx(a->isDArray());
415 decRefArr(adIn);
416 return a;
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);
426 return a;
429 static ArrayData* convKeysetToDArrImpl(ArrayData* adIn) {
430 assertx(!RuntimeOption::EvalHackArrDVArrs);
431 assertx(adIn->isKeyset());
432 auto a = SetArray::ToDArray(adIn, adIn->cowCheck());
433 assertx(a != adIn);
434 assertx(a->isMixed());
435 assertx(a->isDArray());
436 decRefArr(adIn);
437 return a;
440 static ArrayData* convObjToDArrImpl(ObjectData* obj) {
441 assertx(!RuntimeOption::EvalHackArrDVArrs);
442 auto a = castObjToDArray(obj);
443 assertx(a->isMixed());
444 assertx(a->isDArray());
445 decRefObj(obj);
446 return a;
449 namespace {
451 void convToDArrHelper(IRLS& env, const IRInstruction* inst,
452 CallSpec call, bool sync) {
453 auto const args = argGroup(env, inst).ssa(0);
454 cgCallHelper(
455 vmain(env),
456 env,
457 call,
458 callDest(env, inst),
459 sync ? SyncOptions::Sync : SyncOptions::None,
460 args
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 ///////////////////////////////////////////////////////////////////////////////
489 // ConvToStr
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) {
536 int64_t n;
537 if (str->isStrictlyInteger(n)) {
538 return make_tv<KindOfInt64>(n);
540 str->incRefCount();
541 return make_tv<KindOfString>(str);
544 void cgStrictlyIntegerConv(IRLS& env, const IRInstruction* inst) {
545 auto const args = argGroup(env, inst).ssa(0);
546 cgCallHelper(
547 vmain(env),
548 env,
549 CallSpec::direct(strictlyIntegerConvImpl),
550 callDestTV(env, inst),
551 SyncOptions::None,
552 args
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)};
562 if (wide_tv_val) {
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);
574 v << copy{src, dst};
577 ///////////////////////////////////////////////////////////////////////////////