Dont use rAsm implicitly in x64Assembler
[hiphop-php.git] / hphp / runtime / vm / jit / code-gen-arm.cpp
blobbd7e7e154c33f8d11f8e870fc113b4a5fa1e5aa3
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/code-gen-arm.h"
18 #include <vector>
20 #include "folly/Optional.h"
22 #include "hphp/runtime/ext/ext_continuation.h"
23 #include "hphp/runtime/vm/jit/abi-arm.h"
24 #include "hphp/runtime/vm/jit/arg-group.h"
25 #include "hphp/runtime/vm/jit/code-gen-helpers-arm.h"
26 #include "hphp/runtime/vm/jit/jump-smash.h"
27 #include "hphp/runtime/vm/jit/native-calls.h"
28 #include "hphp/runtime/vm/jit/reg-algorithms.h"
29 #include "hphp/runtime/vm/jit/service-requests-arm.h"
30 #include "hphp/runtime/vm/jit/translator-inline.h"
32 namespace HPHP { namespace JIT { namespace ARM {
34 TRACE_SET_MOD(hhir);
36 using namespace vixl;
38 //////////////////////////////////////////////////////////////////////
40 #define NOOP_OPCODE(name) void CodeGenerator::cg##name(IRInstruction*) {}
42 NOOP_OPCODE(DefConst)
43 NOOP_OPCODE(DefFP)
44 NOOP_OPCODE(DefSP)
45 NOOP_OPCODE(AssertLoc)
46 NOOP_OPCODE(AssertStk)
47 NOOP_OPCODE(Nop)
48 NOOP_OPCODE(DefLabel)
49 NOOP_OPCODE(ExceptionBarrier)
50 NOOP_OPCODE(TakeStack)
52 // XXX
53 NOOP_OPCODE(DbgAssertPtr);
55 // When implemented this shouldn't be a nop, but there's no reason to make us
56 // punt on everything until then.
57 NOOP_OPCODE(DbgAssertRetAddr)
59 #undef NOOP_OPCODE
61 //////////////////////////////////////////////////////////////////////
63 #define CALL_OPCODE(name) \
64 void CodeGenerator::cg##name(IRInstruction* i) { cgCallNative(m_as, i); }
66 CALL_OPCODE(Box)
67 CALL_OPCODE(ConvIntToStr)
69 CALL_OPCODE(AllocObj)
70 CALL_OPCODE(NewPackedArray)
72 CALL_OPCODE(ConcatStrStr)
73 CALL_OPCODE(ConcatIntStr)
74 CALL_OPCODE(ConcatStrInt)
76 CALL_OPCODE(PrintStr)
77 CALL_OPCODE(PrintInt)
78 CALL_OPCODE(PrintBool)
80 CALL_OPCODE(AddElemStrKey)
82 CALL_OPCODE(ConvBoolToArr)
83 CALL_OPCODE(ConvDblToArr)
84 CALL_OPCODE(ConvIntToArr)
85 CALL_OPCODE(ConvObjToArr)
86 CALL_OPCODE(ConvStrToArr)
87 CALL_OPCODE(ConvCellToArr)
89 CALL_OPCODE(ConvStrToBool)
90 CALL_OPCODE(ConvCellToBool)
91 CALL_OPCODE(ConvArrToDbl)
92 CALL_OPCODE(ConvObjToDbl)
93 CALL_OPCODE(ConvStrToDbl)
94 CALL_OPCODE(ConvCellToDbl)
96 CALL_OPCODE(ConvObjToInt)
97 CALL_OPCODE(ConvArrToInt)
98 CALL_OPCODE(ConvStrToInt)
100 CALL_OPCODE(RaiseWarning)
101 CALL_OPCODE(RaiseError)
102 CALL_OPCODE(ConvCellToObj)
103 CALL_OPCODE(LookupClsMethod)
104 CALL_OPCODE(RaiseNotice)
105 CALL_OPCODE(LookupClsRDSHandle)
106 CALL_OPCODE(LdSwitchStrIndex)
107 CALL_OPCODE(LdSwitchDblIndex)
108 CALL_OPCODE(LdSwitchObjIndex)
109 CALL_OPCODE(CustomInstanceInit)
110 CALL_OPCODE(LdClsCtor)
112 CALL_OPCODE(LdArrFuncCtx)
113 CALL_OPCODE(LdArrFPushCuf)
114 CALL_OPCODE(LdStrFPushCuf)
115 CALL_OPCODE(NewArray)
116 CALL_OPCODE(NewCol)
117 CALL_OPCODE(Clone)
118 CALL_OPCODE(ClosureStaticLocInit)
119 CALL_OPCODE(VerifyParamCallable)
120 CALL_OPCODE(VerifyParamFail)
121 CALL_OPCODE(WarnNonObjProp)
122 CALL_OPCODE(ThrowNonObjProp)
123 CALL_OPCODE(RaiseUndefProp)
124 CALL_OPCODE(AddNewElem)
125 CALL_OPCODE(ColAddElemC)
126 CALL_OPCODE(ColAddNewElemC)
127 CALL_OPCODE(ArrayAdd)
128 CALL_OPCODE(CreateContFunc)
129 CALL_OPCODE(CreateContMeth)
130 CALL_OPCODE(CreateAFWHFunc)
131 CALL_OPCODE(CreateAFWHMeth)
132 CALL_OPCODE(CreateSRWH)
133 CALL_OPCODE(TypeProfileFunc)
134 CALL_OPCODE(IncStatGrouped)
136 /////////////////////////////////////////////////////////////////////
137 void cgPunt(const char* file, int line, const char* func, uint32_t bcOff,
138 const Func* vmFunc) {
139 FTRACE(1, "punting: {}\n", func);
140 throw FailedCodeGen(file, line, func, bcOff, vmFunc);
143 #define PUNT_OPCODE(name) \
144 void CodeGenerator::cg##name(IRInstruction* inst) { \
145 cgPunt(__FILE__, __LINE__, #name, m_curInst->marker().bcOff, \
146 curFunc()); \
149 #define CG_PUNT(instr) \
150 cgPunt(__FILE__, __LINE__, #instr, m_curInst->marker().bcOff, curFunc())
152 /////////////////////////////////////////////////////////////////////
153 //TODO t3702757: Convert to CALL_OPCODE, the following set works on
154 // x86 but needs a closer look on arm
155 PUNT_OPCODE(AddElemIntKey)
156 PUNT_OPCODE(ConvCellToInt)
157 PUNT_OPCODE(ArrayIdx)
158 PUNT_OPCODE(RaiseArrayIndexNotice)
159 PUNT_OPCODE(RaiseUninitLoc)
160 PUNT_OPCODE(VerifyRetCallable)
161 PUNT_OPCODE(VerifyRetFail)
162 PUNT_OPCODE(GenericIdx)
163 // End of failing set
164 /////////////////////////////////////////////////////////////////////
166 PUNT_OPCODE(ConvArrToBool)
167 PUNT_OPCODE(ConvDblToBool)
168 PUNT_OPCODE(ConvIntToBool)
169 PUNT_OPCODE(ConvObjToBool)
170 PUNT_OPCODE(ConvBoolToDbl)
171 PUNT_OPCODE(ConvIntToDbl)
173 PUNT_OPCODE(ConvBoolToInt)
174 PUNT_OPCODE(ConvDblToInt)
176 PUNT_OPCODE(ConvBoolToStr)
177 PUNT_OPCODE(ConvDblToStr)
178 PUNT_OPCODE(ConvObjToStr)
179 PUNT_OPCODE(ConvResToStr)
180 PUNT_OPCODE(ConvCellToStr)
182 PUNT_OPCODE(CheckTypeMem)
183 PUNT_OPCODE(CheckLoc)
184 PUNT_OPCODE(CastStk)
185 PUNT_OPCODE(CoerceStk)
186 PUNT_OPCODE(CheckDefinedClsEq)
187 PUNT_OPCODE(TryEndCatch)
188 PUNT_OPCODE(LdUnwinderValue)
189 PUNT_OPCODE(DeleteUnwinderException)
190 PUNT_OPCODE(AddDbl)
191 PUNT_OPCODE(SubDbl)
192 PUNT_OPCODE(MulDbl)
193 PUNT_OPCODE(DivDbl)
194 PUNT_OPCODE(Mod)
195 PUNT_OPCODE(Sqrt)
196 PUNT_OPCODE(AbsDbl)
197 PUNT_OPCODE(XorBool)
198 PUNT_OPCODE(ExtendsClass)
199 PUNT_OPCODE(IsWaitHandle)
200 PUNT_OPCODE(InstanceOf)
201 PUNT_OPCODE(InstanceOfIface)
202 PUNT_OPCODE(InterfaceSupportsArr)
203 PUNT_OPCODE(InterfaceSupportsStr)
204 PUNT_OPCODE(InterfaceSupportsInt)
205 PUNT_OPCODE(InterfaceSupportsDbl)
206 PUNT_OPCODE(IsTypeMem)
207 PUNT_OPCODE(IsNTypeMem)
208 PUNT_OPCODE(Gt)
209 PUNT_OPCODE(Gte)
210 PUNT_OPCODE(Lt)
211 PUNT_OPCODE(Lte)
212 PUNT_OPCODE(Eq)
213 PUNT_OPCODE(Neq)
214 PUNT_OPCODE(LtX)
215 PUNT_OPCODE(GtX)
216 PUNT_OPCODE(GteX)
217 PUNT_OPCODE(LteX)
218 PUNT_OPCODE(EqX)
219 PUNT_OPCODE(NeqX)
220 PUNT_OPCODE(Same)
221 PUNT_OPCODE(NSame)
222 PUNT_OPCODE(Floor)
223 PUNT_OPCODE(Ceil)
224 PUNT_OPCODE(InstanceOfBitmask)
225 PUNT_OPCODE(NInstanceOfBitmask)
226 PUNT_OPCODE(IsType)
227 PUNT_OPCODE(IsScalarType)
228 PUNT_OPCODE(IsNType)
229 PUNT_OPCODE(JmpGt)
230 PUNT_OPCODE(JmpGte)
231 PUNT_OPCODE(JmpLt)
232 PUNT_OPCODE(JmpLte)
233 PUNT_OPCODE(JmpEq)
234 PUNT_OPCODE(JmpNeq)
235 PUNT_OPCODE(JmpGtInt)
236 PUNT_OPCODE(JmpGteInt)
237 PUNT_OPCODE(JmpLtInt)
238 PUNT_OPCODE(JmpLteInt)
239 PUNT_OPCODE(JmpEqInt)
240 PUNT_OPCODE(JmpNeqInt)
241 PUNT_OPCODE(JmpSame)
242 PUNT_OPCODE(JmpNSame)
243 PUNT_OPCODE(JmpInstanceOfBitmask)
244 PUNT_OPCODE(JmpNInstanceOfBitmask)
245 PUNT_OPCODE(JmpZero)
246 PUNT_OPCODE(JmpNZero)
247 PUNT_OPCODE(ReqBindJmpGt)
248 PUNT_OPCODE(ReqBindJmpGte)
249 PUNT_OPCODE(ReqBindJmpLt)
250 PUNT_OPCODE(ReqBindJmpLte)
251 PUNT_OPCODE(ReqBindJmpEq)
252 PUNT_OPCODE(ReqBindJmpNeq)
253 PUNT_OPCODE(ReqBindJmpGtInt)
254 PUNT_OPCODE(ReqBindJmpGteInt)
255 PUNT_OPCODE(ReqBindJmpLtInt)
256 PUNT_OPCODE(ReqBindJmpLteInt)
257 PUNT_OPCODE(ReqBindJmpEqInt)
258 PUNT_OPCODE(ReqBindJmpNeqInt)
259 PUNT_OPCODE(ReqBindJmpSame)
260 PUNT_OPCODE(ReqBindJmpNSame)
261 PUNT_OPCODE(ReqBindJmpInstanceOfBitmask)
262 PUNT_OPCODE(ReqBindJmpNInstanceOfBitmask)
263 PUNT_OPCODE(ReqBindJmpZero)
264 PUNT_OPCODE(ReqBindJmpNZero)
265 PUNT_OPCODE(SideExitJmpGt)
266 PUNT_OPCODE(SideExitJmpGte)
267 PUNT_OPCODE(SideExitJmpLt)
268 PUNT_OPCODE(SideExitJmpLte)
269 PUNT_OPCODE(SideExitJmpEq)
270 PUNT_OPCODE(SideExitJmpNeq)
271 PUNT_OPCODE(SideExitJmpGtInt)
272 PUNT_OPCODE(SideExitJmpGteInt)
273 PUNT_OPCODE(SideExitJmpLtInt)
274 PUNT_OPCODE(SideExitJmpLteInt)
275 PUNT_OPCODE(SideExitJmpEqInt)
276 PUNT_OPCODE(SideExitJmpNeqInt)
277 PUNT_OPCODE(SideExitJmpSame)
278 PUNT_OPCODE(SideExitJmpNSame)
279 PUNT_OPCODE(SideExitJmpInstanceOfBitmask)
280 PUNT_OPCODE(SideExitJmpNInstanceOfBitmask)
281 PUNT_OPCODE(SideExitJmpZero)
282 PUNT_OPCODE(SideExitJmpNZero)
283 PUNT_OPCODE(SideExitGuardLoc)
284 PUNT_OPCODE(JmpIndirect)
285 PUNT_OPCODE(CheckSurpriseFlags)
286 PUNT_OPCODE(SurpriseHook)
287 PUNT_OPCODE(FunctionExitSurpriseHook)
288 PUNT_OPCODE(ExitOnVarEnv)
289 PUNT_OPCODE(ReleaseVVOrExit)
290 PUNT_OPCODE(CheckInit)
291 PUNT_OPCODE(CheckInitMem)
292 PUNT_OPCODE(CheckCold)
293 PUNT_OPCODE(CheckNullptr)
294 PUNT_OPCODE(CheckBounds)
295 PUNT_OPCODE(LdVectorSize)
296 PUNT_OPCODE(CheckPackedArrayBounds)
297 PUNT_OPCODE(CheckPackedArrayElemNull)
298 PUNT_OPCODE(VectorHasFrozenCopy)
299 PUNT_OPCODE(VectorDoCow)
300 PUNT_OPCODE(CheckNonNull)
301 PUNT_OPCODE(AssertNonNull)
302 PUNT_OPCODE(Unbox)
303 PUNT_OPCODE(UnboxPtr)
304 PUNT_OPCODE(BoxPtr)
305 PUNT_OPCODE(LdVectorBase)
306 PUNT_OPCODE(LdPairBase)
307 PUNT_OPCODE(LdLocAddr)
308 PUNT_OPCODE(LdMem)
309 PUNT_OPCODE(LdProp)
310 PUNT_OPCODE(LdElem)
311 PUNT_OPCODE(LdPackedArrayElem)
312 PUNT_OPCODE(LdRef)
313 PUNT_OPCODE(LdThis)
314 PUNT_OPCODE(LdRetAddr)
315 PUNT_OPCODE(ConvClsToCctx)
316 PUNT_OPCODE(LdCtx)
317 PUNT_OPCODE(LdCctx)
318 PUNT_OPCODE(LdCls)
319 PUNT_OPCODE(LdClsCached)
320 PUNT_OPCODE(LdClsCachedSafe)
321 PUNT_OPCODE(LdClsCtx)
322 PUNT_OPCODE(LdClsCctx)
323 PUNT_OPCODE(LdClsCns)
324 PUNT_OPCODE(LookupClsCns)
325 PUNT_OPCODE(LdCns)
326 PUNT_OPCODE(LdClsInitData)
327 PUNT_OPCODE(LdClsStaticInitData)
328 PUNT_OPCODE(LookupCns)
329 PUNT_OPCODE(LookupCnsE)
330 PUNT_OPCODE(LookupCnsU)
331 PUNT_OPCODE(DerefClsRDSHandle)
332 PUNT_OPCODE(LookupClsMethodCache)
333 PUNT_OPCODE(LdClsMethodCacheFunc)
334 PUNT_OPCODE(LdClsMethodCacheCls)
335 PUNT_OPCODE(LdClsMethodFCacheFunc)
336 PUNT_OPCODE(LookupClsMethodFCache)
337 PUNT_OPCODE(GetCtxFwdCallDyn);
338 PUNT_OPCODE(GetCtxFwdCall)
339 PUNT_OPCODE(LdClsMethod)
340 PUNT_OPCODE(LdPropAddr)
341 PUNT_OPCODE(LdClsPropAddr)
342 PUNT_OPCODE(LdClsPropAddrCached)
343 PUNT_OPCODE(LdObjMethod)
344 PUNT_OPCODE(LdObjInvoke)
345 PUNT_OPCODE(LdGblAddrDef)
346 PUNT_OPCODE(LdGblAddr)
347 PUNT_OPCODE(LdObjClass)
348 PUNT_OPCODE(LdFunc)
349 PUNT_OPCODE(LdFuncCachedU)
350 PUNT_OPCODE(LdFuncCachedSafe)
351 PUNT_OPCODE(LdSSwitchDestFast)
352 PUNT_OPCODE(LdSSwitchDestSlow)
353 PUNT_OPCODE(JmpSwitchDest)
354 PUNT_OPCODE(ConstructInstance)
355 PUNT_OPCODE(InitProps)
356 PUNT_OPCODE(InitSProps)
357 PUNT_OPCODE(NewInstanceRaw)
358 PUNT_OPCODE(InitObjProps)
359 PUNT_OPCODE(StClosureFunc)
360 PUNT_OPCODE(StClosureArg)
361 PUNT_OPCODE(StClosureCtx)
362 PUNT_OPCODE(NewStructArray)
363 PUNT_OPCODE(FreeActRec)
364 PUNT_OPCODE(CallArray)
365 PUNT_OPCODE(NativeImpl)
366 PUNT_OPCODE(RetCtrl)
367 PUNT_OPCODE(StRetVal)
368 PUNT_OPCODE(RetAdjustStack)
369 PUNT_OPCODE(StMem)
370 PUNT_OPCODE(StProp)
371 PUNT_OPCODE(StLocNT)
372 PUNT_OPCODE(StRef)
373 PUNT_OPCODE(StRaw)
374 PUNT_OPCODE(StElem)
375 PUNT_OPCODE(IterCopy)
376 PUNT_OPCODE(LdStaticLocCached)
377 PUNT_OPCODE(CheckStaticLocInit)
378 PUNT_OPCODE(StaticLocInitCached)
379 PUNT_OPCODE(CufIterSpillFrame)
380 PUNT_OPCODE(ReqRetranslateOpt)
381 PUNT_OPCODE(Mov)
382 PUNT_OPCODE(LdAddr)
383 PUNT_OPCODE(IncRefCtx)
384 PUNT_OPCODE(DecRefThis)
385 PUNT_OPCODE(GenericRetDecRefs)
386 PUNT_OPCODE(DecRef)
387 PUNT_OPCODE(DecRefNZ)
388 PUNT_OPCODE(DefInlineFP)
389 PUNT_OPCODE(InlineReturn)
390 PUNT_OPCODE(DefInlineSP)
391 PUNT_OPCODE(ReDefSP)
392 PUNT_OPCODE(ThingExists);
393 PUNT_OPCODE(PassSP)
394 PUNT_OPCODE(PassFP)
395 PUNT_OPCODE(StashGeneratorSP)
396 PUNT_OPCODE(ReDefGeneratorSP)
397 PUNT_OPCODE(VerifyParamCls)
398 PUNT_OPCODE(VerifyRetCls)
399 PUNT_OPCODE(ConcatCellCell)
400 PUNT_OPCODE(AKExists)
401 PUNT_OPCODE(ContEnter)
402 PUNT_OPCODE(ContPreNext)
403 PUNT_OPCODE(ContStartedCheck)
404 PUNT_OPCODE(ContSetRunning)
405 PUNT_OPCODE(ContValid)
406 PUNT_OPCODE(ContArIncKey)
407 PUNT_OPCODE(ContArUpdateIdx)
408 PUNT_OPCODE(LdContActRec)
409 PUNT_OPCODE(StContArRaw)
410 PUNT_OPCODE(LdContArValue)
411 PUNT_OPCODE(StContArValue)
412 PUNT_OPCODE(LdContArKey)
413 PUNT_OPCODE(StContArKey)
414 PUNT_OPCODE(LdWHState)
415 PUNT_OPCODE(LdWHResult)
416 PUNT_OPCODE(LdAFWHActRec)
417 PUNT_OPCODE(IterInit)
418 PUNT_OPCODE(IterInitK)
419 PUNT_OPCODE(IterNext)
420 PUNT_OPCODE(IterNextK)
421 PUNT_OPCODE(WIterInit)
422 PUNT_OPCODE(WIterInitK)
423 PUNT_OPCODE(WIterNext)
424 PUNT_OPCODE(WIterNextK)
425 PUNT_OPCODE(MIterInit)
426 PUNT_OPCODE(MIterInitK)
427 PUNT_OPCODE(MIterNext)
428 PUNT_OPCODE(MIterNextK)
429 PUNT_OPCODE(IterFree)
430 PUNT_OPCODE(MIterFree)
431 PUNT_OPCODE(DecodeCufIter)
432 PUNT_OPCODE(CIterFree)
433 PUNT_OPCODE(DefMIStateBase)
434 PUNT_OPCODE(BaseG)
435 PUNT_OPCODE(PropX)
436 PUNT_OPCODE(PropDX)
437 PUNT_OPCODE(PropDXStk)
438 PUNT_OPCODE(CGetProp)
439 PUNT_OPCODE(VGetProp)
440 PUNT_OPCODE(VGetPropStk)
441 PUNT_OPCODE(BindProp)
442 PUNT_OPCODE(BindPropStk)
443 PUNT_OPCODE(SetProp)
444 PUNT_OPCODE(SetPropStk)
445 PUNT_OPCODE(UnsetProp)
446 PUNT_OPCODE(SetOpProp)
447 PUNT_OPCODE(SetOpPropStk)
448 PUNT_OPCODE(IncDecProp)
449 PUNT_OPCODE(IncDecPropStk)
450 PUNT_OPCODE(EmptyProp)
451 PUNT_OPCODE(IssetProp)
452 PUNT_OPCODE(ElemX)
453 PUNT_OPCODE(ElemArray)
454 PUNT_OPCODE(ElemDX)
455 PUNT_OPCODE(ElemDXStk)
456 PUNT_OPCODE(ElemUX)
457 PUNT_OPCODE(ElemUXStk)
458 PUNT_OPCODE(ArrayGet)
459 PUNT_OPCODE(StringGet)
460 PUNT_OPCODE(MapGet)
461 PUNT_OPCODE(CGetElem)
462 PUNT_OPCODE(VGetElem)
463 PUNT_OPCODE(VGetElemStk)
464 PUNT_OPCODE(BindElem)
465 PUNT_OPCODE(BindElemStk)
466 PUNT_OPCODE(ArraySet)
467 PUNT_OPCODE(MapSet)
468 PUNT_OPCODE(ArraySetRef)
469 PUNT_OPCODE(SetElem)
470 PUNT_OPCODE(SetElemStk)
471 PUNT_OPCODE(SetWithRefElem)
472 PUNT_OPCODE(SetWithRefElemStk)
473 PUNT_OPCODE(UnsetElem)
474 PUNT_OPCODE(UnsetElemStk)
475 PUNT_OPCODE(SetOpElem)
476 PUNT_OPCODE(SetOpElemStk)
477 PUNT_OPCODE(IncDecElem)
478 PUNT_OPCODE(IncDecElemStk)
479 PUNT_OPCODE(SetNewElem)
480 PUNT_OPCODE(SetNewElemStk)
481 PUNT_OPCODE(SetNewElemArray)
482 PUNT_OPCODE(SetNewElemArrayStk)
483 PUNT_OPCODE(SetWithRefNewElem)
484 PUNT_OPCODE(SetWithRefNewElemStk)
485 PUNT_OPCODE(BindNewElem)
486 PUNT_OPCODE(BindNewElemStk)
487 PUNT_OPCODE(ArrayIsset)
488 PUNT_OPCODE(StringIsset)
489 PUNT_OPCODE(VectorIsset)
490 PUNT_OPCODE(PairIsset)
491 PUNT_OPCODE(MapIsset)
492 PUNT_OPCODE(IssetElem)
493 PUNT_OPCODE(EmptyElem)
494 PUNT_OPCODE(IncStat)
495 PUNT_OPCODE(RBTrace)
496 PUNT_OPCODE(IncTransCounter)
497 PUNT_OPCODE(IncProfCounter)
498 PUNT_OPCODE(DbgAssertType)
499 PUNT_OPCODE(AddIntO)
500 PUNT_OPCODE(SubIntO)
501 PUNT_OPCODE(MulIntO)
503 #undef PUNT_OPCODE
505 //////////////////////////////////////////////////////////////////////
507 static TCA kEndOfTargetChain = reinterpret_cast<TCA>(0xf00ffeeffaaff11f);
509 void CodeGenerator::emitJumpToBlock(CodeBlock& cb,
510 Block* target,
511 ConditionCode cc) {
512 vixl::MacroAssembler as { cb };
514 if (m_state.addresses[target]) {
515 not_implemented();
518 // The block hasn't been emitted yet. Record the location in CodegenState.
519 // CodegenState holds a map from Block* to the head of a linked list, where
520 // the jump instructions themselves are the list nodes.
521 auto next = reinterpret_cast<TCA>(m_state.patches[target]);
522 auto here = cb.frontier();
524 // To avoid encoding 0x0 as the jump target. That would conflict with the use
525 // of nullptr as a sentinel return value from jmpTarget() and jccTarget().
526 // Consider switching those to use folly::Optional or something?
527 if (!next) next = kEndOfTargetChain;
529 // This will never actually be executed as a jump to "next". It's just a
530 // pointer to the next jump instruction to retarget.
531 emitSmashableJump(cb, next, cc);
532 m_state.patches[target] = here;
535 void patchJumps(CodeBlock& cb, CodegenState& state, Block* block) {
536 auto dest = cb.frontier();
537 auto jump = reinterpret_cast<TCA>(state.patches[block]);
539 while (jump && jump != kEndOfTargetChain) {
540 auto nextIfJmp = jmpTarget(jump);
541 auto nextIfJcc = jccTarget(jump);
543 // Exactly one of them must be non-nullptr
544 assert(!(nextIfJmp && nextIfJcc));
545 assert(nextIfJmp || nextIfJcc);
547 if (nextIfJmp) {
548 smashJmp(jump, dest);
549 jump = nextIfJmp;
550 } else {
551 smashJcc(jump, dest);
552 jump = nextIfJcc;
557 void emitFwdJmp(CodeBlock& cb, Block* target, CodegenState& state) {
558 always_assert(false);
561 //////////////////////////////////////////////////////////////////////
563 void CodeGenerator::recordHostCallSyncPoint(vixl::MacroAssembler& as,
564 TCA tca) {
565 auto stackOff = m_curInst->marker().spOff;
566 auto pcOff = m_curInst->marker().bcOff - m_curInst->marker().func->base();
567 m_mcg->fixupMap().recordSyncPoint(tca, pcOff, stackOff);
570 //////////////////////////////////////////////////////////////////////
572 void CodeGenerator::cgConjure(IRInstruction* inst) {
573 always_assert(false);
576 //////////////////////////////////////////////////////////////////////
578 void CodeGenerator::cgJmp(IRInstruction* inst) {
579 emitJumpToBlock(m_mcg->code.main(), inst->taken(), CC_None);
582 void CodeGenerator::cgDbgAssertRefCount(IRInstruction* inst) {
583 vixl::Label done;
584 auto base = x2a(srcLoc(0).reg());
585 auto rCount = rAsm;
586 m_as. Ldr (rCount.W(), base[FAST_REFCOUNT_OFFSET]);
587 m_as. Tbnz (rCount, UncountedBitPos, &done);
588 m_as. Cmp (rCount, RefCountMaxRealistic);
589 m_as. B (&done, vixl::ls);
590 m_as. Brk (0);
591 m_as. bind (&done);
594 void CodeGenerator::cgIncRef(IRInstruction* inst) {
595 SSATmp* src = inst->src(0);
596 auto loc = srcLoc(0);
597 Type type = src->type();
599 if (type.notCounted()) return;
601 auto increfMaybeStatic = [&] {
602 auto base = x2a(loc.reg(0));
603 auto rCount = rAsm.W();
604 m_as. Ldr (rCount, base[FAST_REFCOUNT_OFFSET]);
605 if (!type.needsStaticBitCheck()) {
606 m_as. Add (rCount, rAsm.W(), 1);
607 m_as. Str (rCount, base[FAST_REFCOUNT_OFFSET]);
608 } else {
609 m_as. Cmp (rCount, 0);
610 static_assert(UncountedValue < 0 && StaticValue < 0, "");
611 ifThen(m_as, vixl::ge, [&] {
612 m_as.Add(rCount, rCount, 1);
613 m_as.Str(rCount, base[FAST_REFCOUNT_OFFSET]);
618 if (type.isKnownDataType()) {
619 assert(IS_REFCOUNTED_TYPE(type.toDataType()));
620 increfMaybeStatic();
621 } else {
622 m_as. Cmp (x2a(loc.reg(1)).W(), KindOfRefCountThreshold);
623 ifThen(m_as, vixl::gt, [&] { increfMaybeStatic(); });
627 void CodeGenerator::cgAssertType(IRInstruction* inst) {
628 auto const srcRegs = srcLoc(0);
629 auto const dstRegs = dstLoc(0);
631 PhysReg::Map<PhysReg> moves;
632 if (dstRegs.reg(0) != InvalidReg)
633 moves[dstRegs.reg(0)] = srcRegs.reg(0);
634 if (dstRegs.reg(1) != InvalidReg)
635 moves[dstRegs.reg(1)] = srcRegs.reg(1);
637 auto howTo = doRegMoves(moves, rAsm);
638 for (auto& how : howTo) {
639 if (how.m_kind == MoveInfo::Kind::Move) {
640 m_as. Mov (x2a(how.m_dst), x2a(how.m_src));
641 } else {
642 emitXorSwap(m_as, x2a(how.m_dst), x2a(how.m_src));
647 //////////////////////////////////////////////////////////////////////
649 void CodeGenerator::emitDecRefStaticType(Type type,
650 vixl::Register dataReg) {
651 assert(type.isKnownDataType());
652 assert(!dataReg.Is(rAsm2));
654 vixl::Label allDone;
656 m_as. Ldr (rAsm2.W(), dataReg[FAST_REFCOUNT_OFFSET]);
658 if (type.needsStaticBitCheck()) {
659 m_as.Tbnz (rAsm2, UncountedBitPos, &allDone);
662 m_as. Sub (rAsm2.W(), rAsm2.W(), 1, vixl::SetFlags);
663 m_as. Str (rAsm2.W(), dataReg[FAST_REFCOUNT_OFFSET]);
665 m_as. B (&allDone, vixl::ne);
666 cgCallHelper(m_as,
667 MCGenerator::getDtorCall(type.toDataType()),
668 kVoidDest,
669 SyncOptions::kSyncPoint,
670 argGroup().reg(dataReg));
672 m_as. bind (&allDone);
675 void CodeGenerator::emitDecRefDynamicType(vixl::Register baseReg,
676 int offset) {
677 // Make sure both temp registers are still available
678 assert(!baseReg.Is(rAsm));
679 assert(!baseReg.Is(rAsm2));
681 vixl::Label allDone;
683 // Check the type
684 m_as. Ldrb (rAsm.W(), baseReg[offset + TVOFF(m_type)]);
685 m_as. Cmp (rAsm.W(), KindOfRefCountThreshold);
686 m_as. B (&allDone, vixl::le);
688 // Type is refcounted. Load the refcount.
689 m_as. Ldr (rAsm, baseReg[offset + TVOFF(m_data)]);
690 m_as. Ldr (rAsm2.W(), rAsm[FAST_REFCOUNT_OFFSET]);
692 // Is it static? Note that only the lower 32 bits of rAsm2 are valid right
693 // now, but tbnz is only looking at a single one of them, so this is OK.
694 m_as. Tbnz (rAsm2, UncountedBitPos, &allDone);
696 // Not static. Decrement and write back.
697 m_as. Sub (rAsm2.W(), rAsm2.W(), 1, vixl::SetFlags);
698 m_as. Str (rAsm2.W(), rAsm[FAST_REFCOUNT_OFFSET]);
700 // Did it go to zero?
701 m_as. B (&allDone, vixl::ne);
703 // Went to zero. Have to destruct.
704 cgCallHelper(m_as,
705 CppCall(tv_release_generic),
706 kVoidDest,
707 SyncOptions::kSyncPoint,
708 argGroup().addr(baseReg, offset));
710 m_as. bind (&allDone);
713 void CodeGenerator::emitDecRefMem(Type type,
714 vixl::Register baseReg,
715 int offset) {
716 if (type.needsReg()) {
717 emitDecRefDynamicType(baseReg, offset);
718 } else if (type.maybeCounted()) {
719 m_as. Ldr (rAsm, baseReg[offset + TVOFF(m_data)]);
720 emitDecRefStaticType(type, rAsm);
724 void CodeGenerator::cgDecRefStack(IRInstruction* inst) {
725 emitDecRefMem(inst->typeParam(),
726 x2a(srcLoc(0).reg()),
727 cellsToBytes(inst->extra<DecRefStack>()->offset));
730 void CodeGenerator::cgDecRefLoc(IRInstruction* inst) {
731 emitDecRefMem(inst->typeParam(),
732 x2a(srcLoc(0).reg()),
733 localOffset(inst->extra<DecRefLoc>()->locId));
736 void CodeGenerator::cgDecRefMem(IRInstruction* inst) {
737 emitDecRefMem(inst->typeParam(),
738 x2a(srcLoc(0).reg()),
739 inst->src(1)->intVal());
742 //////////////////////////////////////////////////////////////////////
743 // Arithmetic Instructions
745 void CodeGenerator::cgAddInt(IRInstruction* inst) {
746 auto dstReg = dstLoc(0).reg();
747 auto srcRegL = srcLoc(0).reg();
748 auto srcRegR = srcLoc(1).reg();
750 if (srcRegR != InvalidReg) {
751 m_as. Add(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
752 } else {
753 m_as. Add(x2a(dstReg), x2a(srcRegL), inst->src(1)->intVal());
757 void CodeGenerator::cgSubInt(IRInstruction* inst) {
758 auto dstReg = dstLoc(0).reg();
759 auto srcRegL = srcLoc(0).reg();
760 auto srcRegR = srcLoc(1).reg();
762 if (srcRegR != InvalidReg) {
763 m_as. Sub(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
764 } else {
765 m_as. Sub(x2a(dstReg), x2a(srcRegL), inst->src(1)->intVal());
769 void CodeGenerator::cgMulInt(IRInstruction* inst) {
770 auto dstReg = dstLoc(0).reg();
771 auto srcRegL = srcLoc(0).reg();
772 auto srcRegR = srcLoc(1).reg();
774 m_as. Mul(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
777 //////////////////////////////////////////////////////////////////////
778 // Bitwise Operators
780 void CodeGenerator::cgAndInt(IRInstruction* inst) {
781 auto dstReg = dstLoc(0).reg();
782 auto srcRegL = srcLoc(0).reg();
783 auto srcRegR = srcLoc(1).reg();
785 if (srcRegL != InvalidReg) {
786 m_as. And(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
787 } else {
788 m_as. And(x2a(dstReg), x2a(srcRegL), inst->src(1)->intVal());
792 void CodeGenerator::cgOrInt(IRInstruction* inst) {
793 auto dstReg = dstLoc(0).reg();
794 auto srcRegL = srcLoc(0).reg();
795 auto srcRegR = srcLoc(1).reg();
797 if (srcRegL != InvalidReg) {
798 m_as. Orr(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
799 } else {
800 m_as. Orr(x2a(dstReg), x2a(srcRegL), inst->src(1)->intVal());
804 void CodeGenerator::cgXorInt(IRInstruction* inst) {
805 auto dstReg = dstLoc(0).reg();
806 auto srcRegL = srcLoc(0).reg();
807 auto srcRegR = srcLoc(1).reg();
809 if (srcRegL != InvalidReg) {
810 m_as. Eor(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
811 } else {
812 m_as. Eor(x2a(dstReg), x2a(srcRegL), inst->src(1)->intVal());
816 void CodeGenerator::cgShl(IRInstruction* inst) {
817 auto dstReg = dstLoc(0).reg();
818 auto srcRegL = srcLoc(0).reg();
819 auto srcRegR = srcLoc(1).reg();
821 // TODO: t3870154 add shift-by-immediate support to vixl
822 m_as. lslv(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
825 void CodeGenerator::cgShr(IRInstruction* inst) {
826 auto dstReg = dstLoc(0).reg();
827 auto srcRegL = srcLoc(0).reg();
828 auto srcRegR = srcLoc(1).reg();
830 // TODO: t3870154 add shift-by-immediate support to vixl
831 m_as. asrv(x2a(dstReg), x2a(srcRegL), x2a(srcRegR));
833 //////////////////////////////////////////////////////////////////////
834 // Comparison Operations
836 void CodeGenerator::emitCompareIntAndSet(IRInstruction *inst,
837 vixl::Condition cond) {
838 auto dstReg = dstLoc(0).reg();
839 emitCompareInt(inst);
840 m_as. Cset(x2a(dstReg),cond);
843 void CodeGenerator::emitCompareInt(IRInstruction* inst) {
844 auto srcRegL = srcLoc(0).reg();
845 auto srcRegR = srcLoc(1).reg();
847 if (srcRegR != InvalidReg) {
848 m_as. Cmp(x2a(srcRegL), x2a(srcRegR));
849 } else {
850 m_as. Cmp(x2a(srcRegL), inst->src(1)->intVal());
854 void CodeGenerator::cgLtInt(IRInstruction* inst) {
855 emitCompareIntAndSet(inst,vixl::Condition::lt);
858 void CodeGenerator::cgGtInt(IRInstruction* inst) {
859 emitCompareIntAndSet(inst,vixl::Condition::gt);
863 void CodeGenerator::cgGteInt(IRInstruction* inst) {
864 emitCompareIntAndSet(inst,vixl::Condition::ge);
867 void CodeGenerator::cgLteInt(IRInstruction* inst) {
868 emitCompareIntAndSet(inst,vixl::Condition::le);
872 void CodeGenerator::cgEqInt(IRInstruction* inst) {
873 emitCompareIntAndSet(inst,vixl::Condition::eq);
876 void CodeGenerator::cgNeqInt(IRInstruction* inst) {
877 emitCompareIntAndSet(inst,vixl::Condition::ne);
880 //////////////////////////////////////////////////////////////////////
882 void CodeGenerator::cgShuffle(IRInstruction* inst) {
883 PhysReg::Map<PhysReg> moves;
885 // Put required reg-reg moves in the map, and do spills at the same time.
886 for (auto i = 0; i < inst->numSrcs(); ++i) {
887 auto& rd = inst->extra<Shuffle>()->dests[i];
888 auto& rs = srcLoc(i);
890 if (rd.numAllocated() == 0) continue;
891 if (rd.spilled()) {
892 for (auto j = 0; j < rd.numAllocated(); ++j) {
893 m_as. Str (x2a(rs.reg(j)), vixl::MemOperand(vixl::sp, rd.offset(j)));
895 } else if (!rs.spilled()) {
896 if (rs.reg(0) != InvalidReg) moves[rd.reg(0)] = rs.reg(0);
897 if (rs.reg(1) != InvalidReg) moves[rd.reg(1)] = rs.reg(1);
901 // Do reg-reg moves.
902 auto howTo = doRegMoves(moves, rAsm);
903 for (auto& how : howTo) {
904 if (how.m_kind == MoveInfo::Kind::Move) {
905 emitRegGetsRegPlusImm(m_as, x2a(how.m_dst), x2a(how.m_src), 0);
906 } else {
907 emitXorSwap(m_as, x2a(how.m_src), x2a(how.m_dst));
911 // Now do reloads and reg<-imm.
912 for (auto i = 0; i < inst->numSrcs(); ++i) {
913 auto src = inst->src(i);
914 auto& rd = inst->extra<Shuffle>()->dests[i];
915 auto& rs = srcLoc(i);
916 if (rd.numAllocated() == 0) continue;
917 if (rd.spilled()) continue;
918 if (rs.spilled()) {
919 for (auto j = 0; j < rd.numAllocated(); ++j) {
920 m_as. Ldr (x2a(rd.reg(i)), vixl::MemOperand(vixl::sp, rs.offset(j)));
922 continue;
924 if (rs.numAllocated() == 0) {
925 assert(src->inst()->op() == DefConst);
926 m_as. Mov (x2a(rd.reg(0)), src->rawVal());
928 if (rd.numAllocated() == 2 && rs.numAllocated() < 2) {
929 // Move src known type to register
930 m_as. Mov (x2a(rd.reg(1)), src->type().toDataType());
936 //////////////////////////////////////////////////////////////////////
938 static void shuffleArgs(vixl::MacroAssembler& a,
939 ArgGroup& args,
940 CppCall& call) {
941 assert_not_implemented(args.numStackArgs() == 0);
943 PhysReg::Map<PhysReg> moves;
944 PhysReg::Map<ArgDesc*> argDescs;
946 for (size_t i = 0; i < args.numRegArgs(); i++) {
947 auto kind = args[i].kind();
948 if (!(kind == ArgDesc::Kind::Reg ||
949 kind == ArgDesc::Kind::Addr ||
950 kind == ArgDesc::Kind::TypeReg)) {
951 continue;
953 auto dstReg = args[i].dstReg();
954 auto srcReg = args[i].srcReg();
955 if (dstReg != srcReg) {
956 moves[dstReg] = srcReg;
957 argDescs[dstReg] = &args[i];
959 if (call.isIndirect() && dstReg == call.getReg()) {
960 // an indirect call uses an argument register for the func ptr.
961 // Use rAsm2 instead and update the CppCall
962 moves[rAsm2] = call.getReg();
963 call.updateCallIndirect(rAsm2);
967 auto const howTo = doRegMoves(moves, rAsm);
969 for (auto& how : howTo) {
970 auto srcReg = x2a(how.m_src);
971 auto dstReg = x2a(how.m_dst);
972 if (how.m_kind == MoveInfo::Kind::Move) {
973 auto* argDesc = argDescs[how.m_dst];
974 if (argDesc) {
975 auto kind = argDesc->kind();
976 if (kind == ArgDesc::Kind::Addr) {
977 emitRegGetsRegPlusImm(a, dstReg, srcReg, argDesc->disp().l());
978 } else {
979 if (argDesc->isZeroExtend()) {
980 // "Unsigned eXTend Byte". The dest reg is a 32-bit reg but this
981 // zeroes the top 32 bits, so the intended effect is achieved.
982 a.Uxtb (dstReg.W(), srcReg.W());
983 } else {
984 a.Mov (dstReg, srcReg);
987 if (kind != ArgDesc::Kind::TypeReg) {
988 argDesc->markDone();
990 } else {
991 a. Mov (dstReg, srcReg);
993 } else {
994 emitXorSwap(a, dstReg, srcReg);
998 for (size_t i = 0; i < args.numRegArgs(); ++i) {
999 if (!args[i].done()) {
1000 auto kind = args[i].kind();
1001 auto dstReg = x2a(args[i].dstReg());
1002 if (kind == ArgDesc::Kind::Imm) {
1003 a. Mov (dstReg, args[i].imm().q());
1004 } else if (kind == ArgDesc::Kind::Reg || kind == ArgDesc::Kind::TypeReg) {
1005 // Should have already been done
1006 } else {
1007 not_implemented();
1013 void CodeGenerator::cgCallNative(vixl::MacroAssembler& as,
1014 IRInstruction* inst) {
1015 using namespace NativeCalls;
1017 Opcode opc = inst->op();
1018 always_assert(CallMap::hasInfo(opc));
1020 auto const& info = CallMap::info(opc);
1021 ArgGroup argGroup = info.toArgGroup(m_state.regs, inst);
1023 auto call = [&]() -> CppCall {
1024 switch (info.func.type) {
1025 case FuncType::Call:
1026 return CppCall(info.func.call);
1027 case FuncType::SSA:
1028 return CppCall(inst->src(info.func.srcIdx)->tcaVal());
1030 not_reached();
1031 }();
1033 auto const dest = [&]() -> CallDest {
1034 switch (info.dest) {
1035 case DestType::None: return kVoidDest;
1036 case DestType::TV: return callDestTV(inst);
1037 case DestType::SSA: return callDest(inst);
1038 case DestType::SSA2: return callDest2(inst);
1040 not_reached();
1041 }();
1043 cgCallHelper(as, call, dest, info.sync, argGroup);
1046 void CodeGenerator::cgCallHelper(vixl::MacroAssembler& a,
1047 CppCall call,
1048 const CallDest& dstInfo,
1049 SyncOptions sync,
1050 ArgGroup& args,
1051 RegSet toSave) {
1052 assert(m_curInst->isNative());
1054 auto dstReg0 = dstInfo.reg0;
1055 auto dstReg1 = dstInfo.reg1;
1057 if (debug) {
1058 toSave.forEach([](PhysReg r) { assert(r.isGP()); });
1061 toSave = toSave & kCallerSaved;
1062 assert((toSave & RegSet().add(dstReg0).add(dstReg1)).empty());
1064 // Use vixl's handy helper to push caller-save regs. It uses ldp/stp when
1065 // possible.
1066 CPURegList pushedRegs(vixl::CPURegister::kRegister, vixl::kXRegSize, 0);
1067 toSave.forEach([&](PhysReg r) { pushedRegs.Combine(r); });
1069 // The vixl helper requires you to pass it an even number of registers. If we
1070 // have an odd number of regs to save, remove one from the list we pass, and
1071 // save it ourselves.
1072 folly::Optional<vixl::CPURegister> maybeOddOne;
1073 if (pushedRegs.Count() % 2 == 1) {
1074 maybeOddOne = pushedRegs.PopHighestIndex();
1076 a. PushCPURegList(pushedRegs);
1077 if (maybeOddOne) {
1078 // We're only storing a single reg, but the stack pointer must always be
1079 // 16-byte aligned. This instruction subtracts 16 from the stack pointer,
1080 // then writes the value.
1081 a. Str (maybeOddOne.value(), MemOperand(vixl::sp, -16, vixl::PreIndex));
1084 SCOPE_EXIT {
1085 if (maybeOddOne) {
1086 // Read the value, then add 16 to the stack pointer.
1087 a.Ldr (maybeOddOne.value(), MemOperand(vixl::sp, 16, vixl::PostIndex));
1089 a. PopCPURegList(pushedRegs);
1092 for (size_t i = 0; i < args.numRegArgs(); i++) {
1093 args[i].setDstReg(PhysReg{argReg(i)});
1095 shuffleArgs(a, args, call);
1097 auto syncPoint = emitCall(a, call);
1099 if (RuntimeOption::HHProfServerEnabled || sync != SyncOptions::kNoSyncPoint) {
1100 recordHostCallSyncPoint(a, syncPoint);
1103 auto armDst0 = x2a(dstReg0);
1104 DEBUG_ONLY auto armDst1 = x2a(dstReg1);
1106 switch (dstInfo.type) {
1107 case DestType::TV: not_implemented();
1108 case DestType::SSA:
1109 assert(!armDst1.IsValid());
1110 if (armDst0.IsValid() && !armDst0.Is(vixl::x0)) a.Mov(armDst0, vixl::x0);
1111 break;
1112 case DestType::SSA2: not_implemented();
1113 case DestType::None:
1114 assert(!armDst0.IsValid() && !armDst1.IsValid());
1115 break;
1119 void CodeGenerator::cgCallHelper(vixl::MacroAssembler& a,
1120 CppCall call,
1121 const CallDest& dstInfo,
1122 SyncOptions sync,
1123 ArgGroup& args) {
1124 cgCallHelper(a, call, dstInfo, sync, args, m_state.liveRegs[m_curInst]);
1128 * XXX copypasta but has to be in the class because of curPhysLoc and
1129 * changing that would make callsites real messy
1132 CallDest CodeGenerator::callDest(PhysReg reg0,
1133 PhysReg reg1 /* = InvalidReg */) const {
1134 return { DestType::SSA, reg0, reg1 };
1137 CallDest CodeGenerator::callDest(const IRInstruction* inst) const {
1138 if (!inst->numDsts()) return kVoidDest;
1139 auto loc = dstLoc(0);
1140 return { DestType::SSA, loc.reg(0), loc.reg(1) };
1143 CallDest CodeGenerator::callDestTV(const IRInstruction* inst) const {
1144 if (!inst->numDsts()) return kVoidDest;
1145 auto loc = dstLoc(0);
1146 return { DestType::TV, loc.reg(0), loc.reg(1) };
1149 CallDest CodeGenerator::callDest2(const IRInstruction* inst) const {
1150 if (!inst->numDsts()) return kVoidDest;
1151 auto loc = dstLoc(0);
1152 return { DestType::SSA2, loc.reg(0), loc.reg(1) };
1155 //////////////////////////////////////////////////////////////////////
1157 static vixl::Register enregister(vixl::MacroAssembler& a,
1158 vixl::MemOperand memRef,
1159 vixl::Register scratch) {
1160 a. Ldr (scratch, memRef);
1161 return scratch;
1164 static vixl::Register enregister(vixl::MacroAssembler& a,
1165 vixl::Register reg,
1166 vixl::Register scratch) {
1167 return reg;
1170 template<class Loc, class JmpFn>
1171 void CodeGenerator::emitTypeTest(Type type, vixl::Register typeReg, Loc dataSrc,
1172 JmpFn doJcc) {
1173 assert(!(type <= Type::Cls));
1174 assert(typeReg.Is32Bits());
1176 if (type.equals(Type::Gen)) {
1177 return;
1180 ConditionCode cc;
1181 if (type <= Type::Str) {
1182 // Note: ARM can actually do better here; it has a fused test-and-branch
1183 // instruction. The way this code is factored makes it difficult to use,
1184 // though; the jump instruction will be written by some other code.
1185 m_as. Tst (typeReg, KindOfStringBit);
1186 cc = CC_NE;
1187 } else if (type.equals(Type::UncountedInit)) {
1188 m_as. Tst (typeReg, KindOfUncountedInitBit);
1189 cc = CC_NE;
1190 } else if (type.equals(Type::Uncounted)) {
1191 m_as. Cmp (typeReg, KindOfRefCountThreshold);
1192 cc = CC_LE;
1193 } else if (type.equals(Type::Cell)) {
1194 m_as. Cmp (typeReg, KindOfRef);
1195 cc = CC_L;
1196 } else {
1197 assert(type.isKnownDataType());
1198 DataType dataType = type.toDataType();
1199 assert(dataType == KindOfRef ||
1200 (dataType >= KindOfUninit && dataType <= KindOfResource));
1201 m_as. Cmp (typeReg, dataType);
1202 cc = CC_E;
1204 doJcc(cc);
1205 if (type < Type::Obj) {
1206 assert(type.getClass()->attrs() & AttrFinal);
1207 auto dataReg = enregister(m_as, dataSrc, rAsm);
1208 m_as. Ldr (rAsm, dataReg[ObjectData::getVMClassOffset()]);
1209 m_as. Cmp (rAsm, reinterpret_cast<int64_t>(type.getClass()));
1210 doJcc(CC_E);
1211 } else if (type < Type::Res) {
1212 CG_PUNT(TypeTest-on-Resource);
1213 } else if (type <= Type::Arr && type.hasArrayKind()) {
1214 auto dataReg = enregister(m_as, dataSrc, rAsm);
1215 m_as. Ldrb (rAsm.W(), dataReg[ArrayData::offsetofKind()]);
1216 m_as. Cmp (rAsm.W(), type.getArrayKind());
1217 doJcc(CC_E);
1221 void CodeGenerator::cgGuardLoc(IRInstruction* inst) {
1222 auto const rFP = x2a(srcLoc(0).reg());
1223 auto const baseOff = localOffset(inst->extra<GuardLoc>()->locId);
1224 m_as. Ldrb (rAsm.W(), rFP[baseOff + TVOFF(m_type)]);
1225 emitTypeTest(
1226 inst->typeParam(),
1227 rAsm.W(),
1228 rFP[baseOff + TVOFF(m_data)],
1229 [&] (ConditionCode cc) {
1230 auto const destSK = SrcKey(curFunc(), m_unit.bcOff());
1231 auto const destSR = m_mcg->tx().getSrcRec(destSK);
1232 destSR->emitFallbackJump(this->m_mainCode, ccNegate(cc));
1236 void CodeGenerator::cgGuardStk(IRInstruction* inst) {
1237 auto const rSP = x2a(srcLoc(0).reg());
1238 auto const baseOff = cellsToBytes(inst->extra<GuardStk>()->offset);
1239 m_as. Ldrb (rAsm.W(), rSP[baseOff + TVOFF(m_type)]);
1240 emitTypeTest(
1241 inst->typeParam(),
1242 rAsm.W(),
1243 rSP[baseOff + TVOFF(m_data)],
1244 [&] (ConditionCode cc) {
1245 auto const destSK = SrcKey(curFunc(), m_unit.bcOff());
1246 auto const destSR = m_mcg->tx().getSrcRec(destSK);
1247 destSR->emitFallbackJump(this->m_mainCode, ccNegate(cc));
1251 void CodeGenerator::cgCheckStk(IRInstruction* inst) {
1252 auto const rSP = x2a(srcLoc(0).reg());
1253 auto const baseOff = cellsToBytes(inst->extra<CheckStk>()->offset);
1254 m_as. Ldrb (rAsm.W(), rSP[baseOff + TVOFF(m_type)]);
1255 emitTypeTest(
1256 inst->typeParam(),
1257 rAsm.W(),
1258 rSP[baseOff + TVOFF(m_data)],
1259 [&] (ConditionCode cc) {
1260 emitJumpToBlock(m_mcg->code.main(), inst->taken(), ccNegate(cc));
1265 void CodeGenerator::cgCheckType(IRInstruction* inst) {
1266 auto const src = inst->src(0);
1267 Type srcType = src->type();
1268 auto const rVal = x2a(srcLoc(0).reg(0));
1269 auto const rType = x2a(srcLoc(0).reg(1));
1271 auto doMov = [&] {
1272 auto const valDst = x2a(dstLoc(0).reg(0));
1273 auto const typeDst = x2a(dstLoc(0).reg(1));
1274 // TODO: #3626251: XLS: Let Uses say whether a constant is
1275 // allowed, and if not, assign a register.
1276 if (valDst.IsValid()) {
1277 if (rVal.IsValid()) {
1278 if (!valDst.Is(rVal)) m_as.Mov(valDst, rVal);
1279 } else {
1280 if (src->isConst()) m_as.Mov(valDst, src->rawVal());
1283 if (typeDst.IsValid()) {
1284 if (rType.IsValid()) {
1285 if (!typeDst.Is(rType)) m_as.Mov(typeDst, rType);
1286 } else {
1287 m_as.Mov(typeDst, srcType.toDataType());
1292 auto doJcc = [&] (ConditionCode cc) {
1293 emitJumpToBlock(m_mcg->code.main(), inst->taken(), ccNegate(cc));
1296 Type typeParam = inst->typeParam();
1297 if (src->isA(typeParam) ||
1298 // Boxed types are checked lazily, so there's nothing to be done here.
1299 (srcType.isBoxed() && typeParam.isBoxed())) {
1300 doMov();
1301 return;
1303 if (srcType.not(typeParam)) {
1304 emitJumpToBlock(m_mcg->code.main(), inst->taken(), CC_None);
1305 return;
1308 if (rType.IsValid()) {
1309 emitTypeTest(typeParam, rType.W(), rVal, doJcc);
1310 } else if (typeParam <= Type::Uncounted &&
1311 ((srcType == Type::Str && typeParam.maybe(Type::StaticStr)) ||
1312 (srcType == Type::Arr && typeParam.maybe(Type::StaticArr)))) {
1313 // We carry Str and Arr operands around without a type register,
1314 // even though they're union types. The static and non-static
1315 // subtypes are distinguised by the refcount field.
1316 assert(rVal.IsValid());
1317 m_as. Ldr (rAsm.W(), rVal[FAST_REFCOUNT_OFFSET]);
1318 m_as. Cmp (rAsm, 0);
1319 doJcc(CC_L);
1320 } else {
1321 always_assert_log(
1322 false,
1323 [&] {
1324 return folly::format("Bad src: {} and dst: {} types in '{}'",
1325 srcType, typeParam, *inst).str();
1328 doMov();
1331 void CodeGenerator::cgSideExitGuardStk(IRInstruction* inst) {
1332 auto const sp = x2a(srcLoc(0).reg());
1333 auto const extra = inst->extra<SideExitGuardStk>();
1335 m_as. Ldrb (rAsm.W(), sp[cellsToBytes(extra->checkedSlot) + TVOFF(m_type)]);
1336 emitTypeTest(
1337 inst->typeParam(),
1338 rAsm.W(),
1339 sp[cellsToBytes(extra->checkedSlot) + TVOFF(m_data)],
1340 [&] (ConditionCode cc) {
1341 auto const sk = SrcKey(curFunc(), extra->taken);
1342 emitBindSideExit(this->m_mainCode, this->m_stubsCode, sk, ccNegate(cc));
1347 void CodeGenerator::cgGuardRefs(IRInstruction* inst) {
1348 assert(inst->numSrcs() == 5);
1350 DEBUG_ONLY SSATmp* nParamsTmp = inst->src(1);
1351 DEBUG_ONLY SSATmp* firstBitNumTmp = inst->src(2);
1352 DEBUG_ONLY SSATmp* mask64Tmp = inst->src(3);
1353 DEBUG_ONLY SSATmp* vals64Tmp = inst->src(4);
1355 auto funcPtrLoc = srcLoc(0);
1356 auto nParamsLoc = srcLoc(1);
1357 auto mask64Loc = srcLoc(3);
1358 auto vals64Loc = srcLoc(4);
1360 // Get values in place
1361 auto funcPtrReg = x2a(funcPtrLoc.reg());
1362 assert(funcPtrReg.IsValid());
1364 auto nParamsReg = x2a(nParamsLoc.reg());
1365 assert(nParamsReg.IsValid() || nParamsTmp->isConst());
1367 auto firstBitNum = static_cast<uint32_t>(firstBitNumTmp->intVal());
1368 auto mask64Reg = x2a(mask64Loc.reg());
1369 uint64_t mask64 = mask64Tmp->intVal();
1370 assert(mask64Reg.IsValid() || mask64 == uint32_t(mask64));
1371 assert(mask64);
1373 auto vals64Reg = x2a(vals64Loc.reg());
1374 uint64_t vals64 = vals64Tmp->intVal();
1375 assert(vals64Reg.IsValid() || vals64 == uint32_t(vals64));
1376 assert((vals64 & mask64) == vals64);
1378 auto const destSK = SrcKey(curFunc(), m_unit.bcOff());
1379 auto const destSR = m_mcg->tx().getSrcRec(destSK);
1381 auto thenBody = [&] {
1382 auto bitsOff = sizeof(uint64_t) * (firstBitNum / 64);
1383 auto cond = CC_NE;
1384 auto bitsPtrReg = rAsm;
1386 if (firstBitNum == 0) {
1387 bitsOff = Func::refBitValOff();
1388 bitsPtrReg = funcPtrReg;
1389 } else {
1390 m_as. Ldr (bitsPtrReg, funcPtrReg[Func::sharedOff()]);
1391 bitsOff -= sizeof(uint64_t);
1394 // Don't need the bits pointer after this point
1395 auto bitsReg = rAsm;
1396 // Load the bits
1397 m_as. Ldr (bitsReg, bitsPtrReg[bitsOff]);
1399 // Mask the bits. There are restrictions on what can be encoded as an
1400 // immediate in ARM's logical instructions, and if they're not met,
1401 // the assembler will compensate using ip0 or ip1 as tmps.
1402 if (mask64Reg.IsValid()) {
1403 m_as. And (bitsReg, bitsReg, mask64Reg);
1404 } else {
1405 m_as. And (bitsReg, bitsReg, mask64);
1408 // Now do the compare. There are also restrictions on immediates in
1409 // arithmetic instructions (of which Cmp is one; it's just a subtract that
1410 // sets flags), so same deal as with the mask immediate above.
1411 if (vals64Reg.IsValid()) {
1412 m_as. Cmp (bitsReg, vals64Reg);
1413 } else {
1414 m_as. Cmp (bitsReg, vals64);
1416 destSR->emitFallbackJump(m_mainCode, cond);
1419 if (firstBitNum == 0) {
1420 assert(!nParamsReg.IsValid());
1421 // This is the first 64 bits. No need to check
1422 // nParams.
1423 thenBody();
1424 } else {
1425 assert(nParamsReg.IsValid());
1426 // Check number of args...
1427 m_as. Cmp (nParamsReg, firstBitNum);
1429 if (vals64 != 0 && vals64 != mask64) {
1430 // If we're beyond nParams, then either all params
1431 // are refs, or all params are non-refs, so if vals64
1432 // isn't 0 and isnt mask64, there's no possibility of
1433 // a match
1434 destSR->emitFallbackJump(m_mainCode, CC_LE);
1435 thenBody();
1436 } else {
1437 ifThenElse(m_as, vixl::gt, thenBody, /* else */ [&] {
1438 // If not special builtin...
1439 m_as. Ldr (rAsm, funcPtrReg[Func::attrsOff()]);
1440 m_as. Tst (rAsm, AttrVariadicByRef);
1441 destSR->emitFallbackJump(m_mainCode, vals64 ? CC_Z : CC_NZ);
1447 //////////////////////////////////////////////////////////////////////
1449 void CodeGenerator::cgSyncABIRegs(IRInstruction* inst) {
1450 emitRegGetsRegPlusImm(m_as, rVmFp, x2a(srcLoc(0).reg()), 0);
1451 emitRegGetsRegPlusImm(m_as, rVmSp, x2a(srcLoc(1).reg()), 0);
1454 void CodeGenerator::cgReqBindJmp(IRInstruction* inst) {
1455 emitBindJmp(
1456 m_mainCode,
1457 m_stubsCode,
1458 SrcKey(curFunc(), inst->extra<ReqBindJmp>()->offset)
1462 void CodeGenerator::cgReqRetranslate(IRInstruction* inst) {
1463 assert(m_unit.bcOff() == inst->marker().bcOff);
1464 auto const destSK = SrcKey(curFunc(), m_unit.bcOff());
1465 auto const destSR = m_mcg->tx().getSrcRec(destSK);
1466 destSR->emitFallbackJump(m_mainCode);
1469 void CodeGenerator::cgSpillFrame(IRInstruction* inst) {
1470 auto const func = inst->src(2);
1471 auto const objOrCls = inst->src(3);
1472 auto const invName = inst->extra<SpillFrame>()->invName;
1473 auto const nArgs = inst->extra<SpillFrame>()->numArgs;
1475 auto spReg = x2a(srcLoc(0).reg());
1476 auto fpReg = x2a(srcLoc(1).reg());
1477 auto funcLoc = srcLoc(2);
1478 auto objClsReg = x2a(srcLoc(3).reg());
1479 auto spOff = -kNumActRecCells * sizeof(Cell);
1481 // Saved rbp.
1482 m_as. Str (fpReg, spReg[spOff + AROFF(m_savedRbp)]);
1484 // Num args. Careful here: nArgs is 32 bits and the high bit may be set. Mov's
1485 // immediate argument is intptr_t, and the implicit int32->intptr conversion
1486 // will sign-extend, which isn't what we want.
1487 m_as. Mov (rAsm.W(), (uint32_t)nArgs);
1488 m_as. Str (rAsm.W(), spReg[spOff + AROFF(m_numArgsAndGenCtorFlags)]);
1490 // Magic-call name.
1491 if (invName) {
1492 auto bits = reinterpret_cast<uintptr_t>(invName) | ActRec::kInvNameBit;
1493 m_as. Mov (rAsm, bits);
1494 m_as. Str (rAsm, spReg[spOff + AROFF(m_invName)]);
1495 } else {
1496 m_as. Str (vixl::xzr, spReg[spOff + AROFF(m_invName)]);
1499 // Func and this/class are slightly tricky. The func may be a tuple of a Func*
1500 // and context.
1502 if (objOrCls->isA(Type::Cls)) {
1503 if (objOrCls->isConst()) {
1504 m_as.Mov (rAsm, uintptr_t(objOrCls->clsVal()) | 1);
1505 m_as.Str (rAsm, spReg[spOff + AROFF(m_this)]);
1506 } else {
1507 m_as.Orr (rAsm, objClsReg, 1);
1508 m_as.Str (rAsm, spReg[spOff + AROFF(m_this)]);
1510 } else if (objOrCls->isA(Type::Obj) || objOrCls->isA(Type::Ctx)) {
1511 m_as. Str (objClsReg, spReg[spOff + AROFF(m_this)]);
1512 } else {
1513 assert(objOrCls->isA(Type::InitNull));
1514 m_as.Str (vixl::xzr, spReg[spOff + AROFF(m_this)]);
1517 // Now set func, and possibly this/cls
1518 if (func->isA(Type::Null)) {
1519 // Do nothing
1520 assert(func->isConst());
1521 } else if (func->isConst()) {
1522 m_as. Mov (rAsm, func->funcVal());
1523 m_as. Str (rAsm, spReg[spOff + AROFF(m_func)]);
1524 } else {
1525 auto reg0 = x2a(funcLoc.reg(0));
1526 m_as. Str (reg0, spReg[spOff + AROFF(m_func)]);
1529 // Adjust stack pointer
1530 emitRegGetsRegPlusImm(m_as, x2a(dstLoc(0).reg()), spReg, spOff);
1533 //////////////////////////////////////////////////////////////////////
1535 void CodeGenerator::cgCallBuiltin(IRInstruction* inst) {
1536 auto func = inst->src(0)->funcVal();
1537 auto args = inst->srcs().subpiece(2);
1538 auto numArgs = args.size();
1540 DataType funcReturnType = func->returnType();
1541 int returnOffset = MISOFF(tvBuiltinReturn);
1543 if (FixupMap::eagerRecord(func)) {
1544 // Save VM registers
1545 auto const* pc = curFunc()->unit()->entry() + m_curInst->marker().bcOff;
1546 m_as.Str (rVmFp, rGContextReg[offsetof(ExecutionContext, m_fp)]);
1547 m_as.Str (rVmSp, rGContextReg[offsetof(ExecutionContext, m_stack) +
1548 Stack::topOfStackOffset()]);
1549 m_as.Mov (rAsm, pc);
1550 m_as.Str (rAsm, rGContextReg[offsetof(ExecutionContext, m_pc)]);
1553 // The stack pointer currently points to the MInstrState we need to use.
1554 auto misReg = rAsm;
1555 m_as. Mov (rAsm, vixl::sp);
1557 auto callArgs = argGroup();
1558 if (isCppByRef(funcReturnType)) {
1559 if (isSmartPtrRef(funcReturnType)) {
1560 // first arg is pointer to storage for the return value
1561 returnOffset += TVOFF(m_data);
1563 callArgs.addr(misReg, returnOffset);
1566 for (int i = 0; i < numArgs; ++i) {
1567 auto const& pi = func->params()[i];
1568 if (TVOFF(m_data) && isSmartPtrRef(pi.builtinType())) {
1569 callArgs.addr(srcLoc(i + 2).reg(), TVOFF(m_data));
1570 } else {
1571 callArgs.ssa(i + 2);
1575 misReg = vixl::sp;
1577 auto dloc = dstLoc(0);
1578 auto dstReg = x2a(dloc.reg(0));
1579 auto dstTypeReg = x2a(dloc.reg(1));
1581 cgCallHelper(m_as,
1582 CppCall((TCA)func->nativeFuncPtr()),
1583 isCppByRef(funcReturnType) ? kVoidDest : callDest(dstReg),
1584 SyncOptions::kSyncPoint,
1585 callArgs);
1587 auto returnType = inst->typeParam();
1588 if (!dstReg.IsValid() || returnType.isSimpleType()) {
1589 return;
1592 if (returnType.isReferenceType()) {
1593 assert(isCppByRef(funcReturnType) && isSmartPtrRef(funcReturnType));
1594 vixl::Label notNullptr;
1595 vixl::Label done;
1596 m_as. Ldr (dstReg, misReg[returnOffset + TVOFF(m_data)]);
1597 m_as. Cbnz (dstReg, &notNullptr);
1598 m_as. Mov (dstTypeReg, KindOfNull);
1599 m_as. B (&done);
1600 m_as. bind (&notNullptr);
1601 m_as. Mov (dstTypeReg, returnType.toDataType());
1602 m_as. bind (&done);
1603 return;
1606 if (returnType <= Type::Cell || returnType <= Type::BoxedCell) {
1607 assert(isCppByRef(funcReturnType) && !isSmartPtrRef(funcReturnType));
1608 vixl::Label notUninit;
1609 vixl::Label done;
1610 m_as. Ldrb (dstTypeReg.W(), misReg[returnOffset + TVOFF(m_type)]);
1611 m_as. Cbnz (dstTypeReg, &notUninit);
1612 m_as. Mov (dstTypeReg, KindOfNull);
1613 m_as. B (&done);
1614 m_as. bind (&notUninit);
1615 m_as. Ldr (dstReg, misReg[returnOffset + TVOFF(m_data)]);
1616 m_as. bind (&done);
1617 return;
1620 always_assert(false);
1623 void CodeGenerator::cgCall(IRInstruction* inst) {
1624 auto spReg = x2a(srcLoc(0).reg());
1625 auto* returnBcOffset = inst->src(1);
1626 auto* func = inst->src(2);
1627 SrcRange args = inst->srcs().subpiece(3);
1628 int32_t numArgs = args.size();
1630 int64_t adjustment = cellsToBytes((int64_t)-numArgs);
1631 for (int32_t i = 0; i < numArgs; ++i) {
1632 emitStore(spReg, cellsToBytes(-(i + 1)), args[i], srcLoc(i + 3));
1635 m_as. Mov (rAsm.W(), returnBcOffset->intVal());
1636 m_as. Str (rAsm.W(), spReg[AROFF(m_soff)]);
1637 emitRegGetsRegPlusImm(m_as, spReg, spReg, adjustment);
1639 assert(m_curInst->marker().valid());
1640 SrcKey srcKey = SrcKey(m_curInst->marker().func, m_curInst->marker().bcOff);
1641 bool isImmutable = func->isConst() && !func->isA(Type::Null);
1642 const Func* funcd = isImmutable ? func->funcVal() : nullptr;
1643 int32_t adjust = emitBindCall(mcg->code.main(), mcg->code.stubs(),
1644 srcKey, funcd, numArgs);
1646 emitRegGetsRegPlusImm(m_as, rVmSp, rVmSp, adjust);
1649 //////////////////////////////////////////////////////////////////////
1651 void CodeGenerator::cgBeginCatch(IRInstruction* inst) {
1652 m_as. Brk (0);
1655 void CodeGenerator::cgEndCatch(IRInstruction* inst) {
1656 m_as. Brk (0);
1659 //////////////////////////////////////////////////////////////////////
1661 void CodeGenerator::emitLoadTypedValue(PhysLoc dst,
1662 vixl::Register base,
1663 ptrdiff_t offset,
1664 Block* label) {
1665 auto valueDstReg = x2a(dst.reg(0));
1666 auto typeDstReg = x2a(dst.reg(1));
1668 if (label) not_implemented();
1670 if (valueDstReg.IsFPRegister()) {
1671 not_implemented();
1674 // Avoid clobbering the base reg if we'll need it later
1675 if (base.Is(typeDstReg) && valueDstReg.IsValid()) {
1676 m_as. Mov (rAsm, base);
1677 base = rAsm;
1680 if (typeDstReg.IsValid()) {
1681 m_as. Ldrb (typeDstReg.W(), base[offset + TVOFF(m_type)]);
1684 if (valueDstReg.IsValid()) {
1685 m_as. Ldr (valueDstReg, base[offset + TVOFF(m_data)]);
1689 void CodeGenerator::emitStoreTypedValue(vixl::Register base,
1690 ptrdiff_t offset,
1691 PhysLoc src) {
1692 assert(src.numWords() == 2);
1693 auto reg0 = x2a(src.reg(0));
1694 auto reg1 = x2a(src.reg(1));
1695 m_as. Str (reg0, base[offset + TVOFF(m_data)]);
1696 m_as. Strb (reg1.W(), base[offset + TVOFF(m_type)]);
1699 void CodeGenerator::emitLoad(Type type, PhysLoc dstLoc,
1700 vixl::Register base,
1701 ptrdiff_t offset,
1702 Block* label /* = nullptr */) {
1703 if (type.needsReg()) {
1704 return emitLoadTypedValue(dstLoc, base, offset, label);
1706 if (label) {
1707 not_implemented();
1709 if (type <= Type::Null) return;
1711 auto dstReg = x2a(dstLoc.reg());
1712 if (!dstReg.IsValid()) return;
1714 m_as. Ldr (dstReg, base[offset + TVOFF(m_data)]);
1717 void CodeGenerator::emitStore(vixl::Register base,
1718 ptrdiff_t offset,
1719 SSATmp* src, PhysLoc srcLoc,
1720 bool genStoreType /* = true */) {
1721 auto type = src->type();
1722 if (type.needsReg()) {
1723 return emitStoreTypedValue(base, offset, srcLoc);
1725 if (genStoreType) {
1726 auto dt = type.toDataType();
1727 if (dt == KindOfUninit) {
1728 static_assert(KindOfUninit == 0, "zero register hack");
1729 m_as. Strb (vixl::wzr, base[offset + TVOFF(m_type)]);
1730 } else {
1731 m_as. Mov (rAsm, dt);
1732 m_as. Strb (rAsm.W(), base[offset + TVOFF(m_type)]);
1735 if (type <= Type::Null) {
1736 return;
1738 if (src->isConst()) {
1739 int64_t val = 0;
1740 if (type <= (Type::Bool | Type::Int | Type::Dbl |
1741 Type::Arr | Type::StaticStr | Type::Cls)) {
1742 val = src->rawVal();
1743 } else {
1744 not_reached();
1746 m_as. Mov (rAsm, val);
1747 m_as. Str (rAsm, base[offset + TVOFF(m_data)]);
1748 } else {
1749 auto reg = x2a(srcLoc.reg());
1750 if (src->isA(Type::Bool)) {
1751 m_as. Uxtb (reg.W(), reg.W());
1753 m_as. Str (x2a(srcLoc.reg()), base[offset + TVOFF(m_data)]);
1757 void CodeGenerator::cgLdLoc(IRInstruction* inst) {
1758 auto baseReg = x2a(srcLoc(0).reg());
1759 auto offset = localOffset(inst->extra<LdLoc>()->locId);
1760 emitLoad(inst->dst()->type(), dstLoc(0), baseReg, offset);
1763 void CodeGenerator::cgStLoc(IRInstruction* inst) {
1764 auto baseReg = x2a(srcLoc(0).reg());
1765 auto offset = localOffset(inst->extra<StLoc>()->locId);
1766 emitStore(baseReg, offset, inst->src(1), srcLoc(1), true /* store type */);
1769 void CodeGenerator::cgLdStack(IRInstruction* inst) {
1770 assert(inst->taken() == nullptr);
1771 auto srcReg = x2a(srcLoc(0).reg());
1772 auto offset = cellsToBytes(inst->extra<LdStack>()->offset);
1773 emitLoad(inst->dst()->type(), dstLoc(0), srcReg, offset);
1776 void CodeGenerator::emitLdRaw(IRInstruction* inst, size_t extraOff) {
1777 auto destReg = x2a(dstLoc(0).reg());
1778 auto offset = inst->extra<RawMemData>()->info().offset;
1779 auto src = x2a(srcLoc(0).reg())[offset + extraOff];
1781 switch (inst->extra<RawMemData>()->info().size) {
1782 case sz::byte: m_as. Ldrb (destReg.W(), src); break;
1783 case sz::dword: m_as. Ldr (destReg.W(), src); break;
1784 case sz::qword: m_as. Ldr (destReg, src); break;
1785 default: not_implemented();
1789 void CodeGenerator::cgLdRaw(IRInstruction* inst) {
1790 emitLdRaw(inst, 0);
1793 void CodeGenerator::cgLdContArRaw(IRInstruction* inst) {
1794 emitLdRaw(inst, -c_Continuation::getArOffset());
1797 void CodeGenerator::cgLdARFuncPtr(IRInstruction* inst) {
1798 auto dstReg = x2a(dstLoc(0).reg());
1799 auto baseReg = x2a(srcLoc(0).reg());
1800 auto offset = inst->src(1)->intVal();
1801 m_as. Ldr (dstReg, baseReg[offset + AROFF(m_func)]);
1804 void CodeGenerator::cgLdFuncCached(IRInstruction* inst) {
1805 auto dstReg = x2a(dstLoc(0).reg());
1806 auto const name = inst->extra<LdFuncCachedData>()->name;
1807 auto const ch = Unit::GetNamedEntity(name)->getFuncHandle();
1808 vixl::Label noLookup;
1810 if (!dstReg.IsValid()) {
1811 m_as. Ldr (rAsm, rVmTl[ch]);
1812 dstReg = rAsm;
1813 } else {
1814 m_as. Ldr (dstReg, rVmTl[ch]);
1816 m_as. Cbnz (dstReg, &noLookup);
1818 const Func* (*const func)(const StringData*) = lookupUnknownFunc;
1819 cgCallHelper(
1820 m_as,
1821 CppCall(func),
1822 callDest(inst),
1823 SyncOptions::kSyncPoint,
1824 argGroup().immPtr(inst->extra<LdFuncCached>()->name)
1827 m_as. bind (&noLookup);
1830 void CodeGenerator::cgLdStackAddr(IRInstruction* inst) {
1831 auto const dstReg = x2a(dstLoc(0).reg());
1832 auto const baseReg = x2a(srcLoc(0).reg());
1833 auto const offset = cellsToBytes(inst->extra<LdStackAddr>()->offset);
1834 emitRegGetsRegPlusImm(m_as, dstReg, baseReg, offset);
1837 void CodeGenerator::cgSpillStack(IRInstruction* inst) {
1838 // TODO(2966414): so much of this logic could be shared. The opcode itself
1839 // should probably be broken up.
1840 auto const spDeficit = inst->src(1)->intVal();
1841 auto const spillVals = inst->srcs().subpiece(2);
1842 auto const numSpillSrcs = spillVals.size();
1843 auto const dstReg = x2a(dstLoc(0).reg());
1844 auto const spReg = x2a(srcLoc(0).reg());
1845 auto const spillCells = spillValueCells(inst);
1847 int64_t adjustment = (spDeficit - spillCells) * sizeof(Cell);
1848 for (uint32_t i = 0; i < numSpillSrcs; ++i) {
1849 const int64_t offset = i * sizeof(Cell) + adjustment;
1850 emitStore(spReg, offset, spillVals[i], srcLoc(i + 2));
1852 emitRegGetsRegPlusImm(m_as, dstReg, spReg, adjustment);
1855 void CodeGenerator::cgInterpOneCommon(IRInstruction* inst) {
1856 auto pcOff = inst->extra<InterpOneData>()->bcOff;
1858 auto opc = *(curFunc()->unit()->at(pcOff));
1859 auto* interpOneHelper = interpOneEntryPoints[opc];
1861 cgCallHelper(m_as,
1862 CppCall(interpOneHelper),
1863 callDest(InvalidReg),
1864 SyncOptions::kSyncPoint,
1865 argGroup().ssa(1/*fp*/).ssa(0/*sp*/).imm(pcOff));
1868 void CodeGenerator::cgInterpOne(IRInstruction* inst) {
1869 cgInterpOneCommon(inst);
1871 auto const& extra = *inst->extra<InterpOne>();
1872 auto newSpReg = x2a(dstLoc(0).reg());
1874 auto spAdjustBytes = cellsToBytes(extra.cellsPopped - extra.cellsPushed);
1875 emitRegGetsRegPlusImm(m_as, newSpReg, newSpReg, spAdjustBytes);
1878 void CodeGenerator::cgInterpOneCF(IRInstruction* inst) {
1879 cgInterpOneCommon(inst);
1881 m_as. Ldr (rVmFp, rReturnReg[offsetof(ExecutionContext, m_fp)]);
1882 m_as. Ldr (rVmSp, rReturnReg[offsetof(ExecutionContext, m_stack) +
1883 Stack::topOfStackOffset()]);
1885 emitServiceReq(mcg->code.main(), REQ_RESUME);
1888 //////////////////////////////////////////////////////////////////////
1890 Address CodeGenerator::cgInst(IRInstruction* inst) {
1891 Opcode opc = inst->op();
1892 auto const start = m_as.frontier();
1893 m_curInst = inst;
1894 SCOPE_EXIT { m_curInst = nullptr; };
1896 switch (opc) {
1897 #define O(name, dsts, srcs, flags) \
1898 case name: FTRACE(7, "cg" #name "\n"); \
1899 cg ## name (inst); \
1900 return m_as.frontier() == start ? nullptr : start;
1901 IR_OPCODES
1902 #undef O
1903 default:
1904 assert(0);
1905 return nullptr;