Assembler.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580
  1. using ARMeilleure.CodeGen.Linking;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using Ryujinx.Common.Memory;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Runtime.InteropServices;
  9. namespace ARMeilleure.CodeGen.X86
  10. {
  11. partial class Assembler
  12. {
  13. private const int ReservedBytesForJump = 1;
  14. private const int OpModRMBits = 24;
  15. private const byte RexPrefix = 0x40;
  16. private const byte RexWPrefix = 0x48;
  17. private const byte LockPrefix = 0xf0;
  18. private const int MaxRegNumber = 15;
  19. private struct Jump
  20. {
  21. public bool IsConditional { get; }
  22. public X86Condition Condition { get; }
  23. public Operand JumpLabel { get; }
  24. public long? JumpTarget { get; set; }
  25. public long JumpPosition { get; }
  26. public long Offset { get; set; }
  27. public int InstSize { get; set; }
  28. public Jump(Operand jumpLabel, long jumpPosition)
  29. {
  30. IsConditional = false;
  31. Condition = 0;
  32. JumpLabel = jumpLabel;
  33. JumpTarget = null;
  34. JumpPosition = jumpPosition;
  35. Offset = 0;
  36. InstSize = 0;
  37. }
  38. public Jump(X86Condition condition, Operand jumpLabel, long jumpPosition)
  39. {
  40. IsConditional = true;
  41. Condition = condition;
  42. JumpLabel = jumpLabel;
  43. JumpTarget = null;
  44. JumpPosition = jumpPosition;
  45. Offset = 0;
  46. InstSize = 0;
  47. }
  48. }
  49. private struct Reloc
  50. {
  51. public int JumpIndex { get; set; }
  52. public int Position { get; set; }
  53. public Symbol Symbol { get; set; }
  54. }
  55. private readonly List<Jump> _jumps;
  56. private readonly List<Reloc> _relocs;
  57. private readonly Dictionary<Operand, long> _labels;
  58. private readonly Stream _stream;
  59. public bool HasRelocs => _relocs != null;
  60. public Assembler(Stream stream, bool relocatable)
  61. {
  62. _stream = stream;
  63. _labels = new Dictionary<Operand, long>();
  64. _jumps = new List<Jump>();
  65. _relocs = relocatable ? new List<Reloc>() : null;
  66. }
  67. public void MarkLabel(Operand label)
  68. {
  69. _labels.Add(label, _stream.Position);
  70. }
  71. public void Add(Operand dest, Operand source, OperandType type)
  72. {
  73. WriteInstruction(dest, source, type, X86Instruction.Add);
  74. }
  75. public void Addsd(Operand dest, Operand src1, Operand src2)
  76. {
  77. WriteInstruction(dest, src1, src2, X86Instruction.Addsd);
  78. }
  79. public void Addss(Operand dest, Operand src1, Operand src2)
  80. {
  81. WriteInstruction(dest, src1, src2, X86Instruction.Addss);
  82. }
  83. public void And(Operand dest, Operand source, OperandType type)
  84. {
  85. WriteInstruction(dest, source, type, X86Instruction.And);
  86. }
  87. public void Bsr(Operand dest, Operand source, OperandType type)
  88. {
  89. WriteInstruction(dest, source, type, X86Instruction.Bsr);
  90. }
  91. public void Bswap(Operand dest)
  92. {
  93. WriteInstruction(dest, default, dest.Type, X86Instruction.Bswap);
  94. }
  95. public void Call(Operand dest)
  96. {
  97. WriteInstruction(dest, default, OperandType.None, X86Instruction.Call);
  98. }
  99. public void Cdq()
  100. {
  101. WriteByte(0x99);
  102. }
  103. public void Cmovcc(Operand dest, Operand source, OperandType type, X86Condition condition)
  104. {
  105. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Cmovcc];
  106. WriteOpCode(dest, default, source, type, info.Flags, info.OpRRM | (int)condition, rrm: true);
  107. }
  108. public void Cmp(Operand src1, Operand src2, OperandType type)
  109. {
  110. WriteInstruction(src1, src2, type, X86Instruction.Cmp);
  111. }
  112. public void Cqo()
  113. {
  114. WriteByte(0x48);
  115. WriteByte(0x99);
  116. }
  117. public void Cmpxchg(Operand memOp, Operand src)
  118. {
  119. Debug.Assert(memOp.Kind == OperandKind.Memory);
  120. WriteByte(LockPrefix);
  121. WriteInstruction(memOp, src, src.Type, X86Instruction.Cmpxchg);
  122. }
  123. public void Cmpxchg16(Operand memOp, Operand src)
  124. {
  125. Debug.Assert(memOp.Kind == OperandKind.Memory);
  126. WriteByte(LockPrefix);
  127. WriteByte(0x66);
  128. WriteInstruction(memOp, src, src.Type, X86Instruction.Cmpxchg);
  129. }
  130. public void Cmpxchg16b(Operand memOp)
  131. {
  132. Debug.Assert(memOp.Kind == OperandKind.Memory);
  133. WriteByte(LockPrefix);
  134. WriteInstruction(memOp, default, OperandType.None, X86Instruction.Cmpxchg16b);
  135. }
  136. public void Cmpxchg8(Operand memOp, Operand src)
  137. {
  138. Debug.Assert(memOp.Kind == OperandKind.Memory);
  139. WriteByte(LockPrefix);
  140. WriteInstruction(memOp, src, src.Type, X86Instruction.Cmpxchg8);
  141. }
  142. public void Comisd(Operand src1, Operand src2)
  143. {
  144. WriteInstruction(src1, default, src2, X86Instruction.Comisd);
  145. }
  146. public void Comiss(Operand src1, Operand src2)
  147. {
  148. WriteInstruction(src1, default, src2, X86Instruction.Comiss);
  149. }
  150. public void Cvtsd2ss(Operand dest, Operand src1, Operand src2)
  151. {
  152. WriteInstruction(dest, src1, src2, X86Instruction.Cvtsd2ss);
  153. }
  154. public void Cvtsi2sd(Operand dest, Operand src1, Operand src2, OperandType type)
  155. {
  156. WriteInstruction(dest, src1, src2, X86Instruction.Cvtsi2sd, type);
  157. }
  158. public void Cvtsi2ss(Operand dest, Operand src1, Operand src2, OperandType type)
  159. {
  160. WriteInstruction(dest, src1, src2, X86Instruction.Cvtsi2ss, type);
  161. }
  162. public void Cvtss2sd(Operand dest, Operand src1, Operand src2)
  163. {
  164. WriteInstruction(dest, src1, src2, X86Instruction.Cvtss2sd);
  165. }
  166. public void Div(Operand source)
  167. {
  168. WriteInstruction(default, source, source.Type, X86Instruction.Div);
  169. }
  170. public void Divsd(Operand dest, Operand src1, Operand src2)
  171. {
  172. WriteInstruction(dest, src1, src2, X86Instruction.Divsd);
  173. }
  174. public void Divss(Operand dest, Operand src1, Operand src2)
  175. {
  176. WriteInstruction(dest, src1, src2, X86Instruction.Divss);
  177. }
  178. public void Idiv(Operand source)
  179. {
  180. WriteInstruction(default, source, source.Type, X86Instruction.Idiv);
  181. }
  182. public void Imul(Operand source)
  183. {
  184. WriteInstruction(default, source, source.Type, X86Instruction.Imul128);
  185. }
  186. public void Imul(Operand dest, Operand source, OperandType type)
  187. {
  188. if (source.Kind != OperandKind.Register)
  189. {
  190. throw new ArgumentException($"Invalid source operand kind \"{source.Kind}\".");
  191. }
  192. WriteInstruction(dest, source, type, X86Instruction.Imul);
  193. }
  194. public void Imul(Operand dest, Operand src1, Operand src2, OperandType type)
  195. {
  196. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Imul];
  197. if (src2.Kind != OperandKind.Constant)
  198. {
  199. throw new ArgumentException($"Invalid source 2 operand kind \"{src2.Kind}\".");
  200. }
  201. if (IsImm8(src2.Value, src2.Type) && info.OpRMImm8 != BadOp)
  202. {
  203. WriteOpCode(dest, default, src1, type, info.Flags, info.OpRMImm8, rrm: true);
  204. WriteByte(src2.AsByte());
  205. }
  206. else if (IsImm32(src2.Value, src2.Type) && info.OpRMImm32 != BadOp)
  207. {
  208. WriteOpCode(dest, default, src1, type, info.Flags, info.OpRMImm32, rrm: true);
  209. WriteInt32(src2.AsInt32());
  210. }
  211. else
  212. {
  213. throw new ArgumentException($"Failed to encode constant 0x{src2.Value:X}.");
  214. }
  215. }
  216. public void Insertps(Operand dest, Operand src1, Operand src2, byte imm)
  217. {
  218. WriteInstruction(dest, src1, src2, X86Instruction.Insertps);
  219. WriteByte(imm);
  220. }
  221. public void Jcc(X86Condition condition, Operand dest)
  222. {
  223. if (dest.Kind == OperandKind.Label)
  224. {
  225. _jumps.Add(new Jump(condition, dest, _stream.Position));
  226. // ReservedBytesForJump
  227. WriteByte(0);
  228. }
  229. else
  230. {
  231. throw new ArgumentException("Destination operand must be of kind Label", nameof(dest));
  232. }
  233. }
  234. public void Jcc(X86Condition condition, long offset)
  235. {
  236. if (ConstFitsOnS8(offset))
  237. {
  238. WriteByte((byte)(0x70 | (int)condition));
  239. WriteByte((byte)offset);
  240. }
  241. else if (ConstFitsOnS32(offset))
  242. {
  243. WriteByte(0x0f);
  244. WriteByte((byte)(0x80 | (int)condition));
  245. WriteInt32((int)offset);
  246. }
  247. else
  248. {
  249. throw new ArgumentOutOfRangeException(nameof(offset));
  250. }
  251. }
  252. public void Jmp(long offset)
  253. {
  254. if (ConstFitsOnS8(offset))
  255. {
  256. WriteByte(0xeb);
  257. WriteByte((byte)offset);
  258. }
  259. else if (ConstFitsOnS32(offset))
  260. {
  261. WriteByte(0xe9);
  262. WriteInt32((int)offset);
  263. }
  264. else
  265. {
  266. throw new ArgumentOutOfRangeException(nameof(offset));
  267. }
  268. }
  269. public void Jmp(Operand dest)
  270. {
  271. if (dest.Kind == OperandKind.Label)
  272. {
  273. _jumps.Add(new Jump(dest, _stream.Position));
  274. // ReservedBytesForJump
  275. WriteByte(0);
  276. }
  277. else
  278. {
  279. WriteInstruction(dest, default, OperandType.None, X86Instruction.Jmp);
  280. }
  281. }
  282. public void Ldmxcsr(Operand dest)
  283. {
  284. WriteInstruction(dest, default, OperandType.I32, X86Instruction.Ldmxcsr);
  285. }
  286. public void Lea(Operand dest, Operand source, OperandType type)
  287. {
  288. WriteInstruction(dest, source, type, X86Instruction.Lea);
  289. }
  290. public void LockOr(Operand dest, Operand source, OperandType type)
  291. {
  292. WriteByte(LockPrefix);
  293. WriteInstruction(dest, source, type, X86Instruction.Or);
  294. }
  295. public void Mov(Operand dest, Operand source, OperandType type)
  296. {
  297. WriteInstruction(dest, source, type, X86Instruction.Mov);
  298. }
  299. public void Mov16(Operand dest, Operand source)
  300. {
  301. WriteInstruction(dest, source, OperandType.None, X86Instruction.Mov16);
  302. }
  303. public void Mov8(Operand dest, Operand source)
  304. {
  305. WriteInstruction(dest, source, OperandType.None, X86Instruction.Mov8);
  306. }
  307. public void Movd(Operand dest, Operand source)
  308. {
  309. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd];
  310. if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
  311. {
  312. WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRRM, rrm: true);
  313. }
  314. else
  315. {
  316. WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRMR);
  317. }
  318. }
  319. public void Movdqu(Operand dest, Operand source)
  320. {
  321. WriteInstruction(dest, default, source, X86Instruction.Movdqu);
  322. }
  323. public void Movhlps(Operand dest, Operand src1, Operand src2)
  324. {
  325. WriteInstruction(dest, src1, src2, X86Instruction.Movhlps);
  326. }
  327. public void Movlhps(Operand dest, Operand src1, Operand src2)
  328. {
  329. WriteInstruction(dest, src1, src2, X86Instruction.Movlhps);
  330. }
  331. public void Movq(Operand dest, Operand source)
  332. {
  333. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd];
  334. InstructionFlags flags = info.Flags | InstructionFlags.RexW;
  335. if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
  336. {
  337. WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRRM, rrm: true);
  338. }
  339. else if (dest.Type.IsInteger() || dest.Kind == OperandKind.Memory)
  340. {
  341. WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRMR);
  342. }
  343. else
  344. {
  345. WriteInstruction(dest, source, OperandType.None, X86Instruction.Movq);
  346. }
  347. }
  348. public void Movsd(Operand dest, Operand src1, Operand src2)
  349. {
  350. WriteInstruction(dest, src1, src2, X86Instruction.Movsd);
  351. }
  352. public void Movss(Operand dest, Operand src1, Operand src2)
  353. {
  354. WriteInstruction(dest, src1, src2, X86Instruction.Movss);
  355. }
  356. public void Movsx16(Operand dest, Operand source, OperandType type)
  357. {
  358. WriteInstruction(dest, source, type, X86Instruction.Movsx16);
  359. }
  360. public void Movsx32(Operand dest, Operand source, OperandType type)
  361. {
  362. WriteInstruction(dest, source, type, X86Instruction.Movsx32);
  363. }
  364. public void Movsx8(Operand dest, Operand source, OperandType type)
  365. {
  366. WriteInstruction(dest, source, type, X86Instruction.Movsx8);
  367. }
  368. public void Movzx16(Operand dest, Operand source, OperandType type)
  369. {
  370. WriteInstruction(dest, source, type, X86Instruction.Movzx16);
  371. }
  372. public void Movzx8(Operand dest, Operand source, OperandType type)
  373. {
  374. WriteInstruction(dest, source, type, X86Instruction.Movzx8);
  375. }
  376. public void Mul(Operand source)
  377. {
  378. WriteInstruction(default, source, source.Type, X86Instruction.Mul128);
  379. }
  380. public void Mulsd(Operand dest, Operand src1, Operand src2)
  381. {
  382. WriteInstruction(dest, src1, src2, X86Instruction.Mulsd);
  383. }
  384. public void Mulss(Operand dest, Operand src1, Operand src2)
  385. {
  386. WriteInstruction(dest, src1, src2, X86Instruction.Mulss);
  387. }
  388. public void Neg(Operand dest)
  389. {
  390. WriteInstruction(dest, default, dest.Type, X86Instruction.Neg);
  391. }
  392. public void Not(Operand dest)
  393. {
  394. WriteInstruction(dest, default, dest.Type, X86Instruction.Not);
  395. }
  396. public void Or(Operand dest, Operand source, OperandType type)
  397. {
  398. WriteInstruction(dest, source, type, X86Instruction.Or);
  399. }
  400. public void Pclmulqdq(Operand dest, Operand source, byte imm)
  401. {
  402. WriteInstruction(dest, default, source, X86Instruction.Pclmulqdq);
  403. WriteByte(imm);
  404. }
  405. public void Pcmpeqw(Operand dest, Operand src1, Operand src2)
  406. {
  407. WriteInstruction(dest, src1, src2, X86Instruction.Pcmpeqw);
  408. }
  409. public void Pextrb(Operand dest, Operand source, byte imm)
  410. {
  411. WriteInstruction(dest, default, source, X86Instruction.Pextrb);
  412. WriteByte(imm);
  413. }
  414. public void Pextrd(Operand dest, Operand source, byte imm)
  415. {
  416. WriteInstruction(dest, default, source, X86Instruction.Pextrd);
  417. WriteByte(imm);
  418. }
  419. public void Pextrq(Operand dest, Operand source, byte imm)
  420. {
  421. WriteInstruction(dest, default, source, X86Instruction.Pextrq);
  422. WriteByte(imm);
  423. }
  424. public void Pextrw(Operand dest, Operand source, byte imm)
  425. {
  426. WriteInstruction(dest, default, source, X86Instruction.Pextrw);
  427. WriteByte(imm);
  428. }
  429. public void Pinsrb(Operand dest, Operand src1, Operand src2, byte imm)
  430. {
  431. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrb);
  432. WriteByte(imm);
  433. }
  434. public void Pinsrd(Operand dest, Operand src1, Operand src2, byte imm)
  435. {
  436. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrd);
  437. WriteByte(imm);
  438. }
  439. public void Pinsrq(Operand dest, Operand src1, Operand src2, byte imm)
  440. {
  441. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrq);
  442. WriteByte(imm);
  443. }
  444. public void Pinsrw(Operand dest, Operand src1, Operand src2, byte imm)
  445. {
  446. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrw);
  447. WriteByte(imm);
  448. }
  449. public void Pop(Operand dest)
  450. {
  451. if (dest.Kind == OperandKind.Register)
  452. {
  453. WriteCompactInst(dest, 0x58);
  454. }
  455. else
  456. {
  457. WriteInstruction(dest, default, dest.Type, X86Instruction.Pop);
  458. }
  459. }
  460. public void Popcnt(Operand dest, Operand source, OperandType type)
  461. {
  462. WriteInstruction(dest, source, type, X86Instruction.Popcnt);
  463. }
  464. public void Pshufd(Operand dest, Operand source, byte imm)
  465. {
  466. WriteInstruction(dest, default, source, X86Instruction.Pshufd);
  467. WriteByte(imm);
  468. }
  469. public void Push(Operand source)
  470. {
  471. if (source.Kind == OperandKind.Register)
  472. {
  473. WriteCompactInst(source, 0x50);
  474. }
  475. else
  476. {
  477. WriteInstruction(default, source, source.Type, X86Instruction.Push);
  478. }
  479. }
  480. public void Return()
  481. {
  482. WriteByte(0xc3);
  483. }
  484. public void Ror(Operand dest, Operand source, OperandType type)
  485. {
  486. WriteShiftInst(dest, source, type, X86Instruction.Ror);
  487. }
  488. public void Sar(Operand dest, Operand source, OperandType type)
  489. {
  490. WriteShiftInst(dest, source, type, X86Instruction.Sar);
  491. }
  492. public void Shl(Operand dest, Operand source, OperandType type)
  493. {
  494. WriteShiftInst(dest, source, type, X86Instruction.Shl);
  495. }
  496. public void Shr(Operand dest, Operand source, OperandType type)
  497. {
  498. WriteShiftInst(dest, source, type, X86Instruction.Shr);
  499. }
  500. public void Setcc(Operand dest, X86Condition condition)
  501. {
  502. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Setcc];
  503. WriteOpCode(dest, default, default, OperandType.None, info.Flags, info.OpRRM | (int)condition);
  504. }
  505. public void Stmxcsr(Operand dest)
  506. {
  507. WriteInstruction(dest, default, OperandType.I32, X86Instruction.Stmxcsr);
  508. }
  509. public void Sub(Operand dest, Operand source, OperandType type)
  510. {
  511. WriteInstruction(dest, source, type, X86Instruction.Sub);
  512. }
  513. public void Subsd(Operand dest, Operand src1, Operand src2)
  514. {
  515. WriteInstruction(dest, src1, src2, X86Instruction.Subsd);
  516. }
  517. public void Subss(Operand dest, Operand src1, Operand src2)
  518. {
  519. WriteInstruction(dest, src1, src2, X86Instruction.Subss);
  520. }
  521. public void Test(Operand src1, Operand src2, OperandType type)
  522. {
  523. WriteInstruction(src1, src2, type, X86Instruction.Test);
  524. }
  525. public void Xor(Operand dest, Operand source, OperandType type)
  526. {
  527. WriteInstruction(dest, source, type, X86Instruction.Xor);
  528. }
  529. public void Xorps(Operand dest, Operand src1, Operand src2)
  530. {
  531. WriteInstruction(dest, src1, src2, X86Instruction.Xorps);
  532. }
  533. public void WriteInstruction(
  534. X86Instruction inst,
  535. Operand dest,
  536. Operand source,
  537. OperandType type = OperandType.None)
  538. {
  539. WriteInstruction(dest, default, source, inst, type);
  540. }
  541. public void WriteInstruction(X86Instruction inst, Operand dest, Operand src1, Operand src2)
  542. {
  543. if (src2.Kind == OperandKind.Constant)
  544. {
  545. WriteInstruction(src1, dest, src2, inst);
  546. }
  547. else
  548. {
  549. WriteInstruction(dest, src1, src2, inst);
  550. }
  551. }
  552. public void WriteInstruction(
  553. X86Instruction inst,
  554. Operand dest,
  555. Operand src1,
  556. Operand src2,
  557. OperandType type)
  558. {
  559. WriteInstruction(dest, src1, src2, inst, type);
  560. }
  561. public void WriteInstruction(X86Instruction inst, Operand dest, Operand source, byte imm)
  562. {
  563. WriteInstruction(dest, default, source, inst);
  564. WriteByte(imm);
  565. }
  566. public void WriteInstruction(
  567. X86Instruction inst,
  568. Operand dest,
  569. Operand src1,
  570. Operand src2,
  571. Operand src3)
  572. {
  573. // 3+ operands can only be encoded with the VEX encoding scheme.
  574. Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
  575. WriteInstruction(dest, src1, src2, inst);
  576. WriteByte((byte)(src3.AsByte() << 4));
  577. }
  578. public void WriteInstruction(
  579. X86Instruction inst,
  580. Operand dest,
  581. Operand src1,
  582. Operand src2,
  583. byte imm)
  584. {
  585. WriteInstruction(dest, src1, src2, inst);
  586. WriteByte(imm);
  587. }
  588. private void WriteShiftInst(Operand dest, Operand source, OperandType type, X86Instruction inst)
  589. {
  590. if (source.Kind == OperandKind.Register)
  591. {
  592. X86Register shiftReg = (X86Register)source.GetRegister().Index;
  593. Debug.Assert(shiftReg == X86Register.Rcx, $"Invalid shift register \"{shiftReg}\".");
  594. source = default;
  595. }
  596. else if (source.Kind == OperandKind.Constant)
  597. {
  598. source = Operand.Factory.Const((int)source.Value & (dest.Type == OperandType.I32 ? 0x1f : 0x3f));
  599. }
  600. WriteInstruction(dest, source, type, inst);
  601. }
  602. private void WriteInstruction(Operand dest, Operand source, OperandType type, X86Instruction inst)
  603. {
  604. ref readonly InstructionInfo info = ref _instTable[(int)inst];
  605. if (source != default)
  606. {
  607. if (source.Kind == OperandKind.Constant)
  608. {
  609. ulong imm = source.Value;
  610. if (inst == X86Instruction.Mov8)
  611. {
  612. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm8);
  613. WriteByte((byte)imm);
  614. }
  615. else if (inst == X86Instruction.Mov16)
  616. {
  617. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm32);
  618. WriteInt16((short)imm);
  619. }
  620. else if (IsImm8(imm, type) && info.OpRMImm8 != BadOp)
  621. {
  622. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm8);
  623. WriteByte((byte)imm);
  624. }
  625. else if (!source.Relocatable && IsImm32(imm, type) && info.OpRMImm32 != BadOp)
  626. {
  627. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm32);
  628. WriteInt32((int)imm);
  629. }
  630. else if (dest != default && dest.Kind == OperandKind.Register && info.OpRImm64 != BadOp)
  631. {
  632. int rexPrefix = GetRexPrefix(dest, source, type, rrm: false);
  633. if (rexPrefix != 0)
  634. {
  635. WriteByte((byte)rexPrefix);
  636. }
  637. WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111)));
  638. if (HasRelocs && source.Relocatable)
  639. {
  640. _relocs.Add(new Reloc
  641. {
  642. JumpIndex = _jumps.Count - 1,
  643. Position = (int)_stream.Position,
  644. Symbol = source.Symbol,
  645. });
  646. }
  647. WriteUInt64(imm);
  648. }
  649. else
  650. {
  651. throw new ArgumentException($"Failed to encode constant 0x{imm:X}.");
  652. }
  653. }
  654. else if (source.Kind == OperandKind.Register && info.OpRMR != BadOp)
  655. {
  656. WriteOpCode(dest, default, source, type, info.Flags, info.OpRMR);
  657. }
  658. else if (info.OpRRM != BadOp)
  659. {
  660. WriteOpCode(dest, default, source, type, info.Flags, info.OpRRM, rrm: true);
  661. }
  662. else
  663. {
  664. throw new ArgumentException($"Invalid source operand kind \"{source.Kind}\".");
  665. }
  666. }
  667. else if (info.OpRRM != BadOp)
  668. {
  669. WriteOpCode(dest, default, source, type, info.Flags, info.OpRRM, rrm: true);
  670. }
  671. else if (info.OpRMR != BadOp)
  672. {
  673. WriteOpCode(dest, default, source, type, info.Flags, info.OpRMR);
  674. }
  675. else
  676. {
  677. throw new ArgumentNullException(nameof(source));
  678. }
  679. }
  680. private void WriteInstruction(
  681. Operand dest,
  682. Operand src1,
  683. Operand src2,
  684. X86Instruction inst,
  685. OperandType type = OperandType.None)
  686. {
  687. ref readonly InstructionInfo info = ref _instTable[(int)inst];
  688. if (src2 != default)
  689. {
  690. if (src2.Kind == OperandKind.Constant)
  691. {
  692. ulong imm = src2.Value;
  693. if ((byte)imm == imm && info.OpRMImm8 != BadOp)
  694. {
  695. WriteOpCode(dest, src1, default, type, info.Flags, info.OpRMImm8);
  696. WriteByte((byte)imm);
  697. }
  698. else
  699. {
  700. throw new ArgumentException($"Failed to encode constant 0x{imm:X}.");
  701. }
  702. }
  703. else if (src2.Kind == OperandKind.Register && info.OpRMR != BadOp)
  704. {
  705. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRMR);
  706. }
  707. else if (info.OpRRM != BadOp)
  708. {
  709. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRRM, rrm: true);
  710. }
  711. else
  712. {
  713. throw new ArgumentException($"Invalid source operand kind \"{src2.Kind}\".");
  714. }
  715. }
  716. else if (info.OpRRM != BadOp)
  717. {
  718. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRRM, rrm: true);
  719. }
  720. else if (info.OpRMR != BadOp)
  721. {
  722. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRMR);
  723. }
  724. else
  725. {
  726. throw new ArgumentNullException(nameof(src2));
  727. }
  728. }
  729. private void WriteOpCode(
  730. Operand dest,
  731. Operand src1,
  732. Operand src2,
  733. OperandType type,
  734. InstructionFlags flags,
  735. int opCode,
  736. bool rrm = false)
  737. {
  738. int rexPrefix = GetRexPrefix(dest, src2, type, rrm);
  739. if ((flags & InstructionFlags.RexW) != 0)
  740. {
  741. rexPrefix |= RexWPrefix;
  742. }
  743. int modRM = (opCode >> OpModRMBits) << 3;
  744. MemoryOperand memOp = default;
  745. bool hasMemOp = false;
  746. if (dest != default)
  747. {
  748. if (dest.Kind == OperandKind.Register)
  749. {
  750. int regIndex = dest.GetRegister().Index;
  751. modRM |= (regIndex & 0b111) << (rrm ? 3 : 0);
  752. if ((flags & InstructionFlags.Reg8Dest) != 0 && regIndex >= 4)
  753. {
  754. rexPrefix |= RexPrefix;
  755. }
  756. }
  757. else if (dest.Kind == OperandKind.Memory)
  758. {
  759. memOp = dest.GetMemory();
  760. hasMemOp = true;
  761. }
  762. else
  763. {
  764. throw new ArgumentException("Invalid destination operand kind \"" + dest.Kind + "\".");
  765. }
  766. }
  767. if (src2 != default)
  768. {
  769. if (src2.Kind == OperandKind.Register)
  770. {
  771. int regIndex = src2.GetRegister().Index;
  772. modRM |= (regIndex & 0b111) << (rrm ? 0 : 3);
  773. if ((flags & InstructionFlags.Reg8Src) != 0 && regIndex >= 4)
  774. {
  775. rexPrefix |= RexPrefix;
  776. }
  777. }
  778. else if (src2.Kind == OperandKind.Memory && !hasMemOp)
  779. {
  780. memOp = src2.GetMemory();
  781. hasMemOp = true;
  782. }
  783. else
  784. {
  785. throw new ArgumentException("Invalid source operand kind \"" + src2.Kind + "\".");
  786. }
  787. }
  788. bool needsSibByte = false;
  789. bool needsDisplacement = false;
  790. int sib = 0;
  791. if (hasMemOp)
  792. {
  793. // Either source or destination is a memory operand.
  794. Register baseReg = memOp.BaseAddress.GetRegister();
  795. X86Register baseRegLow = (X86Register)(baseReg.Index & 0b111);
  796. needsSibByte = memOp.Index != default || baseRegLow == X86Register.Rsp;
  797. needsDisplacement = memOp.Displacement != 0 || baseRegLow == X86Register.Rbp;
  798. if (needsDisplacement)
  799. {
  800. if (ConstFitsOnS8(memOp.Displacement))
  801. {
  802. modRM |= 0x40;
  803. }
  804. else /* if (ConstFitsOnS32(memOp.Displacement)) */
  805. {
  806. modRM |= 0x80;
  807. }
  808. }
  809. if (baseReg.Index >= 8)
  810. {
  811. Debug.Assert((uint)baseReg.Index <= MaxRegNumber);
  812. rexPrefix |= RexPrefix | (baseReg.Index >> 3);
  813. }
  814. if (needsSibByte)
  815. {
  816. sib = (int)baseRegLow;
  817. if (memOp.Index != default)
  818. {
  819. int indexReg = memOp.Index.GetRegister().Index;
  820. Debug.Assert(indexReg != (int)X86Register.Rsp, "Using RSP as index register on the memory operand is not allowed.");
  821. if (indexReg >= 8)
  822. {
  823. Debug.Assert((uint)indexReg <= MaxRegNumber);
  824. rexPrefix |= RexPrefix | (indexReg >> 3) << 1;
  825. }
  826. sib |= (indexReg & 0b111) << 3;
  827. }
  828. else
  829. {
  830. sib |= 0b100 << 3;
  831. }
  832. sib |= (int)memOp.Scale << 6;
  833. modRM |= 0b100;
  834. }
  835. else
  836. {
  837. modRM |= (int)baseRegLow;
  838. }
  839. }
  840. else
  841. {
  842. // Source and destination are registers.
  843. modRM |= 0xc0;
  844. }
  845. Debug.Assert(opCode != BadOp, "Invalid opcode value.");
  846. if ((flags & InstructionFlags.Evex) != 0 && HardwareCapabilities.SupportsEvexEncoding)
  847. {
  848. WriteEvexInst(dest, src1, src2, type, flags, opCode);
  849. opCode &= 0xff;
  850. }
  851. else if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
  852. {
  853. // In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits.
  854. int vexByte2 = (flags & InstructionFlags.PrefixMask) switch
  855. {
  856. InstructionFlags.Prefix66 => 1,
  857. InstructionFlags.PrefixF3 => 2,
  858. InstructionFlags.PrefixF2 => 3,
  859. _ => 0,
  860. };
  861. if (src1 != default)
  862. {
  863. vexByte2 |= (src1.GetRegister().Index ^ 0xf) << 3;
  864. }
  865. else
  866. {
  867. vexByte2 |= 0b1111 << 3;
  868. }
  869. ushort opCodeHigh = (ushort)(opCode >> 8);
  870. if ((rexPrefix & 0b1011) == 0 && opCodeHigh == 0xf)
  871. {
  872. // Two-byte form.
  873. WriteByte(0xc5);
  874. vexByte2 |= (~rexPrefix & 4) << 5;
  875. WriteByte((byte)vexByte2);
  876. }
  877. else
  878. {
  879. // Three-byte form.
  880. WriteByte(0xc4);
  881. int vexByte1 = (~rexPrefix & 7) << 5;
  882. switch (opCodeHigh)
  883. {
  884. case 0xf:
  885. vexByte1 |= 1;
  886. break;
  887. case 0xf38:
  888. vexByte1 |= 2;
  889. break;
  890. case 0xf3a:
  891. vexByte1 |= 3;
  892. break;
  893. default:
  894. Debug.Assert(false, $"Failed to VEX encode opcode 0x{opCode:X}.");
  895. break;
  896. }
  897. vexByte2 |= (rexPrefix & 8) << 4;
  898. WriteByte((byte)vexByte1);
  899. WriteByte((byte)vexByte2);
  900. }
  901. opCode &= 0xff;
  902. }
  903. else
  904. {
  905. if (flags.HasFlag(InstructionFlags.Prefix66))
  906. {
  907. WriteByte(0x66);
  908. }
  909. if (flags.HasFlag(InstructionFlags.PrefixF2))
  910. {
  911. WriteByte(0xf2);
  912. }
  913. if (flags.HasFlag(InstructionFlags.PrefixF3))
  914. {
  915. WriteByte(0xf3);
  916. }
  917. if (rexPrefix != 0)
  918. {
  919. WriteByte((byte)rexPrefix);
  920. }
  921. }
  922. if (dest != default && (flags & InstructionFlags.RegOnly) != 0)
  923. {
  924. opCode += dest.GetRegister().Index & 7;
  925. }
  926. if ((opCode & 0xff0000) != 0)
  927. {
  928. WriteByte((byte)(opCode >> 16));
  929. }
  930. if ((opCode & 0xff00) != 0)
  931. {
  932. WriteByte((byte)(opCode >> 8));
  933. }
  934. WriteByte((byte)opCode);
  935. if ((flags & InstructionFlags.RegOnly) == 0)
  936. {
  937. WriteByte((byte)modRM);
  938. if (needsSibByte)
  939. {
  940. WriteByte((byte)sib);
  941. }
  942. if (needsDisplacement)
  943. {
  944. if (ConstFitsOnS8(memOp.Displacement))
  945. {
  946. WriteByte((byte)memOp.Displacement);
  947. }
  948. else /* if (ConstFitsOnS32(memOp.Displacement)) */
  949. {
  950. WriteInt32(memOp.Displacement);
  951. }
  952. }
  953. }
  954. }
  955. private void WriteEvexInst(
  956. Operand dest,
  957. Operand src1,
  958. Operand src2,
  959. OperandType type,
  960. InstructionFlags flags,
  961. int opCode,
  962. bool broadcast = false,
  963. int registerWidth = 128,
  964. int maskRegisterIdx = 0,
  965. bool zeroElements = false)
  966. {
  967. int op1Idx = dest.GetRegister().Index;
  968. int op2Idx = src1.GetRegister().Index;
  969. int op3Idx = src2.GetRegister().Index;
  970. WriteByte(0x62);
  971. // P0
  972. // Extend operand 1 register
  973. bool r = (op1Idx & 8) == 0;
  974. // Extend operand 3 register
  975. bool x = (op3Idx & 16) == 0;
  976. // Extend operand 3 register
  977. bool b = (op3Idx & 8) == 0;
  978. // Extend operand 1 register
  979. bool rp = (op1Idx & 16) == 0;
  980. // Escape code index
  981. byte mm = 0b00;
  982. switch ((ushort)(opCode >> 8))
  983. {
  984. case 0xf00:
  985. mm = 0b01;
  986. break;
  987. case 0xf38:
  988. mm = 0b10;
  989. break;
  990. case 0xf3a:
  991. mm = 0b11;
  992. break;
  993. default:
  994. Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}.");
  995. break;
  996. }
  997. WriteByte(
  998. (byte)(
  999. (r ? 0x80 : 0) |
  1000. (x ? 0x40 : 0) |
  1001. (b ? 0x20 : 0) |
  1002. (rp ? 0x10 : 0) |
  1003. mm));
  1004. // P1
  1005. // Specify 64-bit lane mode
  1006. bool w = Is64Bits(type);
  1007. // Operand 2 register index
  1008. byte vvvv = (byte)(~op2Idx & 0b1111);
  1009. // Opcode prefix
  1010. byte pp = (flags & InstructionFlags.PrefixMask) switch
  1011. {
  1012. InstructionFlags.Prefix66 => 0b01,
  1013. InstructionFlags.PrefixF3 => 0b10,
  1014. InstructionFlags.PrefixF2 => 0b11,
  1015. _ => 0,
  1016. };
  1017. WriteByte(
  1018. (byte)(
  1019. (w ? 0x80 : 0) |
  1020. (vvvv << 3) |
  1021. 0b100 |
  1022. pp));
  1023. // P2
  1024. // Mask register determines what elements to zero, rather than what elements to merge
  1025. bool z = zeroElements;
  1026. // Specifies register-width
  1027. byte ll = 0b00;
  1028. switch (registerWidth)
  1029. {
  1030. case 128:
  1031. ll = 0b00;
  1032. break;
  1033. case 256:
  1034. ll = 0b01;
  1035. break;
  1036. case 512:
  1037. ll = 0b10;
  1038. break;
  1039. default:
  1040. Debug.Fail($"Invalid EVEX vector register width {registerWidth}.");
  1041. break;
  1042. }
  1043. // Embedded broadcast in the case of a memory operand
  1044. bool bcast = broadcast;
  1045. // Extend operand 2 register
  1046. bool vp = (op2Idx & 16) == 0;
  1047. // Mask register index
  1048. Debug.Assert(maskRegisterIdx < 8, $"Invalid mask register index {maskRegisterIdx}.");
  1049. byte aaa = (byte)(maskRegisterIdx & 0b111);
  1050. WriteByte(
  1051. (byte)(
  1052. (z ? 0x80 : 0) |
  1053. (ll << 5) |
  1054. (bcast ? 0x10 : 0) |
  1055. (vp ? 8 : 0) |
  1056. aaa));
  1057. }
  1058. private void WriteCompactInst(Operand operand, int opCode)
  1059. {
  1060. int regIndex = operand.GetRegister().Index;
  1061. if (regIndex >= 8)
  1062. {
  1063. WriteByte(0x41);
  1064. }
  1065. WriteByte((byte)(opCode + (regIndex & 0b111)));
  1066. }
  1067. private static int GetRexPrefix(Operand dest, Operand source, OperandType type, bool rrm)
  1068. {
  1069. int rexPrefix = 0;
  1070. if (Is64Bits(type))
  1071. {
  1072. rexPrefix = RexWPrefix;
  1073. }
  1074. void SetRegisterHighBit(Register reg, int bit)
  1075. {
  1076. if (reg.Index >= 8)
  1077. {
  1078. rexPrefix |= RexPrefix | (reg.Index >> 3) << bit;
  1079. }
  1080. }
  1081. if (dest != default && dest.Kind == OperandKind.Register)
  1082. {
  1083. SetRegisterHighBit(dest.GetRegister(), rrm ? 2 : 0);
  1084. }
  1085. if (source != default && source.Kind == OperandKind.Register)
  1086. {
  1087. SetRegisterHighBit(source.GetRegister(), rrm ? 0 : 2);
  1088. }
  1089. return rexPrefix;
  1090. }
  1091. public (byte[], RelocInfo) GetCode()
  1092. {
  1093. var jumps = CollectionsMarshal.AsSpan(_jumps);
  1094. var relocs = CollectionsMarshal.AsSpan(_relocs);
  1095. // Write jump relative offsets.
  1096. bool modified;
  1097. do
  1098. {
  1099. modified = false;
  1100. for (int i = 0; i < jumps.Length; i++)
  1101. {
  1102. ref Jump jump = ref jumps[i];
  1103. // If jump target not resolved yet, resolve it.
  1104. jump.JumpTarget ??= _labels[jump.JumpLabel];
  1105. long jumpTarget = jump.JumpTarget.Value;
  1106. long offset = jumpTarget - jump.JumpPosition;
  1107. if (offset < 0)
  1108. {
  1109. for (int j = i - 1; j >= 0; j--)
  1110. {
  1111. ref Jump jump2 = ref jumps[j];
  1112. if (jump2.JumpPosition < jumpTarget)
  1113. {
  1114. break;
  1115. }
  1116. offset -= jump2.InstSize - ReservedBytesForJump;
  1117. }
  1118. }
  1119. else
  1120. {
  1121. for (int j = i + 1; j < jumps.Length; j++)
  1122. {
  1123. ref Jump jump2 = ref jumps[j];
  1124. if (jump2.JumpPosition >= jumpTarget)
  1125. {
  1126. break;
  1127. }
  1128. offset += jump2.InstSize - ReservedBytesForJump;
  1129. }
  1130. offset -= ReservedBytesForJump;
  1131. }
  1132. if (jump.IsConditional)
  1133. {
  1134. jump.InstSize = GetJccLength(offset);
  1135. }
  1136. else
  1137. {
  1138. jump.InstSize = GetJmpLength(offset);
  1139. }
  1140. // The jump is relative to the next instruction, not the current one.
  1141. // Since we didn't know the next instruction address when calculating
  1142. // the offset (as the size of the current jump instruction was not known),
  1143. // we now need to compensate the offset with the jump instruction size.
  1144. // It's also worth noting that:
  1145. // - This is only needed for backward jumps.
  1146. // - The GetJmpLength and GetJccLength also compensates the offset
  1147. // internally when computing the jump instruction size.
  1148. if (offset < 0)
  1149. {
  1150. offset -= jump.InstSize;
  1151. }
  1152. if (jump.Offset != offset)
  1153. {
  1154. jump.Offset = offset;
  1155. modified = true;
  1156. }
  1157. }
  1158. }
  1159. while (modified);
  1160. // Write the code, ignoring the dummy bytes after jumps, into a new stream.
  1161. _stream.Seek(0, SeekOrigin.Begin);
  1162. using var codeStream = MemoryStreamManager.Shared.GetStream();
  1163. var assembler = new Assembler(codeStream, HasRelocs);
  1164. bool hasRelocs = HasRelocs;
  1165. int relocIndex = 0;
  1166. int relocOffset = 0;
  1167. var relocEntries = hasRelocs
  1168. ? new RelocEntry[relocs.Length]
  1169. : Array.Empty<RelocEntry>();
  1170. for (int i = 0; i < jumps.Length; i++)
  1171. {
  1172. ref Jump jump = ref jumps[i];
  1173. // If has relocations, calculate their new positions compensating for jumps.
  1174. if (hasRelocs)
  1175. {
  1176. relocOffset += jump.InstSize - ReservedBytesForJump;
  1177. for (; relocIndex < relocEntries.Length; relocIndex++)
  1178. {
  1179. ref Reloc reloc = ref relocs[relocIndex];
  1180. if (reloc.JumpIndex > i)
  1181. {
  1182. break;
  1183. }
  1184. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1185. }
  1186. }
  1187. Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
  1188. _stream.ReadExactly(buffer);
  1189. _stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
  1190. codeStream.Write(buffer);
  1191. if (jump.IsConditional)
  1192. {
  1193. assembler.Jcc(jump.Condition, jump.Offset);
  1194. }
  1195. else
  1196. {
  1197. assembler.Jmp(jump.Offset);
  1198. }
  1199. }
  1200. // Write remaining relocations. This case happens when there are no jumps assembled.
  1201. for (; relocIndex < relocEntries.Length; relocIndex++)
  1202. {
  1203. ref Reloc reloc = ref relocs[relocIndex];
  1204. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1205. }
  1206. _stream.CopyTo(codeStream);
  1207. var code = codeStream.ToArray();
  1208. var relocInfo = new RelocInfo(relocEntries);
  1209. return (code, relocInfo);
  1210. }
  1211. private static bool Is64Bits(OperandType type)
  1212. {
  1213. return type == OperandType.I64 || type == OperandType.FP64;
  1214. }
  1215. private static bool IsImm8(ulong immediate, OperandType type)
  1216. {
  1217. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1218. return ConstFitsOnS8(value);
  1219. }
  1220. private static bool IsImm32(ulong immediate, OperandType type)
  1221. {
  1222. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1223. return ConstFitsOnS32(value);
  1224. }
  1225. private static int GetJccLength(long offset)
  1226. {
  1227. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1228. {
  1229. return 2;
  1230. }
  1231. else if (ConstFitsOnS32(offset < 0 ? offset - 6 : offset))
  1232. {
  1233. return 6;
  1234. }
  1235. else
  1236. {
  1237. throw new ArgumentOutOfRangeException(nameof(offset));
  1238. }
  1239. }
  1240. private static int GetJmpLength(long offset)
  1241. {
  1242. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1243. {
  1244. return 2;
  1245. }
  1246. else if (ConstFitsOnS32(offset < 0 ? offset - 5 : offset))
  1247. {
  1248. return 5;
  1249. }
  1250. else
  1251. {
  1252. throw new ArgumentOutOfRangeException(nameof(offset));
  1253. }
  1254. }
  1255. private static bool ConstFitsOnS8(long value)
  1256. {
  1257. return value == (sbyte)value;
  1258. }
  1259. private static bool ConstFitsOnS32(long value)
  1260. {
  1261. return value == (int)value;
  1262. }
  1263. private void WriteInt16(short value)
  1264. {
  1265. WriteUInt16((ushort)value);
  1266. }
  1267. private void WriteInt32(int value)
  1268. {
  1269. WriteUInt32((uint)value);
  1270. }
  1271. private void WriteByte(byte value)
  1272. {
  1273. _stream.WriteByte(value);
  1274. }
  1275. private void WriteUInt16(ushort value)
  1276. {
  1277. _stream.WriteByte((byte)(value >> 0));
  1278. _stream.WriteByte((byte)(value >> 8));
  1279. }
  1280. private void WriteUInt32(uint value)
  1281. {
  1282. _stream.WriteByte((byte)(value >> 0));
  1283. _stream.WriteByte((byte)(value >> 8));
  1284. _stream.WriteByte((byte)(value >> 16));
  1285. _stream.WriteByte((byte)(value >> 24));
  1286. }
  1287. private void WriteUInt64(ulong value)
  1288. {
  1289. _stream.WriteByte((byte)(value >> 0));
  1290. _stream.WriteByte((byte)(value >> 8));
  1291. _stream.WriteByte((byte)(value >> 16));
  1292. _stream.WriteByte((byte)(value >> 24));
  1293. _stream.WriteByte((byte)(value >> 32));
  1294. _stream.WriteByte((byte)(value >> 40));
  1295. _stream.WriteByte((byte)(value >> 48));
  1296. _stream.WriteByte((byte)(value >> 56));
  1297. }
  1298. }
  1299. }