Avoid register width mismatches when generating vasm
[hiphop-php.git] / hphp / runtime / vm / jit / code-gen-helpers.cpp
blob3bc1608466eb9388e64aee55515f77196140952c
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 ///////////////////////////////////////////////////////////////////////////////
50 TRACE_SET_MOD(hhir);
52 namespace {
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};
70 } else {
71 v << store{v.cns(imm.q()), ref};
75 void emitLdLowPtr(Vout& v, Vptr mem, Vreg reg, size_t size) {
76 if (size == 8) {
77 v << load{mem, reg};
78 } else if (size == 4) {
79 v << loadzlq{mem, reg};
80 } else {
81 not_implemented();
85 void pack2(Vout& v, Vreg s0, Vreg s1, Vreg d0) {
86 auto prep = [&] (Vreg r) {
87 if (VregDbl::allowable(r)) return r;
88 auto t = v.makeReg();
89 v << copy{r, t};
90 return t;
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};
102 return 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)};
114 return;
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));
120 return;
122 always_assert(src_arity >= 1);
124 if (src_arity == 2 && destType <= TBool) {
125 v << movtqb{src.reg(0), dst.reg(0)};
126 } else {
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);
157 return sf;
160 void emitIncRefWork(Vout& v, Vreg data, Vreg type) {
161 auto const sf = v.makeReg();
162 emitCmpTVType(v, sf, KindOfRefCountThreshold, type);
163 // ifRefCountType
164 ifThen(v, CC_G, sf, [&] (Vout& v) {
165 auto const sf2 = v.makeReg();
166 // ifNonStatic
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};
175 ifThenElse(
176 v, CC_E, shouldRelease,
177 [&] (Vout& v) {
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({})};
182 [&] (Vout& v) {
183 emitDecRef(v, obj);
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};
194 return;
196 case CallSpec::Kind::Smashable:
197 v << calls{static_cast<TCA>(target.address()), args};
198 return;
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};
208 } else {
209 auto const base = v.makeReg();
210 v << ldimmq{addr, base};
211 v << callm{base[arrkind * 8], args};
213 static_assert(sizeof(HeaderKind) == 1, "");
214 } return;
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};
223 } return;
225 case CallSpec::Kind::Stub:
226 v << callstub{target.stubAddr(), args};
227 return;
229 not_reached();
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>));
250 return d;
253 Vreg emitLdClsCctx(Vout& v, Vreg src, Vreg dst) {
254 v << decq{src, dst, v.makeReg()};
255 return dst;
258 void emitCmpClass(Vout& v, Vreg sf, const Class* cls, Vptr mem) {
259 auto size = sizeof(LowPtr<Class>);
260 if (size == 8) {
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};
265 } else {
266 not_implemented();
270 void emitCmpClass(Vout& v, Vreg sf, Vreg reg, Vptr mem) {
271 auto size = sizeof(LowPtr<Class>);
272 if (size == 8) {
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};
278 } else {
279 not_implemented();
283 void emitCmpClass(Vout& v, Vreg sf, Vreg reg1, Vreg reg2) {
284 auto size = sizeof(LowPtr<Class>);
285 if (size == 8) {
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};
293 } else {
294 not_implemented();
298 void emitCmpVecLen(Vout& v, Vreg sf, Immed val, Vptr mem) {
299 auto const size = sizeof(Class::veclen_t);
300 if (size == 2) {
301 v << cmpwim{val, mem, sf};
302 } else if (size == 4) {
303 v << cmplim{val, mem, sf};
304 } else {
305 not_implemented();
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)}}),
327 v.makeTuple({})};
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 ///////////////////////////////////////////////////////////////////////////////