Assembler.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456
  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.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
  847. {
  848. // 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.
  849. int vexByte2 = (flags & InstructionFlags.PrefixMask) switch
  850. {
  851. InstructionFlags.Prefix66 => 1,
  852. InstructionFlags.PrefixF3 => 2,
  853. InstructionFlags.PrefixF2 => 3,
  854. _ => 0
  855. };
  856. if (src1 != default)
  857. {
  858. vexByte2 |= (src1.GetRegister().Index ^ 0xf) << 3;
  859. }
  860. else
  861. {
  862. vexByte2 |= 0b1111 << 3;
  863. }
  864. ushort opCodeHigh = (ushort)(opCode >> 8);
  865. if ((rexPrefix & 0b1011) == 0 && opCodeHigh == 0xf)
  866. {
  867. // Two-byte form.
  868. WriteByte(0xc5);
  869. vexByte2 |= (~rexPrefix & 4) << 5;
  870. WriteByte((byte)vexByte2);
  871. }
  872. else
  873. {
  874. // Three-byte form.
  875. WriteByte(0xc4);
  876. int vexByte1 = (~rexPrefix & 7) << 5;
  877. switch (opCodeHigh)
  878. {
  879. case 0xf: vexByte1 |= 1; break;
  880. case 0xf38: vexByte1 |= 2; break;
  881. case 0xf3a: vexByte1 |= 3; break;
  882. default: Debug.Assert(false, $"Failed to VEX encode opcode 0x{opCode:X}."); break;
  883. }
  884. vexByte2 |= (rexPrefix & 8) << 4;
  885. WriteByte((byte)vexByte1);
  886. WriteByte((byte)vexByte2);
  887. }
  888. opCode &= 0xff;
  889. }
  890. else
  891. {
  892. if (flags.HasFlag(InstructionFlags.Prefix66))
  893. {
  894. WriteByte(0x66);
  895. }
  896. if (flags.HasFlag(InstructionFlags.PrefixF2))
  897. {
  898. WriteByte(0xf2);
  899. }
  900. if (flags.HasFlag(InstructionFlags.PrefixF3))
  901. {
  902. WriteByte(0xf3);
  903. }
  904. if (rexPrefix != 0)
  905. {
  906. WriteByte((byte)rexPrefix);
  907. }
  908. }
  909. if (dest != default && (flags & InstructionFlags.RegOnly) != 0)
  910. {
  911. opCode += dest.GetRegister().Index & 7;
  912. }
  913. if ((opCode & 0xff0000) != 0)
  914. {
  915. WriteByte((byte)(opCode >> 16));
  916. }
  917. if ((opCode & 0xff00) != 0)
  918. {
  919. WriteByte((byte)(opCode >> 8));
  920. }
  921. WriteByte((byte)opCode);
  922. if ((flags & InstructionFlags.RegOnly) == 0)
  923. {
  924. WriteByte((byte)modRM);
  925. if (needsSibByte)
  926. {
  927. WriteByte((byte)sib);
  928. }
  929. if (needsDisplacement)
  930. {
  931. if (ConstFitsOnS8(memOp.Displacement))
  932. {
  933. WriteByte((byte)memOp.Displacement);
  934. }
  935. else /* if (ConstFitsOnS32(memOp.Displacement)) */
  936. {
  937. WriteInt32(memOp.Displacement);
  938. }
  939. }
  940. }
  941. }
  942. private void WriteCompactInst(Operand operand, int opCode)
  943. {
  944. int regIndex = operand.GetRegister().Index;
  945. if (regIndex >= 8)
  946. {
  947. WriteByte(0x41);
  948. }
  949. WriteByte((byte)(opCode + (regIndex & 0b111)));
  950. }
  951. private static int GetRexPrefix(Operand dest, Operand source, OperandType type, bool rrm)
  952. {
  953. int rexPrefix = 0;
  954. if (Is64Bits(type))
  955. {
  956. rexPrefix = RexWPrefix;
  957. }
  958. void SetRegisterHighBit(Register reg, int bit)
  959. {
  960. if (reg.Index >= 8)
  961. {
  962. rexPrefix |= RexPrefix | (reg.Index >> 3) << bit;
  963. }
  964. }
  965. if (dest != default && dest.Kind == OperandKind.Register)
  966. {
  967. SetRegisterHighBit(dest.GetRegister(), rrm ? 2 : 0);
  968. }
  969. if (source != default && source.Kind == OperandKind.Register)
  970. {
  971. SetRegisterHighBit(source.GetRegister(), rrm ? 0 : 2);
  972. }
  973. return rexPrefix;
  974. }
  975. public (byte[], RelocInfo) GetCode()
  976. {
  977. var jumps = CollectionsMarshal.AsSpan(_jumps);
  978. var relocs = CollectionsMarshal.AsSpan(_relocs);
  979. // Write jump relative offsets.
  980. bool modified;
  981. do
  982. {
  983. modified = false;
  984. for (int i = 0; i < jumps.Length; i++)
  985. {
  986. ref Jump jump = ref jumps[i];
  987. // If jump target not resolved yet, resolve it.
  988. if (jump.JumpTarget == null)
  989. {
  990. jump.JumpTarget = _labels[jump.JumpLabel];
  991. }
  992. long jumpTarget = jump.JumpTarget.Value;
  993. long offset = jumpTarget - jump.JumpPosition;
  994. if (offset < 0)
  995. {
  996. for (int j = i - 1; j >= 0; j--)
  997. {
  998. ref Jump jump2 = ref jumps[j];
  999. if (jump2.JumpPosition < jumpTarget)
  1000. {
  1001. break;
  1002. }
  1003. offset -= jump2.InstSize - ReservedBytesForJump;
  1004. }
  1005. }
  1006. else
  1007. {
  1008. for (int j = i + 1; j < jumps.Length; j++)
  1009. {
  1010. ref Jump jump2 = ref jumps[j];
  1011. if (jump2.JumpPosition >= jumpTarget)
  1012. {
  1013. break;
  1014. }
  1015. offset += jump2.InstSize - ReservedBytesForJump;
  1016. }
  1017. offset -= ReservedBytesForJump;
  1018. }
  1019. if (jump.IsConditional)
  1020. {
  1021. jump.InstSize = GetJccLength(offset);
  1022. }
  1023. else
  1024. {
  1025. jump.InstSize = GetJmpLength(offset);
  1026. }
  1027. // The jump is relative to the next instruction, not the current one.
  1028. // Since we didn't know the next instruction address when calculating
  1029. // the offset (as the size of the current jump instruction was not known),
  1030. // we now need to compensate the offset with the jump instruction size.
  1031. // It's also worth noting that:
  1032. // - This is only needed for backward jumps.
  1033. // - The GetJmpLength and GetJccLength also compensates the offset
  1034. // internally when computing the jump instruction size.
  1035. if (offset < 0)
  1036. {
  1037. offset -= jump.InstSize;
  1038. }
  1039. if (jump.Offset != offset)
  1040. {
  1041. jump.Offset = offset;
  1042. modified = true;
  1043. }
  1044. }
  1045. }
  1046. while (modified);
  1047. // Write the code, ignoring the dummy bytes after jumps, into a new stream.
  1048. _stream.Seek(0, SeekOrigin.Begin);
  1049. using var codeStream = MemoryStreamManager.Shared.GetStream();
  1050. var assembler = new Assembler(codeStream, HasRelocs);
  1051. bool hasRelocs = HasRelocs;
  1052. int relocIndex = 0;
  1053. int relocOffset = 0;
  1054. var relocEntries = hasRelocs
  1055. ? new RelocEntry[relocs.Length]
  1056. : Array.Empty<RelocEntry>();
  1057. for (int i = 0; i < jumps.Length; i++)
  1058. {
  1059. ref Jump jump = ref jumps[i];
  1060. // If has relocations, calculate their new positions compensating for jumps.
  1061. if (hasRelocs)
  1062. {
  1063. relocOffset += jump.InstSize - ReservedBytesForJump;
  1064. for (; relocIndex < relocEntries.Length; relocIndex++)
  1065. {
  1066. ref Reloc reloc = ref relocs[relocIndex];
  1067. if (reloc.JumpIndex > i)
  1068. {
  1069. break;
  1070. }
  1071. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1072. }
  1073. }
  1074. Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
  1075. _stream.Read(buffer);
  1076. _stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
  1077. codeStream.Write(buffer);
  1078. if (jump.IsConditional)
  1079. {
  1080. assembler.Jcc(jump.Condition, jump.Offset);
  1081. }
  1082. else
  1083. {
  1084. assembler.Jmp(jump.Offset);
  1085. }
  1086. }
  1087. // Write remaining relocations. This case happens when there are no jumps assembled.
  1088. for (; relocIndex < relocEntries.Length; relocIndex++)
  1089. {
  1090. ref Reloc reloc = ref relocs[relocIndex];
  1091. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1092. }
  1093. _stream.CopyTo(codeStream);
  1094. var code = codeStream.ToArray();
  1095. var relocInfo = new RelocInfo(relocEntries);
  1096. return (code, relocInfo);
  1097. }
  1098. private static bool Is64Bits(OperandType type)
  1099. {
  1100. return type == OperandType.I64 || type == OperandType.FP64;
  1101. }
  1102. private static bool IsImm8(ulong immediate, OperandType type)
  1103. {
  1104. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1105. return ConstFitsOnS8(value);
  1106. }
  1107. private static bool IsImm32(ulong immediate, OperandType type)
  1108. {
  1109. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1110. return ConstFitsOnS32(value);
  1111. }
  1112. private static int GetJccLength(long offset)
  1113. {
  1114. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1115. {
  1116. return 2;
  1117. }
  1118. else if (ConstFitsOnS32(offset < 0 ? offset - 6 : offset))
  1119. {
  1120. return 6;
  1121. }
  1122. else
  1123. {
  1124. throw new ArgumentOutOfRangeException(nameof(offset));
  1125. }
  1126. }
  1127. private static int GetJmpLength(long offset)
  1128. {
  1129. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1130. {
  1131. return 2;
  1132. }
  1133. else if (ConstFitsOnS32(offset < 0 ? offset - 5 : offset))
  1134. {
  1135. return 5;
  1136. }
  1137. else
  1138. {
  1139. throw new ArgumentOutOfRangeException(nameof(offset));
  1140. }
  1141. }
  1142. private static bool ConstFitsOnS8(long value)
  1143. {
  1144. return value == (sbyte)value;
  1145. }
  1146. private static bool ConstFitsOnS32(long value)
  1147. {
  1148. return value == (int)value;
  1149. }
  1150. private void WriteInt16(short value)
  1151. {
  1152. WriteUInt16((ushort)value);
  1153. }
  1154. private void WriteInt32(int value)
  1155. {
  1156. WriteUInt32((uint)value);
  1157. }
  1158. private void WriteByte(byte value)
  1159. {
  1160. _stream.WriteByte(value);
  1161. }
  1162. private void WriteUInt16(ushort value)
  1163. {
  1164. _stream.WriteByte((byte)(value >> 0));
  1165. _stream.WriteByte((byte)(value >> 8));
  1166. }
  1167. private void WriteUInt32(uint value)
  1168. {
  1169. _stream.WriteByte((byte)(value >> 0));
  1170. _stream.WriteByte((byte)(value >> 8));
  1171. _stream.WriteByte((byte)(value >> 16));
  1172. _stream.WriteByte((byte)(value >> 24));
  1173. }
  1174. private void WriteUInt64(ulong value)
  1175. {
  1176. _stream.WriteByte((byte)(value >> 0));
  1177. _stream.WriteByte((byte)(value >> 8));
  1178. _stream.WriteByte((byte)(value >> 16));
  1179. _stream.WriteByte((byte)(value >> 24));
  1180. _stream.WriteByte((byte)(value >> 32));
  1181. _stream.WriteByte((byte)(value >> 40));
  1182. _stream.WriteByte((byte)(value >> 48));
  1183. _stream.WriteByte((byte)(value >> 56));
  1184. }
  1185. }
  1186. }