Assembler.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449
  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 Mov(Operand dest, Operand source, OperandType type)
  290. {
  291. WriteInstruction(dest, source, type, X86Instruction.Mov);
  292. }
  293. public void Mov16(Operand dest, Operand source)
  294. {
  295. WriteInstruction(dest, source, OperandType.None, X86Instruction.Mov16);
  296. }
  297. public void Mov8(Operand dest, Operand source)
  298. {
  299. WriteInstruction(dest, source, OperandType.None, X86Instruction.Mov8);
  300. }
  301. public void Movd(Operand dest, Operand source)
  302. {
  303. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd];
  304. if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
  305. {
  306. WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRRM, rrm: true);
  307. }
  308. else
  309. {
  310. WriteOpCode(dest, default, source, OperandType.None, info.Flags, info.OpRMR);
  311. }
  312. }
  313. public void Movdqu(Operand dest, Operand source)
  314. {
  315. WriteInstruction(dest, default, source, X86Instruction.Movdqu);
  316. }
  317. public void Movhlps(Operand dest, Operand src1, Operand src2)
  318. {
  319. WriteInstruction(dest, src1, src2, X86Instruction.Movhlps);
  320. }
  321. public void Movlhps(Operand dest, Operand src1, Operand src2)
  322. {
  323. WriteInstruction(dest, src1, src2, X86Instruction.Movlhps);
  324. }
  325. public void Movq(Operand dest, Operand source)
  326. {
  327. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Movd];
  328. InstructionFlags flags = info.Flags | InstructionFlags.RexW;
  329. if (source.Type.IsInteger() || source.Kind == OperandKind.Memory)
  330. {
  331. WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRRM, rrm: true);
  332. }
  333. else if (dest.Type.IsInteger() || dest.Kind == OperandKind.Memory)
  334. {
  335. WriteOpCode(dest, default, source, OperandType.None, flags, info.OpRMR);
  336. }
  337. else
  338. {
  339. WriteInstruction(dest, source, OperandType.None, X86Instruction.Movq);
  340. }
  341. }
  342. public void Movsd(Operand dest, Operand src1, Operand src2)
  343. {
  344. WriteInstruction(dest, src1, src2, X86Instruction.Movsd);
  345. }
  346. public void Movss(Operand dest, Operand src1, Operand src2)
  347. {
  348. WriteInstruction(dest, src1, src2, X86Instruction.Movss);
  349. }
  350. public void Movsx16(Operand dest, Operand source, OperandType type)
  351. {
  352. WriteInstruction(dest, source, type, X86Instruction.Movsx16);
  353. }
  354. public void Movsx32(Operand dest, Operand source, OperandType type)
  355. {
  356. WriteInstruction(dest, source, type, X86Instruction.Movsx32);
  357. }
  358. public void Movsx8(Operand dest, Operand source, OperandType type)
  359. {
  360. WriteInstruction(dest, source, type, X86Instruction.Movsx8);
  361. }
  362. public void Movzx16(Operand dest, Operand source, OperandType type)
  363. {
  364. WriteInstruction(dest, source, type, X86Instruction.Movzx16);
  365. }
  366. public void Movzx8(Operand dest, Operand source, OperandType type)
  367. {
  368. WriteInstruction(dest, source, type, X86Instruction.Movzx8);
  369. }
  370. public void Mul(Operand source)
  371. {
  372. WriteInstruction(default, source, source.Type, X86Instruction.Mul128);
  373. }
  374. public void Mulsd(Operand dest, Operand src1, Operand src2)
  375. {
  376. WriteInstruction(dest, src1, src2, X86Instruction.Mulsd);
  377. }
  378. public void Mulss(Operand dest, Operand src1, Operand src2)
  379. {
  380. WriteInstruction(dest, src1, src2, X86Instruction.Mulss);
  381. }
  382. public void Neg(Operand dest)
  383. {
  384. WriteInstruction(dest, default, dest.Type, X86Instruction.Neg);
  385. }
  386. public void Not(Operand dest)
  387. {
  388. WriteInstruction(dest, default, dest.Type, X86Instruction.Not);
  389. }
  390. public void Or(Operand dest, Operand source, OperandType type)
  391. {
  392. WriteInstruction(dest, source, type, X86Instruction.Or);
  393. }
  394. public void Pclmulqdq(Operand dest, Operand source, byte imm)
  395. {
  396. WriteInstruction(dest, default, source, X86Instruction.Pclmulqdq);
  397. WriteByte(imm);
  398. }
  399. public void Pcmpeqw(Operand dest, Operand src1, Operand src2)
  400. {
  401. WriteInstruction(dest, src1, src2, X86Instruction.Pcmpeqw);
  402. }
  403. public void Pextrb(Operand dest, Operand source, byte imm)
  404. {
  405. WriteInstruction(dest, default, source, X86Instruction.Pextrb);
  406. WriteByte(imm);
  407. }
  408. public void Pextrd(Operand dest, Operand source, byte imm)
  409. {
  410. WriteInstruction(dest, default, source, X86Instruction.Pextrd);
  411. WriteByte(imm);
  412. }
  413. public void Pextrq(Operand dest, Operand source, byte imm)
  414. {
  415. WriteInstruction(dest, default, source, X86Instruction.Pextrq);
  416. WriteByte(imm);
  417. }
  418. public void Pextrw(Operand dest, Operand source, byte imm)
  419. {
  420. WriteInstruction(dest, default, source, X86Instruction.Pextrw);
  421. WriteByte(imm);
  422. }
  423. public void Pinsrb(Operand dest, Operand src1, Operand src2, byte imm)
  424. {
  425. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrb);
  426. WriteByte(imm);
  427. }
  428. public void Pinsrd(Operand dest, Operand src1, Operand src2, byte imm)
  429. {
  430. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrd);
  431. WriteByte(imm);
  432. }
  433. public void Pinsrq(Operand dest, Operand src1, Operand src2, byte imm)
  434. {
  435. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrq);
  436. WriteByte(imm);
  437. }
  438. public void Pinsrw(Operand dest, Operand src1, Operand src2, byte imm)
  439. {
  440. WriteInstruction(dest, src1, src2, X86Instruction.Pinsrw);
  441. WriteByte(imm);
  442. }
  443. public void Pop(Operand dest)
  444. {
  445. if (dest.Kind == OperandKind.Register)
  446. {
  447. WriteCompactInst(dest, 0x58);
  448. }
  449. else
  450. {
  451. WriteInstruction(dest, default, dest.Type, X86Instruction.Pop);
  452. }
  453. }
  454. public void Popcnt(Operand dest, Operand source, OperandType type)
  455. {
  456. WriteInstruction(dest, source, type, X86Instruction.Popcnt);
  457. }
  458. public void Pshufd(Operand dest, Operand source, byte imm)
  459. {
  460. WriteInstruction(dest, default, source, X86Instruction.Pshufd);
  461. WriteByte(imm);
  462. }
  463. public void Push(Operand source)
  464. {
  465. if (source.Kind == OperandKind.Register)
  466. {
  467. WriteCompactInst(source, 0x50);
  468. }
  469. else
  470. {
  471. WriteInstruction(default, source, source.Type, X86Instruction.Push);
  472. }
  473. }
  474. public void Return()
  475. {
  476. WriteByte(0xc3);
  477. }
  478. public void Ror(Operand dest, Operand source, OperandType type)
  479. {
  480. WriteShiftInst(dest, source, type, X86Instruction.Ror);
  481. }
  482. public void Sar(Operand dest, Operand source, OperandType type)
  483. {
  484. WriteShiftInst(dest, source, type, X86Instruction.Sar);
  485. }
  486. public void Shl(Operand dest, Operand source, OperandType type)
  487. {
  488. WriteShiftInst(dest, source, type, X86Instruction.Shl);
  489. }
  490. public void Shr(Operand dest, Operand source, OperandType type)
  491. {
  492. WriteShiftInst(dest, source, type, X86Instruction.Shr);
  493. }
  494. public void Setcc(Operand dest, X86Condition condition)
  495. {
  496. ref readonly InstructionInfo info = ref _instTable[(int)X86Instruction.Setcc];
  497. WriteOpCode(dest, default, default, OperandType.None, info.Flags, info.OpRRM | (int)condition);
  498. }
  499. public void Stmxcsr(Operand dest)
  500. {
  501. WriteInstruction(dest, default, OperandType.I32, X86Instruction.Stmxcsr);
  502. }
  503. public void Sub(Operand dest, Operand source, OperandType type)
  504. {
  505. WriteInstruction(dest, source, type, X86Instruction.Sub);
  506. }
  507. public void Subsd(Operand dest, Operand src1, Operand src2)
  508. {
  509. WriteInstruction(dest, src1, src2, X86Instruction.Subsd);
  510. }
  511. public void Subss(Operand dest, Operand src1, Operand src2)
  512. {
  513. WriteInstruction(dest, src1, src2, X86Instruction.Subss);
  514. }
  515. public void Test(Operand src1, Operand src2, OperandType type)
  516. {
  517. WriteInstruction(src1, src2, type, X86Instruction.Test);
  518. }
  519. public void Xor(Operand dest, Operand source, OperandType type)
  520. {
  521. WriteInstruction(dest, source, type, X86Instruction.Xor);
  522. }
  523. public void Xorps(Operand dest, Operand src1, Operand src2)
  524. {
  525. WriteInstruction(dest, src1, src2, X86Instruction.Xorps);
  526. }
  527. public void WriteInstruction(
  528. X86Instruction inst,
  529. Operand dest,
  530. Operand source,
  531. OperandType type = OperandType.None)
  532. {
  533. WriteInstruction(dest, default, source, inst, type);
  534. }
  535. public void WriteInstruction(X86Instruction inst, Operand dest, Operand src1, Operand src2)
  536. {
  537. if (src2.Kind == OperandKind.Constant)
  538. {
  539. WriteInstruction(src1, dest, src2, inst);
  540. }
  541. else
  542. {
  543. WriteInstruction(dest, src1, src2, inst);
  544. }
  545. }
  546. public void WriteInstruction(
  547. X86Instruction inst,
  548. Operand dest,
  549. Operand src1,
  550. Operand src2,
  551. OperandType type)
  552. {
  553. WriteInstruction(dest, src1, src2, inst, type);
  554. }
  555. public void WriteInstruction(X86Instruction inst, Operand dest, Operand source, byte imm)
  556. {
  557. WriteInstruction(dest, default, source, inst);
  558. WriteByte(imm);
  559. }
  560. public void WriteInstruction(
  561. X86Instruction inst,
  562. Operand dest,
  563. Operand src1,
  564. Operand src2,
  565. Operand src3)
  566. {
  567. // 3+ operands can only be encoded with the VEX encoding scheme.
  568. Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
  569. WriteInstruction(dest, src1, src2, inst);
  570. WriteByte((byte)(src3.AsByte() << 4));
  571. }
  572. public void WriteInstruction(
  573. X86Instruction inst,
  574. Operand dest,
  575. Operand src1,
  576. Operand src2,
  577. byte imm)
  578. {
  579. WriteInstruction(dest, src1, src2, inst);
  580. WriteByte(imm);
  581. }
  582. private void WriteShiftInst(Operand dest, Operand source, OperandType type, X86Instruction inst)
  583. {
  584. if (source.Kind == OperandKind.Register)
  585. {
  586. X86Register shiftReg = (X86Register)source.GetRegister().Index;
  587. Debug.Assert(shiftReg == X86Register.Rcx, $"Invalid shift register \"{shiftReg}\".");
  588. source = default;
  589. }
  590. else if (source.Kind == OperandKind.Constant)
  591. {
  592. source = Operand.Factory.Const((int)source.Value & (dest.Type == OperandType.I32 ? 0x1f : 0x3f));
  593. }
  594. WriteInstruction(dest, source, type, inst);
  595. }
  596. private void WriteInstruction(Operand dest, Operand source, OperandType type, X86Instruction inst)
  597. {
  598. ref readonly InstructionInfo info = ref _instTable[(int)inst];
  599. if (source != default)
  600. {
  601. if (source.Kind == OperandKind.Constant)
  602. {
  603. ulong imm = source.Value;
  604. if (inst == X86Instruction.Mov8)
  605. {
  606. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm8);
  607. WriteByte((byte)imm);
  608. }
  609. else if (inst == X86Instruction.Mov16)
  610. {
  611. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm32);
  612. WriteInt16((short)imm);
  613. }
  614. else if (IsImm8(imm, type) && info.OpRMImm8 != BadOp)
  615. {
  616. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm8);
  617. WriteByte((byte)imm);
  618. }
  619. else if (!source.Relocatable && IsImm32(imm, type) && info.OpRMImm32 != BadOp)
  620. {
  621. WriteOpCode(dest, default, default, type, info.Flags, info.OpRMImm32);
  622. WriteInt32((int)imm);
  623. }
  624. else if (dest != default && dest.Kind == OperandKind.Register && info.OpRImm64 != BadOp)
  625. {
  626. int rexPrefix = GetRexPrefix(dest, source, type, rrm: false);
  627. if (rexPrefix != 0)
  628. {
  629. WriteByte((byte)rexPrefix);
  630. }
  631. WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111)));
  632. if (HasRelocs && source.Relocatable)
  633. {
  634. _relocs.Add(new Reloc
  635. {
  636. JumpIndex = _jumps.Count - 1,
  637. Position = (int)_stream.Position,
  638. Symbol = source.Symbol
  639. });
  640. }
  641. WriteUInt64(imm);
  642. }
  643. else
  644. {
  645. throw new ArgumentException($"Failed to encode constant 0x{imm:X}.");
  646. }
  647. }
  648. else if (source.Kind == OperandKind.Register && info.OpRMR != BadOp)
  649. {
  650. WriteOpCode(dest, default, source, type, info.Flags, info.OpRMR);
  651. }
  652. else if (info.OpRRM != BadOp)
  653. {
  654. WriteOpCode(dest, default, source, type, info.Flags, info.OpRRM, rrm: true);
  655. }
  656. else
  657. {
  658. throw new ArgumentException($"Invalid source operand kind \"{source.Kind}\".");
  659. }
  660. }
  661. else if (info.OpRRM != BadOp)
  662. {
  663. WriteOpCode(dest, default, source, type, info.Flags, info.OpRRM, rrm: true);
  664. }
  665. else if (info.OpRMR != BadOp)
  666. {
  667. WriteOpCode(dest, default, source, type, info.Flags, info.OpRMR);
  668. }
  669. else
  670. {
  671. throw new ArgumentNullException(nameof(source));
  672. }
  673. }
  674. private void WriteInstruction(
  675. Operand dest,
  676. Operand src1,
  677. Operand src2,
  678. X86Instruction inst,
  679. OperandType type = OperandType.None)
  680. {
  681. ref readonly InstructionInfo info = ref _instTable[(int)inst];
  682. if (src2 != default)
  683. {
  684. if (src2.Kind == OperandKind.Constant)
  685. {
  686. ulong imm = src2.Value;
  687. if ((byte)imm == imm && info.OpRMImm8 != BadOp)
  688. {
  689. WriteOpCode(dest, src1, default, type, info.Flags, info.OpRMImm8);
  690. WriteByte((byte)imm);
  691. }
  692. else
  693. {
  694. throw new ArgumentException($"Failed to encode constant 0x{imm:X}.");
  695. }
  696. }
  697. else if (src2.Kind == OperandKind.Register && info.OpRMR != BadOp)
  698. {
  699. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRMR);
  700. }
  701. else if (info.OpRRM != BadOp)
  702. {
  703. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRRM, rrm: true);
  704. }
  705. else
  706. {
  707. throw new ArgumentException($"Invalid source operand kind \"{src2.Kind}\".");
  708. }
  709. }
  710. else if (info.OpRRM != BadOp)
  711. {
  712. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRRM, rrm: true);
  713. }
  714. else if (info.OpRMR != BadOp)
  715. {
  716. WriteOpCode(dest, src1, src2, type, info.Flags, info.OpRMR);
  717. }
  718. else
  719. {
  720. throw new ArgumentNullException(nameof(src2));
  721. }
  722. }
  723. private void WriteOpCode(
  724. Operand dest,
  725. Operand src1,
  726. Operand src2,
  727. OperandType type,
  728. InstructionFlags flags,
  729. int opCode,
  730. bool rrm = false)
  731. {
  732. int rexPrefix = GetRexPrefix(dest, src2, type, rrm);
  733. if ((flags & InstructionFlags.RexW) != 0)
  734. {
  735. rexPrefix |= RexWPrefix;
  736. }
  737. int modRM = (opCode >> OpModRMBits) << 3;
  738. MemoryOperand memOp = default;
  739. bool hasMemOp = false;
  740. if (dest != default)
  741. {
  742. if (dest.Kind == OperandKind.Register)
  743. {
  744. int regIndex = dest.GetRegister().Index;
  745. modRM |= (regIndex & 0b111) << (rrm ? 3 : 0);
  746. if ((flags & InstructionFlags.Reg8Dest) != 0 && regIndex >= 4)
  747. {
  748. rexPrefix |= RexPrefix;
  749. }
  750. }
  751. else if (dest.Kind == OperandKind.Memory)
  752. {
  753. memOp = dest.GetMemory();
  754. hasMemOp = true;
  755. }
  756. else
  757. {
  758. throw new ArgumentException("Invalid destination operand kind \"" + dest.Kind + "\".");
  759. }
  760. }
  761. if (src2 != default)
  762. {
  763. if (src2.Kind == OperandKind.Register)
  764. {
  765. int regIndex = src2.GetRegister().Index;
  766. modRM |= (regIndex & 0b111) << (rrm ? 0 : 3);
  767. if ((flags & InstructionFlags.Reg8Src) != 0 && regIndex >= 4)
  768. {
  769. rexPrefix |= RexPrefix;
  770. }
  771. }
  772. else if (src2.Kind == OperandKind.Memory && !hasMemOp)
  773. {
  774. memOp = src2.GetMemory();
  775. hasMemOp = true;
  776. }
  777. else
  778. {
  779. throw new ArgumentException("Invalid source operand kind \"" + src2.Kind + "\".");
  780. }
  781. }
  782. bool needsSibByte = false;
  783. bool needsDisplacement = false;
  784. int sib = 0;
  785. if (hasMemOp)
  786. {
  787. // Either source or destination is a memory operand.
  788. Register baseReg = memOp.BaseAddress.GetRegister();
  789. X86Register baseRegLow = (X86Register)(baseReg.Index & 0b111);
  790. needsSibByte = memOp.Index != default || baseRegLow == X86Register.Rsp;
  791. needsDisplacement = memOp.Displacement != 0 || baseRegLow == X86Register.Rbp;
  792. if (needsDisplacement)
  793. {
  794. if (ConstFitsOnS8(memOp.Displacement))
  795. {
  796. modRM |= 0x40;
  797. }
  798. else /* if (ConstFitsOnS32(memOp.Displacement)) */
  799. {
  800. modRM |= 0x80;
  801. }
  802. }
  803. if (baseReg.Index >= 8)
  804. {
  805. Debug.Assert((uint)baseReg.Index <= MaxRegNumber);
  806. rexPrefix |= RexPrefix | (baseReg.Index >> 3);
  807. }
  808. if (needsSibByte)
  809. {
  810. sib = (int)baseRegLow;
  811. if (memOp.Index != default)
  812. {
  813. int indexReg = memOp.Index.GetRegister().Index;
  814. Debug.Assert(indexReg != (int)X86Register.Rsp, "Using RSP as index register on the memory operand is not allowed.");
  815. if (indexReg >= 8)
  816. {
  817. Debug.Assert((uint)indexReg <= MaxRegNumber);
  818. rexPrefix |= RexPrefix | (indexReg >> 3) << 1;
  819. }
  820. sib |= (indexReg & 0b111) << 3;
  821. }
  822. else
  823. {
  824. sib |= 0b100 << 3;
  825. }
  826. sib |= (int)memOp.Scale << 6;
  827. modRM |= 0b100;
  828. }
  829. else
  830. {
  831. modRM |= (int)baseRegLow;
  832. }
  833. }
  834. else
  835. {
  836. // Source and destination are registers.
  837. modRM |= 0xc0;
  838. }
  839. Debug.Assert(opCode != BadOp, "Invalid opcode value.");
  840. if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
  841. {
  842. // 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.
  843. int vexByte2 = (flags & InstructionFlags.PrefixMask) switch
  844. {
  845. InstructionFlags.Prefix66 => 1,
  846. InstructionFlags.PrefixF3 => 2,
  847. InstructionFlags.PrefixF2 => 3,
  848. _ => 0
  849. };
  850. if (src1 != default)
  851. {
  852. vexByte2 |= (src1.GetRegister().Index ^ 0xf) << 3;
  853. }
  854. else
  855. {
  856. vexByte2 |= 0b1111 << 3;
  857. }
  858. ushort opCodeHigh = (ushort)(opCode >> 8);
  859. if ((rexPrefix & 0b1011) == 0 && opCodeHigh == 0xf)
  860. {
  861. // Two-byte form.
  862. WriteByte(0xc5);
  863. vexByte2 |= (~rexPrefix & 4) << 5;
  864. WriteByte((byte)vexByte2);
  865. }
  866. else
  867. {
  868. // Three-byte form.
  869. WriteByte(0xc4);
  870. int vexByte1 = (~rexPrefix & 7) << 5;
  871. switch (opCodeHigh)
  872. {
  873. case 0xf: vexByte1 |= 1; break;
  874. case 0xf38: vexByte1 |= 2; break;
  875. case 0xf3a: vexByte1 |= 3; break;
  876. default: Debug.Assert(false, $"Failed to VEX encode opcode 0x{opCode:X}."); break;
  877. }
  878. vexByte2 |= (rexPrefix & 8) << 4;
  879. WriteByte((byte)vexByte1);
  880. WriteByte((byte)vexByte2);
  881. }
  882. opCode &= 0xff;
  883. }
  884. else
  885. {
  886. if (flags.HasFlag(InstructionFlags.Prefix66))
  887. {
  888. WriteByte(0x66);
  889. }
  890. if (flags.HasFlag(InstructionFlags.PrefixF2))
  891. {
  892. WriteByte(0xf2);
  893. }
  894. if (flags.HasFlag(InstructionFlags.PrefixF3))
  895. {
  896. WriteByte(0xf3);
  897. }
  898. if (rexPrefix != 0)
  899. {
  900. WriteByte((byte)rexPrefix);
  901. }
  902. }
  903. if (dest != default && (flags & InstructionFlags.RegOnly) != 0)
  904. {
  905. opCode += dest.GetRegister().Index & 7;
  906. }
  907. if ((opCode & 0xff0000) != 0)
  908. {
  909. WriteByte((byte)(opCode >> 16));
  910. }
  911. if ((opCode & 0xff00) != 0)
  912. {
  913. WriteByte((byte)(opCode >> 8));
  914. }
  915. WriteByte((byte)opCode);
  916. if ((flags & InstructionFlags.RegOnly) == 0)
  917. {
  918. WriteByte((byte)modRM);
  919. if (needsSibByte)
  920. {
  921. WriteByte((byte)sib);
  922. }
  923. if (needsDisplacement)
  924. {
  925. if (ConstFitsOnS8(memOp.Displacement))
  926. {
  927. WriteByte((byte)memOp.Displacement);
  928. }
  929. else /* if (ConstFitsOnS32(memOp.Displacement)) */
  930. {
  931. WriteInt32(memOp.Displacement);
  932. }
  933. }
  934. }
  935. }
  936. private void WriteCompactInst(Operand operand, int opCode)
  937. {
  938. int regIndex = operand.GetRegister().Index;
  939. if (regIndex >= 8)
  940. {
  941. WriteByte(0x41);
  942. }
  943. WriteByte((byte)(opCode + (regIndex & 0b111)));
  944. }
  945. private static int GetRexPrefix(Operand dest, Operand source, OperandType type, bool rrm)
  946. {
  947. int rexPrefix = 0;
  948. if (Is64Bits(type))
  949. {
  950. rexPrefix = RexWPrefix;
  951. }
  952. void SetRegisterHighBit(Register reg, int bit)
  953. {
  954. if (reg.Index >= 8)
  955. {
  956. rexPrefix |= RexPrefix | (reg.Index >> 3) << bit;
  957. }
  958. }
  959. if (dest != default && dest.Kind == OperandKind.Register)
  960. {
  961. SetRegisterHighBit(dest.GetRegister(), rrm ? 2 : 0);
  962. }
  963. if (source != default && source.Kind == OperandKind.Register)
  964. {
  965. SetRegisterHighBit(source.GetRegister(), rrm ? 0 : 2);
  966. }
  967. return rexPrefix;
  968. }
  969. public (byte[], RelocInfo) GetCode()
  970. {
  971. var jumps = CollectionsMarshal.AsSpan(_jumps);
  972. var relocs = CollectionsMarshal.AsSpan(_relocs);
  973. // Write jump relative offsets.
  974. bool modified;
  975. do
  976. {
  977. modified = false;
  978. for (int i = 0; i < jumps.Length; i++)
  979. {
  980. ref Jump jump = ref jumps[i];
  981. // If jump target not resolved yet, resolve it.
  982. if (jump.JumpTarget == null)
  983. {
  984. jump.JumpTarget = _labels[jump.JumpLabel];
  985. }
  986. long jumpTarget = jump.JumpTarget.Value;
  987. long offset = jumpTarget - jump.JumpPosition;
  988. if (offset < 0)
  989. {
  990. for (int j = i - 1; j >= 0; j--)
  991. {
  992. ref Jump jump2 = ref jumps[j];
  993. if (jump2.JumpPosition < jumpTarget)
  994. {
  995. break;
  996. }
  997. offset -= jump2.InstSize - ReservedBytesForJump;
  998. }
  999. }
  1000. else
  1001. {
  1002. for (int j = i + 1; j < jumps.Length; j++)
  1003. {
  1004. ref Jump jump2 = ref jumps[j];
  1005. if (jump2.JumpPosition >= jumpTarget)
  1006. {
  1007. break;
  1008. }
  1009. offset += jump2.InstSize - ReservedBytesForJump;
  1010. }
  1011. offset -= ReservedBytesForJump;
  1012. }
  1013. if (jump.IsConditional)
  1014. {
  1015. jump.InstSize = GetJccLength(offset);
  1016. }
  1017. else
  1018. {
  1019. jump.InstSize = GetJmpLength(offset);
  1020. }
  1021. // The jump is relative to the next instruction, not the current one.
  1022. // Since we didn't know the next instruction address when calculating
  1023. // the offset (as the size of the current jump instruction was not known),
  1024. // we now need to compensate the offset with the jump instruction size.
  1025. // It's also worth noting that:
  1026. // - This is only needed for backward jumps.
  1027. // - The GetJmpLength and GetJccLength also compensates the offset
  1028. // internally when computing the jump instruction size.
  1029. if (offset < 0)
  1030. {
  1031. offset -= jump.InstSize;
  1032. }
  1033. if (jump.Offset != offset)
  1034. {
  1035. jump.Offset = offset;
  1036. modified = true;
  1037. }
  1038. }
  1039. }
  1040. while (modified);
  1041. // Write the code, ignoring the dummy bytes after jumps, into a new stream.
  1042. _stream.Seek(0, SeekOrigin.Begin);
  1043. using var codeStream = new MemoryStream();
  1044. var assembler = new Assembler(codeStream, HasRelocs);
  1045. bool hasRelocs = HasRelocs;
  1046. int relocIndex = 0;
  1047. int relocOffset = 0;
  1048. var relocEntries = hasRelocs
  1049. ? new RelocEntry[relocs.Length]
  1050. : Array.Empty<RelocEntry>();
  1051. for (int i = 0; i < jumps.Length; i++)
  1052. {
  1053. ref Jump jump = ref jumps[i];
  1054. // If has relocations, calculate their new positions compensating for jumps.
  1055. if (hasRelocs)
  1056. {
  1057. relocOffset += jump.InstSize - ReservedBytesForJump;
  1058. for (; relocIndex < relocEntries.Length; relocIndex++)
  1059. {
  1060. ref Reloc reloc = ref relocs[relocIndex];
  1061. if (reloc.JumpIndex > i)
  1062. {
  1063. break;
  1064. }
  1065. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1066. }
  1067. }
  1068. Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
  1069. _stream.Read(buffer);
  1070. _stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
  1071. codeStream.Write(buffer);
  1072. if (jump.IsConditional)
  1073. {
  1074. assembler.Jcc(jump.Condition, jump.Offset);
  1075. }
  1076. else
  1077. {
  1078. assembler.Jmp(jump.Offset);
  1079. }
  1080. }
  1081. // Write remaining relocations. This case happens when there are no jumps assembled.
  1082. for (; relocIndex < relocEntries.Length; relocIndex++)
  1083. {
  1084. ref Reloc reloc = ref relocs[relocIndex];
  1085. relocEntries[relocIndex] = new RelocEntry(reloc.Position + relocOffset, reloc.Symbol);
  1086. }
  1087. _stream.CopyTo(codeStream);
  1088. var code = codeStream.ToArray();
  1089. var relocInfo = new RelocInfo(relocEntries);
  1090. return (code, relocInfo);
  1091. }
  1092. private static bool Is64Bits(OperandType type)
  1093. {
  1094. return type == OperandType.I64 || type == OperandType.FP64;
  1095. }
  1096. private static bool IsImm8(ulong immediate, OperandType type)
  1097. {
  1098. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1099. return ConstFitsOnS8(value);
  1100. }
  1101. private static bool IsImm32(ulong immediate, OperandType type)
  1102. {
  1103. long value = type == OperandType.I32 ? (int)immediate : (long)immediate;
  1104. return ConstFitsOnS32(value);
  1105. }
  1106. private static int GetJccLength(long offset)
  1107. {
  1108. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1109. {
  1110. return 2;
  1111. }
  1112. else if (ConstFitsOnS32(offset < 0 ? offset - 6 : offset))
  1113. {
  1114. return 6;
  1115. }
  1116. else
  1117. {
  1118. throw new ArgumentOutOfRangeException(nameof(offset));
  1119. }
  1120. }
  1121. private static int GetJmpLength(long offset)
  1122. {
  1123. if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
  1124. {
  1125. return 2;
  1126. }
  1127. else if (ConstFitsOnS32(offset < 0 ? offset - 5 : offset))
  1128. {
  1129. return 5;
  1130. }
  1131. else
  1132. {
  1133. throw new ArgumentOutOfRangeException(nameof(offset));
  1134. }
  1135. }
  1136. private static bool ConstFitsOnS8(long value)
  1137. {
  1138. return value == (sbyte)value;
  1139. }
  1140. private static bool ConstFitsOnS32(long value)
  1141. {
  1142. return value == (int)value;
  1143. }
  1144. private void WriteInt16(short value)
  1145. {
  1146. WriteUInt16((ushort)value);
  1147. }
  1148. private void WriteInt32(int value)
  1149. {
  1150. WriteUInt32((uint)value);
  1151. }
  1152. private void WriteByte(byte value)
  1153. {
  1154. _stream.WriteByte(value);
  1155. }
  1156. private void WriteUInt16(ushort value)
  1157. {
  1158. _stream.WriteByte((byte)(value >> 0));
  1159. _stream.WriteByte((byte)(value >> 8));
  1160. }
  1161. private void WriteUInt32(uint value)
  1162. {
  1163. _stream.WriteByte((byte)(value >> 0));
  1164. _stream.WriteByte((byte)(value >> 8));
  1165. _stream.WriteByte((byte)(value >> 16));
  1166. _stream.WriteByte((byte)(value >> 24));
  1167. }
  1168. private void WriteUInt64(ulong value)
  1169. {
  1170. _stream.WriteByte((byte)(value >> 0));
  1171. _stream.WriteByte((byte)(value >> 8));
  1172. _stream.WriteByte((byte)(value >> 16));
  1173. _stream.WriteByte((byte)(value >> 24));
  1174. _stream.WriteByte((byte)(value >> 32));
  1175. _stream.WriteByte((byte)(value >> 40));
  1176. _stream.WriteByte((byte)(value >> 48));
  1177. _stream.WriteByte((byte)(value >> 56));
  1178. }
  1179. }
  1180. }