LegalizeMD.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. // Build static array of legal instruction forms
  7. #undef MACRO
  8. #define MACRO(name, jnLayout, attrib, byte2, legalforms, opbyte, ...) legalforms,
  9. static const LegalInstrForms _InstrForms[] =
  10. {
  11. #include "MdOpCodes.h"
  12. };
  13. // Local class for tracking the opcodes used by an expanded LDIMM
  14. class LdImmOpcode
  15. {
  16. public:
  17. void Set(Js::OpCode opcode, IntConstType immed, int shift)
  18. {
  19. m_opcode = opcode;
  20. m_immed = immed;
  21. m_shift = shift;
  22. }
  23. Js::OpCode m_opcode;
  24. int m_shift;
  25. IntConstType m_immed;
  26. };
  27. static LegalForms LegalDstForms(IR::Instr * instr)
  28. {
  29. Assert(instr->IsLowered());
  30. return _InstrForms[instr->m_opcode - (Js::OpCode::MDStart+1)].dst;
  31. }
  32. static LegalForms LegalSrcForms(IR::Instr * instr, uint opndNum)
  33. {
  34. Assert(instr->IsLowered());
  35. return _InstrForms[instr->m_opcode - (Js::OpCode::MDStart+1)].src[opndNum-1];
  36. }
  37. RegNum LegalizeMD::GetScratchReg(IR::Instr * instr)
  38. {
  39. return instr->m_func->ShouldLegalizePostRegAlloc() ? SCRATCH_REG : RegNOREG;
  40. }
  41. void LegalizeMD::LegalizeInstr(IR::Instr * instr)
  42. {
  43. if (!instr->IsLowered())
  44. {
  45. AssertMsg(UNREACHED, "Unlowered instruction in m.d. legalizer");
  46. return;
  47. }
  48. // Convert CMP/CMN/TST opcodes into more primitive forms
  49. switch (instr->m_opcode)
  50. {
  51. case Js::OpCode::CMP:
  52. instr->SetDst(IR::RegOpnd::New(NULL, RegZR, instr->GetSrc1()->GetType() , instr->m_func));
  53. instr->m_opcode = Js::OpCode::SUBS;
  54. break;
  55. case Js::OpCode::CMN:
  56. instr->SetDst(IR::RegOpnd::New(NULL, RegZR, instr->GetSrc1()->GetType(), instr->m_func));
  57. instr->m_opcode = Js::OpCode::ADDS;
  58. break;
  59. case Js::OpCode::TST:
  60. instr->SetDst(IR::RegOpnd::New(NULL, RegZR, instr->GetSrc1()->GetType(), instr->m_func));
  61. instr->m_opcode = Js::OpCode::ANDS;
  62. break;
  63. }
  64. LegalizeDst(instr);
  65. LegalizeSrc(instr, instr->GetSrc1(), 1);
  66. LegalizeSrc(instr, instr->GetSrc2(), 2);
  67. }
  68. void LegalizeMD::LegalizeRegOpnd(IR::Instr* instr, IR::Opnd* opnd)
  69. {
  70. // Arm64 does not support 8 or 16 bit register usage, so promote anything smaller than 32 bits up to 32 bits.
  71. IRType ty = opnd->GetType();
  72. switch(ty)
  73. {
  74. case TyInt8:
  75. case TyInt16:
  76. ty = TyInt32;
  77. break;
  78. case TyUint8:
  79. case TyUint16:
  80. ty = TyUint32;
  81. break;
  82. }
  83. if (ty != opnd->GetType())
  84. {
  85. // UseWithNewType will make a copy if the register is already in use. We know it is in use because it is used in this instruction, and we want to reuse this operand rather than making a copy
  86. // so UnUse it before calling UseWithNewType.
  87. opnd->UnUse();
  88. opnd->UseWithNewType(ty, instr->m_func);
  89. }
  90. }
  91. void LegalizeMD::LegalizeDst(IR::Instr * instr)
  92. {
  93. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  94. LegalForms forms = LegalDstForms(instr);
  95. IR::Opnd * opnd = instr->GetDst();
  96. if (opnd == NULL)
  97. {
  98. #ifdef DBG
  99. // No legalization possible, just report error.
  100. if (forms != 0)
  101. {
  102. IllegalInstr(instr, _u("Expected dst opnd"));
  103. }
  104. #endif
  105. return;
  106. }
  107. switch (opnd->GetKind())
  108. {
  109. case IR::OpndKindReg:
  110. #ifdef DBG
  111. if (!(forms & L_RegMask))
  112. {
  113. IllegalInstr(instr, _u("Unexpected reg dst"));
  114. }
  115. #endif
  116. LegalizeRegOpnd(instr, opnd);
  117. break;
  118. case IR::OpndKindMemRef:
  119. {
  120. // MemRefOpnd is a deference of the memory location.
  121. // So extract the location, load it to register, replace the MemRefOpnd with an IndirOpnd taking the
  122. // register as base, and fall through to legalize the IndirOpnd.
  123. intptr_t memLoc = opnd->AsMemRefOpnd()->GetMemLoc();
  124. IR::RegOpnd *newReg = IR::RegOpnd::New(GetScratchReg(instr), TyMachPtr, instr->m_func);
  125. IR::Instr *newInstr = IR::Instr::New(Js::OpCode::LDIMM, newReg,
  126. IR::AddrOpnd::New(memLoc, opnd->AsMemRefOpnd()->GetAddrKind(), instr->m_func, true), instr->m_func);
  127. instr->InsertBefore(newInstr);
  128. LegalizeMD::LegalizeInstr(newInstr);
  129. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(newReg, 0, opnd->GetType(), instr->m_func);
  130. opnd = instr->ReplaceDst(indirOpnd);
  131. }
  132. // FALL THROUGH
  133. case IR::OpndKindIndir:
  134. if (!(forms & L_IndirMask))
  135. {
  136. instr = LegalizeStore(instr, forms);
  137. forms = LegalDstForms(instr);
  138. }
  139. LegalizeIndirOffset(instr, opnd->AsIndirOpnd(), forms);
  140. break;
  141. case IR::OpndKindSym:
  142. if (!(forms & L_SymMask))
  143. {
  144. instr = LegalizeStore(instr, forms);
  145. forms = LegalDstForms(instr);
  146. }
  147. if (fPostRegAlloc)
  148. {
  149. // In order to legalize SymOffset we need to know final argument area, which is only available after lowerer.
  150. // So, don't legalize sym offset here, but it will be done as part of register allocator.
  151. LegalizeSymOffset(instr, opnd->AsSymOpnd(), forms);
  152. }
  153. break;
  154. default:
  155. AssertMsg(UNREACHED, "Unexpected dst opnd kind");
  156. break;
  157. }
  158. }
  159. IR::Instr * LegalizeMD::LegalizeStore(IR::Instr *instr, LegalForms forms)
  160. {
  161. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  162. if (LowererMD::IsAssign(instr) && instr->GetSrc1()->IsRegOpnd())
  163. {
  164. // We can just change this to a store in place.
  165. instr->m_opcode = LowererMD::GetStoreOp(instr->GetSrc1()->GetType());
  166. }
  167. else
  168. {
  169. // Sink the mem opnd. The caller will verify the offset.
  170. // We don't expect to hit this point after register allocation, because we
  171. // can't guarantee that the instruction will be legal.
  172. Assert(!fPostRegAlloc);
  173. IR::Instr * newInstr = instr->SinkDst(LowererMD::GetStoreOp(instr->GetDst()->GetType()), RegNOREG);
  174. LegalizeMD::LegalizeRegOpnd(instr, instr->GetDst());
  175. instr = newInstr;
  176. }
  177. return instr;
  178. }
  179. void LegalizeMD::LegalizeSrc(IR::Instr * instr, IR::Opnd * opnd, uint opndNum)
  180. {
  181. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  182. LegalForms forms = LegalSrcForms(instr, opndNum);
  183. if (opnd == NULL)
  184. {
  185. #ifdef DBG
  186. // No legalization possible, just report error.
  187. if (forms != 0)
  188. {
  189. IllegalInstr(instr, _u("Expected src %d opnd"), opndNum);
  190. }
  191. #endif
  192. return;
  193. }
  194. switch (opnd->GetKind())
  195. {
  196. case IR::OpndKindReg:
  197. #ifdef DBG
  198. if (!(forms & L_RegMask))
  199. {
  200. IllegalInstr(instr, _u("Unexpected reg as src%d opnd"), opndNum);
  201. }
  202. #endif
  203. LegalizeRegOpnd(instr, opnd);
  204. break;
  205. case IR::OpndKindAddr:
  206. case IR::OpndKindHelperCall:
  207. case IR::OpndKindIntConst:
  208. LegalizeImmed(instr, opnd, opndNum, opnd->GetImmediateValue(instr->m_func), forms);
  209. break;
  210. case IR::OpndKindLabel:
  211. LegalizeLabelOpnd(instr, opnd, opndNum);
  212. break;
  213. case IR::OpndKindMemRef:
  214. {
  215. // MemRefOpnd is a deference of the memory location.
  216. // So extract the location, load it to register, replace the MemRefOpnd with an IndirOpnd taking the
  217. // register as base, and fall through to legalize the IndirOpnd.
  218. intptr_t memLoc = opnd->AsMemRefOpnd()->GetMemLoc();
  219. IR::RegOpnd *newReg = IR::RegOpnd::New(GetScratchReg(instr), TyMachPtr, instr->m_func);
  220. IR::Instr *newInstr = IR::Instr::New(Js::OpCode::LDIMM, newReg, IR::AddrOpnd::New(memLoc, IR::AddrOpndKindDynamicMisc, instr->m_func), instr->m_func);
  221. instr->InsertBefore(newInstr);
  222. LegalizeMD::LegalizeInstr(newInstr);
  223. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(newReg, 0, opnd->GetType(), instr->m_func);
  224. if (opndNum == 1)
  225. {
  226. opnd = instr->ReplaceSrc1(indirOpnd);
  227. }
  228. else
  229. {
  230. opnd = instr->ReplaceSrc2(indirOpnd);
  231. }
  232. }
  233. // FALL THROUGH
  234. case IR::OpndKindIndir:
  235. if (!(forms & L_IndirMask))
  236. {
  237. instr = LegalizeLoad(instr, opndNum, forms);
  238. forms = LegalSrcForms(instr, 1);
  239. }
  240. LegalizeIndirOffset(instr, opnd->AsIndirOpnd(), forms);
  241. break;
  242. case IR::OpndKindSym:
  243. if (!(forms & L_SymMask))
  244. {
  245. instr = LegalizeLoad(instr, opndNum, forms);
  246. forms = LegalSrcForms(instr, 1);
  247. }
  248. if (fPostRegAlloc)
  249. {
  250. // In order to legalize SymOffset we need to know final argument area, which is only available after lowerer.
  251. // So, don't legalize sym offset here, but it will be done as part of register allocator.
  252. LegalizeSymOffset(instr, opnd->AsSymOpnd(), forms);
  253. }
  254. break;
  255. default:
  256. AssertMsg(UNREACHED, "Unexpected src opnd kind");
  257. break;
  258. }
  259. }
  260. IR::Instr * LegalizeMD::LegalizeLoad(IR::Instr *instr, uint opndNum, LegalForms forms)
  261. {
  262. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  263. if (LowererMD::IsAssign(instr) && instr->GetDst()->IsRegOpnd())
  264. {
  265. // We can just change this to a load in place.
  266. instr->m_opcode = LowererMD::GetLoadOp(instr->GetDst()->GetType());
  267. }
  268. else
  269. {
  270. // Hoist the memory opnd. The caller will verify the offset.
  271. IR::Opnd* src = (opndNum == 1) ? instr->GetSrc1() : instr->GetSrc2();
  272. AssertMsg(!fPostRegAlloc || src->GetType() == TyMachReg, "Post RegAlloc other types disallowed");
  273. instr = GenerateHoistSrc(instr, opndNum, LowererMD::GetLoadOp(src->GetType()), GetScratchReg(instr));
  274. }
  275. if (instr->m_opcode == Js::OpCode::LDR && instr->GetSrc1()->IsSigned())
  276. {
  277. instr->m_opcode = Js::OpCode::LDRS;
  278. }
  279. return instr;
  280. }
  281. void LegalizeMD::LegalizeIndirOffset(IR::Instr * instr, IR::IndirOpnd * indirOpnd, LegalForms forms)
  282. {
  283. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  284. // For LEA, we have special handling of indiropnds
  285. auto correctSize = [](IR::Instr* instr, IR::IndirOpnd* indirOpnd)
  286. {
  287. // If the base and index operands on an LEA are different sizes, grow the smaller one,
  288. // matching the larger size but keeping the correct signedness for the type
  289. IR::RegOpnd* baseOpnd = indirOpnd->GetBaseOpnd();
  290. IR::RegOpnd* indexOpnd = indirOpnd->GetIndexOpnd();
  291. if ( baseOpnd != nullptr
  292. && indexOpnd != nullptr
  293. && baseOpnd->GetSize() != indexOpnd->GetSize()
  294. )
  295. {
  296. if (baseOpnd->GetSize() < indexOpnd->GetSize())
  297. {
  298. IRType largerType = indexOpnd->GetType();
  299. Assert(IRType_IsSignedInt(largerType) || IRType_IsUnsignedInt(largerType));
  300. IRType sourceType = baseOpnd->GetType();
  301. IRType targetType = IRType_IsSignedInt(sourceType) ? IRType_EnsureSigned(largerType) : IRType_EnsureUnsigned(largerType);
  302. IR::Instr* movInstr = Lowerer::InsertMove(baseOpnd->UseWithNewType(targetType, instr->m_func), baseOpnd, instr, false);
  303. indirOpnd->SetBaseOpnd(movInstr->GetDst()->AsRegOpnd());
  304. }
  305. else
  306. {
  307. Assert(indexOpnd->GetSize() < baseOpnd->GetSize());
  308. IRType largerType = baseOpnd->GetType();
  309. Assert(IRType_IsSignedInt(largerType) || IRType_IsUnsignedInt(largerType));
  310. IRType sourceType = indexOpnd->GetType();
  311. IRType targetType = IRType_IsSignedInt(sourceType) ? IRType_EnsureSigned(largerType) : IRType_EnsureUnsigned(largerType);
  312. IR::Instr* movInstr = Lowerer::InsertMove(indexOpnd->UseWithNewType(targetType, instr->m_func), indexOpnd, instr, false);
  313. indirOpnd->SetIndexOpnd(movInstr->GetDst()->AsRegOpnd());
  314. }
  315. }
  316. };
  317. int32 offset = indirOpnd->GetOffset();
  318. if (indirOpnd->IsFloat())
  319. {
  320. IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
  321. IR::RegOpnd *indexOpnd = indirOpnd->UnlinkIndexOpnd(); //Clears index operand
  322. byte scale = indirOpnd->GetScale();
  323. if (indexOpnd)
  324. {
  325. if (scale > 0)
  326. {
  327. // There is no support for ADD instruction with barrel shifter in encoder, hence add an explicit instruction to left shift the index operand
  328. // Reason is this requires 4 operand in IR and there is no support for this yet.
  329. // If we encounter more such scenarios, its better to solve the root cause.
  330. // Also VSTR & VLDR don't take index operand as parameter
  331. IR::RegOpnd* newIndexOpnd = IR::RegOpnd::New(indexOpnd->GetType(), instr->m_func);
  332. IR::Instr* newInstr = IR::Instr::New(Js::OpCode::LSL, newIndexOpnd, indexOpnd,
  333. IR::IntConstOpnd::New(scale, TyMachReg, instr->m_func), instr->m_func);
  334. instr->InsertBefore(newInstr);
  335. indirOpnd->SetScale(0); //Clears scale
  336. indexOpnd = newIndexOpnd;
  337. }
  338. IR::Instr * instrAdd = Lowerer::HoistIndirIndexOpndAsAdd(instr, indirOpnd, baseOpnd, indexOpnd, GetScratchReg(instr));
  339. LegalizeMD::LegalizeInstr(instrAdd);
  340. }
  341. }
  342. else if (indirOpnd->GetIndexOpnd() != nullptr && offset != 0)
  343. {
  344. // Can't have both offset and index, so hoist the offset and try again.
  345. IR::Instr *addInstr = Lowerer::HoistIndirOffset(instr, indirOpnd, GetScratchReg(instr));
  346. LegalizeMD::LegalizeInstr(addInstr);
  347. if (instr->m_opcode == Js::OpCode::LEA)
  348. {
  349. AssertOrFailFastMsg(indirOpnd->GetBaseOpnd() != nullptr && indirOpnd->GetBaseOpnd()->GetSize() == TySize[TyMachPtr], "Base operand of LEA must have pointer-width!");
  350. correctSize(instr, indirOpnd);
  351. }
  352. return;
  353. }
  354. // Determine scale factor for scaled offsets
  355. int size = indirOpnd->GetSize();
  356. int32 scaledOffset = offset / size;
  357. // Either scaled unsigned 12-bit offset, or unscaled signed 9-bit offset
  358. if (forms & L_IndirSU12I9)
  359. {
  360. if (offset == (scaledOffset * size) && IS_CONST_UINT12(scaledOffset))
  361. {
  362. return;
  363. }
  364. if (IS_CONST_INT9(offset))
  365. {
  366. return;
  367. }
  368. }
  369. if (forms & L_IndirU12Lsl12)
  370. {
  371. // Only one opcode with this form right now
  372. Assert(instr->m_opcode == Js::OpCode::LEA);
  373. AssertOrFailFastMsg(indirOpnd->GetBaseOpnd() != nullptr && indirOpnd->GetBaseOpnd()->GetSize() == TySize[TyMachPtr], "Base operand of LEA must have pointer-width!");
  374. if (offset != 0)
  375. {
  376. // Should have already handled this case
  377. Assert(indirOpnd->GetIndexOpnd() == nullptr);
  378. if (IS_CONST_UINT12(offset) || IS_CONST_UINT12LSL12(offset))
  379. {
  380. return;
  381. }
  382. }
  383. else
  384. {
  385. if (indirOpnd->GetIndexOpnd() != nullptr)
  386. {
  387. correctSize(instr, indirOpnd);
  388. }
  389. return;
  390. }
  391. }
  392. // scaled signed 9-bit offset
  393. if (forms & L_IndirSI7)
  394. {
  395. if (IS_CONST_INT7(scaledOffset))
  396. {
  397. return;
  398. }
  399. }
  400. // Offset is too large, so hoist it and replace it with an index
  401. IR::Instr *addInstr = Lowerer::HoistIndirOffset(instr, indirOpnd, GetScratchReg(instr));
  402. LegalizeMD::LegalizeInstr(addInstr);
  403. }
  404. void LegalizeMD::LegalizeSymOffset(IR::Instr * instr, IR::SymOpnd * symOpnd, LegalForms forms)
  405. {
  406. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  407. AssertMsg(fPostRegAlloc, "LegalizeMD::LegalizeSymOffset can (and will) be called as part of register allocation. Can't call it as part of lowerer, as final argument area is not available yet.");
  408. RegNum baseReg;
  409. int32 offset;
  410. if (!symOpnd->m_sym->IsStackSym())
  411. {
  412. return;
  413. }
  414. EncoderMD::BaseAndOffsetFromSym(symOpnd, &baseReg, &offset, instr->m_func->GetTopFunc());
  415. // Determine scale factor for scaled offsets
  416. int scale;
  417. if (symOpnd->GetSize() == 8)
  418. {
  419. scale = 3;
  420. }
  421. else if (symOpnd->GetSize() == 4)
  422. {
  423. scale = 2;
  424. }
  425. else if (symOpnd->GetSize() == 2)
  426. {
  427. scale = 1;
  428. }
  429. else
  430. {
  431. scale = 0;
  432. }
  433. int32 scaledOffset = offset >> scale;
  434. // Either scaled unsigned 12-bit offset, or unscaled signed 9-bit offset
  435. if (forms & L_SymSU12I9)
  436. {
  437. if (offset == (scaledOffset << scale) && IS_CONST_UINT12(scaledOffset))
  438. {
  439. return;
  440. }
  441. if (IS_CONST_INT9(offset))
  442. {
  443. return;
  444. }
  445. }
  446. // This is so far used for LEA, where the offset ends up needing to be a valid ADD immediate
  447. if (forms & L_SymU12Lsl12)
  448. {
  449. if (IS_CONST_00000FFF(offset) || IS_CONST_00FFF000(offset))
  450. {
  451. return;
  452. }
  453. }
  454. // scaled signed 9-bit offset
  455. if (forms & L_SymSI7)
  456. {
  457. if (IS_CONST_INT7(scaledOffset))
  458. {
  459. return;
  460. }
  461. }
  462. IR::Instr * newInstr;
  463. if (instr->m_opcode == Js::OpCode::LEA)
  464. {
  465. instr->m_opcode = Js::OpCode::ADD;
  466. instr->ReplaceSrc1(IR::RegOpnd::New(NULL, baseReg, TyMachPtr, instr->m_func));
  467. instr->SetSrc2(IR::IntConstOpnd::New(offset, TyMachReg, instr->m_func));
  468. newInstr = instr->HoistSrc2(Js::OpCode::LDIMM, GetScratchReg(instr));
  469. LegalizeMD::LegalizeInstr(newInstr);
  470. LegalizeMD::LegalizeInstr(instr);
  471. }
  472. else
  473. {
  474. newInstr = Lowerer::HoistSymOffset(instr, symOpnd, baseReg, offset, GetScratchReg(instr));
  475. LegalizeMD::LegalizeInstr(newInstr);
  476. }
  477. }
  478. void LegalizeMD::LegalizeImmed(
  479. IR::Instr * instr,
  480. IR::Opnd * opnd,
  481. uint opndNum,
  482. IntConstType immed,
  483. LegalForms forms)
  484. {
  485. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  486. int size = instr->GetDst()->GetSize();
  487. if (!(((forms & L_ImmLog12) && EncoderMD::CanEncodeLogicalConst(immed, size)) ||
  488. ((forms & L_ImmU12) && IS_CONST_UINT12(immed)) ||
  489. ((forms & L_ImmU12Lsl12) && IS_CONST_UINT12LSL12(immed)) ||
  490. ((forms & L_ImmU6) && IS_CONST_UINT6(immed)) ||
  491. ((forms & L_ImmU16) && IS_CONST_UINT16(immed)) ||
  492. ((forms & L_ImmU6U6) && IS_CONST_UINT6UINT6(immed))))
  493. {
  494. if (instr->m_opcode != Js::OpCode::LDIMM)
  495. {
  496. instr = LegalizeMD::GenerateLDIMM(instr, opndNum, GetScratchReg(instr));
  497. }
  498. if (fPostRegAlloc)
  499. {
  500. LegalizeMD::LegalizeLDIMM(instr, immed);
  501. }
  502. }
  503. }
  504. void LegalizeMD::LegalizeLabelOpnd(
  505. IR::Instr * instr,
  506. IR::Opnd * opnd,
  507. uint opndNum)
  508. {
  509. const bool fPostRegAlloc = instr->m_func->ShouldLegalizePostRegAlloc();
  510. if (instr->m_opcode != Js::OpCode::LDIMM)
  511. {
  512. instr = LegalizeMD::GenerateLDIMM(instr, opndNum, GetScratchReg(instr));
  513. }
  514. if (fPostRegAlloc)
  515. {
  516. LegalizeMD::LegalizeLdLabel(instr, opnd);
  517. }
  518. }
  519. IR::Instr * LegalizeMD::GenerateHoistSrc(IR::Instr * instr, uint opndNum, Js::OpCode op, RegNum scratchReg)
  520. {
  521. IR::Instr * newInstr;
  522. if (opndNum == 1)
  523. {
  524. newInstr = instr->HoistSrc1(op, scratchReg);
  525. LegalizeMD::LegalizeRegOpnd(instr, instr->GetSrc1());
  526. }
  527. else
  528. {
  529. newInstr = instr->HoistSrc2(op, scratchReg);
  530. LegalizeMD::LegalizeRegOpnd(instr, instr->GetSrc2());
  531. }
  532. return newInstr;
  533. }
  534. IR::Instr * LegalizeMD::GenerateLDIMM(IR::Instr * instr, uint opndNum, RegNum scratchReg)
  535. {
  536. if (LowererMD::IsAssign(instr) && instr->GetDst()->IsRegOpnd())
  537. {
  538. instr->m_opcode = Js::OpCode::LDIMM;
  539. }
  540. else
  541. {
  542. instr = GenerateHoistSrc(instr, opndNum, Js::OpCode::LDIMM, scratchReg);
  543. }
  544. return instr;
  545. }
  546. void LegalizeMD::LegalizeLDIMM(IR::Instr * instr, IntConstType immed)
  547. {
  548. // In case of inlined entry instruction, we don't know the offset till the encoding phase
  549. if (!instr->isInlineeEntryInstr)
  550. {
  551. // If the source is a tagged int, and the dest is int32 or uint32, untag the value so that it fits in 32 bits.
  552. if (instr->GetDst()->IsIntegral32() && instr->GetSrc1()->IsTaggedInt())
  553. {
  554. immed = (int32)immed;
  555. instr->ReplaceSrc1(IR::IntConstOpnd::New(immed, instr->GetDst()->GetType(), instr->m_func));
  556. }
  557. // Short-circuit simple 16-bit immediates
  558. if ((immed & 0xffff) == immed || (immed & 0xffff0000) == immed || (immed & 0xffff00000000ll) == immed || (immed & 0xffff000000000000ll) == immed)
  559. {
  560. instr->m_opcode = Js::OpCode::MOVZ;
  561. uint32 shift = ShiftTo16((UIntConstType*)&immed);
  562. instr->ReplaceSrc1(IR::IntConstOpnd::New(immed, TyUint16, instr->m_func));
  563. instr->SetSrc2(IR::IntConstOpnd::New(shift, TyUint8, instr->m_func));
  564. return;
  565. }
  566. // Short-circuit simple inverted 16-bit immediates
  567. IntConstType invImmed = ~immed;
  568. if ((invImmed & 0xffff) == invImmed || (invImmed & 0xffff0000) == invImmed || (invImmed & 0xffff00000000ll) == invImmed || (invImmed & 0xffff000000000000ll) == invImmed)
  569. {
  570. instr->m_opcode = Js::OpCode::MOVN;
  571. immed = invImmed;
  572. uint32 shift = ShiftTo16((UIntConstType*)&immed);
  573. instr->ReplaceSrc1(IR::IntConstOpnd::New(immed, TyUint16, instr->m_func));
  574. instr->SetSrc2(IR::IntConstOpnd::New(shift, TyUint8, instr->m_func));
  575. return;
  576. }
  577. // Short-circuit simple inverted 16-bit immediates that can be implicitly truncated to 32 bits
  578. if (immed == (uint32)immed)
  579. {
  580. IntConstType invImmed32 = ~immed & 0xffffffffull;
  581. if ((invImmed32 & 0xffff) == invImmed32 || (invImmed32 & 0xffff0000) == invImmed32)
  582. {
  583. instr->GetDst()->SetType(TyInt32);
  584. uint32 shift = ShiftTo16((UIntConstType*)&invImmed32);
  585. IR::IntConstOpnd *src1 = IR::IntConstOpnd::New(invImmed32 & 0xFFFF, TyInt16, instr->m_func);
  586. IR::IntConstOpnd *src2 = IR::IntConstOpnd::New(shift, TyUint8, instr->m_func);
  587. instr->ReplaceSrc1(src1);
  588. instr->SetSrc2(src2);
  589. instr->m_opcode = Js::OpCode::MOVN;
  590. return;
  591. }
  592. }
  593. // Short-circuit 32-bit logical constants
  594. if (EncoderMD::CanEncodeLogicalConst(immed, 4))
  595. {
  596. instr->GetDst()->SetType(TyInt32);
  597. instr->SetSrc2(instr->GetSrc1());
  598. instr->ReplaceSrc1(IR::RegOpnd::New(NULL, RegZR, TyInt32, instr->m_func));
  599. instr->m_opcode = Js::OpCode::ORR;
  600. return;
  601. }
  602. // Short-circuit 64-bit logical constants
  603. if (instr->GetDst()->GetSize() == 8 && EncoderMD::CanEncodeLogicalConst(immed, 8))
  604. {
  605. instr->SetSrc2(instr->GetSrc1());
  606. instr->ReplaceSrc1(IR::RegOpnd::New(NULL, RegZR, TyInt64, instr->m_func));
  607. instr->m_opcode = Js::OpCode::ORR;
  608. return;
  609. }
  610. // Determine how many 16-bit chunks are all-0 versus all-1
  611. int numZeros = 0;
  612. int numOnes = 0;
  613. for (int wordNum = 0; wordNum < 4; wordNum++)
  614. {
  615. ULONG curWord = (immed >> (16 * wordNum)) & 0xffff;
  616. if (curWord == 0)
  617. {
  618. numZeros++;
  619. }
  620. else if (curWord == 0xffff)
  621. {
  622. numOnes++;
  623. }
  624. }
  625. // Determine whether to obfuscate
  626. bool fDontEncode = Security::DontEncode(instr->GetSrc1());
  627. // Determine whether the initial opcode will be a MOVZ or a MOVN
  628. ULONG wordMask = (numOnes > numZeros) ? 0xffff : 0x0000;
  629. ULONG wordXor = wordMask;
  630. Js::OpCode curOpcode = (wordMask == 0xffff) ? Js::OpCode::MOVN : Js::OpCode::MOVZ;
  631. // Build a theoretical list of opcodes
  632. LdImmOpcode opcodeList[4];
  633. int opcodeListIndex = 0;
  634. for (int wordNum = 0; wordNum < 4; wordNum++)
  635. {
  636. ULONG curWord = (immed >> (16 * wordNum)) & 0xffff;
  637. if (curWord != wordMask)
  638. {
  639. opcodeList[opcodeListIndex++].Set(curOpcode, curWord ^ wordXor, 16 * wordNum);
  640. curOpcode = Js::OpCode::MOVK;
  641. wordXor = 0;
  642. }
  643. }
  644. // Insert extra opcodes as needed
  645. for (int opNum = 0; opNum < opcodeListIndex - 1; opNum++)
  646. {
  647. IR::IntConstOpnd *src1 = IR::IntConstOpnd::New(opcodeList[opNum].m_immed, TyInt16, instr->m_func);
  648. IR::IntConstOpnd *src2 = IR::IntConstOpnd::New(opcodeList[opNum].m_shift, TyUint8, instr->m_func);
  649. IR::Instr * instrMov = IR::Instr::New(opcodeList[opNum].m_opcode, instr->GetDst(), src1, src2, instr->m_func);
  650. instr->InsertBefore(instrMov);
  651. }
  652. // Replace the LDIMM with the final opcode
  653. IR::IntConstOpnd *src1 = IR::IntConstOpnd::New(opcodeList[opcodeListIndex - 1].m_immed, TyInt16, instr->m_func);
  654. IR::IntConstOpnd *src2 = IR::IntConstOpnd::New(opcodeList[opcodeListIndex - 1].m_shift, TyUint8, instr->m_func);
  655. instr->ReplaceSrc1(src1);
  656. instr->SetSrc2(src2);
  657. instr->m_opcode = opcodeList[opcodeListIndex - 1].m_opcode;
  658. if (!fDontEncode)
  659. {
  660. // ARM64_WORKITEM: Hook this up
  661. // LegalizeMD::ObfuscateLDIMM(instrMov, instr);
  662. }
  663. }
  664. else
  665. {
  666. // Since we don't know the value yet, we're going to handle it when we do
  667. // This is done by having the load be from a label operand, which is later
  668. // changed such that its offset is the correct value to ldimm
  669. // InlineeCallInfo is encoded as ((offset into function) << 4) | (argCount & 0xF).
  670. // This will fit into 32 bits as long as the function has less than 2^26 instructions, which should be always.
  671. // The assembly generated becomes something like
  672. // Label (offset:fake)
  673. // MOVZ DST, Label
  674. // MOVK DST, Label <- was the LDIMM
  675. Assert(Security::DontEncode(instr->GetSrc1()));
  676. // The label with the special offset value, used for reloc
  677. IR::LabelInstr *label = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, false);
  678. instr->InsertBefore(label);
  679. Assert((immed & 0x0000000F) == immed);
  680. label->SetOffset((uint32)immed);
  681. label->isInlineeEntryInstr = true;
  682. IR::LabelOpnd *target = IR::LabelOpnd::New(label, instr->m_func);
  683. // We'll handle splitting this up to properly load the immediates now
  684. // Typically (and worst case) we'll need to load 64 bits.
  685. IR::Instr* bits0_15 = IR::Instr::New(Js::OpCode::MOVZ, instr->GetDst(), target, IR::IntConstOpnd::New(0, IRType::TyUint8, instr->m_func, true), instr->m_func);
  686. instr->InsertBefore(bits0_15);
  687. instr->ReplaceSrc1(target);
  688. instr->SetSrc2(IR::IntConstOpnd::New(16, IRType::TyUint8, instr->m_func, true));
  689. instr->m_opcode = Js::OpCode::MOVK;
  690. instr->isInlineeEntryInstr = false;
  691. }
  692. }
  693. void LegalizeMD::ObfuscateLDIMM(IR::Instr * instrMov, IR::Instr * instrMovt)
  694. {
  695. // Are security measures disabled?
  696. if (CONFIG_ISENABLED(Js::DebugFlag) ||
  697. CONFIG_ISENABLED(Js::BenchmarkFlag) ||
  698. PHASE_OFF(Js::EncodeConstantsPhase, instrMov->m_func->GetTopFunc())
  699. )
  700. {
  701. return;
  702. }
  703. UINT_PTR rand = Math::Rand();
  704. // Use this random value as follows:
  705. // bits 0-3: reg to use in pre-LDIMM instr
  706. // bits 4: do/don't emit pre-LDIMM instr
  707. // bits 5-6: emit and/or/add/mov as pre-LDIMM instr
  708. // Similarly for bits 7-13 (mid-LDIMM) and 14-20 (post-LDIMM)
  709. RegNum targetReg = instrMov->GetDst()->AsRegOpnd()->GetReg();
  710. LegalizeMD::EmitRandomNopBefore(instrMov, rand, targetReg);
  711. LegalizeMD::EmitRandomNopBefore(instrMovt, rand >> 7, targetReg);
  712. LegalizeMD::EmitRandomNopBefore(instrMovt->m_next, rand >> 14, targetReg);
  713. }
  714. void LegalizeMD::EmitRandomNopBefore(IR::Instr *insertInstr, UINT_PTR rand, RegNum targetReg)
  715. {
  716. // bits 0-3: reg to use in pre-LDIMM instr
  717. // bits 4: do/don't emit pre-LDIMM instr
  718. // bits 5-6: emit and/or/add/mov as pre-LDIMM instr
  719. if (!(rand & (1 << 4)) && !PHASE_FORCE(Js::EncodeConstantsPhase, insertInstr->m_func->GetTopFunc()))
  720. {
  721. return;
  722. }
  723. IR::Instr * instr;
  724. IR::RegOpnd * opnd1;
  725. IR::Opnd * opnd2 = NULL;
  726. Js::OpCode op = Js::OpCode::InvalidOpCode;
  727. RegNum regNum = (RegNum)((rand & ((1 << 4) - 1)) + RegR0);
  728. opnd1 = IR::RegOpnd::New(NULL, regNum, TyMachReg, insertInstr->m_func);
  729. if (regNum == RegSP || regNum == targetReg) //skip sp & the target reg
  730. {
  731. // ORR pc,pc,0 has unpredicted behavior.
  732. // AND sp,sp,sp has unpredicted behavior.
  733. // We avoid target reg to avoid pipeline stalls.
  734. // Less likely target reg will be RegR17 as we insert nops only for user defined constants and
  735. // RegR12 is mostly used for temporary data such as legalizer post regalloc.
  736. opnd1->SetReg(SCRATCH_REG);
  737. }
  738. switch ((rand >> 5) & 3)
  739. {
  740. case 0:
  741. op = Js::OpCode::AND;
  742. opnd2 = opnd1;
  743. break;
  744. case 1:
  745. op = Js::OpCode::ORR;
  746. opnd2 = IR::IntConstOpnd::New(0, TyMachReg, insertInstr->m_func);
  747. break;
  748. case 2:
  749. op = Js::OpCode::ADD;
  750. opnd2 = IR::IntConstOpnd::New(0, TyMachReg, insertInstr->m_func);
  751. break;
  752. case 3:
  753. op = Js::OpCode::MOV;
  754. break;
  755. }
  756. instr = IR::Instr::New(op, opnd1, opnd1, insertInstr->m_func);
  757. if (opnd2)
  758. {
  759. instr->SetSrc2(opnd2);
  760. }
  761. insertInstr->InsertBefore(instr);
  762. }
  763. void LegalizeMD::LegalizeLdLabel(IR::Instr * instr, IR::Opnd * opnd)
  764. {
  765. Assert(instr->m_opcode == Js::OpCode::LDIMM);
  766. Assert(opnd->IsLabelOpnd());
  767. if (opnd->AsLabelOpnd()->GetLabel()->isInlineeEntryInstr)
  768. {
  769. // We want to leave it as LDIMMs so that we can easily disambiguate later
  770. return;
  771. }
  772. else
  773. {
  774. instr->m_opcode = Js::OpCode::ADR;
  775. }
  776. }
  777. namespace
  778. {
  779. IR::LabelInstr* TryInsertBranchIsland(IR::Instr* instr, IR::LabelInstr* target, int limit, bool forward)
  780. {
  781. int instrCount = 1;
  782. IR::Instr* branchInstr = nullptr;
  783. if (forward)
  784. {
  785. for (IR::Instr* next = instr->m_next; instrCount < limit && next != nullptr; next = next->m_next)
  786. {
  787. if (next->IsBranchInstr() && next->AsBranchInstr()->IsUnconditional())
  788. {
  789. branchInstr = next;
  790. break;
  791. }
  792. ++instrCount;
  793. }
  794. }
  795. else
  796. {
  797. for (IR::Instr* prev = instr->m_prev; instrCount < limit && prev != nullptr; prev = prev->m_prev)
  798. {
  799. if (prev->IsBranchInstr() && prev->AsBranchInstr()->IsUnconditional())
  800. {
  801. branchInstr = prev;
  802. break;
  803. }
  804. ++instrCount;
  805. }
  806. }
  807. if (branchInstr == nullptr)
  808. {
  809. return nullptr;
  810. }
  811. IR::LabelInstr* islandLabel = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, false);
  812. branchInstr->InsertAfter(islandLabel);
  813. IR::BranchInstr* targetBranch = IR::BranchInstr::New(Js::OpCode::B, target, branchInstr->m_func);
  814. islandLabel->InsertAfter(targetBranch);
  815. return islandLabel;
  816. }
  817. }
  818. bool LegalizeMD::LegalizeDirectBranch(IR::BranchInstr *branchInstr, uintptr_t branchOffset)
  819. {
  820. Assert(branchInstr->IsBranchInstr());
  821. uintptr_t labelOffset = branchInstr->GetTarget()->GetOffset();
  822. Assert(labelOffset); //Label offset must be set.
  823. intptr_t wordOffset = intptr_t(labelOffset - branchOffset) / 4;
  824. //We should never run out of 26 bits which corresponds to +-64MB of code size.
  825. AssertMsg(IS_CONST_INT26(wordOffset), "Cannot encode more that 64 MB offset");
  826. Assert(!LowererMD::IsUnconditionalBranch(branchInstr));
  827. int limit = 0;
  828. if (branchInstr->m_opcode == Js::OpCode::TBZ || branchInstr->m_opcode == Js::OpCode::TBNZ)
  829. {
  830. // TBZ and TBNZ are limited to 14 bit offsets.
  831. if (IS_CONST_INT14(wordOffset))
  832. {
  833. return false;
  834. }
  835. limit = 0x00001fff;
  836. }
  837. else
  838. {
  839. // Other conditional branches are limited to 19 bit offsets.
  840. if (IS_CONST_INT19(wordOffset))
  841. {
  842. return false;
  843. }
  844. limit = 0x0003ffff;
  845. }
  846. // Attempt to find an unconditional branch within the range and insert a branch island below it:
  847. // CB. $island
  848. // ...
  849. // B
  850. // $island:
  851. // B $target
  852. IR::LabelInstr* islandLabel = TryInsertBranchIsland(branchInstr, branchInstr->GetTarget(), limit, wordOffset < 0);
  853. if (islandLabel != nullptr)
  854. {
  855. branchInstr->SetTarget(islandLabel);
  856. return true;
  857. }
  858. // Convert a conditional branch which can only be +-256kb(+-8kb for TBZ/TBNZ) to unconditional branch which is +-64MB
  859. // Convert beq Label (where Label is long jump) to something like this
  860. // bne Fallback
  861. // b Label
  862. // Fallback:
  863. IR::LabelInstr *fallbackLabel = IR::LabelInstr::New(Js::OpCode::Label, branchInstr->m_func, false);
  864. IR::BranchInstr *fallbackBranch = IR::BranchInstr::New(branchInstr->m_opcode, fallbackLabel, branchInstr->m_func);
  865. // CBZ | CBNZ | TBZ | TBNZ
  866. if (branchInstr->GetSrc1() != nullptr)
  867. {
  868. fallbackBranch->SetSrc1(branchInstr->UnlinkSrc1());
  869. }
  870. // TBZ | TBNZ
  871. if (branchInstr->GetSrc2() != nullptr)
  872. {
  873. fallbackBranch->SetSrc2(branchInstr->UnlinkSrc2());
  874. }
  875. LowererMD::InvertBranch(fallbackBranch);
  876. branchInstr->InsertBefore(fallbackBranch);
  877. branchInstr->InsertAfter(fallbackLabel);
  878. branchInstr->m_opcode = Js::OpCode::B;
  879. return true;
  880. }
  881. bool LegalizeMD::LegalizeAdrOffset(IR::Instr *instr, uintptr_t instrOffset)
  882. {
  883. Assert(instr->m_opcode == Js::OpCode::ADR);
  884. IR::LabelOpnd* labelOpnd = instr->GetSrc1()->AsLabelOpnd();
  885. IR::LabelInstr* label = labelOpnd->GetLabel();
  886. uintptr_t labelOffset = label->GetOffset();
  887. Assert(labelOffset); //Label offset must be set.
  888. intptr_t wordOffset = intptr_t(labelOffset - instrOffset) / 4;
  889. if (IS_CONST_INT19(wordOffset))
  890. {
  891. return false;
  892. }
  893. //We should never run out of 26 bits which corresponds to +-64MB of code size.
  894. AssertMsg(IS_CONST_INT26(wordOffset), "Cannot encode more that 64 MB offset");
  895. // Attempt to find an unconditional branch within the range and insert a branch island below it:
  896. // ADR $island
  897. // ...
  898. // B
  899. // $island:
  900. // B $target
  901. int limit = 0x0003ffff;
  902. IR::LabelInstr* islandLabel = TryInsertBranchIsland(instr, label, limit, wordOffset < 0);
  903. if (islandLabel != nullptr)
  904. {
  905. labelOpnd->SetLabel(islandLabel);
  906. return true;
  907. }
  908. // Convert an Adr instruction which can only be +-1mb to branch which is +-64MB
  909. // Adr $adrLabel
  910. // b continue
  911. // $adrLabel:
  912. // b label
  913. // $continue:
  914. IR::LabelInstr* continueLabel = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, false);
  915. instr->InsertAfter(continueLabel);
  916. IR::BranchInstr* continueBranch = IR::BranchInstr::New(Js::OpCode::B, continueLabel, instr->m_func);
  917. continueLabel->InsertBefore(continueBranch);
  918. IR::LabelInstr* adrLabel = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, false);
  919. continueLabel->InsertBefore(adrLabel);
  920. IR::BranchInstr* labelBranch = IR::BranchInstr::New(Js::OpCode::B, label, instr->m_func);
  921. continueLabel->InsertBefore(labelBranch);
  922. labelOpnd->SetLabel(adrLabel);
  923. return true;
  924. }
  925. bool LegalizeMD::LegalizeDataAdr(IR::Instr *instr, uintptr_t dataOffset)
  926. {
  927. Assert(instr->m_opcode == Js::OpCode::ADR);
  928. IR::LabelOpnd* labelOpnd = instr->GetSrc1()->AsLabelOpnd();
  929. Assert(labelOpnd->GetLabel()->m_isDataLabel);
  930. // dataOffset provides an upper bound on the distance between instr and the label.
  931. if (IS_CONST_INT19(dataOffset >> 2))
  932. {
  933. return false;
  934. }
  935. // The distance is too large to encode as an ADR isntruction so it must be handled as a 3 instruction load immediate.
  936. // The label address won't be known until after encoding. Assign the label opnd as src1 to let the encoder know to handle them as relocs.
  937. IR::Instr* bits0_15 = IR::Instr::New(Js::OpCode::MOVZ, instr->GetDst(), labelOpnd, IR::IntConstOpnd::New(0, IRType::TyUint8, instr->m_func, true), instr->m_func);
  938. instr->InsertBefore(bits0_15);
  939. IR::Instr* bits16_31 = IR::Instr::New(Js::OpCode::MOVK, instr->GetDst(), labelOpnd, IR::IntConstOpnd::New(16, IRType::TyUint8, instr->m_func, true), instr->m_func);
  940. instr->InsertBefore(bits16_31);
  941. instr->SetSrc2(IR::IntConstOpnd::New(32, IRType::TyUint8, instr->m_func, true));
  942. instr->m_opcode = Js::OpCode::MOVK;
  943. return true;
  944. }
  945. #ifdef DBG
  946. void LegalizeMD::IllegalInstr(IR::Instr * instr, const char16 * msg, ...)
  947. {
  948. va_list argptr;
  949. va_start(argptr, msg);
  950. Output::Print(_u("Illegal instruction: "));
  951. instr->Dump();
  952. Output::Print(msg, argptr);
  953. Assert(UNREACHED);
  954. }
  955. #endif