Assembler.cs 45 KB

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