2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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/code-gen-helpers.h"
19 #include "hphp/runtime/base/countable.h"
20 #include "hphp/runtime/base/datatype.h"
21 #include "hphp/runtime/base/header-kind.h"
22 #include "hphp/runtime/base/rds-header.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/runtime/base/tv-helpers.h"
25 #include "hphp/runtime/vm/class.h"
27 #include "hphp/runtime/vm/jit/abi.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/ssa-tmp.h"
31 #include "hphp/runtime/vm/jit/mc-generator.h"
32 #include "hphp/runtime/vm/jit/translator.h"
33 #include "hphp/runtime/vm/jit/type.h"
34 #include "hphp/runtime/vm/jit/vasm-gen.h"
35 #include "hphp/runtime/vm/jit/vasm-instr.h"
36 #include "hphp/runtime/vm/jit/vasm-reg.h"
38 #include "hphp/util/asm-x64.h"
39 #include "hphp/util/abi-cxx.h"
40 #include "hphp/util/immed.h"
41 #include "hphp/util/low-ptr.h"
42 #include "hphp/util/ringbuffer.h"
43 #include "hphp/util/thread-local.h"
44 #include "hphp/util/trace.h"
46 namespace HPHP
{ namespace jit
{
48 ///////////////////////////////////////////////////////////////////////////////
54 ///////////////////////////////////////////////////////////////////////////////
56 void assertSFNonNegative(Vout
& v
, Vreg sf
) {
57 if (!RuntimeOption::EvalHHIRGenerateAsserts
) return;
58 ifThen(v
, CC_NGE
, sf
, [&] (Vout
& v
) { v
<< ud2
{}; });
61 ///////////////////////////////////////////////////////////////////////////////
65 ///////////////////////////////////////////////////////////////////////////////
67 void emitImmStoreq(Vout
& v
, Immed64 imm
, Vptr ref
) {
68 if (imm
.fits(sz::dword
)) {
69 v
<< storeqi
{imm
.l(), ref
};
71 v
<< store
{v
.cns(imm
.q()), ref
};
75 void emitLdLowPtr(Vout
& v
, Vptr mem
, Vreg reg
, size_t size
) {
78 } else if (size
== 4) {
79 v
<< loadzlq
{mem
, reg
};
85 void pack2(Vout
& v
, Vreg s0
, Vreg s1
, Vreg d0
) {
86 auto prep
= [&] (Vreg r
) {
87 if (VregDbl::allowable(r
)) return r
;
92 // s0 and s1 must be valid VregDbl registers; prep() takes care of it.
93 v
<< unpcklpd
{prep(s1
), prep(s0
), d0
}; // s0,s1 -> d0[0],d0[1]
96 Vreg
zeroExtendIfBool(Vout
& v
, const SSATmp
* src
, Vreg reg
) {
97 if (!src
->isA(TBool
)) return reg
;
99 // Zero-extend the bool from a byte to a quad.
100 auto extended
= v
.makeReg();
101 v
<< movzbq
{reg
, extended
};
105 ///////////////////////////////////////////////////////////////////////////////
107 void copyTV(Vout
& v
, Vloc src
, Vloc dst
, Type destType
) {
108 auto src_arity
= src
.numAllocated();
109 auto dst_arity
= dst
.numAllocated();
111 if (dst_arity
== 2) {
112 always_assert(src_arity
== 2);
113 v
<< copy2
{src
.reg(0), src
.reg(1), dst
.reg(0), dst
.reg(1)};
116 always_assert(dst_arity
== 1);
118 if (src_arity
== 2 && dst
.isFullSIMD()) {
119 pack2(v
, src
.reg(0), src
.reg(1), dst
.reg(0));
122 always_assert(src_arity
>= 1);
124 if (src_arity
== 2 && destType
<= TBool
) {
125 v
<< movtqb
{src
.reg(0), dst
.reg(0)};
127 v
<< copy
{src
.reg(0), dst
.reg(0)};
131 void emitAssertRefCount(Vout
& v
, Vreg data
) {
132 auto const sf
= v
.makeReg();
133 v
<< cmplim
{StaticValue
, data
[FAST_REFCOUNT_OFFSET
], sf
};
135 ifThen(v
, CC_NLE
, sf
, [&] (Vout
& v
) {
136 auto const sf
= v
.makeReg();
137 v
<< cmplim
{RefCountMaxRealistic
, data
[FAST_REFCOUNT_OFFSET
], sf
};
139 ifThen(v
, CC_NBE
, sf
, [&] (Vout
& v
) { v
<< ud2
{}; });
143 void emitIncRef(Vout
& v
, Vreg base
) {
144 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
145 emitAssertRefCount(v
, base
);
147 auto const sf
= v
.makeReg();
148 v
<< inclm
{base
[FAST_REFCOUNT_OFFSET
], sf
};
149 assertSFNonNegative(v
, sf
);
152 Vreg
emitDecRef(Vout
& v
, Vreg base
) {
153 auto const sf
= v
.makeReg();
154 v
<< declm
{base
[FAST_REFCOUNT_OFFSET
], sf
};
155 assertSFNonNegative(v
, sf
);
160 void emitIncRefWork(Vout
& v
, Vreg data
, Vreg type
) {
161 auto const sf
= v
.makeReg();
162 emitCmpTVType(v
, sf
, KindOfRefCountThreshold
, type
);
164 ifThen(v
, CC_G
, sf
, [&] (Vout
& v
) {
165 auto const sf2
= v
.makeReg();
167 v
<< cmplim
{0, data
[FAST_REFCOUNT_OFFSET
], sf2
};
168 ifThen(v
, CC_GE
, sf2
, [&] (Vout
& v
) { emitIncRef(v
, data
); });
172 void emitDecRefWorkObj(Vout
& v
, Vreg obj
) {
173 auto const shouldRelease
= v
.makeReg();
174 v
<< cmplim
{1, obj
[FAST_REFCOUNT_OFFSET
], shouldRelease
};
176 v
, CC_E
, shouldRelease
,
178 // Put fn inside vcall{} triggers a compiler internal error (gcc 4.4.7)
179 auto const fn
= CallSpec::method(&ObjectData::release
);
180 v
<< vcall
{fn
, v
.makeVcallArgs({{obj
}}), v
.makeTuple({})};
188 ///////////////////////////////////////////////////////////////////////////////
190 void emitCall(Vout
& v
, CallSpec target
, RegSet args
) {
191 switch (target
.kind()) {
192 case CallSpec::Kind::Direct
:
193 v
<< call
{static_cast<TCA
>(target
.address()), args
};
196 case CallSpec::Kind::Smashable
:
197 v
<< calls
{static_cast<TCA
>(target
.address()), args
};
200 case CallSpec::Kind::ArrayVirt
: {
201 auto const addr
= reinterpret_cast<intptr_t>(target
.arrayTable());
203 auto const arrkind
= v
.makeReg();
204 v
<< loadzbl
{rarg(0)[HeaderKindOffset
], arrkind
};
206 if (deltaFits(addr
, sz::dword
)) {
207 v
<< callm
{baseless(arrkind
* 8 + addr
), args
};
209 auto const base
= v
.makeReg();
210 v
<< ldimmq
{addr
, base
};
211 v
<< callm
{base
[arrkind
* 8], args
};
213 static_assert(sizeof(HeaderKind
) == 1, "");
216 case CallSpec::Kind::Destructor
: {
217 // this movzbq is only needed because callers aren't required to
218 // zero-extend the type.
219 auto zextType
= v
.makeReg();
220 v
<< movzbl
{target
.reg(), zextType
};
221 auto dtor_ptr
= lookupDestructor(v
, zextType
);
222 v
<< callm
{dtor_ptr
, args
};
225 case CallSpec::Kind::Stub
:
226 v
<< callstub
{target
.stubAddr(), args
};
232 Vptr
lookupDestructor(Vout
& v
, Vreg type
) {
233 auto const table
= reinterpret_cast<intptr_t>(g_destructors
);
234 always_assert_flog(deltaFits(table
, sz::dword
),
235 "Destructor function table is expected to be in the data "
236 "segment, with addresses less than 2^31"
238 auto index
= v
.makeReg();
239 auto indexl
= v
.makeReg();
240 v
<< shrli
{kShiftDataTypeToDestrIndex
, type
, indexl
, v
.makeReg()};
241 v
<< movzlq
{indexl
, index
};
242 return baseless(index
* 8 + safe_cast
<int>(table
));
245 ///////////////////////////////////////////////////////////////////////////////
247 Vreg
emitLdObjClass(Vout
& v
, Vreg obj
, Vreg d
) {
248 emitLdLowPtr(v
, obj
[ObjectData::getVMClassOffset()], d
,
249 sizeof(LowPtr
<Class
>));
253 Vreg
emitLdClsCctx(Vout
& v
, Vreg src
, Vreg dst
) {
254 v
<< decq
{src
, dst
, v
.makeReg()};
258 void emitCmpClass(Vout
& v
, Vreg sf
, const Class
* cls
, Vptr mem
) {
259 auto size
= sizeof(LowPtr
<Class
>);
261 v
<< cmpqm
{v
.cns(cls
), mem
, sf
};
262 } else if (size
== 4) {
263 auto const clsImm
= safe_cast
<uint32_t>(reinterpret_cast<intptr_t>(cls
));
264 v
<< cmplm
{v
.cns(clsImm
), mem
, sf
};
270 void emitCmpClass(Vout
& v
, Vreg sf
, Vreg reg
, Vptr mem
) {
271 auto size
= sizeof(LowPtr
<Class
>);
273 v
<< cmpqm
{reg
, mem
, sf
};
274 } else if (size
== 4) {
275 auto lowCls
= v
.makeReg();
276 v
<< movtql
{reg
, lowCls
};
277 v
<< cmplm
{lowCls
, mem
, sf
};
283 void emitCmpClass(Vout
& v
, Vreg sf
, Vreg reg1
, Vreg reg2
) {
284 auto size
= sizeof(LowPtr
<Class
>);
286 v
<< cmpq
{reg1
, reg2
, sf
};
287 } else if (size
== 4) {
288 auto const l1
= v
.makeReg();
289 auto const l2
= v
.makeReg();
290 v
<< movtql
{reg1
, l1
};
291 v
<< movtql
{reg2
, l2
};
292 v
<< cmpl
{l1
, l2
, sf
};
298 void emitCmpVecLen(Vout
& v
, Vreg sf
, Immed val
, Vptr mem
) {
299 auto const size
= sizeof(Class::veclen_t
);
301 v
<< cmpwim
{val
, mem
, sf
};
302 } else if (size
== 4) {
303 v
<< cmplim
{val
, mem
, sf
};
309 ///////////////////////////////////////////////////////////////////////////////
311 void emitEagerSyncPoint(Vout
& v
, PC pc
, Vreg rds
, Vreg vmfp
, Vreg vmsp
) {
312 v
<< store
{vmfp
, rds
[rds::kVmfpOff
]};
313 v
<< store
{vmsp
, rds
[rds::kVmspOff
]};
314 emitImmStoreq(v
, intptr_t(pc
), rds
[rds::kVmpcOff
]);
317 void emitTransCounterInc(Vout
& v
) {
318 if (!mcg
->tx().isTransDBEnabled()) return;
319 auto t
= v
.cns(mcg
->tx().getTransCounterAddr());
320 v
<< incqmlock
{*t
, v
.makeReg()};
323 void emitRB(Vout
& v
, Trace::RingBufferType t
, const char* msg
) {
324 if (!Trace::moduleEnabled(Trace::ringbuffer
, 1)) return;
325 v
<< vcall
{CallSpec::direct(Trace::ringbufferMsg
),
326 v
.makeVcallArgs({{v
.cns(msg
), v
.cns(strlen(msg
)), v
.cns(t
)}}),
330 void emitIncStat(Vout
& v
, Stats::StatCounter stat
, int n
, bool force
) {
331 if (!force
&& !Stats::enabled()) return;
332 intptr_t disp
= uintptr_t(&Stats::tl_counters
[stat
]) - tlsBase();
333 v
<< addqim
{n
, Vptr
{baseless(disp
), Vptr::FS
}, v
.makeReg()};
336 ///////////////////////////////////////////////////////////////////////////////