CodeGenerator.cs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580
  1. using ARMeilleure.CodeGen.Linking;
  2. using ARMeilleure.CodeGen.Optimizations;
  3. using ARMeilleure.CodeGen.RegisterAllocators;
  4. using ARMeilleure.CodeGen.Unwinding;
  5. using ARMeilleure.Common;
  6. using ARMeilleure.Diagnostics;
  7. using ARMeilleure.IntermediateRepresentation;
  8. using ARMeilleure.Translation;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Diagnostics;
  12. using System.Numerics;
  13. using static ARMeilleure.IntermediateRepresentation.Operand;
  14. using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  15. namespace ARMeilleure.CodeGen.Arm64
  16. {
  17. static class CodeGenerator
  18. {
  19. private const int DWordScale = 3;
  20. private const int RegistersCount = 32;
  21. private const int FpRegister = 29;
  22. private const int LrRegister = 30;
  23. private const int SpRegister = 31;
  24. private const int ZrRegister = 31;
  25. private enum AccessSize
  26. {
  27. Byte,
  28. Hword,
  29. Auto
  30. }
  31. private static Action<CodeGenContext, Operation>[] _instTable;
  32. static CodeGenerator()
  33. {
  34. _instTable = new Action<CodeGenContext, Operation>[EnumUtils.GetCount(typeof(Instruction))];
  35. Add(Instruction.Add, GenerateAdd);
  36. Add(Instruction.BitwiseAnd, GenerateBitwiseAnd);
  37. Add(Instruction.BitwiseExclusiveOr, GenerateBitwiseExclusiveOr);
  38. Add(Instruction.BitwiseNot, GenerateBitwiseNot);
  39. Add(Instruction.BitwiseOr, GenerateBitwiseOr);
  40. Add(Instruction.BranchIf, GenerateBranchIf);
  41. Add(Instruction.ByteSwap, GenerateByteSwap);
  42. Add(Instruction.Call, GenerateCall);
  43. //Add(Instruction.Clobber, GenerateClobber);
  44. Add(Instruction.Compare, GenerateCompare);
  45. Add(Instruction.CompareAndSwap, GenerateCompareAndSwap);
  46. Add(Instruction.CompareAndSwap16, GenerateCompareAndSwap16);
  47. Add(Instruction.CompareAndSwap8, GenerateCompareAndSwap8);
  48. Add(Instruction.ConditionalSelect, GenerateConditionalSelect);
  49. Add(Instruction.ConvertI64ToI32, GenerateConvertI64ToI32);
  50. Add(Instruction.ConvertToFP, GenerateConvertToFP);
  51. Add(Instruction.ConvertToFPUI, GenerateConvertToFPUI);
  52. Add(Instruction.Copy, GenerateCopy);
  53. Add(Instruction.CountLeadingZeros, GenerateCountLeadingZeros);
  54. Add(Instruction.Divide, GenerateDivide);
  55. Add(Instruction.DivideUI, GenerateDivideUI);
  56. Add(Instruction.Fill, GenerateFill);
  57. Add(Instruction.Load, GenerateLoad);
  58. Add(Instruction.Load16, GenerateLoad16);
  59. Add(Instruction.Load8, GenerateLoad8);
  60. Add(Instruction.MemoryBarrier, GenerateMemoryBarrier);
  61. Add(Instruction.Multiply, GenerateMultiply);
  62. Add(Instruction.Multiply64HighSI, GenerateMultiply64HighSI);
  63. Add(Instruction.Multiply64HighUI, GenerateMultiply64HighUI);
  64. Add(Instruction.Negate, GenerateNegate);
  65. Add(Instruction.Return, GenerateReturn);
  66. Add(Instruction.RotateRight, GenerateRotateRight);
  67. Add(Instruction.ShiftLeft, GenerateShiftLeft);
  68. Add(Instruction.ShiftRightSI, GenerateShiftRightSI);
  69. Add(Instruction.ShiftRightUI, GenerateShiftRightUI);
  70. Add(Instruction.SignExtend16, GenerateSignExtend16);
  71. Add(Instruction.SignExtend32, GenerateSignExtend32);
  72. Add(Instruction.SignExtend8, GenerateSignExtend8);
  73. Add(Instruction.Spill, GenerateSpill);
  74. Add(Instruction.SpillArg, GenerateSpillArg);
  75. Add(Instruction.StackAlloc, GenerateStackAlloc);
  76. Add(Instruction.Store, GenerateStore);
  77. Add(Instruction.Store16, GenerateStore16);
  78. Add(Instruction.Store8, GenerateStore8);
  79. Add(Instruction.Subtract, GenerateSubtract);
  80. Add(Instruction.Tailcall, GenerateTailcall);
  81. Add(Instruction.VectorCreateScalar, GenerateVectorCreateScalar);
  82. Add(Instruction.VectorExtract, GenerateVectorExtract);
  83. Add(Instruction.VectorExtract16, GenerateVectorExtract16);
  84. Add(Instruction.VectorExtract8, GenerateVectorExtract8);
  85. Add(Instruction.VectorInsert, GenerateVectorInsert);
  86. Add(Instruction.VectorInsert16, GenerateVectorInsert16);
  87. Add(Instruction.VectorInsert8, GenerateVectorInsert8);
  88. Add(Instruction.VectorOne, GenerateVectorOne);
  89. Add(Instruction.VectorZero, GenerateVectorZero);
  90. Add(Instruction.VectorZeroUpper64, GenerateVectorZeroUpper64);
  91. Add(Instruction.VectorZeroUpper96, GenerateVectorZeroUpper96);
  92. Add(Instruction.ZeroExtend16, GenerateZeroExtend16);
  93. Add(Instruction.ZeroExtend32, GenerateZeroExtend32);
  94. Add(Instruction.ZeroExtend8, GenerateZeroExtend8);
  95. static void Add(Instruction inst, Action<CodeGenContext, Operation> func)
  96. {
  97. _instTable[(int)inst] = func;
  98. }
  99. }
  100. public static CompiledFunction Generate(CompilerContext cctx)
  101. {
  102. ControlFlowGraph cfg = cctx.Cfg;
  103. Logger.StartPass(PassName.Optimization);
  104. if (cctx.Options.HasFlag(CompilerOptions.Optimize))
  105. {
  106. if (cctx.Options.HasFlag(CompilerOptions.SsaForm))
  107. {
  108. Optimizer.RunPass(cfg);
  109. }
  110. BlockPlacement.RunPass(cfg);
  111. }
  112. Arm64Optimizer.RunPass(cfg);
  113. Logger.EndPass(PassName.Optimization, cfg);
  114. Logger.StartPass(PassName.PreAllocation);
  115. StackAllocator stackAlloc = new();
  116. PreAllocator.RunPass(cctx, stackAlloc, out int maxCallArgs);
  117. Logger.EndPass(PassName.PreAllocation, cfg);
  118. Logger.StartPass(PassName.RegisterAllocation);
  119. if (cctx.Options.HasFlag(CompilerOptions.SsaForm))
  120. {
  121. Ssa.Deconstruct(cfg);
  122. }
  123. IRegisterAllocator regAlloc;
  124. if (cctx.Options.HasFlag(CompilerOptions.Lsra))
  125. {
  126. regAlloc = new LinearScanAllocator();
  127. }
  128. else
  129. {
  130. regAlloc = new HybridAllocator();
  131. }
  132. RegisterMasks regMasks = new(
  133. CallingConvention.GetIntAvailableRegisters(),
  134. CallingConvention.GetVecAvailableRegisters(),
  135. CallingConvention.GetIntCallerSavedRegisters(),
  136. CallingConvention.GetVecCallerSavedRegisters(),
  137. CallingConvention.GetIntCalleeSavedRegisters(),
  138. CallingConvention.GetVecCalleeSavedRegisters(),
  139. RegistersCount);
  140. AllocationResult allocResult = regAlloc.RunPass(cfg, stackAlloc, regMasks);
  141. Logger.EndPass(PassName.RegisterAllocation, cfg);
  142. Logger.StartPass(PassName.CodeGeneration);
  143. //Console.Error.WriteLine(IRDumper.GetDump(cfg));
  144. bool relocatable = (cctx.Options & CompilerOptions.Relocatable) != 0;
  145. CodeGenContext context = new(allocResult, maxCallArgs, cfg.Blocks.Count, relocatable);
  146. UnwindInfo unwindInfo = WritePrologue(context);
  147. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
  148. {
  149. context.EnterBlock(block);
  150. for (Operation node = block.Operations.First; node != default;)
  151. {
  152. node = GenerateOperation(context, node);
  153. }
  154. if (block.SuccessorsCount == 0)
  155. {
  156. // The only blocks which can have 0 successors are exit blocks.
  157. Operation last = block.Operations.Last;
  158. Debug.Assert(last.Instruction == Instruction.Tailcall ||
  159. last.Instruction == Instruction.Return);
  160. }
  161. else
  162. {
  163. BasicBlock succ = block.GetSuccessor(0);
  164. if (succ != block.ListNext)
  165. {
  166. context.JumpTo(succ);
  167. }
  168. }
  169. }
  170. (byte[] code, RelocInfo relocInfo) = context.GetCode();
  171. Logger.EndPass(PassName.CodeGeneration);
  172. return new CompiledFunction(code, unwindInfo, relocInfo);
  173. }
  174. private static Operation GenerateOperation(CodeGenContext context, Operation operation)
  175. {
  176. if (operation.Instruction == Instruction.Extended)
  177. {
  178. CodeGeneratorIntrinsic.GenerateOperation(context, operation);
  179. }
  180. else
  181. {
  182. if (IsLoadOrStore(operation) &&
  183. operation.ListNext != default &&
  184. operation.ListNext.Instruction == operation.Instruction &&
  185. TryPairMemoryOp(context, operation, operation.ListNext))
  186. {
  187. // Skip next operation if we managed to pair them.
  188. return operation.ListNext.ListNext;
  189. }
  190. Action<CodeGenContext, Operation> func = _instTable[(int)operation.Instruction];
  191. if (func != null)
  192. {
  193. func(context, operation);
  194. }
  195. else
  196. {
  197. throw new ArgumentException($"Invalid instruction \"{operation.Instruction}\".");
  198. }
  199. }
  200. return operation.ListNext;
  201. }
  202. private static void GenerateAdd(CodeGenContext context, Operation operation)
  203. {
  204. Operand dest = operation.Destination;
  205. Operand src1 = operation.GetSource(0);
  206. Operand src2 = operation.GetSource(1);
  207. // ValidateBinOp(dest, src1, src2);
  208. if (dest.Type.IsInteger())
  209. {
  210. context.Assembler.Add(dest, src1, src2);
  211. }
  212. else
  213. {
  214. context.Assembler.FaddScalar(dest, src1, src2);
  215. }
  216. }
  217. private static void GenerateBitwiseAnd(CodeGenContext context, Operation operation)
  218. {
  219. Operand dest = operation.Destination;
  220. Operand src1 = operation.GetSource(0);
  221. Operand src2 = operation.GetSource(1);
  222. ValidateBinOp(dest, src1, src2);
  223. Debug.Assert(dest.Type.IsInteger());
  224. context.Assembler.And(dest, src1, src2);
  225. }
  226. private static void GenerateBitwiseExclusiveOr(CodeGenContext context, Operation operation)
  227. {
  228. Operand dest = operation.Destination;
  229. Operand src1 = operation.GetSource(0);
  230. Operand src2 = operation.GetSource(1);
  231. ValidateBinOp(dest, src1, src2);
  232. if (dest.Type.IsInteger())
  233. {
  234. context.Assembler.Eor(dest, src1, src2);
  235. }
  236. else
  237. {
  238. context.Assembler.EorVector(dest, src1, src2);
  239. }
  240. }
  241. private static void GenerateBitwiseNot(CodeGenContext context, Operation operation)
  242. {
  243. Operand dest = operation.Destination;
  244. Operand source = operation.GetSource(0);
  245. ValidateUnOp(dest, source);
  246. Debug.Assert(dest.Type.IsInteger());
  247. context.Assembler.Mvn(dest, source);
  248. }
  249. private static void GenerateBitwiseOr(CodeGenContext context, Operation operation)
  250. {
  251. Operand dest = operation.Destination;
  252. Operand src1 = operation.GetSource(0);
  253. Operand src2 = operation.GetSource(1);
  254. ValidateBinOp(dest, src1, src2);
  255. Debug.Assert(dest.Type.IsInteger());
  256. context.Assembler.Orr(dest, src1, src2);
  257. }
  258. private static void GenerateBranchIf(CodeGenContext context, Operation operation)
  259. {
  260. Operand comp = operation.GetSource(2);
  261. Debug.Assert(comp.Kind == OperandKind.Constant);
  262. var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
  263. GenerateCompareCommon(context, operation);
  264. context.JumpTo(cond, context.CurrBlock.GetSuccessor(1));
  265. }
  266. private static void GenerateByteSwap(CodeGenContext context, Operation operation)
  267. {
  268. Operand dest = operation.Destination;
  269. Operand source = operation.GetSource(0);
  270. ValidateUnOp(dest, source);
  271. Debug.Assert(dest.Type.IsInteger());
  272. context.Assembler.Rev(dest, source);
  273. }
  274. private static void GenerateCall(CodeGenContext context, Operation operation)
  275. {
  276. context.Assembler.Blr(operation.GetSource(0));
  277. }
  278. private static void GenerateCompare(CodeGenContext context, Operation operation)
  279. {
  280. Operand dest = operation.Destination;
  281. Operand comp = operation.GetSource(2);
  282. Debug.Assert(dest.Type == OperandType.I32);
  283. Debug.Assert(comp.Kind == OperandKind.Constant);
  284. var cond = ((Comparison)comp.AsInt32()).ToArmCondition();
  285. GenerateCompareCommon(context, operation);
  286. context.Assembler.Cset(dest, cond);
  287. }
  288. private static void GenerateCompareAndSwap(CodeGenContext context, Operation operation)
  289. {
  290. if (operation.SourcesCount == 5) // CompareAndSwap128 has 5 sources, compared to CompareAndSwap64/32's 3.
  291. {
  292. Operand actualLow = operation.GetDestination(0);
  293. Operand actualHigh = operation.GetDestination(1);
  294. Operand temp0 = operation.GetDestination(2);
  295. Operand temp1 = operation.GetDestination(3);
  296. Operand address = operation.GetSource(0);
  297. Operand expectedLow = operation.GetSource(1);
  298. Operand expectedHigh = operation.GetSource(2);
  299. Operand desiredLow = operation.GetSource(3);
  300. Operand desiredHigh = operation.GetSource(4);
  301. GenerateAtomicDcas(
  302. context,
  303. address,
  304. expectedLow,
  305. expectedHigh,
  306. desiredLow,
  307. desiredHigh,
  308. actualLow,
  309. actualHigh,
  310. temp0,
  311. temp1);
  312. }
  313. else
  314. {
  315. Operand actual = operation.GetDestination(0);
  316. Operand result = operation.GetDestination(1);
  317. Operand address = operation.GetSource(0);
  318. Operand expected = operation.GetSource(1);
  319. Operand desired = operation.GetSource(2);
  320. GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Auto);
  321. }
  322. }
  323. private static void GenerateCompareAndSwap16(CodeGenContext context, Operation operation)
  324. {
  325. Operand actual = operation.GetDestination(0);
  326. Operand result = operation.GetDestination(1);
  327. Operand address = operation.GetSource(0);
  328. Operand expected = operation.GetSource(1);
  329. Operand desired = operation.GetSource(2);
  330. GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Hword);
  331. }
  332. private static void GenerateCompareAndSwap8(CodeGenContext context, Operation operation)
  333. {
  334. Operand actual = operation.GetDestination(0);
  335. Operand result = operation.GetDestination(1);
  336. Operand address = operation.GetSource(0);
  337. Operand expected = operation.GetSource(1);
  338. Operand desired = operation.GetSource(2);
  339. GenerateAtomicCas(context, address, expected, desired, actual, result, AccessSize.Byte);
  340. }
  341. private static void GenerateCompareCommon(CodeGenContext context, Operation operation)
  342. {
  343. Operand src1 = operation.GetSource(0);
  344. Operand src2 = operation.GetSource(1);
  345. EnsureSameType(src1, src2);
  346. Debug.Assert(src1.Type.IsInteger());
  347. context.Assembler.Cmp(src1, src2);
  348. }
  349. private static void GenerateConditionalSelect(CodeGenContext context, Operation operation)
  350. {
  351. Operand dest = operation.Destination;
  352. Operand src1 = operation.GetSource(0);
  353. Operand src2 = operation.GetSource(1);
  354. Operand src3 = operation.GetSource(2);
  355. EnsureSameType(dest, src2, src3);
  356. Debug.Assert(dest.Type.IsInteger());
  357. Debug.Assert(src1.Type == OperandType.I32);
  358. context.Assembler.Cmp (src1, Const(src1.Type, 0));
  359. context.Assembler.Csel(dest, src2, src3, ArmCondition.Ne);
  360. }
  361. private static void GenerateConvertI64ToI32(CodeGenContext context, Operation operation)
  362. {
  363. Operand dest = operation.Destination;
  364. Operand source = operation.GetSource(0);
  365. Debug.Assert(dest.Type == OperandType.I32 && source.Type == OperandType.I64);
  366. context.Assembler.Mov(dest, Register(source, OperandType.I32));
  367. }
  368. private static void GenerateConvertToFP(CodeGenContext context, Operation operation)
  369. {
  370. Operand dest = operation.Destination;
  371. Operand source = operation.GetSource(0);
  372. Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
  373. Debug.Assert(dest.Type != source.Type);
  374. Debug.Assert(source.Type != OperandType.V128);
  375. if (source.Type.IsInteger())
  376. {
  377. context.Assembler.ScvtfScalar(dest, source);
  378. }
  379. else
  380. {
  381. context.Assembler.FcvtScalar(dest, source);
  382. }
  383. }
  384. private static void GenerateConvertToFPUI(CodeGenContext context, Operation operation)
  385. {
  386. Operand dest = operation.Destination;
  387. Operand source = operation.GetSource(0);
  388. Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
  389. Debug.Assert(dest.Type != source.Type);
  390. Debug.Assert(source.Type.IsInteger());
  391. context.Assembler.UcvtfScalar(dest, source);
  392. }
  393. private static void GenerateCopy(CodeGenContext context, Operation operation)
  394. {
  395. Operand dest = operation.Destination;
  396. Operand source = operation.GetSource(0);
  397. EnsureSameType(dest, source);
  398. Debug.Assert(dest.Type.IsInteger() || source.Kind != OperandKind.Constant);
  399. // Moves to the same register are useless.
  400. if (dest.Kind == source.Kind && dest.Value == source.Value)
  401. {
  402. return;
  403. }
  404. if (dest.Kind == OperandKind.Register && source.Kind == OperandKind.Constant)
  405. {
  406. if (source.Relocatable)
  407. {
  408. context.ReserveRelocatableConstant(dest, source.Symbol, source.Value);
  409. }
  410. else
  411. {
  412. GenerateConstantCopy(context, dest, source.Value);
  413. }
  414. }
  415. else
  416. {
  417. context.Assembler.Mov(dest, source);
  418. }
  419. }
  420. private static void GenerateCountLeadingZeros(CodeGenContext context, Operation operation)
  421. {
  422. Operand dest = operation.Destination;
  423. Operand source = operation.GetSource(0);
  424. EnsureSameType(dest, source);
  425. Debug.Assert(dest.Type.IsInteger());
  426. context.Assembler.Clz(dest, source);
  427. }
  428. private static void GenerateDivide(CodeGenContext context, Operation operation)
  429. {
  430. Operand dest = operation.Destination;
  431. Operand dividend = operation.GetSource(0);
  432. Operand divisor = operation.GetSource(1);
  433. ValidateBinOp(dest, dividend, divisor);
  434. if (dest.Type.IsInteger())
  435. {
  436. context.Assembler.Sdiv(dest, dividend, divisor);
  437. }
  438. else
  439. {
  440. context.Assembler.FdivScalar(dest, dividend, divisor);
  441. }
  442. }
  443. private static void GenerateDivideUI(CodeGenContext context, Operation operation)
  444. {
  445. Operand dest = operation.Destination;
  446. Operand dividend = operation.GetSource(0);
  447. Operand divisor = operation.GetSource(1);
  448. ValidateBinOp(dest, dividend, divisor);
  449. context.Assembler.Udiv(dest, dividend, divisor);
  450. }
  451. private static void GenerateLoad(CodeGenContext context, Operation operation)
  452. {
  453. Operand value = operation.Destination;
  454. Operand address = operation.GetSource(0);
  455. context.Assembler.Ldr(value, address);
  456. }
  457. private static void GenerateLoad16(CodeGenContext context, Operation operation)
  458. {
  459. Operand value = operation.Destination;
  460. Operand address = operation.GetSource(0);
  461. Debug.Assert(value.Type.IsInteger());
  462. context.Assembler.LdrhRiUn(value, address, 0);
  463. }
  464. private static void GenerateLoad8(CodeGenContext context, Operation operation)
  465. {
  466. Operand value = operation.Destination;
  467. Operand address = operation.GetSource(0);
  468. Debug.Assert(value.Type.IsInteger());
  469. context.Assembler.LdrbRiUn(value, address, 0);
  470. }
  471. private static void GenerateMemoryBarrier(CodeGenContext context, Operation operation)
  472. {
  473. context.Assembler.Dmb(0xf);
  474. }
  475. private static void GenerateMultiply(CodeGenContext context, Operation operation)
  476. {
  477. Operand dest = operation.Destination;
  478. Operand src1 = operation.GetSource(0);
  479. Operand src2 = operation.GetSource(1);
  480. EnsureSameType(dest, src1, src2);
  481. if (dest.Type.IsInteger())
  482. {
  483. context.Assembler.Mul(dest, src1, src2);
  484. }
  485. else
  486. {
  487. context.Assembler.FmulScalar(dest, src1, src2);
  488. }
  489. }
  490. private static void GenerateMultiply64HighSI(CodeGenContext context, Operation operation)
  491. {
  492. Operand dest = operation.Destination;
  493. Operand src1 = operation.GetSource(0);
  494. Operand src2 = operation.GetSource(1);
  495. EnsureSameType(dest, src1, src2);
  496. Debug.Assert(dest.Type == OperandType.I64);
  497. context.Assembler.Smulh(dest, src1, src2);
  498. }
  499. private static void GenerateMultiply64HighUI(CodeGenContext context, Operation operation)
  500. {
  501. Operand dest = operation.Destination;
  502. Operand src1 = operation.GetSource(0);
  503. Operand src2 = operation.GetSource(1);
  504. EnsureSameType(dest, src1, src2);
  505. Debug.Assert(dest.Type == OperandType.I64);
  506. context.Assembler.Umulh(dest, src1, src2);
  507. }
  508. private static void GenerateNegate(CodeGenContext context, Operation operation)
  509. {
  510. Operand dest = operation.Destination;
  511. Operand source = operation.GetSource(0);
  512. ValidateUnOp(dest, source);
  513. if (dest.Type.IsInteger())
  514. {
  515. context.Assembler.Neg(dest, source);
  516. }
  517. else
  518. {
  519. context.Assembler.FnegScalar(dest, source);
  520. }
  521. }
  522. private static void GenerateLoad(CodeGenContext context, Operand value, Operand address, int offset)
  523. {
  524. if (CodeGenCommon.ConstFitsOnUImm12(offset, value.Type))
  525. {
  526. context.Assembler.LdrRiUn(value, address, offset);
  527. }
  528. else if (CodeGenCommon.ConstFitsOnSImm9(offset))
  529. {
  530. context.Assembler.Ldur(value, address, offset);
  531. }
  532. else
  533. {
  534. Operand tempAddress = Register(CodeGenCommon.ReservedRegister);
  535. GenerateConstantCopy(context, tempAddress, (ulong)offset);
  536. context.Assembler.Add(tempAddress, address, tempAddress, ArmExtensionType.Uxtx); // Address might be SP and must be the first input.
  537. context.Assembler.LdrRiUn(value, tempAddress, 0);
  538. }
  539. }
  540. private static void GenerateReturn(CodeGenContext context, Operation operation)
  541. {
  542. WriteEpilogue(context);
  543. context.Assembler.Ret(Register(LrRegister));
  544. }
  545. private static void GenerateRotateRight(CodeGenContext context, Operation operation)
  546. {
  547. Operand dest = operation.Destination;
  548. Operand src1 = operation.GetSource(0);
  549. Operand src2 = operation.GetSource(1);
  550. ValidateShift(dest, src1, src2);
  551. context.Assembler.Ror(dest, src1, src2);
  552. }
  553. private static void GenerateShiftLeft(CodeGenContext context, Operation operation)
  554. {
  555. Operand dest = operation.Destination;
  556. Operand src1 = operation.GetSource(0);
  557. Operand src2 = operation.GetSource(1);
  558. ValidateShift(dest, src1, src2);
  559. context.Assembler.Lsl(dest, src1, src2);
  560. }
  561. private static void GenerateShiftRightSI(CodeGenContext context, Operation operation)
  562. {
  563. Operand dest = operation.Destination;
  564. Operand src1 = operation.GetSource(0);
  565. Operand src2 = operation.GetSource(1);
  566. ValidateShift(dest, src1, src2);
  567. context.Assembler.Asr(dest, src1, src2);
  568. }
  569. private static void GenerateShiftRightUI(CodeGenContext context, Operation operation)
  570. {
  571. Operand dest = operation.Destination;
  572. Operand src1 = operation.GetSource(0);
  573. Operand src2 = operation.GetSource(1);
  574. ValidateShift(dest, src1, src2);
  575. context.Assembler.Lsr(dest, src1, src2);
  576. }
  577. private static void GenerateSignExtend16(CodeGenContext context, Operation operation)
  578. {
  579. Operand dest = operation.Destination;
  580. Operand source = operation.GetSource(0);
  581. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  582. context.Assembler.Sxth(dest, source);
  583. }
  584. private static void GenerateSignExtend32(CodeGenContext context, Operation operation)
  585. {
  586. Operand dest = operation.Destination;
  587. Operand source = operation.GetSource(0);
  588. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  589. context.Assembler.Sxtw(dest, source);
  590. }
  591. private static void GenerateSignExtend8(CodeGenContext context, Operation operation)
  592. {
  593. Operand dest = operation.Destination;
  594. Operand source = operation.GetSource(0);
  595. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  596. context.Assembler.Sxtb(dest, source);
  597. }
  598. private static void GenerateFill(CodeGenContext context, Operation operation)
  599. {
  600. Operand dest = operation.Destination;
  601. Operand offset = operation.GetSource(0);
  602. Debug.Assert(offset.Kind == OperandKind.Constant);
  603. int offs = offset.AsInt32() + context.CallArgsRegionSize + context.FpLrSaveRegionSize;
  604. GenerateLoad(context, dest, Register(SpRegister), offs);
  605. }
  606. private static void GenerateStore(CodeGenContext context, Operand value, Operand address, int offset)
  607. {
  608. if (CodeGenCommon.ConstFitsOnUImm12(offset, value.Type))
  609. {
  610. context.Assembler.StrRiUn(value, address, offset);
  611. }
  612. else if (CodeGenCommon.ConstFitsOnSImm9(offset))
  613. {
  614. context.Assembler.Stur(value, address, offset);
  615. }
  616. else
  617. {
  618. Operand tempAddress = Register(CodeGenCommon.ReservedRegister);
  619. GenerateConstantCopy(context, tempAddress, (ulong)offset);
  620. context.Assembler.Add(tempAddress, address, tempAddress, ArmExtensionType.Uxtx); // Address might be SP and must be the first input.
  621. context.Assembler.StrRiUn(value, tempAddress, 0);
  622. }
  623. }
  624. private static void GenerateSpill(CodeGenContext context, Operation operation)
  625. {
  626. GenerateSpill(context, operation, context.CallArgsRegionSize + context.FpLrSaveRegionSize);
  627. }
  628. private static void GenerateSpillArg(CodeGenContext context, Operation operation)
  629. {
  630. GenerateSpill(context, operation, 0);
  631. }
  632. private static void GenerateStackAlloc(CodeGenContext context, Operation operation)
  633. {
  634. Operand dest = operation.Destination;
  635. Operand offset = operation.GetSource(0);
  636. Debug.Assert(offset.Kind == OperandKind.Constant);
  637. int offs = offset.AsInt32() + context.CallArgsRegionSize + context.FpLrSaveRegionSize;
  638. context.Assembler.Add(dest, Register(SpRegister), Const(dest.Type, offs));
  639. }
  640. private static void GenerateStore(CodeGenContext context, Operation operation)
  641. {
  642. Operand value = operation.GetSource(1);
  643. Operand address = operation.GetSource(0);
  644. context.Assembler.Str(value, address);
  645. }
  646. private static void GenerateStore16(CodeGenContext context, Operation operation)
  647. {
  648. Operand value = operation.GetSource(1);
  649. Operand address = operation.GetSource(0);
  650. Debug.Assert(value.Type.IsInteger());
  651. context.Assembler.StrhRiUn(value, address, 0);
  652. }
  653. private static void GenerateStore8(CodeGenContext context, Operation operation)
  654. {
  655. Operand value = operation.GetSource(1);
  656. Operand address = operation.GetSource(0);
  657. Debug.Assert(value.Type.IsInteger());
  658. context.Assembler.StrbRiUn(value, address, 0);
  659. }
  660. private static void GenerateSpill(CodeGenContext context, Operation operation, int baseOffset)
  661. {
  662. Operand offset = operation.GetSource(0);
  663. Operand source = operation.GetSource(1);
  664. Debug.Assert(offset.Kind == OperandKind.Constant);
  665. int offs = offset.AsInt32() + baseOffset;
  666. GenerateStore(context, source, Register(SpRegister), offs);
  667. }
  668. private static void GenerateSubtract(CodeGenContext context, Operation operation)
  669. {
  670. Operand dest = operation.Destination;
  671. Operand src1 = operation.GetSource(0);
  672. Operand src2 = operation.GetSource(1);
  673. // ValidateBinOp(dest, src1, src2);
  674. if (dest.Type.IsInteger())
  675. {
  676. context.Assembler.Sub(dest, src1, src2);
  677. }
  678. else
  679. {
  680. context.Assembler.FsubScalar(dest, src1, src2);
  681. }
  682. }
  683. private static void GenerateTailcall(CodeGenContext context, Operation operation)
  684. {
  685. WriteEpilogue(context);
  686. context.Assembler.Br(operation.GetSource(0));
  687. }
  688. private static void GenerateVectorCreateScalar(CodeGenContext context, Operation operation)
  689. {
  690. Operand dest = operation.Destination;
  691. Operand source = operation.GetSource(0);
  692. if (dest != default)
  693. {
  694. Debug.Assert(!dest.Type.IsInteger() && source.Type.IsInteger());
  695. OperandType destType = source.Type == OperandType.I64 ? OperandType.FP64 : OperandType.FP32;
  696. context.Assembler.Fmov(Register(dest, destType), source, topHalf: false);
  697. }
  698. }
  699. private static void GenerateVectorExtract(CodeGenContext context, Operation operation)
  700. {
  701. Operand dest = operation.Destination; // Value
  702. Operand src1 = operation.GetSource(0); // Vector
  703. Operand src2 = operation.GetSource(1); // Index
  704. Debug.Assert(src1.Type == OperandType.V128);
  705. Debug.Assert(src2.Kind == OperandKind.Constant);
  706. byte index = src2.AsByte();
  707. Debug.Assert(index < OperandType.V128.GetSizeInBytes() / dest.Type.GetSizeInBytes());
  708. if (dest.Type.IsInteger())
  709. {
  710. context.Assembler.Umov(dest, src1, index, dest.Type == OperandType.I64 ? 3 : 2);
  711. }
  712. else
  713. {
  714. context.Assembler.DupScalar(dest, src1, index, dest.Type == OperandType.FP64 ? 3 : 2);
  715. }
  716. }
  717. private static void GenerateVectorExtract16(CodeGenContext context, Operation operation)
  718. {
  719. Operand dest = operation.Destination; // Value
  720. Operand src1 = operation.GetSource(0); // Vector
  721. Operand src2 = operation.GetSource(1); // Index
  722. Debug.Assert(src1.Type == OperandType.V128);
  723. Debug.Assert(src2.Kind == OperandKind.Constant);
  724. byte index = src2.AsByte();
  725. Debug.Assert(index < 8);
  726. context.Assembler.Umov(dest, src1, index, 1);
  727. }
  728. private static void GenerateVectorExtract8(CodeGenContext context, Operation operation)
  729. {
  730. Operand dest = operation.Destination; // Value
  731. Operand src1 = operation.GetSource(0); // Vector
  732. Operand src2 = operation.GetSource(1); // Index
  733. Debug.Assert(src1.Type == OperandType.V128);
  734. Debug.Assert(src2.Kind == OperandKind.Constant);
  735. byte index = src2.AsByte();
  736. Debug.Assert(index < 16);
  737. context.Assembler.Umov(dest, src1, index, 0);
  738. }
  739. private static void GenerateVectorInsert(CodeGenContext context, Operation operation)
  740. {
  741. Operand dest = operation.Destination;
  742. Operand src1 = operation.GetSource(0); // Vector
  743. Operand src2 = operation.GetSource(1); // Value
  744. Operand src3 = operation.GetSource(2); // Index
  745. EnsureSameReg(dest, src1);
  746. Debug.Assert(src1.Type == OperandType.V128);
  747. Debug.Assert(src3.Kind == OperandKind.Constant);
  748. byte index = src3.AsByte();
  749. if (src2.Type.IsInteger())
  750. {
  751. context.Assembler.Ins(dest, src2, index, src2.Type == OperandType.I64 ? 3 : 2);
  752. }
  753. else
  754. {
  755. context.Assembler.Ins(dest, src2, 0, index, src2.Type == OperandType.FP64 ? 3 : 2);
  756. }
  757. }
  758. private static void GenerateVectorInsert16(CodeGenContext context, Operation operation)
  759. {
  760. Operand dest = operation.Destination;
  761. Operand src1 = operation.GetSource(0); // Vector
  762. Operand src2 = operation.GetSource(1); // Value
  763. Operand src3 = operation.GetSource(2); // Index
  764. EnsureSameReg(dest, src1);
  765. Debug.Assert(src1.Type == OperandType.V128);
  766. Debug.Assert(src3.Kind == OperandKind.Constant);
  767. byte index = src3.AsByte();
  768. context.Assembler.Ins(dest, src2, index, 1);
  769. }
  770. private static void GenerateVectorInsert8(CodeGenContext context, Operation operation)
  771. {
  772. Operand dest = operation.Destination;
  773. Operand src1 = operation.GetSource(0); // Vector
  774. Operand src2 = operation.GetSource(1); // Value
  775. Operand src3 = operation.GetSource(2); // Index
  776. EnsureSameReg(dest, src1);
  777. Debug.Assert(src1.Type == OperandType.V128);
  778. Debug.Assert(src3.Kind == OperandKind.Constant);
  779. byte index = src3.AsByte();
  780. context.Assembler.Ins(dest, src2, index, 0);
  781. }
  782. private static void GenerateVectorOne(CodeGenContext context, Operation operation)
  783. {
  784. Operand dest = operation.Destination;
  785. Debug.Assert(!dest.Type.IsInteger());
  786. context.Assembler.CmeqVector(dest, dest, dest, 2);
  787. }
  788. private static void GenerateVectorZero(CodeGenContext context, Operation operation)
  789. {
  790. Operand dest = operation.Destination;
  791. Debug.Assert(!dest.Type.IsInteger());
  792. context.Assembler.EorVector(dest, dest, dest);
  793. }
  794. private static void GenerateVectorZeroUpper64(CodeGenContext context, Operation operation)
  795. {
  796. Operand dest = operation.Destination;
  797. Operand source = operation.GetSource(0);
  798. Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128);
  799. context.Assembler.Fmov(Register(dest, OperandType.FP64), Register(source, OperandType.FP64));
  800. }
  801. private static void GenerateVectorZeroUpper96(CodeGenContext context, Operation operation)
  802. {
  803. Operand dest = operation.Destination;
  804. Operand source = operation.GetSource(0);
  805. Debug.Assert(dest.Type == OperandType.V128 && source.Type == OperandType.V128);
  806. context.Assembler.Fmov(Register(dest, OperandType.FP32), Register(source, OperandType.FP32));
  807. }
  808. private static void GenerateZeroExtend16(CodeGenContext context, Operation operation)
  809. {
  810. Operand dest = operation.Destination;
  811. Operand source = operation.GetSource(0);
  812. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  813. context.Assembler.Uxth(dest, source);
  814. }
  815. private static void GenerateZeroExtend32(CodeGenContext context, Operation operation)
  816. {
  817. Operand dest = operation.Destination;
  818. Operand source = operation.GetSource(0);
  819. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  820. // We can eliminate the move if source is already 32-bit and the registers are the same.
  821. if (dest.Value == source.Value && source.Type == OperandType.I32)
  822. {
  823. return;
  824. }
  825. context.Assembler.Mov(Register(dest.GetRegister().Index, OperandType.I32), source);
  826. }
  827. private static void GenerateZeroExtend8(CodeGenContext context, Operation operation)
  828. {
  829. Operand dest = operation.Destination;
  830. Operand source = operation.GetSource(0);
  831. Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
  832. context.Assembler.Uxtb(dest, source);
  833. }
  834. private static UnwindInfo WritePrologue(CodeGenContext context)
  835. {
  836. List<UnwindPushEntry> pushEntries = new List<UnwindPushEntry>();
  837. Operand rsp = Register(SpRegister);
  838. int intMask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters;
  839. int vecMask = CallingConvention.GetFpCalleeSavedRegisters() & context.AllocResult.VecUsedRegisters;
  840. int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask);
  841. int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask);
  842. int calleeSaveRegionSize = Align16(intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * 8);
  843. int offset = 0;
  844. WritePrologueCalleeSavesPreIndexed(context, pushEntries, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64);
  845. WritePrologueCalleeSavesPreIndexed(context, pushEntries, ref vecMask, ref offset, calleeSaveRegionSize, OperandType.FP64);
  846. int localSize = Align16(context.AllocResult.SpillRegionSize + context.FpLrSaveRegionSize);
  847. int outArgsSize = context.CallArgsRegionSize;
  848. if (CodeGenCommon.ConstFitsOnSImm7(localSize, DWordScale))
  849. {
  850. if (context.HasCall)
  851. {
  852. context.Assembler.StpRiPre(Register(FpRegister), Register(LrRegister), rsp, -localSize);
  853. context.Assembler.MovSp(Register(FpRegister), rsp);
  854. }
  855. if (outArgsSize != 0)
  856. {
  857. context.Assembler.Sub(rsp, rsp, Const(OperandType.I64, outArgsSize));
  858. }
  859. }
  860. else
  861. {
  862. int frameSize = localSize + outArgsSize;
  863. if (frameSize != 0)
  864. {
  865. if (CodeGenCommon.ConstFitsOnUImm12(frameSize))
  866. {
  867. context.Assembler.Sub(rsp, rsp, Const(OperandType.I64, frameSize));
  868. }
  869. else
  870. {
  871. Operand tempSize = Register(CodeGenCommon.ReservedRegister);
  872. GenerateConstantCopy(context, tempSize, (ulong)frameSize);
  873. context.Assembler.Sub(rsp, rsp, tempSize, ArmExtensionType.Uxtx);
  874. }
  875. }
  876. context.Assembler.StpRiUn(Register(FpRegister), Register(LrRegister), rsp, outArgsSize);
  877. if (outArgsSize != 0)
  878. {
  879. context.Assembler.Add(Register(FpRegister), Register(SpRegister), Const(OperandType.I64, outArgsSize));
  880. }
  881. else
  882. {
  883. context.Assembler.MovSp(Register(FpRegister), Register(SpRegister));
  884. }
  885. }
  886. return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset);
  887. }
  888. private static void WritePrologueCalleeSavesPreIndexed(
  889. CodeGenContext context,
  890. List<UnwindPushEntry> pushEntries,
  891. ref int mask,
  892. ref int offset,
  893. int calleeSaveRegionSize,
  894. OperandType type)
  895. {
  896. if ((BitOperations.PopCount((uint)mask) & 1) != 0)
  897. {
  898. int reg = BitOperations.TrailingZeroCount(mask);
  899. pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg));
  900. mask &= ~(1 << reg);
  901. if (offset != 0)
  902. {
  903. context.Assembler.StrRiUn(Register(reg, type), Register(SpRegister), offset);
  904. }
  905. else
  906. {
  907. context.Assembler.StrRiPre(Register(reg, type), Register(SpRegister), -calleeSaveRegionSize);
  908. }
  909. offset += type.GetSizeInBytes();
  910. }
  911. while (mask != 0)
  912. {
  913. int reg = BitOperations.TrailingZeroCount(mask);
  914. pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg));
  915. mask &= ~(1 << reg);
  916. int reg2 = BitOperations.TrailingZeroCount(mask);
  917. pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: reg2));
  918. mask &= ~(1 << reg2);
  919. if (offset != 0)
  920. {
  921. context.Assembler.StpRiUn(Register(reg, type), Register(reg2, type), Register(SpRegister), offset);
  922. }
  923. else
  924. {
  925. context.Assembler.StpRiPre(Register(reg, type), Register(reg2, type), Register(SpRegister), -calleeSaveRegionSize);
  926. }
  927. offset += type.GetSizeInBytes() * 2;
  928. }
  929. }
  930. private static void WriteEpilogue(CodeGenContext context)
  931. {
  932. Operand rsp = Register(SpRegister);
  933. int localSize = Align16(context.AllocResult.SpillRegionSize + context.FpLrSaveRegionSize);
  934. int outArgsSize = context.CallArgsRegionSize;
  935. if (CodeGenCommon.ConstFitsOnSImm7(localSize, DWordScale))
  936. {
  937. if (outArgsSize != 0)
  938. {
  939. context.Assembler.Add(rsp, rsp, Const(OperandType.I64, outArgsSize));
  940. }
  941. if (context.HasCall)
  942. {
  943. context.Assembler.LdpRiPost(Register(FpRegister), Register(LrRegister), rsp, localSize);
  944. }
  945. }
  946. else
  947. {
  948. if (context.HasCall)
  949. {
  950. context.Assembler.LdpRiUn(Register(FpRegister), Register(LrRegister), rsp, outArgsSize);
  951. }
  952. int frameSize = localSize + outArgsSize;
  953. if (frameSize != 0)
  954. {
  955. if (CodeGenCommon.ConstFitsOnUImm12(frameSize))
  956. {
  957. context.Assembler.Add(rsp, rsp, Const(OperandType.I64, frameSize));
  958. }
  959. else
  960. {
  961. Operand tempSize = Register(CodeGenCommon.ReservedRegister);
  962. GenerateConstantCopy(context, tempSize, (ulong)frameSize);
  963. context.Assembler.Add(rsp, rsp, tempSize, ArmExtensionType.Uxtx);
  964. }
  965. }
  966. }
  967. int intMask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters;
  968. int vecMask = CallingConvention.GetFpCalleeSavedRegisters() & context.AllocResult.VecUsedRegisters;
  969. int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask);
  970. int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask);
  971. int offset = intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * 8;
  972. int calleeSaveRegionSize = Align16(offset);
  973. WriteEpilogueCalleeSavesPostIndexed(context, ref vecMask, ref offset, calleeSaveRegionSize, OperandType.FP64);
  974. WriteEpilogueCalleeSavesPostIndexed(context, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64);
  975. }
  976. private static void WriteEpilogueCalleeSavesPostIndexed(
  977. CodeGenContext context,
  978. ref int mask,
  979. ref int offset,
  980. int calleeSaveRegionSize,
  981. OperandType type)
  982. {
  983. while (mask != 0)
  984. {
  985. int reg = BitUtils.HighestBitSet(mask);
  986. mask &= ~(1 << reg);
  987. if (mask != 0)
  988. {
  989. int reg2 = BitUtils.HighestBitSet(mask);
  990. mask &= ~(1 << reg2);
  991. offset -= type.GetSizeInBytes() * 2;
  992. if (offset != 0)
  993. {
  994. context.Assembler.LdpRiUn(Register(reg2, type), Register(reg, type), Register(SpRegister), offset);
  995. }
  996. else
  997. {
  998. context.Assembler.LdpRiPost(Register(reg2, type), Register(reg, type), Register(SpRegister), calleeSaveRegionSize);
  999. }
  1000. }
  1001. else
  1002. {
  1003. offset -= type.GetSizeInBytes();
  1004. if (offset != 0)
  1005. {
  1006. context.Assembler.LdrRiUn(Register(reg, type), Register(SpRegister), offset);
  1007. }
  1008. else
  1009. {
  1010. context.Assembler.LdrRiPost(Register(reg, type), Register(SpRegister), calleeSaveRegionSize);
  1011. }
  1012. }
  1013. }
  1014. }
  1015. private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value)
  1016. {
  1017. if (value == 0)
  1018. {
  1019. context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
  1020. }
  1021. else if (CodeGenCommon.TryEncodeBitMask(dest.Type, value, out _, out _, out _))
  1022. {
  1023. context.Assembler.Orr(dest, Register(ZrRegister, dest.Type), Const(dest.Type, (long)value));
  1024. }
  1025. else
  1026. {
  1027. int hw = 0;
  1028. bool first = true;
  1029. while (value != 0)
  1030. {
  1031. int valueLow = (ushort)value;
  1032. if (valueLow != 0)
  1033. {
  1034. if (first)
  1035. {
  1036. context.Assembler.Movz(dest, valueLow, hw);
  1037. first = false;
  1038. }
  1039. else
  1040. {
  1041. context.Assembler.Movk(dest, valueLow, hw);
  1042. }
  1043. }
  1044. hw++;
  1045. value >>= 16;
  1046. }
  1047. }
  1048. }
  1049. private static void GenerateAtomicCas(
  1050. CodeGenContext context,
  1051. Operand address,
  1052. Operand expected,
  1053. Operand desired,
  1054. Operand actual,
  1055. Operand result,
  1056. AccessSize accessSize)
  1057. {
  1058. int startOffset = context.StreamOffset;
  1059. switch (accessSize)
  1060. {
  1061. case AccessSize.Byte:
  1062. context.Assembler.Ldaxrb(actual, address);
  1063. break;
  1064. case AccessSize.Hword:
  1065. context.Assembler.Ldaxrh(actual, address);
  1066. break;
  1067. default:
  1068. context.Assembler.Ldaxr(actual, address);
  1069. break;
  1070. }
  1071. context.Assembler.Cmp(actual, expected);
  1072. context.JumpToNear(ArmCondition.Ne);
  1073. switch (accessSize)
  1074. {
  1075. case AccessSize.Byte:
  1076. context.Assembler.Stlxrb(desired, address, result);
  1077. break;
  1078. case AccessSize.Hword:
  1079. context.Assembler.Stlxrh(desired, address, result);
  1080. break;
  1081. default:
  1082. context.Assembler.Stlxr(desired, address, result);
  1083. break;
  1084. }
  1085. context.Assembler.Cbnz(result, startOffset - context.StreamOffset); // Retry if store failed.
  1086. context.JumpHere();
  1087. context.Assembler.Clrex();
  1088. }
  1089. private static void GenerateAtomicDcas(
  1090. CodeGenContext context,
  1091. Operand address,
  1092. Operand expectedLow,
  1093. Operand expectedHigh,
  1094. Operand desiredLow,
  1095. Operand desiredHigh,
  1096. Operand actualLow,
  1097. Operand actualHigh,
  1098. Operand temp0,
  1099. Operand temp1)
  1100. {
  1101. int startOffset = context.StreamOffset;
  1102. context.Assembler.Ldaxp(actualLow, actualHigh, address);
  1103. context.Assembler.Eor(temp0, actualHigh, expectedHigh);
  1104. context.Assembler.Eor(temp1, actualLow, expectedLow);
  1105. context.Assembler.Orr(temp0, temp1, temp0);
  1106. context.JumpToNearIfNotZero(temp0);
  1107. Operand result = Register(temp0, OperandType.I32);
  1108. context.Assembler.Stlxp(desiredLow, desiredHigh, address, result);
  1109. context.Assembler.Cbnz(result, startOffset - context.StreamOffset); // Retry if store failed.
  1110. context.JumpHere();
  1111. context.Assembler.Clrex();
  1112. }
  1113. private static bool TryPairMemoryOp(CodeGenContext context, Operation currentOp, Operation nextOp)
  1114. {
  1115. if (!TryGetMemOpBaseAndOffset(currentOp, out Operand op1Base, out int op1Offset))
  1116. {
  1117. return false;
  1118. }
  1119. if (!TryGetMemOpBaseAndOffset(nextOp, out Operand op2Base, out int op2Offset))
  1120. {
  1121. return false;
  1122. }
  1123. if (op1Base != op2Base)
  1124. {
  1125. return false;
  1126. }
  1127. OperandType valueType = GetMemOpValueType(currentOp);
  1128. if (valueType != GetMemOpValueType(nextOp) || op1Offset + valueType.GetSizeInBytes() != op2Offset)
  1129. {
  1130. return false;
  1131. }
  1132. if (!CodeGenCommon.ConstFitsOnSImm7(op1Offset, valueType.GetSizeInBytesLog2()))
  1133. {
  1134. return false;
  1135. }
  1136. if (currentOp.Instruction == Instruction.Load)
  1137. {
  1138. context.Assembler.LdpRiUn(currentOp.Destination, nextOp.Destination, op1Base, op1Offset);
  1139. }
  1140. else if (currentOp.Instruction == Instruction.Store)
  1141. {
  1142. context.Assembler.StpRiUn(currentOp.GetSource(1), nextOp.GetSource(1), op1Base, op1Offset);
  1143. }
  1144. else
  1145. {
  1146. return false;
  1147. }
  1148. return true;
  1149. }
  1150. private static bool IsLoadOrStore(Operation operation)
  1151. {
  1152. return operation.Instruction == Instruction.Load || operation.Instruction == Instruction.Store;
  1153. }
  1154. private static OperandType GetMemOpValueType(Operation operation)
  1155. {
  1156. if (operation.Destination != default)
  1157. {
  1158. return operation.Destination.Type;
  1159. }
  1160. return operation.GetSource(1).Type;
  1161. }
  1162. private static bool TryGetMemOpBaseAndOffset(Operation operation, out Operand baseAddress, out int offset)
  1163. {
  1164. baseAddress = default;
  1165. offset = 0;
  1166. Operand address = operation.GetSource(0);
  1167. if (address.Kind != OperandKind.Memory)
  1168. {
  1169. return false;
  1170. }
  1171. MemoryOperand memOp = address.GetMemory();
  1172. Operand baseOp = memOp.BaseAddress;
  1173. if (baseOp == default)
  1174. {
  1175. baseOp = memOp.Index;
  1176. if (baseOp == default || memOp.Scale != Multiplier.x1)
  1177. {
  1178. return false;
  1179. }
  1180. }
  1181. if (memOp.Index != default)
  1182. {
  1183. return false;
  1184. }
  1185. baseAddress = memOp.BaseAddress;
  1186. offset = memOp.Displacement;
  1187. return true;
  1188. }
  1189. private static Operand Register(Operand operand, OperandType type = OperandType.I64)
  1190. {
  1191. return Register(operand.GetRegister().Index, type);
  1192. }
  1193. private static Operand Register(int register, OperandType type = OperandType.I64)
  1194. {
  1195. return Factory.Register(register, RegisterType.Integer, type);
  1196. }
  1197. private static int Align16(int value)
  1198. {
  1199. return (value + 0xf) & ~0xf;
  1200. }
  1201. [Conditional("DEBUG")]
  1202. private static void ValidateUnOp(Operand dest, Operand source)
  1203. {
  1204. // Destination and source aren't forced to be equals
  1205. // EnsureSameReg (dest, source);
  1206. EnsureSameType(dest, source);
  1207. }
  1208. [Conditional("DEBUG")]
  1209. private static void ValidateBinOp(Operand dest, Operand src1, Operand src2)
  1210. {
  1211. // Destination and source aren't forced to be equals
  1212. // EnsureSameReg (dest, src1);
  1213. EnsureSameType(dest, src1, src2);
  1214. }
  1215. [Conditional("DEBUG")]
  1216. private static void ValidateShift(Operand dest, Operand src1, Operand src2)
  1217. {
  1218. // Destination and source aren't forced to be equals
  1219. // EnsureSameReg (dest, src1);
  1220. EnsureSameType(dest, src1);
  1221. Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32);
  1222. }
  1223. private static void EnsureSameReg(Operand op1, Operand op2)
  1224. {
  1225. Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
  1226. Debug.Assert(op1.Kind == op2.Kind);
  1227. Debug.Assert(op1.Value == op2.Value);
  1228. }
  1229. private static void EnsureSameType(Operand op1, Operand op2)
  1230. {
  1231. Debug.Assert(op1.Type == op2.Type);
  1232. }
  1233. private static void EnsureSameType(Operand op1, Operand op2, Operand op3)
  1234. {
  1235. Debug.Assert(op1.Type == op2.Type);
  1236. Debug.Assert(op1.Type == op3.Type);
  1237. }
  1238. private static void EnsureSameType(Operand op1, Operand op2, Operand op3, Operand op4)
  1239. {
  1240. Debug.Assert(op1.Type == op2.Type);
  1241. Debug.Assert(op1.Type == op3.Type);
  1242. Debug.Assert(op1.Type == op4.Type);
  1243. }
  1244. }
  1245. }