2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 "runtime/vm/translator/hopt/codegen.h"
21 #include "folly/ScopeGuard.h"
22 #include "util/trace.h"
23 #include "util/util.h"
25 #include "runtime/base/array/hphp_array.h"
26 #include "runtime/base/comparisons.h"
27 #include "runtime/base/complex_types.h"
28 #include "runtime/base/runtime_option.h"
29 #include "runtime/base/string_data.h"
30 #include "runtime/base/types.h"
31 #include "runtime/ext/ext_closure.h"
32 #include "runtime/ext/ext_continuation.h"
33 #include "runtime/ext/ext_collections.h"
34 #include "runtime/vm/bytecode.h"
35 #include "runtime/vm/runtime.h"
36 #include "runtime/base/stats.h"
37 #include "runtime/vm/translator/targetcache.h"
38 #include "runtime/vm/translator/translator-inline.h"
39 #include "runtime/vm/translator/translator-x64.h"
40 #include "runtime/vm/translator/translator-x64-internal.h"
41 #include "runtime/vm/translator/translator.h"
42 #include "runtime/vm/translator/types.h"
43 #include "runtime/vm/translator/x64-util.h"
44 #include "runtime/vm/translator/hopt/ir.h"
45 #include "runtime/vm/translator/hopt/linearscan.h"
46 #include "runtime/vm/translator/hopt/nativecalls.h"
47 #include "runtime/vm/translator/hopt/print.h"
49 using HPHP::VM::Transl::TCA
;
50 using namespace HPHP::VM::Transl::TargetCache
;
58 //////////////////////////////////////////////////////////////////////
61 using namespace Transl::reg
;
63 static const HPHP::Trace::Module TRACEMOD
= HPHP::Trace::hhir
;
66 * It's not normally ok to directly use tracelet abi registers in
67 * codegen, unless you're directly dealing with an instruction that
68 * does near-end-of-tracelet glue. (Or also we sometimes use them
69 * just for some static_assertions relating to calls to helpers from
70 * tx64 that hardcode these registers.)
75 const size_t kTypeShiftBits
= (offsetof(TypedValue
, m_type
) % 8) * 8;
77 // left shift an immediate DataType, for type, to the correct position
78 // within one of the registers used to pass a TypedValue by value.
79 uint64_t toDataTypeForCall(Type type
) {
80 return uint64_t(type
.toDataType()) << kTypeShiftBits
;
83 int64_t spillSlotsToSize(int n
) {
84 return n
* sizeof(int64_t);
87 void cgPunt(const char* file
, int line
, const char* func
) {
88 if (dumpIREnabled()) {
89 HPHP::Trace::trace("--------- CG_PUNT %s %d %s\n", file
, line
, func
);
91 throw FailedCodeGen(file
, line
, func
);
94 #define CG_PUNT(instr) cgPunt(__FILE__, __LINE__, #instr)
102 enum Kind
{ Move
, Xchg
};
104 MoveInfo(Kind kind
, int reg1
, int reg2
):
105 m_kind(kind
), m_reg1(reg1
), m_reg2(reg2
) {}
108 PhysReg m_reg1
, m_reg2
;
112 void doRegMoves(int (&moves
)[N
], int rTmp
,
113 std::vector
<MoveInfo
>& howTo
) {
114 assert(howTo
.empty());
118 // Iterate over the nodes filling in outDegree[] and cycles[] as we go
121 for (int node
= 0; node
< N
; ++node
) {
122 // If a node's source is itself, its a nop
123 if (moves
[node
] == node
) moves
[node
] = -1;
124 if (node
== rTmp
&& moves
[node
] >= 0) {
125 // ERROR: rTmp cannot be referenced in moves[].
132 for (int startNode
= 0; startNode
< N
; ++startNode
) {
133 // If startNode has not been visited yet, begin walking
134 // a path from start node
135 if (index
[startNode
] < 0) {
136 int node
= startNode
;
138 index
[node
] = nextIndex
++;
139 if (moves
[node
] >= 0) {
140 int nextNode
= moves
[node
];
141 ++outDegree
[nextNode
];
142 if (index
[nextNode
] < 0) {
143 // If there is an edge from v to nextNode and nextNode has not been
144 // visited, extend the current path to include nextNode and recurse
148 // There is an edge from v to nextNode but nextNode has already been
149 // visited, check if nextNode is on the current path
150 if (index
[nextNode
] >= index
[startNode
]) {
151 // nextNode is on the current path so we've found a cycle
152 int length
= nextIndex
- index
[nextNode
];
153 CycleInfo ci
= { nextNode
, length
};
154 cycles
[numCycles
] = ci
;
161 // Handle all moves that aren't part of a cycle
165 for (int node
= 0; node
< N
; ++node
) {
166 if (outDegree
[node
] == 0) {
171 for (int i
= 0; i
< qBack
; ++i
) {
173 if (moves
[node
] >= 0) {
174 int nextNode
= moves
[node
];
175 howTo
.push_back(MoveInfo(MoveInfo::Move
, nextNode
, node
));
176 --outDegree
[nextNode
];
177 if (outDegree
[nextNode
] == 0) {
184 // Deal with any cycles we encountered
185 for (int i
= 0; i
< numCycles
; ++i
) {
186 if (cycles
[i
].length
== 2) {
187 int v
= cycles
[i
].node
;
189 howTo
.push_back(MoveInfo(MoveInfo::Xchg
, w
, v
));
190 } else if (cycles
[i
].length
== 3) {
191 int v
= cycles
[i
].node
;
193 howTo
.push_back(MoveInfo(MoveInfo::Xchg
, w
, v
));
195 howTo
.push_back(MoveInfo(MoveInfo::Xchg
, x
, w
));
197 int v
= cycles
[i
].node
;
198 howTo
.push_back(MoveInfo(MoveInfo::Move
, v
, rTmp
));
202 howTo
.push_back(MoveInfo(MoveInfo::Move
, x
, w
));
206 howTo
.push_back(MoveInfo(MoveInfo::Move
, rTmp
, w
));
211 const char* getContextName(Class
* ctx
) {
212 return ctx
? ctx
->name()->data() : ":anonymous:";
215 } // unnamed namespace
217 //////////////////////////////////////////////////////////////////////
219 ArgDesc::ArgDesc(SSATmp
* tmp
, bool val
) : m_imm(-1), m_zeroExtend(false),
221 if (tmp
->type() == Type::None
) {
226 if (tmp
->inst()->op() == DefConst
) {
227 m_srcReg
= InvalidReg
;
229 m_imm
= tmp
->getValBits();
231 m_imm
= toDataTypeForCall(tmp
->type());
236 if (tmp
->type().isNull()) {
237 m_srcReg
= InvalidReg
;
241 m_imm
= toDataTypeForCall(tmp
->type());
246 if (val
|| tmp
->numNeededRegs() > 1) {
247 auto reg
= tmp
->getReg(val
? 0 : 1);
248 assert(reg
!= InvalidReg
);
251 // If val is false then we're passing tmp's type. TypeReg lets
252 // CodeGenerator know that the value might require some massaging
253 // to be in the right format for the call.
254 m_kind
= val
? Reg
: TypeReg
;
255 // zero extend any boolean value that we pass to the helper in case
256 // the helper expects it (e.g., as TypedValue)
257 if (val
&& tmp
->isA(Type::Bool
)) m_zeroExtend
= true;
261 m_srcReg
= InvalidReg
;
262 m_imm
= toDataTypeForCall(tmp
->type());
266 const Func
* CodeGenerator::getCurFunc() const {
267 always_assert(m_state
.lastMarker
&&
268 "We shouldn't be looking for a func when we have no marker");
269 return m_state
.lastMarker
->func
;
272 Address
CodeGenerator::cgInst(IRInstruction
* inst
) {
273 Opcode opc
= inst
->op();
274 auto const start
= m_as
.code
.frontier
;
276 #define O(name, dsts, srcs, flags) \
277 case name: cg ## name (inst); \
278 return m_as.code.frontier == start ? nullptr : start;
288 #define NOOP_OPCODE(opcode) \
289 void CodeGenerator::cg##opcode(IRInstruction*) {}
291 #define PUNT_OPCODE(opcode) \
292 void CodeGenerator::cg##opcode(IRInstruction*) { CG_PUNT(opcode); }
294 #define CALL_OPCODE(opcode) \
295 void CodeGenerator::cg##opcode(IRInstruction* i) { cgCallNative(i); }
297 #define CALL_STK_OPCODE(opcode) \
298 CALL_OPCODE(opcode) \
299 CALL_OPCODE(opcode ## Stk)
303 NOOP_OPCODE(DefConst
)
307 NOOP_OPCODE(AssertLoc
)
308 NOOP_OPCODE(OverrideLoc
)
309 NOOP_OPCODE(AssertStk
)
311 NOOP_OPCODE(DefLabel
)
312 NOOP_OPCODE(ExceptionBarrier
)
314 CALL_OPCODE(AddElemStrKey
)
315 CALL_OPCODE(AddElemIntKey
)
316 CALL_OPCODE(AddNewElem
)
317 CALL_OPCODE(ArrayAdd
)
320 CALL_OPCODE(ConvBoolToArr
);
321 CALL_OPCODE(ConvDblToArr
);
322 CALL_OPCODE(ConvIntToArr
);
323 CALL_OPCODE(ConvObjToArr
);
324 CALL_OPCODE(ConvStrToArr
);
325 CALL_OPCODE(ConvCellToArr
);
327 CALL_OPCODE(ConvArrToBool
);
328 CALL_OPCODE(ConvStrToBool
);
329 CALL_OPCODE(ConvCellToBool
);
331 CALL_OPCODE(ConvArrToDbl
);
332 CALL_OPCODE(ConvObjToDbl
);
333 CALL_OPCODE(ConvStrToDbl
);
334 CALL_OPCODE(ConvCellToDbl
);
336 CALL_OPCODE(ConvArrToInt
);
337 CALL_OPCODE(ConvDblToInt
);
338 CALL_OPCODE(ConvObjToInt
);
339 CALL_OPCODE(ConvStrToInt
);
340 CALL_OPCODE(ConvCellToInt
);
342 CALL_OPCODE(ConvCellToObj
);
344 CALL_OPCODE(ConvDblToStr
);
345 CALL_OPCODE(ConvIntToStr
);
346 CALL_OPCODE(ConvObjToStr
);
347 CALL_OPCODE(ConvCellToStr
);
349 CALL_OPCODE(CreateCont
)
350 CALL_OPCODE(FillContLocals
)
351 CALL_OPCODE(NewArray
)
352 CALL_OPCODE(NewTuple
)
354 CALL_OPCODE(LdClsCtor
);
355 CALL_OPCODE(CreateCl
)
356 CALL_OPCODE(PrintStr
)
357 CALL_OPCODE(PrintInt
)
358 CALL_OPCODE(PrintBool
)
359 CALL_OPCODE(DbgAssertPtr
)
360 CALL_OPCODE(LdSwitchDblIndex
)
361 CALL_OPCODE(LdSwitchStrIndex
)
362 CALL_OPCODE(LdSwitchObjIndex
)
363 CALL_OPCODE(VerifyParamCallable
)
364 CALL_OPCODE(VerifyParamFail
)
365 CALL_OPCODE(RaiseUninitLoc
)
366 CALL_OPCODE(WarnNonObjProp
)
367 CALL_OPCODE(ThrowNonObjProp
)
368 CALL_OPCODE(RaiseUndefProp
)
369 CALL_OPCODE(RaiseError
)
370 CALL_OPCODE(RaiseWarning
)
371 CALL_OPCODE(IncStatGrouped
)
372 CALL_OPCODE(StaticLocInit
)
373 CALL_OPCODE(StaticLocInitCached
)
376 // Vector instruction helpers
379 CALL_STK_OPCODE(PropDX
)
380 CALL_OPCODE(CGetProp
)
381 CALL_STK_OPCODE(VGetProp
)
382 CALL_STK_OPCODE(BindProp
)
383 CALL_STK_OPCODE(SetProp
)
384 CALL_OPCODE(UnsetProp
)
385 CALL_STK_OPCODE(SetOpProp
)
386 CALL_STK_OPCODE(IncDecProp
)
387 CALL_OPCODE(EmptyProp
)
388 CALL_OPCODE(IssetProp
)
390 CALL_STK_OPCODE(ElemDX
)
391 CALL_STK_OPCODE(ElemUX
)
392 CALL_OPCODE(ArrayGet
)
393 CALL_OPCODE(CGetElem
)
394 CALL_STK_OPCODE(VGetElem
)
395 CALL_STK_OPCODE(BindElem
)
396 CALL_OPCODE(ArraySet
)
397 CALL_OPCODE(ArraySetRef
)
398 CALL_STK_OPCODE(SetElem
)
399 CALL_STK_OPCODE(UnsetElem
)
400 CALL_STK_OPCODE(SetOpElem
)
401 CALL_STK_OPCODE(IncDecElem
)
402 CALL_STK_OPCODE(SetNewElem
)
403 CALL_STK_OPCODE(BindNewElem
)
404 CALL_OPCODE(ArrayIsset
)
405 CALL_OPCODE(IssetElem
)
406 CALL_OPCODE(EmptyElem
)
411 // Thread chain of patch locations using the 4 byte space in each jmp/jcc
412 void prependPatchAddr(CodegenState
& state
, Block
* block
, TCA patchAddr
) {
413 auto &patches
= state
.patches
;
414 ssize_t diff
= patches
[block
] ? (patchAddr
- (TCA
)patches
[block
]) : 0;
415 assert(deltaFits(diff
, sz::dword
));
416 *(int32_t*)(patchAddr
) = (int32_t)diff
;
417 patches
[block
] = patchAddr
;
420 Address
CodeGenerator::emitFwdJcc(Asm
& a
, ConditionCode cc
, Block
* target
) {
422 Address start
= a
.code
.frontier
;
423 a
.jcc(cc
, a
.code
.frontier
);
424 TCA immPtr
= a
.code
.frontier
- 4;
425 prependPatchAddr(m_state
, target
, immPtr
);
429 Address
CodeGenerator::emitFwdJmp(Asm
& a
, Block
* target
, CodegenState
& state
) {
430 Address start
= a
.code
.frontier
;
431 a
.jmp(a
.code
.frontier
);
432 TCA immPtr
= a
.code
.frontier
- 4;
433 prependPatchAddr(state
, target
, immPtr
);
437 Address
CodeGenerator::emitFwdJmp(Asm
& a
, Block
* target
) {
438 return emitFwdJmp(a
, target
, m_state
);
441 Address
CodeGenerator::emitFwdJcc(ConditionCode cc
, Block
* target
) {
442 return emitFwdJcc(m_as
, cc
, target
);
445 Address
CodeGenerator::emitFwdJmp(Block
* target
) {
446 return emitFwdJmp(m_as
, target
);
449 // Patch with service request REQ_BIND_JMPCC_FIRST/SECOND
450 Address
CodeGenerator::emitSmashableFwdJccAtEnd(ConditionCode cc
, Block
* target
,
451 IRInstruction
* toSmash
) {
452 Address start
= m_as
.code
.frontier
;
454 m_tx64
->prepareForSmash(m_as
, TranslatorX64::kJmpLen
+
455 TranslatorX64::kJmpccLen
);
456 Address tcaJcc
= emitFwdJcc(cc
, target
);
458 toSmash
->setTCA(tcaJcc
);
460 emitFwdJcc(cc
, target
);
465 void CodeGenerator::emitJccDirectExit(IRInstruction
* inst
,
467 if (cc
== CC_None
) return;
468 auto* toSmash
= inst
->getTCA() == kIRDirectJccJmpActive
? inst
: nullptr;
469 emitSmashableFwdJccAtEnd(cc
, inst
->getTaken(), toSmash
);
472 // Patch with service request REQ_BIND_JCC
473 Address
CodeGenerator::emitSmashableFwdJcc(ConditionCode cc
, Block
* target
,
474 IRInstruction
* toSmash
) {
475 Address start
= m_as
.code
.frontier
;
478 m_tx64
->prepareForSmash(m_as
, TranslatorX64::kJmpccLen
);
479 Address tcaJcc
= emitFwdJcc(cc
, target
);
480 toSmash
->setTCA(tcaJcc
);
484 void emitLoadImm(CodeGenerator::Asm
& as
, int64_t val
, PhysReg dstReg
) {
485 as
.emitImmReg(val
, dstReg
);
489 emitMovRegReg(CodeGenerator::Asm
& as
, PhysReg srcReg
, PhysReg dstReg
) {
490 if (srcReg
!= dstReg
) as
.movq(srcReg
, dstReg
);
493 static void emitLea(CodeGenerator::Asm
& as
, MemoryRef mr
, PhysReg dst
) {
494 if (dst
== InvalidReg
) return;
495 if (mr
.r
.disp
== 0) {
496 emitMovRegReg(as
, mr
.r
.base
, dst
);
502 void shuffle2(CodeGenerator::Asm
& a
,
503 PhysReg s0
, PhysReg s1
, PhysReg d0
, PhysReg d1
) {
505 if (d0
== s1
&& d1
!= InvalidReg
) {
510 a
. movq (s1
, d1
); // save s1 first; d1 != s0
514 if (d0
!= InvalidReg
) emitMovRegReg(a
, s0
, d0
); // d0 != s1
515 if (d1
!= InvalidReg
) emitMovRegReg(a
, s1
, d1
);
519 static void signExtendBool(X64Assembler
& as
, const SSATmp
* src
) {
520 auto reg
= src
->getReg();
521 if (reg
!= InvalidReg
) {
522 // sign-extend the bool from a byte to a quad
523 as
.movsbq(rbyte(reg
), r64(reg
));
527 static void zeroExtendIfBool(X64Assembler
& as
, const SSATmp
* src
) {
528 if (src
->isA(Type::Bool
)) {
529 auto reg
= src
->getReg();
530 if (reg
!= InvalidReg
) {
531 // zero-extend the bool from a byte to a quad
532 // note: movzbl actually extends the value to 64 bits.
533 as
.movzbl(rbyte(reg
), r32(reg
));
538 static void prepUnaryXmmOp(X64Assembler
& a
, const SSATmp
* ssa
, RegXMM xmm
) {
539 RegNumber
src(ssa
->getReg());
540 if (ssa
->getReg() == InvalidReg
) {
542 assert(ssa
->isConst());
543 a
.mov_imm64_reg(ssa
->getValBits(), rScratch
);
545 if (ssa
->isA(Type::Int
| Type::Bool
)) {
546 // Expand non-const bools to 64-bit.
547 // Consts are already moved into src as 64-bit values above.
548 if (!ssa
->isConst()) zeroExtendIfBool(a
, ssa
);
549 // cvtsi2sd doesn't modify the high bits of its target, which can
550 // cause false dependencies to prevent register renaming from kicking
551 // in. Break the dependency chain by zeroing out the destination reg.
552 a
. pxor_xmm_xmm(xmm
, xmm
);
553 a
. cvtsi2sd_reg64_xmm(src
, xmm
);
555 a
. mov_reg64_xmm(src
, xmm
);
559 static void prepBinaryXmmOp(X64Assembler
& a
, const SSATmp
* left
,
560 const SSATmp
* right
) {
561 prepUnaryXmmOp(a
, left
, xmm0
);
562 prepUnaryXmmOp(a
, right
, xmm1
);
565 static void doubleCmp(X64Assembler
& a
, RegXMM xmm0
, RegXMM xmm1
) {
566 a
. ucomisd_xmm_xmm(xmm0
, xmm1
);
569 // PF means the doubles were unordered. We treat this as !equal, so
571 a
. or_imm32_reg64(1, rScratch
);
575 void CodeGenerator::cgJcc(IRInstruction
* inst
) {
576 SSATmp
* src1
= inst
->getSrc(0);
577 SSATmp
* src2
= inst
->getSrc(1);
578 Opcode opc
= inst
->op();
579 ConditionCode cc
= queryJmpToCC(opc
);
580 Type src1Type
= src1
->type();
581 Type src2Type
= src2
->type();
583 // can't generate CMP instructions correctly for anything that isn't
584 // a bool or a numeric, and we can't mix bool/numerics because
585 // -1 == true in PHP, but not in HHIR binary representation
586 if (!((src1Type
== Type::Int
&& src2Type
== Type::Int
) ||
587 ((src1Type
== Type::Int
|| src1Type
== Type::Dbl
) &&
588 (src2Type
== Type::Int
|| src2Type
== Type::Dbl
)) ||
589 (src1Type
== Type::Bool
&& src2Type
== Type::Bool
) ||
590 (src1Type
== Type::Cls
&& src2Type
== Type::Cls
))) {
593 if (src1Type
== Type::Dbl
|| src2Type
== Type::Dbl
) {
594 prepBinaryXmmOp(m_as
, src1
, src2
);
595 doubleCmp(m_as
, xmm0
, xmm1
);
597 if (src1Type
== Type::Cls
&& src2Type
== Type::Cls
) {
598 assert(opc
== JmpSame
|| opc
== JmpNSame
);
600 auto srcReg1
= src1
->getReg();
601 auto srcReg2
= src2
->getReg();
603 // Note: when both src1 and src2 are constants, we should transform the
604 // branch into an unconditional jump earlier in the IR.
605 if (src1
->isConst()) {
606 // TODO: use compare with immediate or make sure simplifier
607 // canonicalizes this so that constant is src2
609 m_as
. mov_imm64_reg(src1
->getValRawInt(), srcReg1
);
611 if (src2
->isConst()) {
612 if (src1Type
.subtypeOf(Type::Bool
)) {
613 m_as
. cmpb (src2
->getValRawInt(), Reg8(int(srcReg1
)));
615 m_as
. cmp_imm64_reg64(src2
->getValRawInt(), srcReg1
);
618 // Note the reverse syntax in the assembler.
619 // This cmp will compute srcReg1 - srcReg2
620 if (src1Type
.subtypeOf(Type::Bool
)) {
621 m_as
. cmpb (Reg8(int(srcReg2
)), Reg8(int(srcReg1
)));
623 m_as
. cmp_reg64_reg64(srcReg2
, srcReg1
);
628 emitJccDirectExit(inst
, cc
);
631 void CodeGenerator::cgJmpGt (IRInstruction
* inst
) { cgJcc(inst
); }
632 void CodeGenerator::cgJmpGte (IRInstruction
* inst
) { cgJcc(inst
); }
633 void CodeGenerator::cgJmpLt (IRInstruction
* inst
) { cgJcc(inst
); }
634 void CodeGenerator::cgJmpLte (IRInstruction
* inst
) { cgJcc(inst
); }
635 void CodeGenerator::cgJmpEq (IRInstruction
* inst
) { cgJcc(inst
); }
636 void CodeGenerator::cgJmpNeq (IRInstruction
* inst
) { cgJcc(inst
); }
637 void CodeGenerator::cgJmpSame (IRInstruction
* inst
) { cgJcc(inst
); }
638 void CodeGenerator::cgJmpNSame(IRInstruction
* inst
) { cgJcc(inst
); }
641 * Once the arg sources and dests are all assigned; emit moves and exchanges
642 * to put all the args in desired registers. In addition to moves and
643 * exchanges, shuffleArgs also handles adding lea-offsets for dest registers
644 * (dest = src + lea-offset) and zero extending bools (dest = zeroExtend(src)).
646 typedef Transl::X64Assembler Asm
;
647 static void shuffleArgs(Asm
& a
, ArgGroup
& args
) {
648 // First schedule arg moves
649 for (size_t i
= 0; i
< args
.size(); ++i
) {
650 // We don't support memory-to-register moves currently.
651 assert(args
[i
].getKind() == ArgDesc::Reg
||
652 args
[i
].getKind() == ArgDesc::TypeReg
||
653 args
[i
].getKind() == ArgDesc::Imm
||
654 args
[i
].getKind() == ArgDesc::Addr
||
655 args
[i
].getKind() == ArgDesc::None
);
657 // Handle register-to-register moves.
658 int moves
[kNumX64Regs
];
659 ArgDesc
* argDescs
[kNumX64Regs
];
660 memset(moves
, -1, sizeof moves
);
661 memset(argDescs
, 0, sizeof argDescs
);
662 for (size_t i
= 0; i
< args
.size(); ++i
) {
663 auto kind
= args
[i
].getKind();
664 if (!(kind
== ArgDesc::Reg
||
665 kind
== ArgDesc::Addr
||
666 kind
== ArgDesc::TypeReg
)) {
669 auto dstReg
= args
[i
].getDstReg();
670 auto srcReg
= args
[i
].getSrcReg();
671 if (dstReg
!= srcReg
) {
672 moves
[int(dstReg
)] = int(srcReg
);
673 argDescs
[int(dstReg
)] = &args
[i
];
676 std::vector
<MoveInfo
> howTo
;
677 doRegMoves(moves
, int(reg::rScratch
), howTo
);
678 for (size_t i
= 0; i
< howTo
.size(); ++i
) {
679 if (howTo
[i
].m_kind
== MoveInfo::Move
) {
680 if (howTo
[i
].m_reg2
== reg::rScratch
) {
681 a
. movq (howTo
[i
].m_reg1
, howTo
[i
].m_reg2
);
683 ArgDesc
* argDesc
= argDescs
[int(howTo
[i
].m_reg2
)];
684 ArgDesc::Kind kind
= argDesc
->getKind();
685 if (kind
== ArgDesc::Reg
|| kind
== ArgDesc::TypeReg
) {
686 if (argDesc
->isZeroExtend()) {
687 a
. movzbl (rbyte(howTo
[i
].m_reg1
), r32(howTo
[i
].m_reg2
));
689 a
. movq (howTo
[i
].m_reg1
, howTo
[i
].m_reg2
);
692 assert(kind
== ArgDesc::Addr
);
693 a
. lea (howTo
[i
].m_reg1
[argDesc
->getImm().q()],
696 if (kind
!= ArgDesc::TypeReg
) {
701 a
. xchgq (howTo
[i
].m_reg1
, howTo
[i
].m_reg2
);
704 // Handle const-to-register moves, type shifting,
705 // load-effective address and zero extending for bools.
706 // Ignore args that have been handled by the
708 for (size_t i
= 0; i
< args
.size(); ++i
) {
709 if (!args
[i
].done()) {
710 ArgDesc::Kind kind
= args
[i
].getKind();
711 PhysReg dst
= args
[i
].getDstReg();
712 if (kind
== ArgDesc::Imm
) {
713 emitLoadImm(a
, args
[i
].getImm().q(), dst
);
714 } else if (kind
== ArgDesc::TypeReg
) {
715 a
. shlq (kTypeShiftBits
, dst
);
716 } else if (kind
== ArgDesc::Addr
) {
717 a
. addq (args
[i
].getImm(), dst
);
718 } else if (args
[i
].isZeroExtend()) {
719 a
. movzbl (rbyte(dst
), r32(dst
));
720 } else if (RuntimeOption::EvalHHIRGenerateAsserts
&&
721 kind
== ArgDesc::None
) {
722 emitLoadImm(a
, 0xbadbadbadbadbad, dst
);
728 void CodeGenerator::cgCallNative(Asm
& a
, IRInstruction
* inst
) {
729 using namespace NativeCalls
;
730 Opcode opc
= inst
->op();
731 always_assert(CallMap::hasInfo(opc
));
733 const CallInfo
& info
= CallMap::getInfo(opc
);
735 for (auto const& arg
: info
.args
) {
736 SSATmp
* src
= inst
->getSrc(arg
.srcIdx
);
742 argGroup
.typedValue(src
);
745 argGroup
.vectorKeyS(src
);
748 argGroup
.vectorKeyIS(src
);
751 always_assert(0 && "We can't generate a native call for this");
757 switch (info
.func
.type
) {
759 addr
= info
.func
.ptr
;
762 addr
= inst
->getSrc(info
.func
.srcIdx
)->getValTCA();
767 info
.dest
!= DestType::None
? inst
->getDst(0) : nullptr,
773 void CodeGenerator::cgCallHelper(Asm
& a
,
779 PhysReg dstReg0
= InvalidReg
;
780 PhysReg dstReg1
= InvalidReg
;
782 dstReg0
= dst
->getReg(0);
783 dstReg1
= dst
->getReg(1);
785 return cgCallHelper(a
, Transl::Call(addr
), dstReg0
, dstReg1
, sync
, args
,
789 void CodeGenerator::cgCallHelper(Asm
& a
,
795 cgCallHelper(a
, Transl::Call(addr
), dstReg
, InvalidReg
, sync
, args
, destType
);
798 void CodeGenerator::cgCallHelper(Asm
& a
,
799 const Transl::Call
& call
,
804 cgCallHelper(a
, call
, dstReg
, InvalidReg
, sync
, args
, destType
);
807 void CodeGenerator::cgCallHelper(Asm
& a
,
808 const Transl::Call
& call
,
814 cgCallHelper(a
, call
, dstReg0
, dstReg1
, sync
, args
,
815 m_state
.liveRegs
[m_curInst
], destType
);
818 void CodeGenerator::cgCallHelper(Asm
& a
,
819 const Transl::Call
& call
,
826 assert(int(args
.size()) <= kNumRegisterArgs
);
827 assert(m_curInst
->isNative());
829 // Save the register that are live at the point after this IR instruction.
830 // However, don't save the destination registers that will be overwritten
832 toSave
= toSave
& kCallerSaved
;
833 assert((toSave
& RegSet().add(dstReg0
).add(dstReg1
)).empty());
834 PhysRegSaverParity
<1> regSaver(a
, toSave
);
836 // Assign registers to the arguments
837 for (size_t i
= 0; i
< args
.size(); i
++) {
838 args
[i
].setDstReg(argNumToRegName
[i
]);
841 shuffleArgs(a
, args
);
843 // do the call; may use a trampoline
844 m_tx64
->emitCall(a
, call
);
846 if (sync
!= kNoSyncPoint
) {
847 recordSyncPoint(a
, sync
);
850 // copy the call result to the destination register(s)
851 if (destType
== DestType::TV
) {
852 // rax contains m_type and m_aux but we're expecting just the
853 // type in the lower bits, so shift the type result register.
854 auto rval
= packed_tv
? reg::rdx
: reg::rax
;
855 auto rtyp
= packed_tv
? reg::rax
: reg::rdx
;
856 if (kTypeShiftBits
> 0) a
.shrq(kTypeShiftBits
, rtyp
);
857 shuffle2(a
, rval
, rtyp
, dstReg0
, dstReg1
);
858 } else if (destType
== DestType::SSA
) {
859 // copy the single-register result to dstReg0
860 assert(dstReg1
== InvalidReg
);
861 if (dstReg0
!= InvalidReg
) emitMovRegReg(a
, reg::rax
, dstReg0
);
863 // void return type, no registers have values
864 assert(dstReg0
== InvalidReg
&& dstReg1
== InvalidReg
);
868 void CodeGenerator::cgMov(IRInstruction
* inst
) {
869 assert(!inst
->getSrc(0)->hasReg(1)); // TODO: t2082361: handle Gen & Cell
870 SSATmp
* dst
= inst
->getDst();
871 SSATmp
* src
= inst
->getSrc(0);
872 auto dstReg
= dst
->getReg();
873 if (!src
->hasReg(0)) {
874 assert(src
->isConst());
875 if (src
->type() == Type::Bool
) {
876 emitLoadImm(m_as
, (int64_t)src
->getValBool(), dstReg
);
878 emitLoadImm(m_as
, src
->getValRawInt(), dstReg
);
881 auto srcReg
= src
->getReg();
882 emitMovRegReg(m_as
, srcReg
, dstReg
);
886 template<class OpInstr
, class Oper
>
887 void CodeGenerator::cgUnaryIntOp(SSATmp
* dst
,
891 if (src
->type() != Type::Int
&& src
->type() != Type::Bool
) {
892 assert(0); CG_PUNT(UnaryIntOp
);
894 auto dstReg
= dst
->getReg();
895 auto srcReg
= src
->getReg();
896 assert(dstReg
!= InvalidReg
);
899 // Integer operations require 64-bit representations
900 zeroExtendIfBool(a
, src
);
902 if (srcReg
!= InvalidReg
) {
903 emitMovRegReg(a
, srcReg
, dstReg
);
906 assert(src
->isConst());
907 emitLoadImm(a
, oper(src
->getValRawInt()), dstReg
);
911 void CodeGenerator::cgNotWork(SSATmp
* dst
, SSATmp
* src
) {
912 cgUnaryIntOp(dst
, src
, &Asm::not, [](int64_t i
) { return ~i
; });
915 void CodeGenerator::cgNegateWork(SSATmp
* dst
, SSATmp
* src
) {
916 cgUnaryIntOp(dst
, src
, &Asm::neg
, [](int64_t i
) { return -i
; });
919 void CodeGenerator::cgNegate(IRInstruction
* inst
) {
920 cgNegateWork(inst
->getDst(), inst
->getSrc(0));
923 inline static Reg8
convertToReg8(PhysReg reg
) { return rbyte(reg
); }
924 inline static Reg64
convertToReg64(PhysReg reg
) { return reg
; }
926 template<class Oper
, class RegType
>
927 void CodeGenerator::cgBinaryIntOp(IRInstruction
* inst
,
928 void (Asm::*instrIR
)(Immed
, RegType
),
929 void (Asm::*instrRR
)(RegType
, RegType
),
930 void (Asm::*movInstr
)(RegType
, RegType
),
931 void (*extendInstr
)(Asm
&, const SSATmp
*),
933 RegType (*convertReg
)(PhysReg
),
934 Commutativity commuteFlag
) {
935 const SSATmp
* dst
= inst
->getDst();
936 const SSATmp
* src1
= inst
->getSrc(0);
937 const SSATmp
* src2
= inst
->getSrc(1);
938 if (!(src1
->isA(Type::Bool
) || src1
->isA(Type::Int
)) ||
939 !(src2
->isA(Type::Bool
) || src2
->isA(Type::Int
))) {
940 CG_PUNT(cgBinaryIntOp
);
943 bool const commutative
= commuteFlag
== Commutative
;
944 auto const dstReg
= dst
->getReg();
945 auto const src1Reg
= src1
->getReg();
946 auto const src2Reg
= src2
->getReg();
948 bool const useBoolOps
= src1
->isA(Type::Bool
) && src2
->isA(Type::Bool
);
950 auto const dstOpReg
= convertReg(dstReg
);
951 auto const src1OpReg
= convertReg(src1Reg
);
952 auto const src2OpReg
= convertReg(src2Reg
);
953 auto const rOpScratch
= convertReg(rScratch
);
955 // We can only use byte operations if both operands are boolean. If so, we'll
956 // sign extend *after* the operation is complete to save a movzbl.
958 // Extend booleans: integer operations require 64-bit
960 zeroExtendIfBool(m_as
, src1
);
961 zeroExtendIfBool(m_as
, src2
);
964 auto signExtendIfNecessary
= [&] {
966 extendInstr(m_as
, dst
);
971 if (src1Reg
!= InvalidReg
&& src2Reg
!= InvalidReg
) {
972 if (dstReg
== src1Reg
) {
973 (a
.*instrRR
) (src2OpReg
, dstOpReg
);
974 } else if (dstReg
== src2Reg
) {
976 (a
.*instrRR
) (src1OpReg
, dstOpReg
);
978 (a
.*movInstr
)(src1OpReg
, rOpScratch
);
979 (a
.*instrRR
) (src2OpReg
, rOpScratch
);
980 (a
.*movInstr
)(rOpScratch
, dstOpReg
);
983 emitMovRegReg(a
, src1Reg
, dstReg
);
984 (a
.*instrRR
) (src2OpReg
, dstOpReg
);
986 signExtendIfNecessary();
991 if (src1Reg
== InvalidReg
&& src2Reg
== InvalidReg
) {
992 assert(src1
->isConst() && src2
->isConst());
993 int64_t value
= useBoolOps
?
994 (int64_t)oper(src1
->getValBool(), src2
->getValBool()) :
995 oper(src1
->getValRawInt(), src2
->getValRawInt());
996 emitLoadImm(a
, value
, dstReg
);
1000 // One register, and one immediate.
1002 auto immedSrc
= (src2Reg
== InvalidReg
? src2
: src1
);
1003 auto immed
= useBoolOps
?
1004 (int64_t)immedSrc
->getValBool() : immedSrc
->getValRawInt();
1005 auto srcReg
= (src2Reg
== InvalidReg
? src1
: src2
)->getReg();
1006 if (srcReg
== dstReg
) {
1007 (a
.*instrIR
) (immed
, dstOpReg
);
1009 emitLoadImm(a
, immed
, dstReg
);
1010 (a
.*instrRR
) (convertReg(srcReg
), dstOpReg
);
1012 signExtendIfNecessary();
1017 if (src1Reg
== InvalidReg
) {
1018 if (dstReg
== src2Reg
) {
1019 emitLoadImm(a
, useBoolOps
?
1020 (int64_t)src1
->getValBool() : src1
->getValRawInt(), rScratch
);
1021 (a
.*instrRR
) (src2OpReg
, rOpScratch
);
1022 (a
.*movInstr
)(rOpScratch
, dstOpReg
);
1024 emitLoadImm(a
, useBoolOps
?
1025 (int64_t)src1
->getValBool() : src1
->getValRawInt(), dstReg
);
1026 (a
.*instrRR
) (src2OpReg
, dstOpReg
);
1028 signExtendIfNecessary();
1032 assert(src2Reg
== InvalidReg
);
1033 emitMovRegReg(a
, src1Reg
, dstReg
);
1035 useBoolOps
? src2
->getValBool() : src2
->getValRawInt(), dstOpReg
);
1036 signExtendIfNecessary();
1039 template<class Oper
, class RegType
>
1040 void CodeGenerator::cgBinaryOp(IRInstruction
* inst
,
1041 void (Asm::*instrIR
)(Immed
, RegType
),
1042 void (Asm::*instrRR
)(RegType
, RegType
),
1043 void (Asm::*movInstr
)(RegType
, RegType
),
1044 void (Asm::*fpInstr
)(RegXMM
, RegXMM
),
1045 void (*extendInstr
)(Asm
&, const SSATmp
*),
1047 RegType (*convertReg
)(PhysReg
),
1048 Commutativity commuteFlag
) {
1049 const SSATmp
* dst
= inst
->getDst();
1050 const SSATmp
* src1
= inst
->getSrc(0);
1051 const SSATmp
* src2
= inst
->getSrc(1);
1052 if (!(src1
->isA(Type::Bool
) || src1
->isA(Type::Int
) || src1
->isA(Type::Dbl
))
1054 !(src2
->isA(Type::Bool
) || src2
->isA(Type::Int
) || src2
->isA(Type::Dbl
)) )
1056 CG_PUNT(cgBinaryOp
);
1058 if (src1
->isA(Type::Dbl
) || src2
->isA(Type::Dbl
)) {
1059 prepBinaryXmmOp(m_as
, src1
, src2
);
1060 (m_as
.*fpInstr
)(xmm1
, xmm0
);
1061 m_as
. mov_xmm_reg64(xmm0
, dst
->getReg());
1064 cgBinaryIntOp(inst
, instrIR
, instrRR
, movInstr
, extendInstr
,
1065 oper
, convertReg
, commuteFlag
);
1068 bool CodeGenerator::emitIncDecHelper(SSATmp
* dst
, SSATmp
* src1
, SSATmp
* src2
,
1069 void(Asm::*emitFunc
)(Reg64
)) {
1070 if (src1
->getReg() != InvalidReg
&&
1071 dst
->getReg() != InvalidReg
&&
1072 src1
->isA(Type::Int
) &&
1074 src2
->isConst() && src2
->isA(Type::Int
) && src2
->getValInt() == 1) {
1075 emitMovRegReg(m_as
, src1
->getReg(), dst
->getReg());
1076 (m_as
.*emitFunc
)(dst
->getReg());
1083 * If src2 is 1, this generates dst = src1 + 1 using the "inc" x86 instruction.
1084 * The return value is whether or not the instruction could be generated.
1086 bool CodeGenerator::emitInc(SSATmp
* dst
, SSATmp
* src1
, SSATmp
* src2
) {
1087 return emitIncDecHelper(dst
, src1
, src2
, &Asm::incq
);
1091 * If src2 is 1, this generates dst = src1 - 1 using the "dec" x86 instruction.
1092 * The return value is whether or not the instruction could be generated.
1094 bool CodeGenerator::emitDec(SSATmp
* dst
, SSATmp
* src1
, SSATmp
* src2
) {
1095 return emitIncDecHelper(dst
, src1
, src2
, &Asm::decq
);
1098 void CodeGenerator::cgOpAdd(IRInstruction
* inst
) {
1099 SSATmp
* dst
= inst
->getDst();
1100 SSATmp
* src1
= inst
->getSrc(0);
1101 SSATmp
* src2
= inst
->getSrc(1);
1103 // Special cases: x = y + 1
1104 if (emitInc(dst
, src1
, src2
) || emitInc(dst
, src2
, src1
)) return;
1106 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
))
1121 &Asm::addsd_xmm_xmm
,
1122 nullptr, // not used
1123 std::plus
<int64_t>(),
1129 void CodeGenerator::cgOpSub(IRInstruction
* inst
) {
1130 SSATmp
* dst
= inst
->getDst();
1131 SSATmp
* src1
= inst
->getSrc(0);
1132 SSATmp
* src2
= inst
->getSrc(1);
1134 if (emitDec(dst
, src1
, src2
)) return;
1136 if (src1
->isConst() && src1
->isA(Type::Int
) && src1
->getValInt() == 0 &&
1137 !src2
->isA(Type::Dbl
)) {
1138 return cgNegateWork(dst
, src2
);
1141 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
)) {
1146 &signExtendBool
, // bool "-1" needs to be sign extended as int
1155 &Asm::subsd_xmm_xmm
,
1156 nullptr, // not used
1157 std::minus
<int64_t>(),
1163 void CodeGenerator::cgOpAnd(IRInstruction
* inst
) {
1164 SSATmp
* src1
= inst
->getSrc(0);
1165 SSATmp
* src2
= inst
->getSrc(1);
1167 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
)) {
1173 [] (bool a
, bool b
) { return a
& b
; },
1181 nullptr, // not used
1182 [] (int64_t a
, int64_t b
) { return a
& b
; },
1188 void CodeGenerator::cgOpOr(IRInstruction
* inst
) {
1189 SSATmp
* src1
= inst
->getSrc(0);
1190 SSATmp
* src2
= inst
->getSrc(1);
1192 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
)) {
1198 [] (bool a
, bool b
) { return a
| b
; },
1206 nullptr, // not used
1207 [] (int64_t a
, int64_t b
) { return a
| b
; },
1213 void CodeGenerator::cgOpDiv(IRInstruction
* inst
) {
1217 void CodeGenerator::cgOpXor(IRInstruction
* inst
) {
1218 SSATmp
* dst
= inst
->getDst();
1219 SSATmp
* src1
= inst
->getSrc(0);
1220 SSATmp
* src2
= inst
->getSrc(1);
1221 if (src2
->isConst() && src2
->type() == Type::Int
&&
1222 src2
->getValInt() == ~0L) {
1223 return cgNotWork(dst
, src1
);
1226 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
)) {
1232 [] (bool a
, bool b
) { return a
^ b
; },
1240 nullptr, // not used
1241 [] (int64_t a
, int64_t b
) { return a
^ b
; },
1247 void CodeGenerator::cgOpMul(IRInstruction
* inst
) {
1248 SSATmp
* src1
= inst
->getSrc(0);
1249 SSATmp
* src2
= inst
->getSrc(1);
1251 if (src1
->isA(Type::Bool
) & src2
->isA(Type::Bool
)) {
1257 [] (bool a
, bool b
) { return a
& b
; },
1261 // Boolean multiplication is the same as &
1266 &Asm::mulsd_xmm_xmm
,
1267 nullptr, // not used
1268 std::multiplies
<int64_t>(),
1274 void CodeGenerator::cgOpNot(IRInstruction
* inst
) {
1275 auto const src
= inst
->getSrc(0);
1276 auto const dstReg
= inst
->getDst()->getReg();
1279 if (src
->isConst()) {
1280 a
. movb (!src
->getValBool(), rbyte(dstReg
));
1282 if (dstReg
!= src
->getReg()) {
1283 a
. movb (rbyte(src
->getReg()), rbyte(dstReg
));
1285 a
. xorb (1, rbyte(dstReg
));
1289 ///////////////////////////////////////////////////////////////////////////////
1290 // Comparison Operators
1291 ///////////////////////////////////////////////////////////////////////////////
1293 #define DISPATCHER(name) \
1294 HOT_FUNC_VM int64_t ccmp_ ## name (StringData* a1, StringData* a2) \
1295 { return name(a1, a2); } \
1296 HOT_FUNC_VM int64_t ccmp_ ## name (StringData* a1, int64_t a2) \
1297 { return name(a1, a2); } \
1298 HOT_FUNC_VM int64_t ccmp_ ## name (StringData* a1, ObjectData* a2) \
1299 { return name(a1, Object(a2)); } \
1300 HOT_FUNC_VM int64_t ccmp_ ## name (ObjectData* a1, ObjectData* a2) \
1301 { return name(Object(a1), Object(a2)); } \
1302 HOT_FUNC_VM int64_t ccmp_ ## name (ObjectData* a1, int64_t a2) \
1303 { return name(Object(a1), a2); } \
1304 HOT_FUNC_VM int64_t ccmp_ ## name (ArrayData* a1, ArrayData* a2) \
1305 { return name(Array(a1), Array(a2)); }
1314 template <typename A
, typename B
>
1315 inline int64_t ccmp_nsame(A a
, B b
) { return !ccmp_same(a
, b
); }
1317 template <typename A
, typename B
>
1318 inline int64_t ccmp_nequal(A a
, B b
) { return !ccmp_equal(a
, b
); }
1320 template <typename A
, typename B
>
1321 inline int64_t ccmp_lte(A a
, B b
) { return !ccmp_more(a
, b
); }
1323 template <typename A
, typename B
>
1324 inline int64_t ccmp_gte(A a
, B b
) { return !ccmp_less(a
, b
); }
1326 #define CG_OP_CMP(inst, setter, name) \
1327 cgOpCmpHelper(inst, &Asm:: setter, ccmp_ ## name, ccmp_ ## name, \
1328 ccmp_ ## name, ccmp_ ## name, ccmp_ ## name, ccmp_ ## name)
1330 // SRON - string, resource, object, or number
1331 static bool typeIsSRON(Type t
) {
1333 || t
== Type::Obj
// encompases object and resource
1339 void CodeGenerator::cgOpCmpHelper(
1340 IRInstruction
* inst
,
1341 void (Asm::*setter
)(Reg8
),
1342 int64_t (*str_cmp_str
)(StringData
*, StringData
*),
1343 int64_t (*str_cmp_int
)(StringData
*, int64_t),
1344 int64_t (*str_cmp_obj
)(StringData
*, ObjectData
*),
1345 int64_t (*obj_cmp_obj
)(ObjectData
*, ObjectData
*),
1346 int64_t (*obj_cmp_int
)(ObjectData
*, int64_t),
1347 int64_t (*arr_cmp_arr
)( ArrayData
*, ArrayData
*)
1349 SSATmp
* dst
= inst
->getDst();
1350 SSATmp
* src1
= inst
->getSrc(0);
1351 SSATmp
* src2
= inst
->getSrc(1);
1353 Type type1
= src1
->type();
1354 Type type2
= src2
->type();
1356 auto src1Reg
= src1
->getReg();
1357 auto src2Reg
= src2
->getReg();
1358 auto dstReg
= dst
->getReg();
1360 auto setFromFlags
= [&] {
1361 (m_as
.*setter
)(rbyte(dstReg
));
1363 // It is possible that some pass has been done after simplification; if such
1364 // a pass invalidates our invariants, then just punt.
1366 // simplifyCmp has done const-const optimization
1368 // If the types are the same and there is only one constant,
1369 // simplifyCmp has moved it to the right.
1370 if (src1
->isConst()) {
1371 CG_PUNT(cgOpCmpHelper_const
);
1374 /////////////////////////////////////////////////////////////////////////////
1375 // case 1: null/string cmp string
1376 // simplifyCmp has converted the null to ""
1377 if (type1
.isString() && type2
.isString()) {
1379 args
.ssa(src1
).ssa(src2
);
1380 cgCallHelper(m_as
, (TCA
)str_cmp_str
, dst
, kSyncPoint
, args
);
1383 /////////////////////////////////////////////////////////////////////////////
1384 // case 2: bool/null cmp anything
1385 // simplifyCmp has converted all args to bool
1386 else if (type1
== Type::Bool
&& type2
== Type::Bool
) {
1387 if (src2
->isConst()) {
1388 m_as
. cmpb (src2
->getValBool(), Reg8(int(src1Reg
)));
1390 m_as
. cmpb (Reg8(int(src2Reg
)), Reg8(int(src1Reg
)));
1395 /////////////////////////////////////////////////////////////////////////////
1396 // case 3, 4, and 7: string/resource/object/number (sron) cmp sron
1397 // These cases must be amalgamated because Type::Obj can refer to an object
1398 // or to a resource.
1399 // strings are canonicalized to the left, ints to the right
1400 else if (typeIsSRON(type1
) && typeIsSRON(type2
)) {
1401 // the common case: int cmp int
1402 if (type1
== Type::Int
&& type2
== Type::Int
) {
1403 if (src2
->isConst()) {
1404 m_as
.cmp_imm64_reg64(src2
->getValInt(), src1Reg
);
1406 m_as
.cmp_reg64_reg64(src2Reg
, src1Reg
);
1411 else if (type1
== Type::Dbl
|| type2
== Type::Dbl
) {
1412 if ((type1
== Type::Dbl
|| type1
== Type::Int
) &&
1413 (type2
== Type::Dbl
|| type2
== Type::Int
)) {
1414 prepBinaryXmmOp(m_as
, src1
, src2
);
1415 doubleCmp(m_as
, xmm0
, xmm1
);
1418 CG_PUNT(cgOpCmpHelper_Dbl
);
1422 else if (type1
.isString()) {
1423 // string cmp string is dealt with in case 1
1424 // string cmp double is punted above
1426 if (type2
== Type::Int
) {
1428 args
.ssa(src1
).ssa(src2
);
1429 cgCallHelper(m_as
, (TCA
)str_cmp_int
, dst
, kSyncPoint
, args
);
1430 } else if (type2
== Type::Obj
) {
1432 args
.ssa(src1
).ssa(src2
);
1433 cgCallHelper(m_as
, (TCA
)str_cmp_obj
, dst
, kSyncPoint
, args
);
1435 CG_PUNT(cgOpCmpHelper_sx
);
1439 else if (type1
== Type::Obj
) {
1440 // string cmp object/resource is dealt with above
1441 // object cmp double is punted above
1443 if (type2
== Type::Obj
) {
1445 args
.ssa(src1
).ssa(src2
);
1446 cgCallHelper(m_as
, (TCA
)obj_cmp_obj
, dst
, kSyncPoint
, args
);
1447 } else if (type2
== Type::Int
) {
1449 args
.ssa(src1
).ssa(src2
);
1450 cgCallHelper(m_as
, (TCA
)obj_cmp_int
, dst
, kSyncPoint
, args
);
1452 CG_PUNT(cgOpCmpHelper_ox
);
1459 /////////////////////////////////////////////////////////////////////////////
1460 // case 5: array cmp array
1461 else if (type1
.isArray() && type2
.isArray()) {
1463 args
.ssa(src1
).ssa(src2
);
1464 cgCallHelper(m_as
, (TCA
)arr_cmp_arr
, dst
, kSyncPoint
, args
);
1467 /////////////////////////////////////////////////////////////////////////////
1468 // case 6: array cmp anything
1469 // simplifyCmp has already dealt with this case.
1471 /////////////////////////////////////////////////////////////////////////////
1473 // We have a type which is not a common type. It might be a cell or a box.
1474 CG_PUNT(cgOpCmpHelper_unimplemented
);
1478 void CodeGenerator::cgOpEq(IRInstruction
* inst
) {
1479 CG_OP_CMP(inst
, sete
, equal
);
1482 void CodeGenerator::cgOpNeq(IRInstruction
* inst
) {
1483 CG_OP_CMP(inst
, setne
, nequal
);
1486 void CodeGenerator::cgOpSame(IRInstruction
* inst
) {
1487 CG_OP_CMP(inst
, sete
, same
);
1490 void CodeGenerator::cgOpNSame(IRInstruction
* inst
) {
1491 CG_OP_CMP(inst
, setne
, nsame
);
1494 void CodeGenerator::cgOpLt(IRInstruction
* inst
) {
1495 CG_OP_CMP(inst
, setl
, less
);
1498 void CodeGenerator::cgOpGt(IRInstruction
* inst
) {
1499 CG_OP_CMP(inst
, setg
, more
);
1502 void CodeGenerator::cgOpLte(IRInstruction
* inst
) {
1503 CG_OP_CMP(inst
, setle
, lte
);
1506 void CodeGenerator::cgOpGte(IRInstruction
* inst
) {
1507 CG_OP_CMP(inst
, setge
, gte
);
1510 ///////////////////////////////////////////////////////////////////////////////
1511 // Type check operators
1512 ///////////////////////////////////////////////////////////////////////////////
1514 template<class OpndType
>
1515 ConditionCode
CodeGenerator::emitTypeTest(Type type
, OpndType src
,
1517 assert(!type
.subtypeOf(Type::Cls
));
1519 if (type
.isString()) {
1520 emitTestTVType(m_as
, KindOfStringBit
, src
);
1522 } else if (type
.equals(Type::UncountedInit
)) {
1523 emitTestTVType(m_as
, KindOfUncountedInitBit
, src
);
1525 } else if (type
.equals(Type::Uncounted
)) {
1526 emitCmpTVType(m_as
, KindOfRefCountThreshold
, src
);
1528 } else if (type
.equals(Type::Cell
)) {
1529 emitCmpTVType(m_as
, KindOfRef
, src
);
1531 } else if (type
.equals(Type::Gen
)) {
1532 return CC_None
; // nothing to check
1534 DataType dataType
= type
.toDataType();
1535 assert(dataType
== KindOfRef
||
1536 (dataType
>= KindOfUninit
&& dataType
<= KindOfObject
));
1537 emitCmpTVType(m_as
, dataType
, src
);
1540 return negate
? ccNegate(cc
) : cc
;
1543 template<class OpndType
>
1544 ConditionCode
CodeGenerator::emitTypeTest(IRInstruction
* inst
, OpndType src
,
1546 return emitTypeTest(inst
->getTypeParam(), src
, negate
);
1549 void CodeGenerator::emitSetCc(IRInstruction
* inst
, ConditionCode cc
) {
1550 if (cc
== CC_None
) return;
1551 m_as
.setcc(cc
, rbyte(inst
->getDst()->getReg()));
1554 ConditionCode
CodeGenerator::emitIsTypeTest(IRInstruction
* inst
, bool negate
) {
1555 if (inst
->getTypeParam().subtypeOf(Type::Obj
)) {
1556 // Task #2094715: Handle isType tests for Type::Obj, which
1557 // requires checking that checked object is not a resource
1558 CG_PUNT(cgIsTypeObject
);
1561 SSATmp
* src
= inst
->getSrc(0);
1562 if (src
->isA(Type::PtrToGen
)) {
1563 PhysReg base
= src
->getReg();
1564 return emitTypeTest(inst
, base
[TVOFF(m_type
)], negate
);
1566 assert(src
->isA(Type::Gen
));
1567 assert(!src
->isConst());
1568 PhysReg srcReg
= src
->getReg(1); // type register
1569 return emitTypeTest(inst
, srcReg
, negate
);
1572 template<class OpndType
>
1573 void CodeGenerator::emitGuardType(OpndType src
, IRInstruction
* inst
) {
1574 emitGuardOrFwdJcc(inst
, emitTypeTest(inst
, src
, true));
1577 void CodeGenerator::cgGuardTypeCell(PhysReg baseReg
,
1579 IRInstruction
* inst
) {
1580 emitGuardType(baseReg
[offset
+ TVOFF(m_type
)], inst
);
1583 void CodeGenerator::cgIsTypeMemCommon(IRInstruction
* inst
, bool negate
) {
1584 emitSetCc(inst
, emitIsTypeTest(inst
, negate
));
1587 void CodeGenerator::cgIsTypeCommon(IRInstruction
* inst
, bool negate
) {
1588 emitSetCc(inst
, emitIsTypeTest(inst
, negate
));
1591 void CodeGenerator::cgJmpIsTypeCommon(IRInstruction
* inst
, bool negate
) {
1592 emitJccDirectExit(inst
, emitIsTypeTest(inst
, negate
));
1595 void CodeGenerator::cgIsType(IRInstruction
* inst
) {
1596 cgIsTypeCommon(inst
, false);
1599 void CodeGenerator::cgIsNType(IRInstruction
* inst
) {
1600 cgIsTypeCommon(inst
, true);
1603 void CodeGenerator::cgJmpIsType(IRInstruction
* inst
) {
1604 cgJmpIsTypeCommon(inst
, false);
1607 void CodeGenerator::cgJmpIsNType(IRInstruction
* inst
) {
1608 cgJmpIsTypeCommon(inst
, true);
1611 void CodeGenerator::cgIsTypeMem(IRInstruction
* inst
) {
1612 cgIsTypeMemCommon(inst
, false);
1615 void CodeGenerator::cgIsNTypeMem(IRInstruction
* inst
) {
1616 cgIsTypeMemCommon(inst
, true);
1619 ///////////////////////////////////////////////////////////////////////////////
1621 HOT_FUNC_VM
static bool instanceOfHelper(const Class
* objClass
,
1622 const Class
* testClass
) {
1623 return testClass
&& objClass
->classof(testClass
);
1626 HOT_FUNC_VM
static bool instanceOfHelperIFace(const Class
* objClass
,
1627 const Class
* testClass
) {
1628 return testClass
&& objClass
->classof(testClass
->preClass());
1631 void CodeGenerator::cgInstanceOf(IRInstruction
* inst
) {
1632 const bool ifaceHint
= inst
->getSrc(2)->getValBool();
1634 TCA(ifaceHint
? instanceOfHelperIFace
: instanceOfHelper
),
1638 .ssa(inst
->getSrc(0))
1639 .ssa(inst
->getSrc(1)));
1643 * Check instanceof using instance bitmasks.
1645 * Note it's not necessary to check whether the test class is defined:
1646 * if it doesn't exist than the candidate can't be an instance of it
1647 * and will fail this check.
1649 void CodeGenerator::emitInstanceBitmaskCheck(IRInstruction
* inst
) {
1650 auto const rObjClass
= inst
->getSrc(0)->getReg(0);
1651 auto const testClassName
= inst
->getSrc(1)->getValStr();
1656 if (!Class::getInstanceBitMask(testClassName
, offset
, mask
)) {
1657 always_assert(!"cgInstanceOfBitmask had no bitmask");
1659 a
. testb (int8_t(mask
), rObjClass
[offset
]);
1662 void CodeGenerator::cgInstanceOfBitmask(IRInstruction
* inst
) {
1664 emitInstanceBitmaskCheck(inst
);
1665 a
. setnz (rbyte(inst
->getDst()->getReg()));
1668 void CodeGenerator::cgNInstanceOfBitmask(IRInstruction
* inst
) {
1670 emitInstanceBitmaskCheck(inst
);
1671 a
. setz (rbyte(inst
->getDst()->getReg()));
1674 void CodeGenerator::cgJmpInstanceOfBitmask(IRInstruction
* inst
) {
1675 emitInstanceBitmaskCheck(inst
);
1676 emitJccDirectExit(inst
, CC_NZ
);
1679 void CodeGenerator::cgJmpNInstanceOfBitmask(IRInstruction
* inst
) {
1680 emitInstanceBitmaskCheck(inst
);
1681 emitJccDirectExit(inst
, CC_Z
);
1685 * Check instanceof using the superclass vector on the end of the
1688 void CodeGenerator::cgExtendsClass(IRInstruction
* inst
) {
1689 auto const rObjClass
= inst
->getSrc(0)->getReg();
1690 auto const testClass
= inst
->getSrc(1)->getValClass();
1691 auto rTestClass
= inst
->getSrc(1)->getReg();
1692 auto const rdst
= rbyte(inst
->getDst()->getReg());
1699 if (rTestClass
== InvalidReg
) { // TODO(#2031606)
1700 rTestClass
= rScratch
; // careful below about asm-x64 smashing this
1701 emitLoadImm(a
, (int64_t)testClass
, rTestClass
);
1704 // Test if it is the exact same class. TODO(#2044801): we should be
1705 // doing this control flow at the IR level.
1706 if (!(testClass
->attrs() & AttrAbstract
)) {
1707 a
. cmpq (rTestClass
, rObjClass
);
1713 auto const vecOffset
= Class::classVecOff() +
1714 sizeof(Class
*) * (testClass
->classVecLen() - 1);
1716 // Check the length of the class vectors---if the candidate's is at
1717 // least as long as the potential base (testClass) it might be a
1719 asm_label(a
, notExact
);
1720 a
. cmpl (testClass
->classVecLen(),
1721 rObjClass
[Class::classVecLenOff()]);
1722 a
. jb8 (falseLabel
);
1724 // If it's a subclass, rTestClass must be at the appropriate index.
1725 a
. cmpq (rTestClass
, rObjClass
[vecOffset
]);
1729 asm_label(a
, falseLabel
);
1730 a
. xorl (r32(rdst
), r32(rdst
));
1735 void CodeGenerator::cgConvDblToBool(IRInstruction
* inst
) {
1736 SSATmp
* dst
= inst
->getDst();
1737 auto dstReg
= dst
->getReg();
1738 assert(dstReg
!= InvalidReg
);
1739 SSATmp
* src
= inst
->getSrc(0);
1740 auto srcReg
= src
->getReg();
1741 if (srcReg
== InvalidReg
) {
1742 assert(src
->isConst());
1743 double constVal
= src
->getValDbl();
1744 if (constVal
== 0.0) {
1745 m_as
.xor_reg64_reg64(dstReg
, dstReg
);
1747 m_as
.mov_imm64_reg(1, dstReg
);
1750 m_as
.movq(srcReg
, dstReg
);
1751 m_as
.shlq(1, dstReg
); // 0.0 stays zero and -0.0 is now 0.0
1752 m_as
.setne(rbyte(dstReg
)); // lower byte becomes 1 if dstReg != 0
1753 m_as
.movzbl(rbyte(dstReg
), r32(dstReg
));
1757 void CodeGenerator::cgConvIntToBool(IRInstruction
* inst
) {
1758 SSATmp
* dst
= inst
->getDst();
1759 auto dstReg
= dst
->getReg();
1760 assert(dstReg
!= InvalidReg
);
1761 SSATmp
* src
= inst
->getSrc(0);
1762 auto srcReg
= src
->getReg();
1764 if (srcReg
== InvalidReg
) {
1765 assert(src
->isConst());
1766 int64_t constVal
= src
->getValInt();
1767 if (constVal
== 0) {
1768 m_as
.xor_reg64_reg64(dstReg
, dstReg
);
1770 m_as
.mov_imm64_reg(1, dstReg
);
1773 m_as
.test_reg64_reg64(srcReg
, srcReg
);
1774 m_as
.setne(rbyte(dstReg
));
1775 m_as
.movzbl(rbyte(dstReg
), r32(dstReg
));
1779 void CodeGenerator::cgConvBoolToDbl(IRInstruction
* inst
) {
1780 // cvtsi2sd doesn't modify the high bits of its target, which can
1781 // cause false dependencies to prevent register renaming from kicking
1782 // in. Break the dependency chain by zeroing out xmm0.
1783 m_as
.pxor_xmm_xmm(xmm0
, xmm0
);
1784 SSATmp
* dst
= inst
->getDst();
1785 auto dstReg
= dst
->getReg();
1786 assert(dstReg
!= InvalidReg
);
1787 SSATmp
* src
= inst
->getSrc(0);
1788 auto srcReg
= src
->getReg();
1789 if (srcReg
== InvalidReg
) {
1790 assert(src
->isConst());
1791 int64_t constVal
= src
->getValRawInt();
1792 if (constVal
== 0) {
1793 m_as
.xor_reg64_reg64(dstReg
, dstReg
);
1795 m_as
.mov_imm64_reg(1, dstReg
);
1798 m_as
.movzbl(rbyte(srcReg
), r32(dstReg
));
1800 m_as
.cvtsi2sd_reg64_xmm(dstReg
, xmm0
);
1801 m_as
.mov_xmm_reg64(xmm0
, dstReg
);
1804 void CodeGenerator::cgConvIntToDbl(IRInstruction
* inst
) {
1805 // cvtsi2sd doesn't modify the high bits of its target, which can
1806 // cause false dependencies to prevent register renaming from kicking
1807 // in. Break the dependency chain by zeroing out xmm0.
1808 m_as
.pxor_xmm_xmm(xmm0
, xmm0
);
1809 SSATmp
* dst
= inst
->getDst();
1810 auto dstReg
= dst
->getReg();
1811 assert(dstReg
!= InvalidReg
);
1812 SSATmp
* src
= inst
->getSrc(0);
1813 auto srcReg
= src
->getReg();
1814 if (srcReg
== InvalidReg
) {
1815 assert(src
->isConst());
1816 int64_t constVal
= src
->getValRawInt();
1817 if (constVal
== 0) {
1818 m_as
.xor_reg64_reg64(dstReg
, dstReg
);
1820 m_as
.mov_imm64_reg(constVal
, dstReg
);
1822 m_as
.cvtsi2sd_reg64_xmm(dstReg
, xmm0
);
1824 m_as
.cvtsi2sd_reg64_xmm(srcReg
, xmm0
);
1826 m_as
.mov_xmm_reg64(xmm0
, dstReg
);
1829 void CodeGenerator::cgConvBoolToInt(IRInstruction
* inst
) {
1830 SSATmp
* dst
= inst
->getDst();
1831 auto dstReg
= dst
->getReg();
1832 assert(dstReg
!= InvalidReg
);
1833 SSATmp
* src
= inst
->getSrc(0);
1834 auto srcReg
= src
->getReg();
1835 assert(src
->isConst() == (srcReg
== InvalidReg
));
1836 if (srcReg
== InvalidReg
) {
1837 int64_t constVal
= src
->getValRawInt();
1838 if (constVal
== 0) {
1839 m_as
.xor_reg64_reg64(dstReg
, dstReg
);
1841 m_as
.mov_imm64_reg(1, dstReg
);
1844 m_as
.movzbl(rbyte(srcReg
), r32(dstReg
));
1848 void CodeGenerator::cgConvBoolToStr(IRInstruction
* inst
) {
1849 SSATmp
* dst
= inst
->getDst();
1850 auto dstReg
= dst
->getReg();
1851 assert(dstReg
!= InvalidReg
);
1852 SSATmp
* src
= inst
->getSrc(0);
1853 auto srcReg
= src
->getReg();
1854 assert(src
->isConst() == (srcReg
== InvalidReg
));
1855 if (srcReg
== InvalidReg
) {
1856 auto constVal
= src
->getValBool();
1858 m_as
.mov_imm64_reg((uint64_t)StringData::GetStaticString(""), dstReg
);
1860 m_as
.mov_imm64_reg((uint64_t)StringData::GetStaticString("1"), dstReg
);
1863 m_as
.testb(Reg8(int(srcReg
)), Reg8(int(srcReg
)));
1864 m_as
.mov_imm64_reg((uint64_t)StringData::GetStaticString(""), dstReg
);
1865 m_as
.mov_imm64_reg((uint64_t)StringData::GetStaticString("1"), rScratch
);
1866 m_as
.cmov_reg64_reg64(CC_NZ
, rScratch
, dstReg
);
1870 void CodeGenerator::cgUnboxPtr(IRInstruction
* inst
) {
1871 SSATmp
* dst
= inst
->getDst();
1872 SSATmp
* src
= inst
->getSrc(0);
1874 auto srcReg
= src
->getReg();
1875 auto dstReg
= dst
->getReg();
1877 assert(srcReg
!= InvalidReg
);
1878 assert(dstReg
!= InvalidReg
);
1880 emitMovRegReg(m_as
, srcReg
, dstReg
);
1881 emitDerefIfVariant(m_as
, PhysReg(dstReg
));
1884 void CodeGenerator::cgUnbox(IRInstruction
* inst
) {
1885 SSATmp
* dst
= inst
->getDst();
1886 SSATmp
* src
= inst
->getSrc(0);
1887 auto dstValReg
= dst
->getReg(0);
1888 auto dstTypeReg
= dst
->getReg(1);
1889 auto srcValReg
= src
->getReg(0);
1890 auto srcTypeReg
= src
->getReg(1);
1892 assert(dstValReg
!= dstTypeReg
);
1893 assert(src
->type().equals(Type::Gen
));
1894 assert(dst
->type().notBoxed());
1896 emitCmpTVType(m_as
, HPHP::KindOfRef
, srcTypeReg
);
1897 ifThenElse(CC_E
, [&] {
1898 // srcTypeReg == KindOfRef; srcValReg is RefData*
1899 const size_t ref_tv_off
= RefData::tvOffset();
1900 if (dstValReg
!= srcValReg
) {
1901 m_as
.loadq(srcValReg
[ref_tv_off
+ TVOFF(m_data
)], dstValReg
);
1902 emitLoadTVType(m_as
, srcValReg
[ref_tv_off
+ TVOFF(m_type
)],
1905 emitLoadTVType(m_as
, srcValReg
[ref_tv_off
+ TVOFF(m_type
)],
1907 m_as
.loadq(srcValReg
[ref_tv_off
+ TVOFF(m_data
)], dstValReg
);
1910 // srcTypeReg != KindOfRef; copy src -> dst
1911 shuffle2(m_as
, srcValReg
, srcTypeReg
, dstValReg
, dstTypeReg
);
1915 void CodeGenerator::cgLdFuncCachedCommon(IRInstruction
* inst
) {
1916 SSATmp
* dst
= inst
->getDst();
1917 SSATmp
* methodName
= inst
->getSrc(0);
1919 const StringData
* name
= methodName
->getValStr();
1920 CacheHandle ch
= TargetCache::allocFixedFunction(name
);
1921 size_t funcCacheOff
= ch
+ offsetof(FixedFuncCache
, m_func
);
1923 auto dstReg
= dst
->getReg();
1924 if (dstReg
== InvalidReg
) {
1925 // happens if LdFixedFunc and FCall not in same trace
1926 m_as
. cmpq(0, rVmTl
[funcCacheOff
]);
1928 m_as
. loadq (rVmTl
[funcCacheOff
], dstReg
);
1929 m_as
. testq (dstReg
, dstReg
);
1933 void CodeGenerator::cgLdFuncCached(IRInstruction
* inst
) {
1934 cgLdFuncCachedCommon(inst
);
1935 // jz off to the helper call in astubs
1936 unlikelyIfBlock(CC_Z
, [&] (Asm
& a
) {
1937 // this helper tries the autoload map, and fatals on failure
1938 cgCallNative(a
, inst
);
1943 void CodeGenerator::cgLdFuncCachedSafe(IRInstruction
* inst
) {
1944 cgLdFuncCachedCommon(inst
);
1945 if (Block
* taken
= inst
->getTaken()) {
1946 emitFwdJcc(m_as
, CC_Z
, taken
);
1950 void CodeGenerator::cgLdFunc(IRInstruction
* inst
) {
1951 SSATmp
* dst
= inst
->getDst();
1952 SSATmp
* methodName
= inst
->getSrc(0);
1954 TargetCache::CacheHandle ch
= TargetCache::FuncCache::alloc();
1955 // raises an error if function not found
1956 cgCallHelper(m_as
, (TCA
)FuncCache::lookup
, dst
->getReg(), kSyncPoint
,
1957 ArgGroup().imm(ch
).ssa(methodName
));
1960 static void emitLdObjClass(CodeGenerator::Asm
& a
,
1963 a
.loadq (objReg
[ObjectData::getVMClassOffset()], dstReg
);
1966 void CodeGenerator::cgLdObjClass(IRInstruction
* inst
) {
1967 auto dstReg
= inst
->getDst()->getReg();
1968 auto objReg
= inst
->getSrc(0)->getReg();
1970 emitLdObjClass(m_as
, objReg
, dstReg
);
1973 void CodeGenerator::cgLdObjMethod(IRInstruction
*inst
) {
1974 auto cls
= inst
->getSrc(0);
1975 auto clsReg
= cls
->getReg();
1976 auto name
= inst
->getSrc(1);
1977 auto actRec
= inst
->getSrc(2);
1978 auto actRecReg
= actRec
->getReg();
1979 CacheHandle handle
= Transl::TargetCache::MethodCache::alloc();
1981 // lookup in the targetcache
1982 assert(MethodCache::kNumLines
== 1);
1984 MethodCache::Pair p
;
1985 static_assert(sizeof(p
.m_value
) == 8,
1986 "MethodCache::Pair::m_value assumed to be 8 bytes");
1987 static_assert(sizeof(p
.m_key
) == 8,
1988 "MethodCache::Pair::m_key assumed to be 8 bytes");
1991 // preload handle->m_value
1992 m_as
.loadq(rVmTl
[handle
+ offsetof(MethodCache::Pair
, m_value
)], rScratch
);
1993 m_as
.cmpq (rVmTl
[handle
+ offsetof(MethodCache::Pair
, m_key
)], clsReg
);
1994 ifThenElse(CC_E
, // if handle->key == cls
1995 [&] { // then actReg->m_func = handle->value
1996 m_as
.storeq(rScratch
, actRecReg
[AROFF(m_func
)]);
1998 [&] { // else call slow path helper
1999 cgCallHelper(m_as
, (TCA
)methodCacheSlowPath
, InvalidReg
,
2001 ArgGroup().addr(rVmTl
, handle
)
2008 void CodeGenerator::cgRetVal(IRInstruction
* inst
) {
2009 auto const rFp
= inst
->getSrc(0)->getReg();
2010 auto* const val
= inst
->getSrc(1);
2013 // Store return value at the top of the caller's eval stack
2014 // (a) Store the type
2015 if (val
->type().needsReg()) {
2016 emitStoreTVType(a
, val
->getReg(1), rFp
[AROFF(m_r
) + TVOFF(m_type
)]);
2018 emitStoreTVType(a
, val
->type().toDataType(),
2019 rFp
[AROFF(m_r
) + TVOFF(m_type
)]);
2022 // (b) Store the actual value (not necessary when storing Null)
2023 if (val
->type().isNull()) return;
2024 if (val
->inst()->op() == DefConst
) {
2025 a
. storeq (val
->getValRawInt(),
2026 rFp
[AROFF(m_r
) + TVOFF(m_data
)]);
2028 zeroExtendIfBool(m_as
, val
);
2029 a
. storeq (val
->getReg(), rFp
[AROFF(m_r
) + TVOFF(m_data
)]);
2033 void CodeGenerator::cgRetAdjustStack(IRInstruction
* inst
) {
2034 auto const rFp
= inst
->getSrc(0)->getReg();
2035 auto const dstSp
= inst
->getDst()->getReg();
2037 a
. lea (rFp
[AROFF(m_r
)], dstSp
);
2040 void CodeGenerator::cgLdRetAddr(IRInstruction
* inst
) {
2041 auto fpReg
= inst
->getSrc(0)->getReg(0);
2042 assert(fpReg
!= InvalidReg
);
2043 m_as
.push(fpReg
[AROFF(m_savedRip
)]);
2046 void checkFrame(ActRec
* fp
, Cell
* sp
, bool checkLocals
) {
2047 const Func
* func
= fp
->m_func
;
2048 if (fp
->hasVarEnv()) {
2049 assert(fp
->getVarEnv()->getCfp() == fp
);
2051 // TODO: validate this pointer from actrec
2052 int numLocals
= func
->numLocals();
2053 DEBUG_ONLY Cell
* firstSp
= ((Cell
*)fp
) - func
->numSlotsInFrame();
2054 assert(sp
<= firstSp
|| func
->isGenerator());
2056 int numParams
= func
->numParams();
2057 for (int i
=0; i
< numLocals
; i
++) {
2058 if (i
>= numParams
&& func
->isGenerator() && i
< func
->numNamedLocals()) {
2061 assert(checkTv(frame_local(fp
, i
)));
2064 // We unfortunately can't do the same kind of check for the stack
2065 // because it may contain ActRecs.
2067 for (Cell
* c
=sp
; c
< firstSp
; c
++) {
2068 TypedValue
* tv
= (TypedValue
*)c
;
2069 assert(tvIsPlausible(tv
));
2070 DataType t
= tv
->m_type
;
2071 if (IS_REFCOUNTED_TYPE(t
)) {
2072 assert(tv
->m_data
.pstr
->getCount() > 0);
2078 void traceRet(ActRec
* fp
, Cell
* sp
, void* rip
) {
2079 if (rip
== TranslatorX64::Get()->getCallToExit()) {
2082 checkFrame(fp
, sp
, /*checkLocals*/ false);
2083 assert(sp
<= (Cell
*)fp
|| fp
->m_func
->isGenerator());
2084 // check return value if stack not empty
2085 if (sp
< (Cell
*)fp
) assertTv(sp
);
2088 void CodeGenerator::emitTraceRet(CodeGenerator::Asm
& a
) {
2089 // call to a trace function
2090 a
. movq (rVmFp
, rdi
);
2091 a
. movq (rVmSp
, rsi
);
2092 a
. loadq (*rsp
, rdx
); // return ip from native stack
2093 // do the call; may use a trampoline
2094 m_tx64
->emitCall(a
, TCA(traceRet
));
2097 void CodeGenerator::cgRetCtrl(IRInstruction
* inst
) {
2098 SSATmp
* sp
= inst
->getSrc(0);
2099 SSATmp
* fp
= inst
->getSrc(1);
2101 // Make sure rVmFp and rVmSp are set appropriately
2102 emitMovRegReg(m_as
, sp
->getReg(), rVmSp
);
2103 emitMovRegReg(m_as
, fp
->getReg(), rVmFp
);
2105 // Return control to caller
2106 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2110 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2115 void CodeGenerator::emitReqBindAddr(const Func
* func
,
2118 dest
= m_tx64
->emitServiceReq(TranslatorX64::SRFlags::SRNone
,
2125 void CodeGenerator::cgJmpSwitchDest(IRInstruction
* inst
) {
2126 JmpSwitchData
* data
= inst
->getExtra
<JmpSwitchDest
>();
2127 SSATmp
* index
= inst
->getSrc(0);
2128 auto indexReg
= index
->getReg();
2130 if (!index
->isConst()) {
2131 if (data
->bounded
) {
2133 m_as
. subq(data
->base
, indexReg
);
2135 m_as
. cmpq(data
->cases
- 2, indexReg
);
2136 m_tx64
->prepareForSmash(m_as
, TranslatorX64::kJmpccLen
);
2137 TCA def
= m_tx64
->emitServiceReq(REQ_BIND_JMPCC_SECOND
, 3,
2138 m_as
.code
.frontier
, data
->defaultOff
, CC_AE
);
2142 TCA
* table
= m_tx64
->m_globalData
.alloc
<TCA
>(sizeof(TCA
), data
->cases
);
2143 TCA afterLea
= m_as
.code
.frontier
+ TranslatorX64::kLeaRipLen
;
2144 ptrdiff_t diff
= (TCA
)table
- afterLea
;
2145 assert(deltaFits(diff
, sz::dword
));
2146 m_as
. lea(rip
[diff
], rScratch
);
2147 assert(m_as
.code
.frontier
== afterLea
);
2148 m_as
. jmp(rScratch
[indexReg
*8]);
2150 for (int i
= 0; i
< data
->cases
; i
++) {
2151 emitReqBindAddr(data
->func
, table
[i
], data
->targets
[i
]);
2154 int64_t indexVal
= index
->getValInt();
2156 if (data
->bounded
) {
2157 indexVal
-= data
->base
;
2158 if (indexVal
>= data
->cases
- 2 || indexVal
< 0) {
2159 m_tx64
->emitBindJmp(m_as
, SrcKey(data
->func
, data
->defaultOff
));
2163 m_tx64
->emitBindJmp(m_as
, SrcKey(data
->func
, data
->targets
[indexVal
]));
2167 typedef FixedStringMap
<TCA
,true> SSwitchMap
;
2169 static TCA
sswitchHelperFast(const StringData
* val
,
2170 const SSwitchMap
* table
,
2172 TCA
* dest
= table
->find(val
);
2173 return dest
? *dest
: *def
;
2176 void CodeGenerator::cgLdSSwitchDestFast(IRInstruction
* inst
) {
2177 auto data
= inst
->getExtra
<LdSSwitchDestFast
>();
2179 auto table
= m_tx64
->m_globalData
.alloc
<SSwitchMap
>(64);
2180 table
->init(data
->numCases
);
2181 for (int64_t i
= 0; i
< data
->numCases
; ++i
) {
2182 table
->add(data
->cases
[i
].str
, nullptr);
2183 TCA
* addr
= table
->find(data
->cases
[i
].str
);
2184 emitReqBindAddr(data
->func
, *addr
, data
->cases
[i
].dest
);
2186 TCA
* def
= m_tx64
->m_globalData
.alloc
<TCA
>(sizeof(TCA
), 1);
2187 emitReqBindAddr(data
->func
, *def
, data
->defaultOff
);
2190 TCA(sswitchHelperFast
),
2194 .ssa(inst
->getSrc(0))
2199 static TCA
sswitchHelperSlow(TypedValue typedVal
,
2200 const StringData
** strs
,
2203 TypedValue
* cell
= tvToCell(&typedVal
);
2204 for (int i
= 0; i
< numStrs
; ++i
) {
2205 if (tvAsCVarRef(cell
).equal(strs
[i
])) return jmptab
[i
];
2207 return jmptab
[numStrs
]; // default case
2210 void CodeGenerator::cgLdSSwitchDestSlow(IRInstruction
* inst
) {
2211 auto data
= inst
->getExtra
<LdSSwitchDestSlow
>();
2213 auto strtab
= m_tx64
->m_globalData
.alloc
<const StringData
*>(
2214 sizeof(const StringData
*), data
->numCases
);
2215 auto jmptab
= m_tx64
->m_globalData
.alloc
<TCA
>(sizeof(TCA
),
2216 data
->numCases
+ 1);
2217 for (int i
= 0; i
< data
->numCases
; ++i
) {
2218 strtab
[i
] = data
->cases
[i
].str
;
2219 emitReqBindAddr(data
->func
, jmptab
[i
], data
->cases
[i
].dest
);
2221 emitReqBindAddr(data
->func
, jmptab
[data
->numCases
], data
->defaultOff
);
2224 TCA(sswitchHelperSlow
),
2228 .typedValue(inst
->getSrc(0))
2230 .imm(data
->numCases
)
2235 * It'd be nice not to have the cgMov here (and just copy propagate
2236 * the source or something), but for now we're keeping it allocated to
2237 * rVmFp so inlined calls to C++ helpers that use the rbp chain to
2238 * find the caller's ActRec will work correctly.
2240 * This instruction primarily exists to assist in optimizing away
2241 * unused activation records, so it's usually not going to happen
2244 void CodeGenerator::cgDefInlineFP(IRInstruction
* inst
) {
2245 auto const fp
= inst
->getSrc(0)->getReg();
2246 auto const fakeRet
= m_tx64
->getRetFromInlinedFrame();
2247 auto const retBCOff
= inst
->getExtra
<DefInlineFP
>()->offset
;
2249 m_as
. storeq (fakeRet
, fp
[AROFF(m_savedRip
)]);
2250 m_as
. storel (retBCOff
, fp
[AROFF(m_soff
)]);
2255 void CodeGenerator::cgInlineReturn(IRInstruction
* inst
) {
2256 auto fpReg
= inst
->getSrc(0)->getReg();
2257 assert(fpReg
== rVmFp
);
2258 m_as
. loadq (fpReg
[AROFF(m_savedRbp
)], rVmFp
);
2261 void CodeGenerator::cgReDefSP(IRInstruction
* inst
) {
2262 // TODO(#2288359): this instruction won't be necessary (for
2263 // non-generator frames) when we don't track rVmSp independently
2264 // from rVmFp. In generator frames we'll have to track offsets from
2265 // a DefGeneratorSP or something similar.
2266 auto fp
= inst
->getSrc(0)->getReg();
2267 auto dst
= inst
->getDst()->getReg();
2268 auto off
= -inst
->getExtra
<ReDefSP
>()->offset
* sizeof(Cell
);
2269 emitLea(m_as
, fp
[off
], dst
);
2272 void CodeGenerator::cgStashGeneratorSP(IRInstruction
* inst
) {
2276 void CodeGenerator::cgReDefGeneratorSP(IRInstruction
* inst
) {
2280 void CodeGenerator::cgFreeActRec(IRInstruction
* inst
) {
2281 m_as
.loadq(inst
->getSrc(0)->getReg()[AROFF(m_savedRbp
)],
2282 inst
->getDst()->getReg());
2285 void CodeGenerator::cgAllocSpill(IRInstruction
* inst
) {
2286 SSATmp
* numSlots
= inst
->getSrc(0);
2288 assert(numSlots
->isConst());
2289 int64_t n
= numSlots
->getValInt();
2290 assert(n
>= 0 && n
% 2 == 0);
2292 m_as
.sub_imm32_reg64(spillSlotsToSize(n
), reg::rsp
);
2296 void CodeGenerator::cgFreeSpill(IRInstruction
* inst
) {
2297 SSATmp
* numSlots
= inst
->getSrc(0);
2299 assert(numSlots
->isConst());
2300 int64_t n
= numSlots
->getValInt();
2301 assert(n
>= 0 && n
% 2 == 0);
2303 m_as
.add_imm32_reg64(spillSlotsToSize(n
), reg::rsp
);
2307 void CodeGenerator::cgSpill(IRInstruction
* inst
) {
2308 SSATmp
* dst
= inst
->getDst();
2309 SSATmp
* src
= inst
->getSrc(0);
2311 assert(dst
->numNeededRegs() == src
->numNeededRegs());
2312 for (int locIndex
= 0; locIndex
< src
->numNeededRegs(); ++locIndex
) {
2313 auto srcReg
= src
->getReg(locIndex
);
2315 // We do not need to mask booleans, since the IR will reload the spill
2316 auto sinfo
= dst
->getSpillInfo(locIndex
);
2317 assert(sinfo
.type() == SpillInfo::Memory
);
2318 m_as
. storeq(srcReg
, reg::rsp
[sizeof(uint64_t) * sinfo
.mem()]);
2322 void CodeGenerator::cgReload(IRInstruction
* inst
) {
2323 SSATmp
* dst
= inst
->getDst();
2324 SSATmp
* src
= inst
->getSrc(0);
2326 assert(dst
->numNeededRegs() == src
->numNeededRegs());
2327 for (int locIndex
= 0; locIndex
< src
->numNeededRegs(); ++locIndex
) {
2328 auto dstReg
= dst
->getReg(locIndex
);
2330 auto sinfo
= src
->getSpillInfo(locIndex
);
2331 assert(sinfo
.type() == SpillInfo::Memory
);
2332 m_as
. loadq(reg::rsp
[sizeof(uint64_t) * sinfo
.mem()], dstReg
);
2336 void CodeGenerator::cgStPropWork(IRInstruction
* inst
, bool genTypeStore
) {
2337 SSATmp
* obj
= inst
->getSrc(0);
2338 SSATmp
* prop
= inst
->getSrc(1);
2339 SSATmp
* src
= inst
->getSrc(2);
2340 cgStore(obj
->getReg(), prop
->getValInt(), src
, genTypeStore
);
2342 void CodeGenerator::cgStProp(IRInstruction
* inst
) {
2343 cgStPropWork(inst
, true);
2345 void CodeGenerator::cgStPropNT(IRInstruction
* inst
) {
2346 cgStPropWork(inst
, false);
2349 void CodeGenerator::cgStMemWork(IRInstruction
* inst
, bool genStoreType
) {
2350 SSATmp
* addr
= inst
->getSrc(0);
2351 SSATmp
* offset
= inst
->getSrc(1);
2352 SSATmp
* src
= inst
->getSrc(2);
2353 cgStore(addr
->getReg(), offset
->getValInt(), src
, genStoreType
);
2355 void CodeGenerator::cgStMem(IRInstruction
* inst
) {
2356 cgStMemWork(inst
, true);
2358 void CodeGenerator::cgStMemNT(IRInstruction
* inst
) {
2359 cgStMemWork(inst
, false);
2362 void CodeGenerator::cgStRefWork(IRInstruction
* inst
, bool genStoreType
) {
2363 auto destReg
= inst
->getDst()->getReg();
2364 auto addrReg
= inst
->getSrc(0)->getReg();
2365 SSATmp
* src
= inst
->getSrc(1);
2366 cgStore(addrReg
, RefData::tvOffset(), src
, genStoreType
);
2367 if (destReg
!= InvalidReg
) emitMovRegReg(m_as
, addrReg
, destReg
);
2370 void CodeGenerator::cgStRef(IRInstruction
* inst
) {
2371 cgStRefWork(inst
, true);
2373 void CodeGenerator::cgStRefNT(IRInstruction
* inst
) {
2374 cgStRefWork(inst
, false);
2377 static int64_t getLocalOffset(int64_t index
) {
2378 return -cellsToBytes(index
+ 1);
2381 static int64_t getLocalOffset(SSATmp
* index
) {
2382 return getLocalOffset(index
->getValInt());
2385 int CodeGenerator::getIterOffset(SSATmp
* tmp
) {
2386 const Func
* func
= getCurFunc();
2387 int64_t index
= tmp
->getValInt();
2388 return -cellsToBytes(((index
+ 1) * kNumIterCells
+ func
->numLocals()));
2391 void CodeGenerator::cgStLoc(IRInstruction
* inst
) {
2392 cgStore(inst
->getSrc(0)->getReg(),
2393 getLocalOffset(inst
->getExtra
<StLoc
>()->locId
),
2395 true /* store type */);
2398 void CodeGenerator::cgStLocNT(IRInstruction
* inst
) {
2399 cgStore(inst
->getSrc(0)->getReg(),
2400 getLocalOffset(inst
->getExtra
<StLocNT
>()->locId
),
2402 false /* store type */);
2405 void CodeGenerator::cgSyncVMRegs(IRInstruction
* inst
) {
2406 emitMovRegReg(m_as
, inst
->getSrc(0)->getReg(), rVmFp
);
2407 emitMovRegReg(m_as
, inst
->getSrc(1)->getReg(), rVmSp
);
2410 void CodeGenerator::cgExitTrace(IRInstruction
* inst
) {
2411 SSATmp
* func
= inst
->getSrc(0);
2412 SSATmp
* pc
= inst
->getSrc(1);
2413 SSATmp
* sp
= inst
->getSrc(2);
2414 SSATmp
* fp
= inst
->getSrc(3);
2415 SSATmp
* notTakenPC
= nullptr;
2416 IRInstruction
* toSmash
= nullptr;
2417 assert(pc
->isConst() && inst
->getNumSrcs() <= 6);
2419 TraceExitType::ExitType exitType
= getExitType(inst
->op());
2420 if (exitType
== TraceExitType::Normal
&& inst
->getExtra
<ExitTrace
>()) {
2421 // Unconditional trace exit
2422 toSmash
= inst
->getExtra
<ExitTrace
>()->toSmash
;
2424 } else if (exitType
== TraceExitType::NormalCc
) {
2425 // Exit at trace end which is the target of a conditional branch
2426 notTakenPC
= inst
->getSrc(4);
2427 assert(notTakenPC
->isConst());
2428 if (inst
->getExtra
<ExitTraceCc
>()) {
2429 toSmash
= inst
->getExtra
<ExitTraceCc
>()->toSmash
;
2433 using namespace HPHP::VM::Transl
;
2435 Asm
& a
= m_as
; // Note: m_as is the same as m_atubs for Exit Traces,
2436 // unless exit trace was moved to end of main trace
2438 emitMovRegReg(a
, sp
->getReg(), rVmSp
);
2439 emitMovRegReg(a
, fp
->getReg(), rVmFp
);
2441 // Get the SrcKey for the dest
2442 SrcKey
destSK(func
->getValFunc(), pc
->getValInt());
2445 case TraceExitType::NormalCc
:
2447 TCA smashAddr
= toSmash
->getTCA();
2448 if (smashAddr
== kIRDirectJmpInactive
) {
2449 // The jump in the main trace has been optimized away
2450 // this exit trace is no longer needed
2453 // Patch the original jcc;jmp, don't emit another
2454 IRInstruction
* jcc
= toSmash
;
2455 Opcode opc
= jcc
->op();
2456 ConditionCode cc
= queryJmpToCC(opc
);
2457 uint64_t taken
= pc
->getValInt();
2458 uint64_t notTaken
= notTakenPC
->getValInt();
2460 m_astubs
.setcc(cc
, rbyte(serviceReqArgRegs
[4]));
2461 m_tx64
->emitServiceReq(TranslatorX64::SRFlags::SRInline
,
2462 REQ_BIND_JMPCC_FIRST
,
2469 // NormalCc exit but not optimized to jcc directly to destination
2470 m_tx64
->emitBindJmp(a
, destSK
, REQ_BIND_JMP
);
2473 case TraceExitType::Normal
:
2475 TCA smashAddr
= toSmash
? toSmash
->getTCA() : nullptr;
2477 assert(smashAddr
!= kIRDirectJmpInactive
);
2478 if (smashAddr
!= kIRDirectJccJmpActive
) {
2479 // kIRDirectJccJmpActive only needs NormalCc exit in astubs
2481 m_tx64
->emitServiceReq(TranslatorX64::SRFlags::SRInline
,
2484 uint64_t(destSK
.offset()));
2488 assert(smashAddr
== kIRDirectJmpInactive
);
2489 m_tx64
->emitBindJmp(a
, destSK
, REQ_BIND_JMP
);
2493 case TraceExitType::Slow
:
2494 case TraceExitType::SlowNoProgress
:
2495 if (RuntimeOption::EnableInstructionCounts
||
2496 HPHP::Trace::moduleEnabled(HPHP::Trace::stats
, 3)) {
2497 Stats::emitInc(m_as
,
2498 Stats::opcodeToIRPostStatCounter(
2499 Op(*getCurFunc()->unit()->at(destSK
.m_offset
))),
2505 if (HPHP::Trace::moduleEnabled(HPHP::Trace::punt
, 1)) {
2506 VM::Op op
= (VM::Op
)*func
->getValFunc()->unit()->at(destSK
.m_offset
);
2507 std::string name
= folly::format(
2509 exitType
== TraceExitType::SlowNoProgress
? "-np" : "",
2510 VM::opcodeToName(op
)).str();
2511 m_tx64
->emitRecordPunt(a
, name
);
2513 if (RuntimeOption::EvalHHIRDisableTx64
) {
2514 // Emit a service request to interpret a single instruction before
2515 // creating a new translation
2516 m_tx64
->emitServiceReq(TranslatorX64::SRFlags::SRInline
,
2518 2ull, uint64_t(destSK
.offset()), 1);
2520 if (exitType
== TraceExitType::Slow
) {
2521 m_tx64
->emitBindJmp(a
, destSK
, REQ_BIND_JMP_NO_IR
);
2522 } else { // SlowNoProgress
2523 m_tx64
->emitReqRetransNoIR(a
, destSK
);
2528 case TraceExitType::GuardFailure
: {
2529 SrcRec
* destSR
= m_tx64
->getSrcRec(destSK
);
2530 m_tx64
->emitFallbackUncondJmp(a
, *destSR
);
2536 void CodeGenerator::cgExitTraceCc(IRInstruction
* inst
) {
2540 void CodeGenerator::cgExitSlow(IRInstruction
* inst
) {
2544 void CodeGenerator::cgExitSlowNoProgress(IRInstruction
* inst
) {
2548 void CodeGenerator::cgExitGuardFailure(IRInstruction
* inst
) {
2552 static void emitAssertFlagsNonNegative(CodeGenerator::Asm
& as
) {
2553 ifThen(as
, CC_NGE
, [&] { as
.ud2(); });
2556 static void emitAssertRefCount(CodeGenerator::Asm
& as
, PhysReg base
) {
2557 as
.cmpl(HPHP::RefCountStaticValue
, base
[FAST_REFCOUNT_OFFSET
]);
2558 ifThen(as
, CC_NBE
, [&] { as
.ud2(); });
2561 static void emitIncRef(CodeGenerator::Asm
& as
, PhysReg base
) {
2562 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2563 emitAssertRefCount(as
, base
);
2566 as
.incl(base
[FAST_REFCOUNT_OFFSET
]);
2567 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2568 // Assert that the ref count is greater than zero
2569 emitAssertFlagsNonNegative(as
);
2573 void CodeGenerator::cgIncRefWork(Type type
, SSATmp
* src
) {
2574 assert(type
.maybeCounted());
2575 auto increfMaybeStatic
= [&] {
2576 auto base
= src
->getReg(0);
2577 if (!type
.needsStaticBitCheck()) {
2578 emitIncRef(m_as
, base
);
2580 m_as
.cmpl(RefCountStaticValue
, base
[FAST_REFCOUNT_OFFSET
]);
2581 ifThen(m_as
, CC_NE
, [&] { emitIncRef(m_as
, base
); });
2585 if (type
.isKnownDataType()) {
2586 assert(IS_REFCOUNTED_TYPE(type
.toDataType()));
2587 increfMaybeStatic();
2589 m_as
.cmpl(KindOfRefCountThreshold
, r32(src
->getReg(1)));
2590 ifThen(m_as
, CC_NLE
, [&] { increfMaybeStatic(); });
2594 void CodeGenerator::cgIncRef(IRInstruction
* inst
) {
2595 SSATmp
* dst
= inst
->getDst();
2596 SSATmp
* src
= inst
->getSrc(0);
2597 Type type
= src
->type();
2599 cgIncRefWork(type
, src
);
2600 shuffle2(m_as
, src
->getReg(0), src
->getReg(1),
2601 dst
->getReg(0), dst
->getReg(1));
2604 void CodeGenerator::cgDecRefStack(IRInstruction
* inst
) {
2605 cgDecRefMem(inst
->getTypeParam(),
2606 inst
->getSrc(0)->getReg(),
2607 cellsToBytes(inst
->getExtra
<DecRefStack
>()->offset
),
2611 void CodeGenerator::cgDecRefThis(IRInstruction
* inst
) {
2612 SSATmp
* fp
= inst
->getSrc(0);
2613 Block
* exit
= inst
->getTaken();
2614 auto fpReg
= fp
->getReg();
2615 auto scratchReg
= rScratch
;
2617 // Load AR->m_this into rScratch
2618 m_as
.loadq(fpReg
[AROFF(m_this
)], scratchReg
);
2620 auto decrefIfAvailable
= [&] {
2621 // Check if this is available and we're not in a static context instead
2622 m_as
.testb(1, rbyte(scratchReg
));
2623 ifThen(m_as
, CC_Z
, [&] {
2624 // In the case where the refCount hits zero, we need to store zero
2625 // back to m_this in case a local destructor does debug_backtrace.
2630 true /* genZeroCheck */,
2632 a
. storeq (0, fpReg
[AROFF(m_this
)]);
2638 if (getCurFunc()->isPseudoMain()) {
2639 // In pseudo-mains, emit check for presence of m_this
2640 m_as
.testq(scratchReg
, scratchReg
);
2641 ifThen(m_as
, CC_NZ
, [&] { decrefIfAvailable(); });
2643 decrefIfAvailable();
2647 void CodeGenerator::cgDecRefKillThis(IRInstruction
* inst
) {
2648 auto const src
= inst
->getSrc(0);
2649 auto const fpReg
= inst
->getSrc(1)->getReg();
2651 assert(!inst
->getTaken());
2657 true /* genZeroCheck */,
2659 a
. storeq (0, fpReg
[AROFF(m_this
)]);
2664 void CodeGenerator::cgDecRefLoc(IRInstruction
* inst
) {
2665 cgDecRefMem(inst
->getTypeParam(),
2666 inst
->getSrc(0)->getReg(),
2667 getLocalOffset(inst
->getExtra
<DecRefLoc
>()->locId
),
2671 void CodeGenerator::cgGenericRetDecRefs(IRInstruction
* inst
) {
2672 auto const rFp
= inst
->getSrc(0)->getReg();
2673 auto const retVal
= inst
->getSrc(1);
2674 auto const numLocals
= inst
->getSrc(2)->getValInt();
2675 auto const rDest
= inst
->getDst()->getReg();
2678 RegSet retvalRegs
= retVal
->getRegs();
2679 assert(retvalRegs
.size() <= 2);
2682 * The generic decref helpers preserve these two registers.
2684 * XXX/TODO: we ideally shouldn't be moving the return value into
2685 * these regs and then back; it'd be better to precolor allocation
2686 * this way, or failing that remap the return value SSATmp with a
2687 * separate instruction. This scheme also won't work for us once we
2688 * want hackIR to support handling surprise flags, because
2689 * setprofile will look on the VM stack for the return value.
2691 auto spillRegs
= RegSet(r14
).add(r15
);
2694 * Since we're making a call using a custom ABI to the generic
2695 * decref helper, it's important that our src and dest registers are
2696 * allocated to the registers we expect, and that no other SSATmp's
2697 * are still allocated to registers at this time.
2699 const auto UNUSED expectedLiveRegs
= RegSet(rFp
).add(rDest
) | retvalRegs
;
2700 assert((m_state
.liveRegs
[m_curInst
] - expectedLiveRegs
).empty());
2701 assert(rFp
== rVmFp
&&
2702 "free locals helper assumes the frame pointer is rVmFp");
2703 assert(rDest
== rVmSp
&&
2704 "free locals helper adjusts rVmSp, which must be our dst reg");
2707 a
. lea (rFp
[AROFF(m_r
)], rDest
);
2711 // Remove overlap so we don't move registers that are already in the
2713 auto intersectedRegs
= spillRegs
& retvalRegs
;
2714 spillRegs
-= intersectedRegs
;
2715 retvalRegs
-= intersectedRegs
;
2717 auto grabPair
= [&] (std::pair
<PhysReg
,PhysReg
>& out
) {
2718 assert(!retvalRegs
.empty() && !spillRegs
.empty());
2719 retvalRegs
.findFirst(out
.first
);
2720 spillRegs
.findFirst(out
.second
);
2721 retvalRegs
.remove(out
.first
);
2722 spillRegs
.remove(out
.second
);
2725 auto savePairA
= std::make_pair(InvalidReg
, InvalidReg
);
2726 auto savePairB
= std::make_pair(InvalidReg
, InvalidReg
);
2727 if (!retvalRegs
.empty()) {
2728 grabPair(savePairA
);
2729 if (!retvalRegs
.empty()) {
2730 grabPair(savePairB
);
2734 if (savePairA
.first
!= InvalidReg
) {
2735 a
. movq (savePairA
.first
, savePairA
.second
);
2736 if (savePairB
.first
!= InvalidReg
) {
2737 a
.movq (savePairB
.first
, savePairB
.second
);
2741 auto const target
= numLocals
> kNumFreeLocalsHelpers
2742 ? m_tx64
->m_freeManyLocalsHelper
2743 : m_tx64
->m_freeLocalsHelpers
[numLocals
- 1];
2745 a
. subq (0x8, rsp
); // For parity; callee does retq $0x8.
2746 a
. lea (rFp
[-numLocals
* sizeof(TypedValue
)], rVmSp
);
2750 if (savePairA
.first
!= InvalidReg
) {
2751 a
. movq (savePairA
.second
, savePairA
.first
);
2752 if (savePairB
.first
!= InvalidReg
) {
2753 a
.movq (savePairB
.second
, savePairB
.first
);
2759 tv_release_generic(TypedValue
* tv
) {
2760 assert(VM::Transl::tx64
->stateIsDirty());
2761 assert(tv
->m_type
>= KindOfString
&& tv
->m_type
<= KindOfRef
);
2762 g_destructors
[typeToDestrIndex(tv
->m_type
)](tv
->m_data
.pref
);
2766 tv_release_typed(RefData
* pv
, DataType dt
) {
2767 assert(VM::Transl::tx64
->stateIsDirty());
2768 assert(dt
>= KindOfString
&& dt
<= KindOfRef
);
2769 g_destructors
[typeToDestrIndex(dt
)](pv
);
2772 Address
CodeGenerator::getDtorGeneric() {
2773 return (Address
)tv_release_generic
;
2776 Address
CodeGenerator::getDtorTyped() {
2777 return (Address
)tv_release_typed
;
2781 // This method generates code that checks the static bit and jumps if the bit
2782 // is set. If regIsCount is true, reg contains the _count field. Otherwise,
2783 // it's assumed to contain m_data field.
2785 // Return value: the address to be patched with the address to jump to in case
2786 // the static bit is set. If the check is unnecessary, this method retuns NULL.
2787 Address
CodeGenerator::cgCheckStaticBit(Type type
,
2790 if (!type
.needsStaticBitCheck()) return NULL
;
2793 // reg has the _count value
2794 m_as
.cmp_imm32_reg32(RefCountStaticValue
, reg
);
2796 // reg has the data pointer
2797 m_as
.cmp_imm32_disp_reg32(RefCountStaticValue
, FAST_REFCOUNT_OFFSET
, reg
);
2800 Address addrToPatch
= m_as
.code
.frontier
;
2801 m_as
.jcc8(CC_E
, addrToPatch
);
2807 // Using the given dataReg, this method generates code that checks the static
2808 // bit out of dataReg, and emits a DecRef if needed.
2809 // NOTE: the flags are left with the result of the DecRef's subtraction,
2810 // which can then be tested immediately after this.
2812 // Return value: the address to be patched if a RefCountedStaticValue check is
2813 // emitted; NULL otherwise.
2815 Address
CodeGenerator::cgCheckStaticBitAndDecRef(Type type
,
2818 assert(type
.maybeCounted());
2820 Address patchStaticCheck
= nullptr;
2821 const auto scratchReg
= rScratch
;
2823 bool canUseScratch
= dataReg
!= scratchReg
;
2825 // TODO: run experiments to check whether the 'if' code sequence
2826 // is any better than the 'else' branch below; otherwise, always
2827 // use the 'else' code sequence
2828 if (type
.needsStaticBitCheck() && canUseScratch
) {
2829 // If we need to check for static value, then load the _count into a
2830 // register to avoid doing two loads. The generated sequence is:
2832 // scratchReg = [dataReg + offset(_count)]
2833 // if scratchReg == RefCountStaticValue then skip DecRef
2834 // scratchReg = scratchReg - 1
2835 // ( if exit != NULL, emit:
2838 // [dataReg + offset(_count)] = scratchReg
2840 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2841 emitAssertRefCount(m_as
, dataReg
);
2843 // Load _count in scratchReg
2844 m_as
.loadl(dataReg
[FAST_REFCOUNT_OFFSET
], r32(scratchReg
));
2846 // Check for RefCountStaticValue
2847 patchStaticCheck
= cgCheckStaticBit(type
, scratchReg
,
2848 true /* reg has _count */);
2850 // Decrement count and store it back in memory.
2851 // If there's an exit, emit jump to it when _count would get down to 0
2852 m_as
.decq(scratchReg
);
2854 emitFwdJcc(CC_E
, exit
);
2856 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2857 // Assert that the ref count is greater than zero
2858 emitAssertFlagsNonNegative(m_as
);
2860 m_as
.store_reg32_disp_reg64(scratchReg
, FAST_REFCOUNT_OFFSET
, dataReg
);
2863 // Can't use scratch reg, so emit code that operates directly in
2864 // memory. Compared to the sequence above, this will result in one
2865 // extra load, but it has the advantage of producing a instruction
2868 // ( if needStaticBitCheck, emit :
2869 // cmp [dataReg + offset(_count)], RefCountStaticValue
2870 // je LabelAfterDecRef
2872 // ( if exit != NULL, emit:
2873 // cmp [dataReg + offset(_count)], 1
2876 // sub [dataReg + offset(_count)], 1
2878 // If necessary, check for RefCountStaticValue
2879 patchStaticCheck
= cgCheckStaticBit(type
, dataReg
,
2880 false /* passing dataReg */);
2882 // If there's an exit, emit jump to it if _count would get down to 0
2884 m_as
.cmp_imm32_disp_reg32(1, FAST_REFCOUNT_OFFSET
, dataReg
);
2885 emitFwdJcc(CC_E
, exit
);
2887 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2888 emitAssertRefCount(m_as
, dataReg
);
2892 m_as
.decl(dataReg
[FAST_REFCOUNT_OFFSET
]);
2894 if (RuntimeOption::EvalHHIRGenerateAsserts
) {
2895 // Assert that the ref count is not less than zero
2896 emitAssertFlagsNonNegative(m_as
);
2900 return patchStaticCheck
;
2905 // Returns the address to be patched with the address to jump to in case
2906 // the type is not ref-counted.
2908 Address
CodeGenerator::cgCheckRefCountedType(PhysReg typeReg
) {
2909 emitCmpTVType(m_as
, KindOfRefCountThreshold
, typeReg
);
2910 Address addrToPatch
= m_as
.code
.frontier
;
2911 m_as
.jcc8(CC_LE
, addrToPatch
);
2915 Address
CodeGenerator::cgCheckRefCountedType(PhysReg baseReg
, int64_t offset
) {
2916 emitCmpTVType(m_as
, KindOfRefCountThreshold
, baseReg
[offset
+ TVOFF(m_type
)]);
2917 Address addrToPatch
= m_as
.code
.frontier
;
2918 m_as
.jcc8(CC_LE
, addrToPatch
);
2923 // Generates dec-ref of a typed value with statically known type.
2925 void CodeGenerator::cgDecRefStaticType(Type type
,
2929 std::function
<void(Asm
&)>
2931 assert(type
!= Type::Cell
&& type
!= Type::Gen
);
2932 assert(type
.isKnownDataType());
2934 if (type
.notCounted()) return;
2936 // Check for RefCountStaticValue if needed, do the actual DecRef,
2937 // and leave flags set based on the subtract result, which is
2939 Address patchStaticCheck
;
2941 patchStaticCheck
= cgCheckStaticBitAndDecRef(type
, dataReg
, exit
);
2943 // Set exit as NULL so that the code doesn't jump to error checking.
2944 patchStaticCheck
= cgCheckStaticBitAndDecRef(type
, dataReg
, nullptr);
2947 // If not exiting on count down to zero, emit the zero-check and
2949 if (genZeroCheck
&& exit
== nullptr) {
2950 // Emit jump to m_astubs (to call release) if count got down to zero
2951 unlikelyIfBlock(CC_Z
, [&] (Asm
& a
) {
2952 if (slowPathWork
) slowPathWork(a
);
2953 // Emit the call to release in m_astubs
2954 cgCallHelper(a
, m_tx64
->getDtorCall(type
.toDataType()),
2955 InvalidReg
, InvalidReg
, kSyncPoint
,
2956 ArgGroup().reg(dataReg
));
2959 if (patchStaticCheck
) {
2960 m_as
.patchJcc8(patchStaticCheck
, m_as
.code
.frontier
);
2965 // Generates dec-ref of a typed value with dynamic (statically unknown) type,
2966 // when the type is stored in typeReg.
2968 void CodeGenerator::cgDecRefDynamicType(PhysReg typeReg
,
2971 bool genZeroCheck
) {
2972 // Emit check for ref-counted type
2973 Address patchTypeCheck
= cgCheckRefCountedType(typeReg
);
2975 // Emit check for RefCountStaticValue and the actual DecRef
2976 Address patchStaticCheck
;
2978 patchStaticCheck
= cgCheckStaticBitAndDecRef(Type::Cell
, dataReg
, exit
);
2980 patchStaticCheck
= cgCheckStaticBitAndDecRef(Type::Cell
, dataReg
, nullptr);
2983 // If not exiting on count down to zero, emit the zero-check and release call
2984 if (genZeroCheck
&& exit
== nullptr) {
2985 // Emit jump to m_astubs (to call release) if count got down to zero
2986 unlikelyIfBlock(CC_Z
, [&] (Asm
& a
) {
2987 // Emit call to release in m_astubs
2988 cgCallHelper(a
, getDtorTyped(), InvalidReg
, kSyncPoint
,
2989 ArgGroup().reg(dataReg
).reg(typeReg
));
2992 // Patch checks to jump around the DecRef
2993 if (patchTypeCheck
) m_as
.patchJcc8(patchTypeCheck
, m_as
.code
.frontier
);
2994 if (patchStaticCheck
) m_as
.patchJcc8(patchStaticCheck
, m_as
.code
.frontier
);
2998 // Generates dec-ref of a typed value with dynamic (statically
2999 // unknown) type, when all we have is the baseReg and offset of
3000 // the typed value. This method assumes that baseReg is not the
3001 // scratch register.
3003 void CodeGenerator::cgDecRefDynamicTypeMem(PhysReg baseReg
,
3006 auto scratchReg
= rScratch
;
3008 assert(baseReg
!= scratchReg
);
3010 // Emit check for ref-counted type
3011 Address patchTypeCheck
= cgCheckRefCountedType(baseReg
, offset
);
3012 if (exit
== nullptr && RuntimeOption::EvalHHIRGenericDtorHelper
) {
3014 // This PhysRegSaverParity saves rdi redundantly if
3015 // !liveRegs[m_curInst].contains(rdi), but its
3016 // necessary to maintain stack alignment. We can do better
3017 // by making the helpers adjust the stack for us in the cold
3018 // path, which calls the destructor.
3019 PhysRegSaverParity
<0> regSaver(m_as
, RegSet(rdi
));
3022 * rVmSp is ok here because this is part of the special
3023 * ABI to m_irPopRHelper. We're not using a secret dependency
3024 * on the frame or stack---we're only going to use that ABI if
3025 * we happen to have that register allocated for baseReg.
3027 if (offset
== 0 && baseReg
== rVmSp
) {
3028 // Decref'ing top of vm stack, very likely a popR
3029 m_tx64
->emitCall(m_as
, m_tx64
->m_irPopRHelper
);
3031 if (baseReg
== rsp
) {
3032 // Because we just pushed %rdi, %rsp is 8 bytes below where
3033 // offset is expecting it to be.
3034 offset
+= sizeof(int64_t);
3036 m_as
.lea(baseReg
[offset
], rdi
);
3037 m_tx64
->emitCall(m_as
, m_tx64
->m_dtorGenericStub
);
3039 recordSyncPoint(m_as
);
3041 if (patchTypeCheck
) {
3042 m_as
.patchJcc8(patchTypeCheck
, m_as
.code
.frontier
);
3046 // Load m_data into the scratch reg
3047 m_as
.loadq(baseReg
[offset
+ TVOFF(m_data
)], scratchReg
);
3049 // Emit check for RefCountStaticValue and the actual DecRef
3050 Address patchStaticCheck
= cgCheckStaticBitAndDecRef(Type::Cell
, scratchReg
,
3053 // If not exiting on count down to zero, emit the zero-check and release call
3054 if (exit
== nullptr) {
3055 // Emit jump to m_astubs (to call release) if count got down to zero
3056 unlikelyIfBlock(CC_Z
, [&] (Asm
& a
) {
3057 // Emit call to release in m_astubs
3058 a
.lea(baseReg
[offset
], scratchReg
);
3059 cgCallHelper(a
, getDtorGeneric(), InvalidReg
, kSyncPoint
,
3060 ArgGroup().reg(scratchReg
));
3064 // Patch checks to jump around the DecRef
3065 if (patchTypeCheck
) m_as
.patchJcc8(patchTypeCheck
, m_as
.code
.frontier
);
3066 if (patchStaticCheck
) m_as
.patchJcc8(patchStaticCheck
, m_as
.code
.frontier
);
3070 // Generates the dec-ref of a typed value in memory address [baseReg + offset].
3071 // This handles cases where type is either static or dynamic.
3073 void CodeGenerator::cgDecRefMem(Type type
,
3077 auto scratchReg
= rScratch
;
3078 assert(baseReg
!= scratchReg
);
3080 if (type
.needsReg()) {
3081 // The type is dynamic, but we don't have two registers available
3082 // to load the type and the data.
3083 cgDecRefDynamicTypeMem(baseReg
, offset
, exit
);
3084 } else if (type
.maybeCounted()) {
3085 m_as
.loadq(baseReg
[offset
+ TVOFF(m_data
)], scratchReg
);
3086 cgDecRefStaticType(type
, scratchReg
, exit
, true);
3090 void CodeGenerator::cgDecRefMem(IRInstruction
* inst
) {
3091 assert(inst
->getSrc(0)->type().isPtr());
3092 cgDecRefMem(inst
->getTypeParam(),
3093 inst
->getSrc(0)->getReg(),
3094 inst
->getSrc(1)->getValInt(),
3098 void CodeGenerator::cgDecRefWork(IRInstruction
* inst
, bool genZeroCheck
) {
3099 SSATmp
* src
= inst
->getSrc(0);
3100 if (!isRefCounted(src
)) return;
3101 Block
* exit
= inst
->getTaken();
3102 Type type
= src
->type();
3103 if (type
.isKnownDataType()) {
3104 cgDecRefStaticType(type
, src
->getReg(), exit
, genZeroCheck
);
3106 cgDecRefDynamicType(src
->getReg(1),
3113 void CodeGenerator::cgDecRef(IRInstruction
*inst
) {
3114 // DecRef may bring the count to zero, and run the destructor.
3115 // Generate code for this.
3116 assert(!inst
->getTaken());
3117 cgDecRefWork(inst
, true);
3120 void CodeGenerator::cgDecRefNZ(IRInstruction
* inst
) {
3121 // DecRefNZ cannot bring the count to zero.
3122 // Therefore, we don't generate zero-checking code.
3123 assert(!inst
->getTaken());
3124 cgDecRefWork(inst
, false);
3127 void CodeGenerator::cgDecRefNZOrBranch(IRInstruction
* inst
) {
3128 assert(inst
->getTaken());
3129 cgDecRefWork(inst
, true);
3132 void CodeGenerator::cgSpillFrame(IRInstruction
* inst
) {
3133 auto const sp
= inst
->getSrc(0);
3134 auto const fp
= inst
->getSrc(1);
3135 auto const func
= inst
->getSrc(2);
3136 auto const objOrCls
= inst
->getSrc(3);
3137 auto const magicName
= inst
->getExtra
<SpillFrame
>()->invName
;
3138 auto const nArgs
= inst
->getExtra
<SpillFrame
>()->numArgs
;
3140 const int64_t spOffset
= -kNumActRecCells
* sizeof(Cell
);
3142 DEBUG_ONLY
bool setThis
= true;
3144 auto spReg
= sp
->getReg();
3146 if (objOrCls
->isA(Type::Cls
)) {
3148 if (objOrCls
->isConst()) {
3149 m_as
.store_imm64_disp_reg64(uintptr_t(objOrCls
->getValClass()) | 1,
3150 spOffset
+ int(AROFF(m_this
)),
3153 Reg64 clsPtrReg
= objOrCls
->getReg();
3154 m_as
.movq (clsPtrReg
, rScratch
);
3155 m_as
.orq (1, rScratch
);
3156 m_as
.storeq(rScratch
, spReg
[spOffset
+ int(AROFF(m_this
))]);
3158 } else if (objOrCls
->isA(Type::Obj
)) {
3159 // store this pointer
3160 m_as
.store_reg64_disp_reg64(objOrCls
->getReg(),
3161 spOffset
+ int(AROFF(m_this
)),
3163 } else if (objOrCls
->isA(Type::Ctx
)) {
3164 // Stores either a this pointer or a Cctx -- statically unknown.
3165 Reg64 objOrClsPtrReg
= objOrCls
->getReg();
3166 m_as
.storeq(objOrClsPtrReg
, spReg
[spOffset
+ int(AROFF(m_this
))]);
3168 assert(objOrCls
->isA(Type::InitNull
));
3169 // no obj or class; this happens in FPushFunc
3170 int offset_m_this
= spOffset
+ int(AROFF(m_this
));
3171 // When func is either Type::FuncCls or Type::FuncCtx,
3172 // m_this/m_cls will be initialized below
3173 if (!func
->isConst() && (func
->isA(Type::FuncCtx
))) {
3174 // m_this is unioned with m_cls and will be initialized below
3177 m_as
.store_imm64_disp_reg64(0, offset_m_this
, spReg
);
3180 // actRec->m_invName
3181 // ActRec::m_invName is encoded as a pointer with bit kInvNameBit
3182 // set to distinguish it from m_varEnv and m_extrArgs
3183 uintptr_t invName
= !magicName
3185 : reinterpret_cast<uintptr_t>(magicName
) | ActRec::kInvNameBit
;
3186 m_as
.store_imm64_disp_reg64(invName
,
3187 spOffset
+ int(AROFF(m_invName
)),
3189 // actRec->m_func and possibly actRec->m_cls
3190 // Note m_cls is unioned with m_this and may overwrite previous value
3191 if (func
->type().isNull()) {
3192 assert(func
->isConst());
3193 } else if (func
->isConst()) {
3194 const Func
* f
= func
->getValFunc();
3195 m_as
. mov_imm64_reg((uint64_t)f
, rScratch
);
3196 m_as
.store_reg64_disp_reg64(rScratch
,
3197 spOffset
+ int(AROFF(m_func
)),
3199 if (func
->isA(Type::FuncCtx
)) {
3200 // Fill in m_cls if provided with both func* and class*
3201 CG_PUNT(cgAllocActRec
);
3204 int offset_m_func
= spOffset
+ int(AROFF(m_func
));
3205 m_as
.store_reg64_disp_reg64(func
->getReg(0),
3208 if (func
->isA(Type::FuncCtx
)) {
3209 int offset_m_cls
= spOffset
+ int(AROFF(m_cls
));
3210 m_as
.store_reg64_disp_reg64(func
->getReg(1),
3213 setThis
= true; /* m_this and m_cls are in a union */
3217 // actRec->m_savedRbp
3218 m_as
.store_reg64_disp_reg64(fp
->getReg(),
3219 spOffset
+ int(AROFF(m_savedRbp
)),
3222 // actRec->m_numArgsAndCtorFlag
3223 m_as
.store_imm32_disp_reg(nArgs
,
3224 spOffset
+ int(AROFF(m_numArgsAndCtorFlag
)),
3228 inst
->getDst()->getReg(),
3232 const Func
* loadClassCtor(Class
* cls
) {
3233 const Func
* f
= cls
->getCtor();
3234 if (UNLIKELY(!(f
->attrs() & AttrPublic
))) {
3236 UNUSED
MethodLookup::LookupResult res
=
3237 g_vmContext
->lookupCtorMethod(f
, cls
, true /*raise*/);
3238 assert(res
== MethodLookup::MethodFoundWithThis
);
3244 irNewInstanceHelper(Class
* cls
,
3248 Cell
* obj
= sp
- 1; // this is where the newly allocated object will go
3249 ActRec
* ar
= (ActRec
*)(uintptr_t(obj
) - sizeof(ActRec
));
3251 Instance
* newObj
= newInstanceHelper(cls
, numArgs
, ar
, prevAr
);
3252 // store obj into the stack
3253 obj
->m_data
.pobj
= newObj
;
3254 obj
->m_type
= KindOfObject
;
3258 static inline ALWAYS_INLINE Class
* getKnownClass(Class
** classCache
,
3259 const StringData
* clsName
) {
3260 Class
* cls
= *classCache
;
3261 if (UNLIKELY(cls
== nullptr)) {
3262 // lookupKnownClass does its own VMRegAnchor'ing.
3263 cls
= TargetCache::lookupKnownClass
<false>(classCache
, clsName
, true);
3264 assert(*classCache
&& *classCache
== cls
);
3270 Instance
* createClHelper(Class
* cls
, int numArgs
, ActRec
* ar
, TypedValue
* sp
) {
3271 Instance
* newObj
= newInstance(cls
);
3272 newObj
->incRefCount();
3273 return static_cast<c_Closure
*>(newObj
)->init(numArgs
, ar
, sp
);
3276 void CodeGenerator::cgAllocObj(IRInstruction
* inst
) {
3277 SSATmp
* dst
= inst
->getDst();
3278 SSATmp
* cls
= inst
->getSrc(0);
3280 Instance
* (*helper
)(Class
*) = newInstance
;
3290 void CodeGenerator::cgInlineCreateCont(IRInstruction
* inst
) {
3291 auto const& data
= *inst
->getExtra
<InlineCreateCont
>();
3292 auto const helper
= data
.origFunc
->isMethod()
3293 ? &VMExecutionContext::createContinuationHelper
<true>
3294 : &VMExecutionContext::createContinuationHelper
<false>;
3298 reinterpret_cast<TCA
>(helper
),
3302 .immPtr(data
.origFunc
)
3303 .immPtr(data
.genFunc
)
3304 .ssa(inst
->getSrc(0))
3305 .immPtr(nullptr) // getArgs array
3306 // Deliberately ignoring frameStaticClass parameter, because
3307 // it's unused if we have a $this pointer, and we don't inline
3308 // functions with a null $this.
3310 if (data
.origFunc
->isMethod()) {
3311 // We can't support a null $this.
3312 assert(inst
->getSrc(0)->isA(Type::Obj
));
3316 void CodeGenerator::cgCallArray(IRInstruction
* inst
) {
3317 Offset pc
= inst
->getExtra
<CallArray
>()->pc
;
3318 Offset after
= inst
->getExtra
<CallArray
>()->after
;
3321 args
.imm(pc
).imm(after
);
3323 // fCallArrayHelper makes the actual call by smashing its return address.
3324 cgCallHelper(m_as
, (TCA
)TranslatorX64::fCallArrayHelper
,
3325 nullptr, kSyncPoint
, args
);
3328 void CodeGenerator::cgCall(IRInstruction
* inst
) {
3329 SSATmp
* actRec
= inst
->getSrc(0);
3330 SSATmp
* returnBcOffset
= inst
->getSrc(1);
3331 SSATmp
* func
= inst
->getSrc(2);
3332 SrcRange args
= inst
->getSrcs().subpiece(3);
3333 int32_t numArgs
= args
.size();
3335 auto spReg
= actRec
->getReg();
3336 // put all outgoing arguments onto the VM stack
3337 int64_t adjustment
= (-(int64_t)numArgs
) * sizeof(Cell
);
3338 for (int32_t i
= 0; i
< numArgs
; i
++) {
3339 // Type::None here means that the simplifier proved that the value
3340 // matches the value already in memory, thus the store is redundant.
3341 if (args
[i
]->type() != Type::None
) {
3342 cgStore(spReg
, -(i
+ 1) * sizeof(Cell
), args
[i
]);
3345 // store the return bytecode offset into the outgoing actrec
3346 uint64_t returnBc
= returnBcOffset
->getValInt();
3347 m_as
.store_imm32_disp_reg(returnBc
, AROFF(m_soff
), spReg
);
3348 if (adjustment
!= 0) {
3349 m_as
.add_imm32_reg64(adjustment
, spReg
);
3352 assert(m_state
.lastMarker
);
3353 SrcKey srcKey
= SrcKey(m_state
.lastMarker
->func
, m_state
.lastMarker
->bcOff
);
3354 bool isImmutable
= (func
->isConst() && !func
->type().isNull());
3355 const Func
* funcd
= isImmutable
? func
->getValFunc() : nullptr;
3356 assert(&m_as
== &m_tx64
->getAsm());
3357 int32_t adjust
= m_tx64
->emitBindCall(srcKey
, funcd
, numArgs
);
3359 m_as
.addq (adjust
, rVmSp
);
3363 void CodeGenerator::cgCastStk(IRInstruction
*inst
) {
3364 Type type
= inst
->getTypeParam();
3365 SSATmp
* sp
= inst
->getSrc(0);
3366 uint32_t offset
= inst
->getExtra
<CastStk
>()->offset
;
3367 PhysReg spReg
= sp
->getReg();
3370 args
.addr(spReg
, cellsToBytes(offset
));
3373 if (type
.subtypeOf(Type::Bool
)) {
3374 tvCastHelper
= (TCA
)tvCastToBooleanInPlace
;
3375 } else if (type
.subtypeOf(Type::Int
)) {
3376 // if casting to integer, pass 10 as the base for the conversion
3378 tvCastHelper
= (TCA
)tvCastToInt64InPlace
;
3379 } else if (type
.subtypeOf(Type::Dbl
)) {
3380 tvCastHelper
= (TCA
)tvCastToDoubleInPlace
;
3381 } else if (type
.subtypeOf(Type::Arr
)) {
3382 tvCastHelper
= (TCA
)tvCastToArrayInPlace
;
3383 } else if (type
.subtypeOf(Type::Str
)) {
3384 tvCastHelper
= (TCA
)tvCastToStringInPlace
;
3385 } else if (type
.subtypeOf(Type::Obj
)) {
3386 tvCastHelper
= (TCA
)tvCastToObjectInPlace
;
3390 cgCallHelper(m_as
, tvCastHelper
, nullptr,
3391 kSyncPoint
, args
, DestType::None
);
3394 void CodeGenerator::cgCallBuiltin(IRInstruction
* inst
) {
3395 SSATmp
* f
= inst
->getSrc(0);
3396 auto args
= inst
->getSrcs().subpiece(1);
3397 int32_t numArgs
= args
.size();
3398 SSATmp
* dst
= inst
->getDst();
3399 auto dstReg
= dst
->getReg(0);
3400 auto dstType
= dst
->getReg(1);
3401 Type returnType
= inst
->getTypeParam();
3403 const Func
* func
= f
->getValFunc();
3404 DataType funcReturnType
= func
->returnType();
3405 int returnOffset
= HHIR_MISOFF(tvBuiltinReturn
);
3407 // RSP points to the MInstrState we need to use.
3408 // workaround the fact that rsp moves when we spill registers around call
3409 PhysReg misReg
= rScratch
;
3410 emitMovRegReg(m_as
, reg::rsp
, misReg
);
3413 if (isCppByRef(funcReturnType
)) {
3414 // first arg is pointer to storage for that return value
3415 if (isSmartPtrRef(funcReturnType
)) {
3416 returnOffset
+= TVOFF(m_data
);
3418 // misReg is pointing to an MInstrState struct on the C stack. Pass
3419 // the address of tvBuiltinReturn to the native function as the location
3420 // it can construct the return Array, String, Object, or Variant.
3421 callArgs
.addr(misReg
, returnOffset
); // &misReg[returnOffset]
3424 // non-pointer args are plain values passed by value. String, Array,
3425 // Object, and Variant are passed by const&, ie a pointer to stack memory
3426 // holding the value, so expect PtrToT types for these.
3427 // Pointers to smartptr types (String, Array, Object) need adjusting to
3428 // point to &ptr->m_data.
3429 for (int i
= 0; i
< numArgs
; i
++) {
3430 const Func::ParamInfo
& pi
= func
->params()[i
];
3431 if (TVOFF(m_data
) && isSmartPtrRef(pi
.builtinType())) {
3432 assert(args
[i
]->type().isPtr() && args
[i
]->getReg() != InvalidReg
);
3433 callArgs
.addr(args
[i
]->getReg(), TVOFF(m_data
));
3435 callArgs
.ssa(args
[i
]);
3439 // if the return value is returned by reference, we don't need the
3440 // return value from this call since we know where the value is.
3441 cgCallHelper(m_as
, Transl::Call((TCA
)func
->nativeFuncPtr()),
3442 isCppByRef(funcReturnType
) ? InvalidReg
: dstReg
,
3443 kSyncPoint
, callArgs
);
3445 // load return value from builtin
3446 // for primitive return types (int, bool), the return value
3447 // is already in dstReg (the builtin call returns in rax). For return
3448 // by reference (String, Object, Array, Variant), the builtin writes the
3449 // return value into MInstrState::tvBuiltinReturn TV, from where it
3450 // has to be tested and copied.
3451 if (dstReg
== InvalidReg
|| returnType
.isSimpleType()) {
3454 // after the call, RSP is back pointing to MInstrState and rSratch
3455 // has been clobberred.
3458 if (returnType
.isReferenceType()) {
3459 assert(isCppByRef(funcReturnType
) && isSmartPtrRef(funcReturnType
));
3460 // return type is String, Array, or Object; fold nullptr to KindOfNull
3461 m_as
. loadq (misReg
[returnOffset
], dstReg
);
3462 emitLoadImm(m_as
, returnType
.toDataType(), dstType
);
3463 emitLoadImm(m_as
, KindOfNull
, rScratch
);
3464 m_as
. testq (dstReg
, dstReg
);
3465 m_as
. cmov_reg64_reg64 (CC_Z
, rScratch
, dstType
);
3468 if (returnType
.subtypeOf(Type::Cell
)
3469 || returnType
.subtypeOf(Type::BoxedCell
)) {
3470 // return type is Variant; fold KindOfUninit to KindOfNull
3471 assert(isCppByRef(funcReturnType
) && !isSmartPtrRef(funcReturnType
));
3472 assert(misReg
!= dstType
);
3473 emitLoadTVType(m_as
, misReg
[returnOffset
+ TVOFF(m_type
)], dstType
);
3474 m_as
. loadq (misReg
[returnOffset
+ TVOFF(m_data
)], dstReg
);
3475 emitLoadImm(m_as
, KindOfNull
, rScratch
);
3476 static_assert(KindOfUninit
== 0, "KindOfUninit must be 0 for test");
3477 m_as
. testb (rbyte(dstType
), rbyte(dstType
));
3478 m_as
. cmov_reg64_reg64 (CC_Z
, rScratch
, dstType
);
3484 void CodeGenerator::cgSpillStack(IRInstruction
* inst
) {
3485 SSATmp
* dst
= inst
->getDst();
3486 SSATmp
* sp
= inst
->getSrc(0);
3487 auto const spDeficit
= inst
->getSrc(1)->getValInt();
3488 auto const spillVals
= inst
->getSrcs().subpiece(2);
3489 auto const numSpillSrcs
= spillVals
.size();
3490 auto const dstReg
= dst
->getReg();
3491 auto const spReg
= sp
->getReg();
3492 auto const spillCells
= spillValueCells(inst
);
3494 int64_t adjustment
= (spDeficit
- spillCells
) * sizeof(Cell
);
3495 for (uint32_t i
= 0; i
< numSpillSrcs
; ++i
) {
3496 const int64_t offset
= i
* sizeof(Cell
) + adjustment
;
3497 if (spillVals
[i
]->type() == Type::None
) {
3498 // The simplifier detected that we're storing the same value
3499 // already in there.
3503 auto* val
= spillVals
[i
];
3504 auto* inst
= val
->inst();
3505 while (inst
->isPassthrough()) {
3506 inst
= inst
->getPassthroughValue()->inst();
3508 // If our value came from a LdStack on the same sp and offset,
3509 // we don't need to spill it.
3510 if (inst
->op() == LdStack
&& inst
->getSrc(0) == sp
&&
3511 inst
->getExtra
<LdStack
>()->offset
* sizeof(Cell
) == offset
) {
3512 FTRACE(1, "{}: Not spilling spill value {} from {}\n",
3513 __func__
, i
, inst
->toString());
3515 cgStore(spReg
, offset
, val
);
3519 emitAdjustSp(spReg
, dstReg
, adjustment
);
3522 void CodeGenerator::emitAdjustSp(PhysReg spReg
,
3524 int64_t adjustment
/* bytes */) {
3525 if (adjustment
!= 0) {
3526 if (dstReg
!= spReg
) {
3527 m_as
. lea (spReg
[adjustment
], dstReg
);
3529 m_as
. addq (adjustment
, dstReg
);
3532 emitMovRegReg(m_as
, spReg
, dstReg
);
3536 void CodeGenerator::cgNativeImpl(IRInstruction
* inst
) {
3537 SSATmp
* func
= inst
->getSrc(0);
3538 SSATmp
* fp
= inst
->getSrc(1);
3540 assert(func
->isConst());
3541 assert(func
->type() == Type::Func
);
3543 BuiltinFunction builtinFuncPtr
= func
->getValFunc()->builtinFuncPtr();
3544 emitMovRegReg(m_as
, fp
->getReg(), argNumToRegName
[0]);
3545 m_as
.call((TCA
)builtinFuncPtr
);
3546 recordSyncPoint(m_as
);
3549 void CodeGenerator::cgLdThis(IRInstruction
* inst
) {
3550 SSATmp
* dst
= inst
->getDst();
3551 SSATmp
* src
= inst
->getSrc(0);
3552 Block
* label
= inst
->getTaken();
3553 // mov dst, [fp + 0x20]
3554 auto dstReg
= dst
->getReg();
3556 // the destination of LdThis could be dead but the instruction
3557 // itself still useful because of the checks that it does (if it has
3558 // a label). So we need to make sure there is a dstReg for this
3560 if (dstReg
!= InvalidReg
) {
3561 // instruction's result is not dead
3562 m_as
.loadq(src
->getReg()[AROFF(m_this
)], dstReg
);
3564 if (label
== NULL
) return; // no need to perform its checks
3565 if (dstReg
!= InvalidReg
) {
3567 m_as
.testb(1, rbyte(dstReg
));
3569 m_as
.testb(1, src
->getReg()[AROFF(m_this
)]);
3572 emitFwdJcc(CC_NZ
, label
);
3575 static void emitLdClsCctx(CodeGenerator::Asm
& a
,
3578 emitMovRegReg(a
, srcReg
, dstReg
);
3582 void CodeGenerator::cgLdClsCtx(IRInstruction
* inst
) {
3583 PhysReg srcReg
= inst
->getSrc(0)->getReg();
3584 PhysReg dstReg
= inst
->getDst()->getReg();
3585 // Context could be either a this object or a class ptr
3586 m_as
. testb(1, rbyte(srcReg
));
3588 [&] { emitLdClsCctx(m_as
, srcReg
, dstReg
); }, // ctx is a class
3589 [&] { emitLdObjClass(m_as
, srcReg
, dstReg
); } // ctx is this ptr
3593 void CodeGenerator::cgLdClsCctx(IRInstruction
* inst
) {
3594 PhysReg srcReg
= inst
->getSrc(0)->getReg();
3595 PhysReg dstReg
= inst
->getDst()->getReg();
3596 emitLdClsCctx(m_as
, srcReg
, dstReg
);
3599 void CodeGenerator::cgLdCtx(IRInstruction
* inst
) {
3600 PhysReg dstReg
= inst
->getDst()->getReg();
3601 PhysReg srcReg
= inst
->getSrc(0)->getReg();
3602 if (dstReg
!= InvalidReg
) {
3603 m_as
.loadq(srcReg
[AROFF(m_this
)], dstReg
);
3607 void CodeGenerator::cgLdCctx(IRInstruction
* inst
) {
3608 return cgLdCtx(inst
);
3611 void CodeGenerator::cgLdConst(IRInstruction
* inst
) {
3612 auto const dstReg
= inst
->getDst()->getReg();
3613 auto const val
= inst
->getExtra
<LdConst
>()->as
<uintptr_t>();
3614 if (dstReg
== InvalidReg
) return;
3615 emitLoadImm(m_as
, val
, dstReg
);
3618 void CodeGenerator::cgLdARFuncPtr(IRInstruction
* inst
) {
3619 SSATmp
* dst
= inst
->getDst();
3620 SSATmp
* baseAddr
= inst
->getSrc(0);
3621 SSATmp
* offset
= inst
->getSrc(1);
3623 auto dstReg
= dst
->getReg();
3624 auto baseReg
= baseAddr
->getReg();
3626 assert(offset
->isConst());
3628 m_as
.load_reg64_disp_reg64(baseReg
,
3629 offset
->getValInt() + AROFF(m_func
),
3633 void CodeGenerator::cgLdContLocalsPtr(IRInstruction
* inst
) {
3634 auto rCont
= inst
->getSrc(0)->getReg();
3635 auto rLocals
= inst
->getDst()->getReg();
3636 m_as
. loadl (rCont
[CONTOFF(m_localsOffset
)], r32(rLocals
));
3637 m_as
. addq (rCont
, rLocals
);
3640 static int getNativeTypeSize(Type type
) {
3641 if (type
.subtypeOf(Type::Int
| Type::Func
)) return sz::qword
;
3642 if (type
.subtypeOf(Type::Bool
)) return sz::byte
;
3646 void CodeGenerator::cgLdRaw(IRInstruction
* inst
) {
3647 SSATmp
* dest
= inst
->getDst();
3648 SSATmp
* addr
= inst
->getSrc(0);
3649 SSATmp
* offset
= inst
->getSrc(1);
3651 assert(!(dest
->isConst()));
3653 Reg64 addrReg
= addr
->getReg();
3654 PhysReg destReg
= dest
->getReg();
3656 if (addr
->isConst()) {
3658 emitLoadImm(m_as
, addr
->getValRawInt(), addrReg
);
3661 if (offset
->isConst()) {
3662 assert(offset
->type() == Type::Int
);
3663 int64_t kind
= offset
->getValInt();
3664 RawMemSlot
& slot
= RawMemSlot::Get(RawMemSlot::Kind(kind
));
3665 int ldSize
= slot
.getSize();
3666 int64_t off
= slot
.getOffset();
3667 if (ldSize
== sz::qword
) {
3668 m_as
.loadq (addrReg
[off
], destReg
);
3669 } else if (ldSize
== sz::dword
) {
3670 m_as
.loadl (addrReg
[off
], r32(destReg
));
3672 assert(ldSize
== sz::byte
);
3673 m_as
.loadzbl (addrReg
[off
], r32(destReg
));
3676 int ldSize
= getNativeTypeSize(dest
->type());
3677 Reg64 offsetReg
= r64(offset
->getReg());
3678 if (ldSize
== sz::qword
) {
3679 m_as
.loadq (addrReg
[offsetReg
], destReg
);
3681 // Not yet supported by our assembler
3682 assert(ldSize
== sz::byte
);
3688 void CodeGenerator::cgStRaw(IRInstruction
* inst
) {
3689 auto baseReg
= inst
->getSrc(0)->getReg();
3690 int64_t kind
= inst
->getSrc(1)->getValInt();
3691 SSATmp
* value
= inst
->getSrc(2);
3693 RawMemSlot
& slot
= RawMemSlot::Get(RawMemSlot::Kind(kind
));
3694 assert(value
->type().equals(slot
.type()));
3695 int stSize
= slot
.getSize();
3696 int64_t off
= slot
.getOffset();
3697 auto dest
= baseReg
[off
];
3699 if (value
->isConst()) {
3700 if (stSize
== sz::qword
) {
3701 m_as
.storeq(value
->getValRawInt(), dest
);
3702 } else if (stSize
== sz::dword
) {
3703 m_as
.storel(value
->getValRawInt(), dest
);
3705 assert(stSize
== sz::byte
);
3706 m_as
.storeb(value
->getValBool(), dest
);
3709 if (stSize
== sz::qword
) {
3710 m_as
.storeq(r64(value
->getReg()), dest
);
3711 } else if (stSize
== sz::dword
) {
3712 m_as
.storel(r32(value
->getReg()), dest
);
3714 assert(stSize
== sz::byte
);
3715 m_as
.storeb(rbyte(value
->getReg()), dest
);
3720 void CodeGenerator::cgLdStaticLocCached(IRInstruction
* inst
) {
3721 auto ch
= inst
->getSrc(0)->getValRawInt();
3722 auto outReg
= inst
->getDst()->getReg();
3724 m_as
.loadq (rVmTl
[ch
], outReg
);
3725 m_as
.testq (outReg
, outReg
);
3726 emitFwdJcc(m_as
, CC_Z
, inst
->getTaken());
3729 // If label is set and type is not Gen, this method generates a check
3730 // that bails to the label if the loaded typed value doesn't match type.
3731 void CodeGenerator::cgLoadTypedValue(PhysReg base
,
3733 IRInstruction
* inst
) {
3734 Block
* label
= inst
->getTaken();
3735 Type type
= inst
->getTypeParam();
3736 SSATmp
* dst
= inst
->getDst();
3738 assert(type
== dst
->type());
3739 assert(type
.needsReg());
3740 auto valueDstReg
= dst
->getReg(0);
3741 auto typeDstReg
= dst
->getReg(1);
3742 if (valueDstReg
== InvalidReg
&& typeDstReg
== InvalidReg
&&
3743 (label
== nullptr || type
== Type::Gen
)) {
3747 bool useScratchReg
= (base
== typeDstReg
&& valueDstReg
!= InvalidReg
);
3748 if (useScratchReg
) {
3749 // Save base to rScratch, because base will be overwritten.
3750 m_as
.mov_reg64_reg64(base
, reg::rScratch
);
3753 // Load type if it's not dead
3754 if (typeDstReg
!= InvalidReg
) {
3755 emitLoadTVType(m_as
, base
[off
+ TVOFF(m_type
)], typeDstReg
);
3757 // Check type needed
3758 emitGuardType(typeDstReg
, inst
);
3761 // Check type needed
3762 cgGuardTypeCell(base
, off
, inst
);
3765 // Load value if it's not dead
3766 if (valueDstReg
== InvalidReg
) return;
3768 if (useScratchReg
) {
3769 m_as
.loadq(reg::rScratch
[off
+ TVOFF(m_data
)], valueDstReg
);
3771 m_as
.loadq(base
[off
+ TVOFF(m_data
)], valueDstReg
);
3775 void CodeGenerator::cgStoreTypedValue(PhysReg base
,
3778 assert(src
->type().needsReg());
3779 m_as
.storeq(src
->getReg(0), base
[off
+ TVOFF(m_data
)]);
3780 emitStoreTVType(m_as
, src
->getReg(1), base
[off
+ TVOFF(m_type
)]);
3783 void CodeGenerator::cgStore(PhysReg base
,
3786 bool genStoreType
) {
3787 Type type
= src
->type();
3788 if (type
.needsReg()) {
3789 cgStoreTypedValue(base
, off
, src
);
3794 emitStoreTVType(m_as
, type
.toDataType(), base
[off
+ TVOFF(m_type
)]);
3796 if (type
.isNull()) {
3797 // no need to store a value for null or uninit
3800 if (src
->isConst()) {
3802 if (type
.subtypeOf(Type::Bool
| Type::Int
| Type::Dbl
|
3803 Type::Arr
| Type::StaticStr
| Type::Cls
)) {
3804 val
= src
->getValBits();
3808 m_as
.storeq(val
, base
[off
+ TVOFF(m_data
)]);
3810 zeroExtendIfBool(m_as
, src
);
3811 m_as
.storeq(src
->getReg(), base
[off
+ TVOFF(m_data
)]);
3815 void CodeGenerator::emitGuardOrFwdJcc(IRInstruction
* inst
, ConditionCode cc
) {
3816 if (cc
== CC_None
) return;
3817 Block
* label
= inst
->getTaken();
3818 if (inst
&& inst
->getTCA() == kIRDirectGuardActive
) {
3819 if (dumpIREnabled()) {
3820 m_tx64
->prepareForSmash(m_as
, TranslatorX64::kJmpccLen
);
3821 inst
->setTCA(m_as
.code
.frontier
);
3823 // Get the SrcKey for the dest
3824 SrcKey
destSK(getCurFunc(), m_curTrace
->getBcOff());
3825 SrcRec
* destSR
= m_tx64
->getSrcRec(destSK
);
3826 m_tx64
->emitFallbackCondJmp(m_as
, *destSR
, cc
);
3828 emitFwdJcc(cc
, label
);
3832 void CodeGenerator::cgLoad(PhysReg base
,
3834 IRInstruction
* inst
) {
3835 Type type
= inst
->getTypeParam();
3836 if (type
.needsReg()) {
3837 return cgLoadTypedValue(base
, off
, inst
);
3839 Block
* label
= inst
->getTaken();
3840 if (label
!= NULL
) {
3841 cgGuardTypeCell(base
, off
, inst
);
3843 if (type
.isNull()) return; // these are constants
3844 auto dstReg
= inst
->getDst()->getReg();
3845 // if dstReg == InvalidReg then the value of this load is dead
3846 if (dstReg
== InvalidReg
) return;
3848 if (type
== Type::Bool
) {
3849 m_as
.load_reg64_disp_reg32(base
, off
+ TVOFF(m_data
), dstReg
);
3851 m_as
.load_reg64_disp_reg64(base
, off
+ TVOFF(m_data
), dstReg
);
3855 void CodeGenerator::cgLdProp(IRInstruction
* inst
) {
3856 cgLoad(inst
->getSrc(0)->getReg(), inst
->getSrc(1)->getValInt(), inst
);
3859 void CodeGenerator::cgLdMem(IRInstruction
* inst
) {
3860 cgLoad(inst
->getSrc(0)->getReg(), inst
->getSrc(1)->getValInt(), inst
);
3863 void CodeGenerator::cgLdRef(IRInstruction
* inst
) {
3864 cgLoad(inst
->getSrc(0)->getReg(), RefData::tvOffset(), inst
);
3867 void CodeGenerator::recordSyncPoint(Asm
& as
,
3868 SyncOptions sync
/* = kSyncPoint */) {
3869 assert(m_state
.lastMarker
);
3871 Offset stackOff
= m_state
.lastMarker
->stackOff
;
3873 case kSyncPointAdjustOne
:
3882 Offset pcOff
= m_state
.lastMarker
->bcOff
- m_state
.lastMarker
->func
->base();
3884 FTRACE(3, "IR recordSyncPoint: {} {} {}\n", as
.code
.frontier
, pcOff
,
3886 m_tx64
->recordSyncPoint(as
, pcOff
, stackOff
);
3889 void CodeGenerator::cgLdAddr(IRInstruction
* inst
) {
3890 auto base
= inst
->getSrc(0)->getReg();
3891 int64_t offset
= inst
->getSrc(1)->getValInt();
3892 m_as
.lea (base
[offset
], inst
->getDst()->getReg());
3895 void CodeGenerator::cgLdLoc(IRInstruction
* inst
) {
3896 cgLoad(inst
->getSrc(0)->getReg(),
3897 getLocalOffset(inst
->getExtra
<LdLoc
>()->locId
),
3901 void CodeGenerator::cgLdLocAddr(IRInstruction
* inst
) {
3902 auto const fpReg
= inst
->getSrc(0)->getReg();
3903 auto const offset
= getLocalOffset(inst
->getExtra
<LdLocAddr
>()->locId
);
3904 if (inst
->getDst()->hasReg()) {
3905 m_as
.lea(fpReg
[offset
], inst
->getDst()->getReg());
3909 void CodeGenerator::cgLdStackAddr(IRInstruction
* inst
) {
3910 auto const base
= inst
->getSrc(0)->getReg();
3911 auto const offset
= cellsToBytes(inst
->getExtra
<LdStackAddr
>()->offset
);
3912 m_as
.lea (base
[offset
], inst
->getDst()->getReg());
3915 void CodeGenerator::cgLdStack(IRInstruction
* inst
) {
3916 assert(inst
->getTaken() == nullptr);
3917 cgLoad(inst
->getSrc(0)->getReg(),
3918 cellsToBytes(inst
->getExtra
<LdStack
>()->offset
),
3922 void CodeGenerator::cgGuardStk(IRInstruction
* inst
) {
3923 cgGuardTypeCell(inst
->getSrc(0)->getReg(),
3924 cellsToBytes(inst
->getExtra
<GuardStk
>()->offset
),
3928 void CodeGenerator::cgGuardLoc(IRInstruction
* inst
) {
3929 cgGuardTypeCell(inst
->getSrc(0)->getReg(),
3930 getLocalOffset(inst
->getExtra
<GuardLoc
>()->locId
),
3934 void CodeGenerator::cgDefMIStateBase(IRInstruction
* inst
) {
3935 assert(inst
->getDst()->type() == Type::PtrToCell
);
3936 assert(inst
->getDst()->getReg() == rsp
);
3939 void CodeGenerator::cgGuardType(IRInstruction
* inst
) {
3940 Type type
= inst
->getTypeParam();
3941 SSATmp
* src
= inst
->getSrc(0);
3942 auto srcTypeReg
= src
->getReg(1);
3943 assert(srcTypeReg
!= InvalidReg
);
3946 cc
= emitTypeTest(type
, srcTypeReg
, true);
3947 emitFwdJcc(cc
, inst
->getTaken());
3949 auto dstReg
= inst
->getDst()->getReg();
3950 if (dstReg
!= InvalidReg
) {
3951 emitMovRegReg(m_as
, src
->getReg(0), dstReg
);
3955 void CodeGenerator::cgGuardRefs(IRInstruction
* inst
) {
3956 assert(inst
->getNumSrcs() == 6);
3958 SSATmp
* funcPtrTmp
= inst
->getSrc(0);
3959 SSATmp
* nParamsTmp
= inst
->getSrc(1);
3960 SSATmp
* bitsPtrTmp
= inst
->getSrc(2);
3961 SSATmp
* firstBitNumTmp
= inst
->getSrc(3);
3962 SSATmp
* mask64Tmp
= inst
->getSrc(4);
3963 SSATmp
* vals64Tmp
= inst
->getSrc(5);
3964 Block
* exitLabel
= inst
->getTaken();
3966 // Get values in place
3967 assert(funcPtrTmp
->type() == Type::Func
);
3968 auto funcPtrReg
= funcPtrTmp
->getReg();
3969 assert(funcPtrReg
!= InvalidReg
);
3971 assert(nParamsTmp
->type() == Type::Int
);
3972 auto nParamsReg
= nParamsTmp
->getReg();
3973 assert(nParamsReg
!= InvalidReg
);
3975 assert(bitsPtrTmp
->type() == Type::Int
);
3976 auto bitsPtrReg
= bitsPtrTmp
->getReg();
3977 assert(bitsPtrReg
!= InvalidReg
);
3979 assert(firstBitNumTmp
->isConst() && firstBitNumTmp
->type() == Type::Int
);
3980 uint32_t firstBitNum
= (uint32_t)(firstBitNumTmp
->getValInt());
3982 assert(mask64Tmp
->type() == Type::Int
);
3983 assert(mask64Tmp
->inst()->op() == LdConst
);
3984 auto mask64Reg
= mask64Tmp
->getReg();
3985 assert(mask64Reg
!= InvalidReg
);
3986 int64_t mask64
= mask64Tmp
->getValInt();
3988 assert(vals64Tmp
->type() == Type::Int
);
3989 assert(vals64Tmp
->inst()->op() == LdConst
);
3990 auto vals64Reg
= vals64Tmp
->getReg();
3991 assert(vals64Reg
!= InvalidReg
);
3992 int64_t vals64
= vals64Tmp
->getValInt();
3994 auto thenBody
= [&] {
3995 auto bitsValReg
= rScratch
;
3996 // Load the bit values in bitValReg:
3997 // bitsValReg <- [bitsValPtr + (firstBitNum / 64)]
3998 m_as
.load_reg64_disp_reg64(bitsPtrReg
, sizeof(uint64_t) * (firstBitNum
/ 64),
4000 // bitsValReg <- bitsValReg & mask64
4001 m_as
.and_reg64_reg64(mask64Reg
, bitsValReg
);
4003 // If bitsValReg != vals64Reg, then goto Exit
4004 m_as
.cmp_reg64_reg64(bitsValReg
, vals64Reg
);
4005 emitFwdJcc(CC_NE
, exitLabel
);
4008 // If few enough args...
4009 m_as
.cmp_imm32_reg32(firstBitNum
+ 1, nParamsReg
);
4010 if (vals64
== 0 && mask64
== 0) {
4011 ifThen(m_as
, CC_NL
, thenBody
);
4012 } else if (vals64
!= 0 && vals64
!= mask64
) {
4013 emitFwdJcc(CC_L
, exitLabel
);
4015 } else if (vals64
!= 0) {
4016 ifThenElse(CC_NL
, thenBody
, /* else */ [&] {
4017 // If not special builtin...
4018 m_as
.testl(AttrVariadicByRef
, funcPtrReg
[Func::attrsOff()]);
4019 emitFwdJcc(CC_Z
, exitLabel
);
4022 ifThenElse(CC_NL
, thenBody
, /* else */ [&] {
4023 m_as
.testl(AttrVariadicByRef
, funcPtrReg
[Func::attrsOff()]);
4024 emitFwdJcc(CC_NZ
, exitLabel
);
4029 void CodeGenerator::cgLdPropAddr(IRInstruction
* inst
) {
4030 SSATmp
* dst
= inst
->getDst();
4031 SSATmp
* obj
= inst
->getSrc(0);
4032 SSATmp
* prop
= inst
->getSrc(1);
4034 assert(prop
->isConst() && prop
->type() == Type::Int
);
4036 auto dstReg
= dst
->getReg();
4037 auto objReg
= obj
->getReg();
4039 assert(objReg
!= InvalidReg
);
4040 assert(dstReg
!= InvalidReg
);
4042 int64_t offset
= prop
->getValInt();
4043 m_as
.lea_reg64_disp_reg64(objReg
, offset
, dstReg
);
4046 void CodeGenerator::cgLdClsMethod(IRInstruction
* inst
) {
4047 SSATmp
* dst
= inst
->getDst();
4048 SSATmp
* cls
= inst
->getSrc(0);
4049 SSATmp
* mSlot
= inst
->getSrc(1);
4051 assert(cls
->type() == Type::Cls
);
4052 assert(mSlot
->isConst() && mSlot
->type() == Type::Int
);
4053 uint64_t mSlotInt64
= mSlot
->getValRawInt();
4054 // We're going to multiply mSlotVal by sizeof(Func*) and use
4055 // it as a 32-bit offset (methOff) below.
4056 if (mSlotInt64
> (std::numeric_limits
<uint32_t>::max() / sizeof(Func
*))) {
4057 CG_PUNT(cgLdClsMethod_large_offset
);
4059 int32_t mSlotVal
= (uint32_t) mSlotInt64
;
4061 Reg64 dstReg
= dst
->getReg();
4062 assert(dstReg
!= InvalidReg
);
4064 Reg64 clsReg
= cls
->getReg();
4065 if (clsReg
== InvalidReg
) {
4066 CG_PUNT(LdClsMethod
);
4069 Offset vecOff
= Class::getMethodsOffset() + Class::MethodMap::vecOff();
4070 int32_t methOff
= mSlotVal
* sizeof(Func
*);
4071 m_as
.loadq(clsReg
[vecOff
], dstReg
);
4072 m_as
.loadq(dstReg
[methOff
], dstReg
);
4075 void CodeGenerator::cgLdClsMethodCache(IRInstruction
* inst
) {
4076 SSATmp
* dst
= inst
->getDst();
4077 SSATmp
* className
= inst
->getSrc(0);
4078 SSATmp
* methodName
= inst
->getSrc(1);
4079 SSATmp
* baseClass
= inst
->getSrc(2);
4080 Block
* label
= inst
->getTaken();
4082 // Stats::emitInc(a, Stats::TgtCache_StaticMethodHit);
4083 const StringData
* cls
= className
->getValStr();
4084 const StringData
* method
= methodName
->getValStr();
4085 auto const ne
= baseClass
->getValNamedEntity();
4086 TargetCache::CacheHandle ch
=
4087 TargetCache::StaticMethodCache::alloc(cls
,
4089 getContextName(getCurClass()));
4090 auto funcDestReg
= dst
->getReg(0);
4091 auto classDestReg
= dst
->getReg(1);
4092 auto offsetof_func
= offsetof(TargetCache::StaticMethodCache
, m_func
);
4093 auto offsetof_cls
= offsetof(TargetCache::StaticMethodCache
, m_cls
);
4095 assert(funcDestReg
!= InvalidReg
&& classDestReg
!= InvalidReg
);
4096 // Attempt to retrieve the func* and class* from cache
4097 m_as
.loadq(rVmTl
[ch
+ offsetof_func
], funcDestReg
);
4098 m_as
.loadq(rVmTl
[ch
+ offsetof_cls
], classDestReg
);
4099 m_as
.testq(funcDestReg
, funcDestReg
);
4100 // May have retrieved a NULL from the cache
4101 // handle case where method is not entered in the cache
4102 unlikelyIfBlock(CC_E
, [&] (Asm
& a
) {
4103 if (false) { // typecheck
4104 const UNUSED Func
* f
= StaticMethodCache::lookupIR(ch
, ne
, cls
, method
);
4106 // can raise an error if class is undefined
4108 (TCA
)StaticMethodCache::lookupIR
,
4111 ArgGroup().imm(ch
) // Handle ch
4112 .immPtr(ne
) // NamedEntity* np.second
4113 .immPtr(cls
) // className
4114 .immPtr(method
) // methodName
4116 // recordInstrCall is done in cgCallHelper
4117 a
.testq(funcDestReg
, funcDestReg
);
4118 a
.loadq(rVmTl
[ch
+ offsetof_cls
], classDestReg
);
4119 // if StaticMethodCache::lookupIR() returned NULL, jmp to label
4120 emitFwdJcc(a
, CC_Z
, label
);
4125 * Helper to emit getting the value for ActRec's m_this/m_cls slot
4126 * from a This pointer depending on whether the callee method is
4129 void CodeGenerator::emitGetCtxFwdCallWithThis(PhysReg ctxReg
,
4130 bool staticCallee
) {
4132 // Load (this->m_cls | 0x1) into ctxReg.
4133 m_as
.loadq(ctxReg
[ObjectData::getVMClassOffset()], ctxReg
);
4134 m_as
.orq(1, ctxReg
);
4136 // Just incref $this.
4137 emitIncRef(m_as
, ctxReg
);
4142 * This method is similar to emitGetCtxFwdCallWithThis above, but
4143 * whether or not the callee is a static method is unknown at JIT
4144 * time, and that is determined dynamically by looking up into the
4145 * StaticMethodFCache.
4147 void CodeGenerator::emitGetCtxFwdCallWithThisDyn(PhysReg destCtxReg
,
4150 Label NonStaticCall
, End
;
4152 // thisReg is holding $this. Should we pass it to the callee?
4153 m_as
.cmpl(1, rVmTl
[ch
+ offsetof(StaticMethodFCache
, m_static
)]);
4154 m_as
.jcc8(CC_NE
, NonStaticCall
);
4155 // If calling a static method...
4157 // Load (this->m_cls | 0x1) into destCtxReg
4158 m_as
.loadq(thisReg
[ObjectData::getVMClassOffset()], destCtxReg
);
4159 m_as
.orq(1, destCtxReg
);
4162 // Else: calling non-static method
4164 asm_label(m_as
, NonStaticCall
);
4165 emitMovRegReg(m_as
, thisReg
, destCtxReg
);
4166 emitIncRef(m_as
, destCtxReg
);
4168 asm_label(m_as
, End
);
4171 void CodeGenerator::cgGetCtxFwdCall(IRInstruction
* inst
) {
4172 PhysReg destCtxReg
= inst
->getDst()->getReg(0);
4173 SSATmp
* srcCtxTmp
= inst
->getSrc(0);
4174 const Func
* callee
= inst
->getSrc(1)->getValFunc();
4175 bool withThis
= srcCtxTmp
->isA(Type::Obj
);
4177 // Eagerly move src into the dest reg
4178 emitMovRegReg(m_as
, srcCtxTmp
->getReg(0), destCtxReg
);
4181 // If we don't know whether we have a This, we need to check dynamically
4183 m_as
.testb(1, rbyte(destCtxReg
));
4184 m_as
.jcc8(CC_NZ
, End
);
4187 // If we have a This pointer in destCtxReg, then select either This
4188 // or its Class based on whether callee is static or not
4189 emitGetCtxFwdCallWithThis(destCtxReg
, (callee
->attrs() & AttrStatic
));
4191 asm_label(m_as
, End
);
4194 void CodeGenerator::cgLdClsMethodFCache(IRInstruction
* inst
) {
4195 PhysReg funcDestReg
= inst
->getDst()->getReg(0);
4196 PhysReg destCtxReg
= inst
->getDst()->getReg(1);
4197 const Class
* cls
= inst
->getSrc(0)->getValClass();
4198 const StringData
* methName
= inst
->getSrc(1)->getValStr();
4199 SSATmp
* srcCtxTmp
= inst
->getSrc(2);
4200 PhysReg srcCtxReg
= srcCtxTmp
->getReg(0);
4201 Block
* exitLabel
= inst
->getTaken();
4202 const StringData
* clsName
= cls
->name();
4203 CacheHandle ch
= StaticMethodFCache::alloc(clsName
, methName
,
4204 getContextName(getCurClass()));
4206 assert(funcDestReg
!= InvalidReg
&& destCtxReg
!= InvalidReg
);
4207 emitMovRegReg(m_as
, srcCtxReg
, destCtxReg
);
4208 m_as
.loadq(rVmTl
[ch
], funcDestReg
);
4209 m_as
.testq(funcDestReg
, funcDestReg
);
4213 // Handle case where method is not entered in the cache
4214 unlikelyIfBlock(CC_E
, [&] (Asm
& a
) {
4215 const Func
* (*lookup
)(CacheHandle
, const Class
*, const StringData
*) =
4216 StaticMethodFCache::lookupIR
;
4217 // preserve destCtxReg across the call since it wouldn't be otherwise
4218 RegSet toSave
= m_state
.liveRegs
[inst
] | RegSet(destCtxReg
);
4219 cgCallHelper(a
, Transl::Call((TCA
)lookup
),
4220 funcDestReg
, InvalidReg
,
4226 // If entry found in target cache, jump back to m_as.
4227 // Otherwise, bail to exit label
4228 a
.testq(funcDestReg
, funcDestReg
);
4229 emitFwdJcc(a
, CC_Z
, exitLabel
);
4232 auto t
= srcCtxTmp
->type();
4233 assert(!t
.equals(Type::Cls
));
4234 if (t
.equals(Type::Cctx
)) {
4235 return; // done: destCtxReg already has srcCtxReg
4236 } else if (t
== Type::Obj
) {
4237 // unconditionally run code produced by emitGetCtxFwdCallWithThisDyn below
4239 } else if (t
== Type::Ctx
) {
4240 // dynamically check if we have a This pointer and
4241 // call emitGetCtxFwdCallWithThisDyn below
4242 m_as
.testb(1, rbyte(destCtxReg
));
4243 m_as
.jcc8(CC_NZ
, End
);
4248 // If we have a 'this' pointer ...
4249 emitGetCtxFwdCallWithThisDyn(destCtxReg
, destCtxReg
, ch
);
4251 asm_label(m_as
, End
);
4254 void CodeGenerator::cgLdClsPropAddrCached(IRInstruction
* inst
) {
4255 using namespace Transl::TargetCache
;
4256 SSATmp
* dst
= inst
->getDst();
4257 SSATmp
* cls
= inst
->getSrc(0);
4258 SSATmp
* propName
= inst
->getSrc(1);
4259 SSATmp
* clsName
= inst
->getSrc(2);
4260 SSATmp
* cxt
= inst
->getSrc(3);
4261 Block
* target
= inst
->getTaken();
4263 const StringData
* propNameString
= propName
->getValStr();
4264 const StringData
* clsNameString
= clsName
->getValStr();
4266 string
sds(Util::toLower(clsNameString
->data()) + ":" +
4267 string(propNameString
->data(), propNameString
->size()));
4268 StackStringData
sd(sds
.c_str(), sds
.size(), AttachLiteral
);
4269 CacheHandle ch
= SPropCache::alloc(&sd
);
4271 auto dstReg
= dst
->getReg();
4272 // Cls is live in the slow path call to lookupIR, so we have to be
4273 // careful not to clobber it before the branch to slow path. So
4274 // use the scratch register as a temporary destination if cls is
4275 // assigned the same register as the dst register.
4276 auto tmpReg
= dstReg
;
4277 if (dstReg
== InvalidReg
|| dstReg
== cls
->getReg()) {
4278 tmpReg
= PhysReg(rScratch
);
4281 // Could be optimized to cmp against zero when !label && dstReg == InvalidReg
4282 m_as
.loadq(rVmTl
[ch
], tmpReg
);
4283 m_as
.testq(tmpReg
, tmpReg
);
4284 unlikelyIfBlock(CC_E
, [&] (Asm
& a
) {
4286 target
? (TCA
)SPropCache::lookupIR
<false>
4287 : (TCA
)SPropCache::lookupIR
<true>, // raise on error
4289 kSyncPoint
, // could re-enter to initialize properties
4290 ArgGroup().imm(ch
).ssa(cls
).ssa(propName
).ssa(cxt
));
4292 a
.testq(tmpReg
, tmpReg
);
4293 emitFwdJcc(a
, CC_Z
, target
);
4296 if (dstReg
!= InvalidReg
) {
4297 emitMovRegReg(m_as
, tmpReg
, dstReg
);
4301 void CodeGenerator::cgLdClsPropAddr(IRInstruction
* inst
) {
4302 SSATmp
* dst
= inst
->getDst();
4303 SSATmp
* cls
= inst
->getSrc(0);
4304 SSATmp
* prop
= inst
->getSrc(1);
4305 SSATmp
* cxt
= inst
->getSrc(2);
4306 Block
* target
= inst
->getTaken();
4307 auto dstReg
= dst
->getReg();
4308 if (dstReg
== InvalidReg
&& target
) {
4309 // result is unused but this instruction was not eliminated
4310 // because its essential
4314 target
? (TCA
)SPropCache::lookupSProp
<false>
4315 : (TCA
)SPropCache::lookupSProp
<true>, // raise on error
4317 kSyncPoint
, // could re-enter to initialize properties
4318 ArgGroup().ssa(cls
).ssa(prop
).ssa(cxt
));
4320 m_as
.testq(dstReg
, dstReg
);
4321 emitFwdJcc(m_as
, CC_Z
, target
);
4325 TargetCache::CacheHandle
CodeGenerator::cgLdClsCachedCommon(
4326 IRInstruction
* inst
) {
4327 SSATmp
* dst
= inst
->getDst();
4328 const StringData
* className
= inst
->getSrc(0)->getValStr();
4329 auto ch
= TargetCache::allocKnownClass(className
);
4330 auto dstReg
= dst
->getReg();
4331 if (dstReg
== InvalidReg
) {
4332 m_as
. cmpq (0, rVmTl
[ch
]);
4334 m_as
. loadq (rVmTl
[ch
], dstReg
);
4335 m_as
. testq (dstReg
, dstReg
);
4341 void CodeGenerator::cgLdClsCached(IRInstruction
* inst
) {
4342 auto ch
= cgLdClsCachedCommon(inst
);
4343 unlikelyIfBlock(CC_E
, [&] (Asm
& a
) {
4344 // Passing only two arguments to lookupKnownClass, since the
4345 // third is ignored in the checkOnly==false case.
4347 (TCA
)TargetCache::lookupKnownClass
<false>,
4350 ArgGroup().addr(rVmTl
, intptr_t(ch
)).ssas(inst
, 0));
4354 void CodeGenerator::cgLdClsCachedSafe(IRInstruction
* inst
) {
4355 cgLdClsCachedCommon(inst
);
4356 if (Block
* taken
= inst
->getTaken()) {
4357 emitFwdJcc(CC_Z
, taken
);
4361 void CodeGenerator::cgLdCls(IRInstruction
* inst
) {
4362 SSATmp
* dst
= inst
->getDst();
4363 SSATmp
* className
= inst
->getSrc(0);
4365 CacheHandle ch
= ClassCache::alloc();
4366 cgCallHelper(m_as
, (TCA
)ClassCache::lookup
, dst
, kSyncPoint
,
4367 ArgGroup().imm(ch
).ssa(className
));
4370 static StringData
* fullConstName(SSATmp
* cls
, SSATmp
* cnsName
) {
4371 return StringData::GetStaticString(
4372 Util::toLower(cls
->getValStr()->data()) + "::" +
4373 cnsName
->getValStr()->data());
4376 void CodeGenerator::cgLdClsCns(IRInstruction
* inst
) {
4377 SSATmp
* cnsName
= inst
->getSrc(0);
4378 SSATmp
* cls
= inst
->getSrc(1);
4380 StringData
* fullName
= fullConstName(cls
, cnsName
);
4381 TargetCache::CacheHandle ch
= TargetCache::allocClassConstant(fullName
);
4382 // note that we bail from the trace if the target cache entry is empty
4383 // for this class constant or if the type assertion fails.
4384 // TODO: handle the slow case helper call.
4385 cgLoad(rVmTl
, ch
, inst
);
4388 void CodeGenerator::cgLookupClsCns(IRInstruction
* inst
) {
4389 SSATmp
* cnsName
= inst
->getSrc(0);
4390 SSATmp
* cls
= inst
->getSrc(1);
4392 assert(inst
->getTypeParam() == Type::Cell
);
4393 assert(cnsName
->isConst() && cnsName
->type() == Type::StaticStr
);
4394 assert(cls
->isConst() && cls
->type() == Type::StaticStr
);
4396 StringData
* fullName
= fullConstName(cls
, cnsName
);
4397 TargetCache::CacheHandle ch
= TargetCache::allocClassConstant(fullName
);
4400 args
.addr(rVmTl
, ch
)
4401 .immPtr(Unit::GetNamedEntity(cls
->getValStr()))
4402 .immPtr(cls
->getValStr())
4403 .immPtr(cnsName
->getValStr());
4405 cgCallHelper(m_as
, TCA(TargetCache::lookupClassConstantTv
),
4406 inst
->getDst(), kSyncPoint
, args
, DestType::TV
);
4409 void CodeGenerator::cgLdCns(IRInstruction
* inst
) {
4410 const StringData
* cnsName
= inst
->getSrc(0)->getValStr();
4412 TargetCache::CacheHandle ch
= StringData::DefCnsHandle(cnsName
, false);
4413 // Has an unlikely branch to a LookupCns
4414 cgLoad(rVmTl
, ch
, inst
);
4417 static TypedValue
lookupCnsHelper(const TypedValue
* tv
, StringData
* nm
) {
4418 assert(tv
->m_type
== KindOfUninit
);
4419 TypedValue
*cns
= nullptr;
4421 if (UNLIKELY(tv
->m_data
.pref
!= nullptr)) {
4422 ClassInfo::ConstantInfo
* ci
=
4423 (ClassInfo::ConstantInfo
*)(void*)tv
->m_data
.pref
;
4424 cns
= const_cast<Variant
&>(ci
->getDeferredValue()).asTypedValue();
4425 tvReadCell(cns
, &c1
);
4427 if (UNLIKELY(TargetCache::s_constants
!= nullptr)) {
4428 cns
= TargetCache::s_constants
->HphpArray::nvGet(nm
);
4431 cns
= Unit::loadCns(const_cast<StringData
*>(nm
));
4433 if (UNLIKELY(!cns
)) {
4434 raise_notice(Strings::UNDEFINED_CONSTANT
, nm
->data(), nm
->data());
4435 c1
.m_data
.pstr
= const_cast<StringData
*>(nm
);
4436 c1
.m_type
= KindOfStaticString
;
4438 c1
.m_type
= cns
->m_type
;
4439 c1
.m_data
= cns
->m_data
;
4445 void CodeGenerator::cgLookupCns(IRInstruction
* inst
) {
4446 SSATmp
* cnsNameTmp
= inst
->getSrc(0);
4448 assert(inst
->getTypeParam() == Type::Cell
);
4449 assert(cnsNameTmp
->isConst() && cnsNameTmp
->type() == Type::StaticStr
);
4451 const StringData
* cnsName
= cnsNameTmp
->getValStr();
4452 TargetCache::CacheHandle ch
= StringData::DefCnsHandle(cnsName
, false);
4455 args
.addr(rVmTl
, ch
)
4458 cgCallHelper(m_as
, TCA(lookupCnsHelper
),
4459 inst
->getDst(), kSyncPoint
, args
, DestType::TV
);
4463 static inline int64_t ak_exist_string_helper(StringData
* key
, ArrayData
* arr
) {
4465 if (key
->isStrictlyInteger(n
)) {
4466 return arr
->exists(n
);
4468 return arr
->exists(StrNR(key
));
4472 static int64_t ak_exist_string(StringData
* key
, ArrayData
* arr
) {
4473 int64_t res
= ak_exist_string_helper(key
, arr
);
4478 static int64_t ak_exist_int(int64_t key
, ArrayData
* arr
) {
4479 bool res
= arr
->exists(key
);
4484 static int64_t ak_exist_string_obj(StringData
* key
, ObjectData
* obj
) {
4485 if (obj
->isCollection()) {
4486 return collectionOffsetContains(obj
, key
);
4488 CArrRef arr
= obj
->o_toArray();
4489 int64_t res
= ak_exist_string_helper(key
, arr
.get());
4494 static int64_t ak_exist_int_obj(int64_t key
, ObjectData
* obj
) {
4495 if (obj
->isCollection()) {
4496 return collectionOffsetContains(obj
, key
);
4498 CArrRef arr
= obj
->o_toArray();
4499 bool res
= arr
.get()->exists(key
);
4503 void CodeGenerator::cgAKExists(IRInstruction
* inst
) {
4504 SSATmp
* arr
= inst
->getSrc(0);
4505 SSATmp
* key
= inst
->getSrc(1);
4507 if (key
->type().isNull()) {
4508 if (arr
->isA(Type::Arr
)) {
4510 (TCA
)ak_exist_string
,
4513 ArgGroup().immPtr(empty_string
.get()).ssa(arr
));
4515 m_as
.mov_imm64_reg(0, inst
->getDst()->getReg());
4522 ? (key
->isA(Type::Int
) ? (TCA
)ak_exist_int_obj
: (TCA
)ak_exist_string_obj
)
4523 : (key
->isA(Type::Int
) ? (TCA
)ak_exist_int
: (TCA
)ak_exist_string
);
4529 ArgGroup().ssa(key
).ssa(arr
));
4532 HOT_FUNC_VM
static TypedValue
* ldGblAddrHelper(StringData
* name
) {
4533 return g_vmContext
->m_globalVarEnv
->lookup(name
);
4536 HOT_FUNC_VM
static TypedValue
* ldGblAddrDefHelper(StringData
* name
) {
4537 TypedValue
* r
= g_vmContext
->m_globalVarEnv
->lookupAdd(name
);
4542 void CodeGenerator::cgLdGblAddr(IRInstruction
* inst
) {
4543 auto dstReg
= inst
->getDst()->getReg();
4544 cgCallHelper(m_as
, (TCA
)ldGblAddrHelper
, dstReg
, kNoSyncPoint
,
4545 ArgGroup().ssa(inst
->getSrc(0)));
4546 m_as
.testq(dstReg
, dstReg
);
4547 emitFwdJcc(CC_Z
, inst
->getTaken());
4550 void CodeGenerator::cgLdGblAddrDef(IRInstruction
* inst
) {
4551 cgCallHelper(m_as
, (TCA
)ldGblAddrDefHelper
, inst
->getDst(), kNoSyncPoint
,
4552 ArgGroup().ssa(inst
->getSrc(0)));
4555 void CodeGenerator::cgJmpZeroHelper(IRInstruction
* inst
,
4557 SSATmp
* src
= inst
->getSrc(0);
4559 auto srcReg
= src
->getReg();
4560 if (src
->isConst()) {
4561 bool valIsZero
= src
->getValRawInt() == 0;
4562 if ((cc
== CC_Z
&& valIsZero
) ||
4563 (cc
== CC_NZ
&& !valIsZero
)) {
4564 // assert(false) here after new simplifier pass, t2019643
4565 // For now, materialize the test condition and use a Jcc
4566 m_as
.xor_reg64_reg64(rScratch
, rScratch
);
4567 m_as
.test_reg64_reg64(rScratch
, rScratch
);
4569 // Update the instr opcode since cgExitTrace uses it
4570 // to determine correct cc for service request.
4571 inst
->setOpcode(JmpZero
);
4573 // Fall through to next bytecode, disable DirectJmp
4574 inst
->setTCA(kIRDirectJmpInactive
);
4578 if (src
->type() == Type::Bool
) {
4579 m_as
.testb(Reg8(int(srcReg
)), Reg8(int(srcReg
)));
4581 m_as
.test_reg64_reg64(srcReg
, srcReg
);
4585 emitJccDirectExit(inst
, cc
);
4588 void CodeGenerator::cgJmpZero(IRInstruction
* inst
) {
4589 cgJmpZeroHelper(inst
, CC_Z
);
4592 void CodeGenerator::cgJmpNZero(IRInstruction
* inst
) {
4593 cgJmpZeroHelper(inst
, CC_NZ
);
4596 void CodeGenerator::cgJmp_(IRInstruction
* inst
) {
4597 assert(inst
->getNumSrcs() == inst
->getTaken()->getLabel()->getNumDsts());
4598 if (unsigned n
= inst
->getNumSrcs()) {
4599 // Parallel-copy sources to the label's destination registers.
4600 // TODO: t2040286: this only works if all destinations fit in registers.
4601 SrcRange srcs
= inst
->getSrcs();
4602 DstRange dsts
= inst
->getTaken()->getLabel()->getDsts();
4604 for (unsigned i
= 0, j
= 0; i
< n
; i
++) {
4605 assert(srcs
[i
]->type().subtypeOf(dsts
[i
].type()));
4606 SSATmp
*dst
= &dsts
[i
], *src
= srcs
[i
];
4607 if (dst
->getReg(0) == InvalidReg
) continue; // dst is unused.
4608 // first dst register
4610 args
[j
++].setDstReg(dst
->getReg(0));
4611 // second dst register, if any
4612 if (dst
->numNeededRegs() == 2) {
4613 if (src
->numNeededRegs() < 2) {
4614 // src has known data type, but dst doesn't - pass immediate type
4615 assert(src
->type().isKnownDataType());
4616 args
.imm(src
->type().toDataType());
4618 // pass src's second register
4619 assert(src
->getReg(1) != InvalidReg
);
4620 args
.reg(src
->getReg(1));
4622 args
[j
++].setDstReg(dst
->getReg(1));
4625 shuffleArgs(m_as
, args
);
4627 if (!m_state
.noTerminalJmp_
) {
4628 emitFwdJmp(inst
->getTaken());
4632 void CodeGenerator::cgJmpIndirect(IRInstruction
* inst
) {
4633 m_as
.jmp(inst
->getSrc(0)->getReg());
4636 void CodeGenerator::cgCheckInit(IRInstruction
* inst
) {
4637 Block
* label
= inst
->getTaken();
4639 SSATmp
* src
= inst
->getSrc(0);
4641 if (src
->type().isInit()) return;
4643 auto typeReg
= src
->getReg(1);
4644 assert(typeReg
!= InvalidReg
);
4646 static_assert(KindOfUninit
== 0, "cgCheckInit assumes KindOfUninit == 0");
4647 m_as
.testb (rbyte(typeReg
), rbyte(typeReg
));
4648 emitFwdJcc(CC_Z
, label
);
4651 void CodeGenerator::cgCheckInitMem(IRInstruction
* inst
) {
4652 Block
* label
= inst
->getTaken();
4654 SSATmp
* base
= inst
->getSrc(0);
4655 int64_t offset
= inst
->getSrc(1)->getValInt();
4656 Type t
= base
->type().deref();
4657 if (t
.isInit()) return;
4658 emitCmpTVType(m_as
, KindOfUninit
, base
->getReg()[offset
+ TVOFF(m_type
)]);
4659 emitFwdJcc(CC_Z
, label
);
4662 void CodeGenerator::cgExitWhenSurprised(IRInstruction
* inst
) {
4663 Block
* label
= inst
->getTaken();
4664 m_tx64
->emitTestSurpriseFlags(m_as
);
4665 emitFwdJcc(CC_NZ
, label
);
4668 void CodeGenerator::cgExitOnVarEnv(IRInstruction
* inst
) {
4669 SSATmp
* fp
= inst
->getSrc(0);
4670 Block
* label
= inst
->getTaken();
4672 assert(!(fp
->isConst()));
4674 auto fpReg
= fp
->getReg();
4675 m_as
. cmpq (0, fpReg
[AROFF(m_varEnv
)]);
4676 emitFwdJcc(CC_NE
, label
);
4679 void CodeGenerator::cgReleaseVVOrExit(IRInstruction
* inst
) {
4680 auto* const label
= inst
->getTaken();
4681 auto const rFp
= inst
->getSrc(0)->getReg();
4683 m_as
. cmpq (0, rFp
[AROFF(m_varEnv
)]);
4684 unlikelyIfBlock(CC_NZ
, [&] (Asm
& a
) {
4685 a
. testl (ActRec::kExtraArgsBit
, rFp
[AROFF(m_varEnv
)]);
4686 emitFwdJcc(a
, CC_Z
, label
);
4689 TCA(static_cast<void (*)(ActRec
*)>(ExtraArgs::deallocate
)),
4692 ArgGroup().reg(rFp
),
4698 void CodeGenerator::cgBoxPtr(IRInstruction
* inst
) {
4699 SSATmp
* dst
= inst
->getDst();
4700 SSATmp
* addr
= inst
->getSrc(0);
4701 auto base
= addr
->getReg();
4702 auto dstReg
= dst
->getReg();
4703 emitMovRegReg(m_as
, base
, dstReg
);
4704 ConditionCode cc
= emitTypeTest(Type::BoxedCell
, base
[TVOFF(m_type
)], true);
4705 ifThen(m_as
, cc
, [&] {
4706 cgCallHelper(m_as
, (TCA
)tvBox
, dstReg
, kNoSyncPoint
, ArgGroup().ssa(addr
));
4710 void CodeGenerator::cgDefCns(IRInstruction
* inst
) {
4711 UNUSED SSATmp
* dst
= inst
->getDst();
4712 UNUSED SSATmp
* cnsName
= inst
->getSrc(0);
4713 UNUSED SSATmp
* val
= inst
->getSrc(1);
4714 using namespace TargetCache
;
4718 // TODO: Kill this #2031980
4719 static StringData
* concat_value(TypedValue tv1
, TypedValue tv2
) {
4720 return concat_tv(tv1
.m_type
, tv1
.m_data
.num
, tv2
.m_type
, tv2
.m_data
.num
);
4723 void CodeGenerator::cgConcat(IRInstruction
* inst
) {
4724 SSATmp
* dst
= inst
->getDst();
4725 SSATmp
* tl
= inst
->getSrc(0);
4726 SSATmp
* tr
= inst
->getSrc(1);
4728 Type lType
= tl
->type();
4729 Type rType
= tr
->type();
4730 // We have specialized helpers for concatenating two strings, a
4731 // string and an int, and an int and a string.
4732 void* fptr
= nullptr;
4733 if (lType
.isString() && rType
.isString()) {
4734 fptr
= (void*)concat_ss
;
4735 } else if (lType
.isString() && rType
== Type::Int
) {
4736 fptr
= (void*)concat_si
;
4737 } else if (lType
== Type::Int
&& rType
.isString()) {
4738 fptr
= (void*)concat_is
;
4741 cgCallHelper(m_as
, (TCA
)fptr
, dst
, kNoSyncPoint
,
4745 if (lType
.subtypeOf(Type::Obj
) || lType
.needsReg() ||
4746 rType
.subtypeOf(Type::Obj
) || rType
.needsReg()) {
4749 cgCallHelper(m_as
, (TCA
)concat_value
, dst
, kNoSyncPoint
,
4750 ArgGroup().typedValue(tl
).typedValue(tr
));
4754 void CodeGenerator::cgInterpOne(IRInstruction
* inst
) {
4755 SSATmp
* fp
= inst
->getSrc(0);
4756 SSATmp
* sp
= inst
->getSrc(1);
4757 SSATmp
* pcOffTmp
= inst
->getSrc(2);
4758 SSATmp
* spAdjustmentTmp
= inst
->getSrc(3);
4759 Type resultType
= inst
->getTypeParam();
4760 int64_t pcOff
= pcOffTmp
->getValInt();
4762 auto opc
= *(getCurFunc()->unit()->at(pcOff
));
4763 void* interpOneHelper
= interpOneEntryPoints
[opc
];
4765 auto dstReg
= InvalidReg
;
4766 cgCallHelper(m_as
, (TCA
)interpOneHelper
, dstReg
, kSyncPoint
,
4767 ArgGroup().ssa(fp
).ssa(sp
).imm(pcOff
));
4769 auto newSpReg
= inst
->getDst()->getReg();
4770 assert(newSpReg
== sp
->getReg());
4772 int64_t spAdjustBytes
= cellsToBytes(spAdjustmentTmp
->getValInt());
4773 if (spAdjustBytes
!= 0) {
4774 m_as
.addq(spAdjustBytes
, newSpReg
);
4778 void CodeGenerator::cgInterpOneCF(IRInstruction
* inst
) {
4779 SSATmp
* fp
= inst
->getSrc(0);
4780 SSATmp
* sp
= inst
->getSrc(1);
4781 int64_t pcOff
= inst
->getSrc(2)->getValInt();
4783 auto opc
= *(getCurFunc()->unit()->at(pcOff
));
4784 void* interpOneHelper
= interpOneEntryPoints
[opc
];
4786 auto dstReg
= InvalidReg
;
4787 cgCallHelper(m_as
, (TCA
)interpOneHelper
, dstReg
, kSyncPoint
,
4788 ArgGroup().ssa(fp
).ssa(sp
).imm(pcOff
));
4790 // The interpOne method returns a pointer to the current ExecutionContext
4791 // in rax. Use it read the 'm_fp' and 'm_stack.m_top' fields into the
4792 // rVmFp and rVmSp registers.
4793 m_as
.loadq(rax
[offsetof(VMExecutionContext
, m_fp
)], rVmFp
);
4794 m_as
.loadq(rax
[offsetof(VMExecutionContext
, m_stack
) +
4795 Stack::topOfStackOffset()], rVmSp
);
4797 m_tx64
->emitServiceReq(TranslatorX64::SRFlags::SREmitInA
, REQ_RESUME
, 0ull);
4800 void CodeGenerator::cgDefFunc(IRInstruction
* inst
) {
4801 SSATmp
* dst
= inst
->getDst();
4802 SSATmp
* func
= inst
->getSrc(0);
4803 cgCallHelper(m_as
, (TCA
)defFuncHelper
, dst
, kSyncPoint
,
4804 ArgGroup().ssa(func
), DestType::None
);
4807 void CodeGenerator::cgFillContThis(IRInstruction
* inst
) {
4808 SSATmp
* cont
= inst
->getSrc(0);
4809 auto baseReg
= inst
->getSrc(1)->getReg();
4810 int64_t offset
= inst
->getSrc(2)->getValInt();
4811 auto scratch
= rScratch
;
4812 auto contReg
= cont
->getReg();
4814 m_as
.loadq(contReg
[CONTOFF(m_obj
)], scratch
);
4815 m_as
.testq(scratch
, scratch
);
4816 ifThen(m_as
, CC_NZ
, [&] {
4817 m_as
.addl(1, scratch
[FAST_REFCOUNT_OFFSET
]);
4818 m_as
.storeq(scratch
, baseReg
[offset
+ TVOFF(m_data
)]);
4819 emitStoreTVType(m_as
, KindOfObject
, baseReg
[offset
+ TVOFF(m_type
)]);
4823 void CodeGenerator::cgContEnter(IRInstruction
* inst
) {
4824 auto contAR
= inst
->getSrc(0);
4825 auto addr
= inst
->getSrc(1);
4826 auto returnOff
= inst
->getSrc(2);
4827 auto curFp
= inst
->getSrc(3)->getReg();
4828 auto contARReg
= contAR
->getReg();
4830 m_as
. storel (returnOff
->getValInt(), contARReg
[AROFF(m_soff
)]);
4831 m_as
. storeq (curFp
, contARReg
[AROFF(m_savedRbp
)]);
4832 m_as
. movq (contARReg
, rStashedAR
);
4834 m_as
. call (addr
->getReg());
4837 void CodeGenerator::emitContVarEnvHelperCall(SSATmp
* fp
, TCA helper
) {
4838 auto scratch
= rScratch
;
4840 m_as
. loadq (fp
->getReg()[AROFF(m_varEnv
)], scratch
);
4841 m_as
. testq (scratch
, scratch
);
4842 unlikelyIfBlock(CC_NZ
, [&] (Asm
& a
) {
4843 cgCallHelper(a
, helper
, InvalidReg
, kNoSyncPoint
,
4844 ArgGroup().ssa(fp
));
4848 void CodeGenerator::cgUnlinkContVarEnv(IRInstruction
* inst
) {
4849 emitContVarEnvHelperCall(
4851 (TCA
)VMExecutionContext::packContVarEnvLinkage
);
4854 void CodeGenerator::cgLinkContVarEnv(IRInstruction
* inst
) {
4855 emitContVarEnvHelperCall(
4857 (TCA
)VMExecutionContext::unpackContVarEnvLinkage
);
4860 void CodeGenerator::cgContRaiseCheck(IRInstruction
* inst
) {
4861 SSATmp
* cont
= inst
->getSrc(0);
4862 m_as
.test_imm32_disp_reg32(0x1, CONTOFF(m_should_throw
),
4864 emitFwdJcc(CC_NZ
, inst
->getTaken());
4867 void CodeGenerator::cgContPreNext(IRInstruction
* inst
) {
4868 auto contReg
= inst
->getSrc(0)->getReg();
4870 const Offset doneOffset
= CONTOFF(m_done
);
4871 static_assert((doneOffset
+ 1) == CONTOFF(m_running
),
4872 "m_done should immediately precede m_running");
4873 // Check m_done and m_running at the same time
4874 m_as
.test_imm32_disp_reg32(0x0101, doneOffset
, contReg
);
4875 emitFwdJcc(CC_NZ
, inst
->getTaken());
4878 m_as
.add_imm64_disp_reg64(0x1, CONTOFF(m_index
), contReg
);
4880 m_as
.store_imm8_disp_reg(0x1, CONTOFF(m_running
), contReg
);
4883 void CodeGenerator::cgContStartedCheck(IRInstruction
* inst
) {
4884 m_as
.cmp_imm64_disp_reg64(0, CONTOFF(m_index
),
4885 inst
->getSrc(0)->getReg());
4886 emitFwdJcc(CC_L
, inst
->getTaken());
4889 void CodeGenerator::cgIterNextK(IRInstruction
* inst
) {
4890 cgIterNextCommon(inst
, true);
4893 void CodeGenerator::cgIterNext(IRInstruction
* inst
) {
4894 cgIterNextCommon(inst
, false);
4897 void CodeGenerator::cgIterNextCommon(IRInstruction
* inst
, bool isNextK
) {
4898 PhysReg fpReg
= inst
->getSrc(0)->getReg();
4900 args
.addr(fpReg
, getIterOffset(inst
->getSrc(1)))
4901 .addr(fpReg
, getLocalOffset(inst
->getSrc(2)));
4903 args
.addr(fpReg
, getLocalOffset(inst
->getSrc(3)));
4905 TCA helperAddr
= isNextK
? (TCA
)iter_next_key
: (TCA
)iter_next
;
4906 cgCallHelper(m_as
, helperAddr
, inst
->getDst(), kSyncPoint
, args
);
4909 void CodeGenerator::cgIterInit(IRInstruction
* inst
) {
4910 cgIterInitCommon(inst
, false);
4913 void iterFreeHelper(Iter
* iter
) {
4917 void CodeGenerator::cgIterFree(IRInstruction
* inst
) {
4918 PhysReg fpReg
= inst
->getSrc(0)->getReg();
4919 int64_t offset
= getIterOffset(inst
->getSrc(1));
4920 cgCallHelper(m_as
, (TCA
)iterFreeHelper
, InvalidReg
, kSyncPoint
,
4921 ArgGroup().addr(fpReg
, offset
));
4924 void CodeGenerator::cgIterInitK(IRInstruction
* inst
) {
4925 cgIterInitCommon(inst
, true);
4928 void CodeGenerator::cgIterInitCommon(IRInstruction
* inst
, bool isInitK
) {
4929 PhysReg fpReg
= inst
->getSrc(1)->getReg();
4930 int64_t iterOffset
= getIterOffset(inst
->getSrc(2));
4931 int64_t valLocalOffset
= getLocalOffset(inst
->getSrc(3));
4932 SSATmp
* src
= inst
->getSrc(0);
4934 args
.addr(fpReg
, iterOffset
).ssa(src
);
4935 if (src
->isArray()) {
4936 args
.addr(fpReg
, valLocalOffset
);
4938 args
.addr(fpReg
, getLocalOffset(inst
->getSrc(4)));
4940 TCA helperAddr
= isInitK
? (TCA
)new_iter_array_key
: (TCA
)new_iter_array
;
4941 cgCallHelper(m_as
, helperAddr
, inst
->getDst(), kSyncPoint
, args
);
4943 assert(src
->type() == Type::Obj
);
4944 args
.imm(uintptr_t(getCurClass())).addr(fpReg
, valLocalOffset
);
4946 args
.addr(fpReg
, getLocalOffset(inst
->getSrc(4)));
4950 // new_iter_object decrefs its src object if it propagates an
4951 // exception out, so we use kSyncPointAdjustOne, which adjusts the
4952 // stack pointer by 1 stack element on an unwind, skipping over
4954 cgCallHelper(m_as
, (TCA
)new_iter_object
, inst
->getDst(),
4955 kSyncPointAdjustOne
, args
);
4959 void CodeGenerator::cgIncStat(IRInstruction
*inst
) {
4960 Stats::emitInc(m_as
,
4961 Stats::StatCounter(inst
->getSrc(0)->getValInt()),
4962 inst
->getSrc(1)->getValInt(),
4964 inst
->getSrc(2)->getValBool());
4967 void CodeGenerator::cgIncTransCounter(IRInstruction
* inst
) {
4968 m_tx64
->emitTransCounterInc(m_as
);
4971 void CodeGenerator::cgDbgAssertRefCount(IRInstruction
* inst
) {
4972 emitAssertRefCount(m_as
, inst
->getSrc(0)->getReg());
4975 void traceCallback(ActRec
* fp
, Cell
* sp
, int64_t pcOff
, void* rip
) {
4976 if (HPHP::Trace::moduleEnabled(HPHP::Trace::hhirTracelets
)) {
4977 FTRACE(0, "{} {} {}\n", fp
->m_func
->fullName()->data(), pcOff
, rip
);
4979 checkFrame(fp
, sp
, /*checkLocals*/true);
4982 void CodeGenerator::cgVerifyParamCls(IRInstruction
* inst
) {
4983 SSATmp
* objClass
= inst
->getSrc(0);
4984 assert(!objClass
->isConst());
4985 auto objClassReg
= objClass
->getReg();
4986 SSATmp
* constraint
= inst
->getSrc(1);
4988 if (constraint
->isConst()) {
4989 m_as
. cmpq(constraint
->getValClass(), objClassReg
);
4991 m_as
. cmpq(constraint
->getReg(), objClassReg
);
4994 // The native call for this instruction is the slow path that does
4995 // proper subtype checking. The comparison above is just to
4996 // short-circuit the overhead when the Classes are an exact match.
4997 ifThen(m_as
, CC_NE
, [&]{ cgCallNative(inst
); });
5000 void CodeGenerator::emitTraceCall(CodeGenerator::Asm
& as
, int64_t pcOff
,
5001 Transl::TranslatorX64
* tx64
) {
5002 // call to a trace function
5003 as
.mov_imm64_reg((int64_t)as
.code
.frontier
, reg::rcx
);
5004 as
.mov_reg64_reg64(rVmFp
, reg::rdi
);
5005 as
.mov_reg64_reg64(rVmSp
, reg::rsi
);
5006 as
.mov_imm64_reg(pcOff
, reg::rdx
);
5007 // do the call; may use a trampoline
5008 tx64
->emitCall(as
, (TCA
)traceCallback
);
5011 static void patchJumps(Asm
& as
, CodegenState
& state
, Block
* block
) {
5012 void* list
= state
.patches
[block
];
5013 Address labelAddr
= as
.code
.frontier
;
5015 int32_t* toPatch
= (int32_t*)list
;
5016 int32_t diffToNext
= *toPatch
;
5017 ssize_t diff
= labelAddr
- ((Address
)list
+ sizeof(int32_t));
5018 *toPatch
= safe_cast
<int32_t>(diff
); // patch the jump address
5019 if (diffToNext
== 0) break;
5020 void* next
= (TCA
)list
- diffToNext
;
5025 void CodeGenerator::cgBlock(Block
* block
, vector
<TransBCMapping
>* bcMap
) {
5026 for (IRInstruction
& instr
: *block
) {
5027 IRInstruction
* inst
= &instr
;
5028 if (inst
->op() == Marker
) {
5029 m_state
.lastMarker
= inst
->getExtra
<Marker
>();
5030 if (m_tx64
&& m_tx64
->isTransDBEnabled() && bcMap
) {
5031 bcMap
->push_back((TransBCMapping
){Offset(m_state
.lastMarker
->bcOff
),
5033 m_astubs
.code
.frontier
});
5037 auto nuller
= folly::makeGuard([&]{ m_curInst
= nullptr; });
5038 auto* addr
= cgInst(inst
);
5039 if (m_state
.asmInfo
&& addr
) {
5040 m_state
.asmInfo
->instRanges
[inst
] = TcaRange(addr
, m_as
.code
.frontier
);
5041 m_state
.asmInfo
->asmRanges
[block
] =
5042 TcaRange(m_state
.asmInfo
->asmRanges
[block
].start(), m_as
.code
.frontier
);
5047 void cgTrace(Trace
* trace
, Asm
& amain
, Asm
& astubs
, Transl::TranslatorX64
* tx64
,
5048 vector
<TransBCMapping
>* bcMap
, CodegenState
& state
) {
5049 state
.lastMarker
= nullptr;
5050 if (RuntimeOption::EvalHHIRGenerateAsserts
&& trace
->isMain()) {
5051 CodeGenerator::emitTraceCall(amain
, trace
->getBcOff(), tx64
);
5053 auto chooseAs
= [&](Block
* b
) {
5054 return b
->getHint() != Block::Unlikely
? &amain
: &astubs
;
5056 auto& blocks
= trace
->getBlocks();
5057 for (auto it
= blocks
.begin(), end
= blocks
.end(); it
!= end
;) {
5058 Block
* block
= *it
; ++it
;
5059 Asm
* as
= chooseAs(block
);
5060 TCA asmStart
= as
->code
.frontier
;
5061 TCA astubsStart
= astubs
.code
.frontier
;
5062 patchJumps(*as
, state
, block
);
5064 // Grab the next block that will go into this assembler
5065 Block
* nextThisAs
= nullptr;
5066 for (auto next
= it
; next
!= end
; ++next
) {
5067 if (chooseAs(*next
) == as
) {
5073 // If the block ends with a Jmp_ to the next block for this
5074 // assembler, it doesn't need to actually emit a jmp.
5075 IRInstruction
* last
= block
->back();
5076 state
.noTerminalJmp_
=
5077 last
->op() == Jmp_
&& last
->getTaken() == nextThisAs
;
5079 CodeGenerator
cg(trace
, *as
, astubs
, tx64
, state
);
5080 if (state
.asmInfo
) {
5081 state
.asmInfo
->asmRanges
[block
] = TcaRange(asmStart
, as
->code
.frontier
);
5083 cg
.cgBlock(block
, bcMap
);
5084 Block
* next
= block
->getNext();
5085 if (next
&& next
!= nextThisAs
) {
5086 // if there's a fallthrough block and it's not the next thing
5087 // going into this assembler, then emit a jump to it.
5088 CodeGenerator::emitFwdJmp(*as
, next
, state
);
5090 if (state
.asmInfo
) {
5091 state
.asmInfo
->asmRanges
[block
] = TcaRange(asmStart
, as
->code
.frontier
);
5092 if (as
!= &astubs
) {
5093 state
.asmInfo
->astubRanges
[block
] = TcaRange(astubsStart
,
5094 astubs
.code
.frontier
);
5100 void CodeGenerator::print() const {
5101 JIT::print(std::cout
, m_curTrace
, m_state
.asmInfo
);
5105 * Compute and save registers that are live *across* each inst, not including
5106 * registers whose lifetimes end at inst, nor registers defined by inst.
5108 LiveRegs
computeLiveRegs(const IRFactory
* factory
, Block
* start_block
) {
5109 StateVector
<Block
, RegSet
> liveMap(factory
, RegSet());
5110 LiveRegs
live_regs(factory
, RegSet());
5113 RegSet
& live
= liveMap
[block
];
5114 if (Block
* taken
= block
->getTaken()) live
= liveMap
[taken
];
5115 if (Block
* next
= block
->getNext()) live
|= liveMap
[next
];
5116 for (auto it
= block
->end(); it
!= block
->begin(); ) {
5117 IRInstruction
& inst
= *--it
;
5118 for (const SSATmp
& dst
: inst
.getDsts()) {
5119 live
-= dst
.getRegs();
5121 live_regs
[inst
] = live
;
5122 for (SSATmp
* src
: inst
.getSrcs()) {
5123 live
|= src
->getRegs();
5127 factory
->numBlocks(),
5133 // select instructions for the trace and its exits
5134 void genCodeForTrace(Trace
* trace
,
5135 CodeGenerator::Asm
& as
,
5136 CodeGenerator::Asm
& astubs
,
5137 IRFactory
* irFactory
,
5138 vector
<TransBCMapping
>* bcMap
,
5139 Transl::TranslatorX64
* tx64
,
5141 assert(trace
->isMain());
5142 LiveRegs live_regs
= computeLiveRegs(irFactory
, trace
->front());
5143 CodegenState
state(irFactory
, live_regs
, asmInfo
);
5144 cgTrace(trace
, as
, astubs
, tx64
, bcMap
, state
);
5145 for (Trace
* exit
: trace
->getExitTraces()) {
5146 cgTrace(exit
, astubs
, astubs
, tx64
, nullptr, state
);