macro_jit_x64.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/logging/log.h"
  6. #include "common/microprofile.h"
  7. #include "common/x64/xbyak_util.h"
  8. #include "video_core/engines/maxwell_3d.h"
  9. #include "video_core/macro/macro_interpreter.h"
  10. #include "video_core/macro/macro_jit_x64.h"
  11. MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255, 47));
  12. MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0));
  13. namespace Tegra {
  14. static const Xbyak::Reg64 PARAMETERS = Xbyak::util::r9;
  15. static const Xbyak::Reg64 REGISTERS = Xbyak::util::r10;
  16. static const Xbyak::Reg64 STATE = Xbyak::util::r11;
  17. static const Xbyak::Reg64 NEXT_PARAMETER = Xbyak::util::r12;
  18. static const Xbyak::Reg32 RESULT = Xbyak::util::r13d;
  19. static const Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
  20. static const Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
  21. static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
  22. PARAMETERS,
  23. REGISTERS,
  24. STATE,
  25. NEXT_PARAMETER,
  26. RESULT,
  27. METHOD_ADDRESS,
  28. BRANCH_HOLDER,
  29. });
  30. MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
  31. std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) {
  32. return std::make_unique<MacroJITx64Impl>(maxwell3d, code);
  33. }
  34. MacroJITx64Impl::MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code)
  35. : Xbyak::CodeGenerator(MAX_CODE_SIZE), code(code), maxwell3d(maxwell3d) {
  36. Compile();
  37. }
  38. MacroJITx64Impl::~MacroJITx64Impl() = default;
  39. void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) {
  40. MICROPROFILE_SCOPE(MacroJitExecute);
  41. ASSERT_OR_EXECUTE(program != nullptr, { return; });
  42. JITState state{};
  43. state.maxwell3d = &maxwell3d;
  44. state.registers = {};
  45. state.parameters = parameters.data();
  46. program(&state);
  47. }
  48. void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
  49. const bool is_a_zero = opcode.src_a == 0;
  50. const bool is_b_zero = opcode.src_b == 0;
  51. const bool valid_operation = !is_a_zero && !is_b_zero;
  52. const bool is_move_operation = !is_a_zero && is_b_zero;
  53. const bool has_zero_register = is_a_zero || is_b_zero;
  54. Xbyak::Reg32 src_a;
  55. Xbyak::Reg32 src_b;
  56. if (!optimizer.zero_reg_skip) {
  57. src_a = Compile_GetRegister(opcode.src_a, RESULT);
  58. src_b = Compile_GetRegister(opcode.src_b, ebx);
  59. } else {
  60. if (!is_a_zero) {
  61. src_a = Compile_GetRegister(opcode.src_a, RESULT);
  62. }
  63. if (!is_b_zero) {
  64. src_b = Compile_GetRegister(opcode.src_b, ebx);
  65. }
  66. }
  67. Xbyak::Label skip_carry{};
  68. bool has_emitted = false;
  69. switch (opcode.alu_operation) {
  70. case Macro::ALUOperation::Add:
  71. if (optimizer.zero_reg_skip) {
  72. if (valid_operation) {
  73. add(src_a, src_b);
  74. }
  75. } else {
  76. add(src_a, src_b);
  77. }
  78. if (!optimizer.can_skip_carry) {
  79. setc(byte[STATE + offsetof(JITState, carry_flag)]);
  80. }
  81. break;
  82. case Macro::ALUOperation::AddWithCarry:
  83. bt(dword[STATE + offsetof(JITState, carry_flag)], 0);
  84. adc(src_a, src_b);
  85. setc(byte[STATE + offsetof(JITState, carry_flag)]);
  86. break;
  87. case Macro::ALUOperation::Subtract:
  88. if (optimizer.zero_reg_skip) {
  89. if (valid_operation) {
  90. sub(src_a, src_b);
  91. has_emitted = true;
  92. }
  93. } else {
  94. sub(src_a, src_b);
  95. has_emitted = true;
  96. }
  97. if (!optimizer.can_skip_carry && has_emitted) {
  98. setc(byte[STATE + offsetof(JITState, carry_flag)]);
  99. }
  100. break;
  101. case Macro::ALUOperation::SubtractWithBorrow:
  102. bt(dword[STATE + offsetof(JITState, carry_flag)], 0);
  103. sbb(src_a, src_b);
  104. setc(byte[STATE + offsetof(JITState, carry_flag)]);
  105. break;
  106. case Macro::ALUOperation::Xor:
  107. if (optimizer.zero_reg_skip) {
  108. if (valid_operation) {
  109. xor_(src_a, src_b);
  110. }
  111. } else {
  112. xor_(src_a, src_b);
  113. }
  114. break;
  115. case Macro::ALUOperation::Or:
  116. if (optimizer.zero_reg_skip) {
  117. if (valid_operation) {
  118. or_(src_a, src_b);
  119. }
  120. } else {
  121. or_(src_a, src_b);
  122. }
  123. break;
  124. case Macro::ALUOperation::And:
  125. if (optimizer.zero_reg_skip) {
  126. if (!has_zero_register) {
  127. and_(src_a, src_b);
  128. }
  129. } else {
  130. and_(src_a, src_b);
  131. }
  132. break;
  133. case Macro::ALUOperation::AndNot:
  134. if (optimizer.zero_reg_skip) {
  135. if (!is_a_zero) {
  136. not_(src_b);
  137. and_(src_a, src_b);
  138. }
  139. } else {
  140. not_(src_b);
  141. and_(src_a, src_b);
  142. }
  143. break;
  144. case Macro::ALUOperation::Nand:
  145. if (optimizer.zero_reg_skip) {
  146. if (!is_a_zero) {
  147. and_(src_a, src_b);
  148. not_(src_a);
  149. }
  150. } else {
  151. and_(src_a, src_b);
  152. not_(src_a);
  153. }
  154. break;
  155. default:
  156. UNIMPLEMENTED_MSG("Unimplemented ALU operation {}",
  157. static_cast<std::size_t>(opcode.alu_operation.Value()));
  158. break;
  159. }
  160. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  161. }
  162. void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
  163. if (optimizer.skip_dummy_addimmediate) {
  164. // Games tend to use this as an exit instruction placeholder. It's to encode an instruction
  165. // without doing anything. In our case we can just not emit anything.
  166. if (opcode.result_operation == Macro::ResultOperation::Move && opcode.dst == 0) {
  167. return;
  168. }
  169. }
  170. // Check for redundant moves
  171. if (optimizer.optimize_for_method_move &&
  172. opcode.result_operation == Macro::ResultOperation::MoveAndSetMethod) {
  173. if (next_opcode.has_value()) {
  174. const auto next = *next_opcode;
  175. if (next.result_operation == Macro::ResultOperation::MoveAndSetMethod) {
  176. return;
  177. }
  178. }
  179. }
  180. if (optimizer.zero_reg_skip && opcode.src_a == 0) {
  181. if (opcode.immediate == 0) {
  182. xor_(RESULT, RESULT);
  183. } else {
  184. mov(RESULT, opcode.immediate);
  185. }
  186. } else {
  187. auto result = Compile_GetRegister(opcode.src_a, RESULT);
  188. if (opcode.immediate > 2) {
  189. add(result, opcode.immediate);
  190. } else if (opcode.immediate == 1) {
  191. inc(result);
  192. } else if (opcode.immediate < 0) {
  193. sub(result, opcode.immediate * -1);
  194. }
  195. }
  196. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  197. }
  198. void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
  199. auto dst = Compile_GetRegister(opcode.src_a, RESULT);
  200. auto src = Compile_GetRegister(opcode.src_b, eax);
  201. if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) {
  202. shr(src, opcode.bf_src_bit);
  203. } else if (opcode.bf_src_bit == 31) {
  204. xor_(src, src);
  205. }
  206. // Don't bother masking the whole register since we're using a 32 bit register
  207. if (opcode.bf_size != 31 && opcode.bf_size != 0) {
  208. and_(src, opcode.GetBitfieldMask());
  209. } else if (opcode.bf_size == 0) {
  210. xor_(src, src);
  211. }
  212. if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) {
  213. shl(src, opcode.bf_dst_bit);
  214. } else if (opcode.bf_dst_bit == 31) {
  215. xor_(src, src);
  216. }
  217. const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
  218. if (mask != 0xffffffff) {
  219. and_(dst, mask);
  220. }
  221. or_(dst, src);
  222. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  223. }
  224. void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
  225. auto dst = Compile_GetRegister(opcode.src_a, eax);
  226. auto src = Compile_GetRegister(opcode.src_b, RESULT);
  227. shr(src, al);
  228. if (opcode.bf_size != 0 && opcode.bf_size != 31) {
  229. and_(src, opcode.GetBitfieldMask());
  230. } else if (opcode.bf_size == 0) {
  231. xor_(src, src);
  232. }
  233. if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) {
  234. shl(src, opcode.bf_dst_bit);
  235. } else if (opcode.bf_dst_bit == 31) {
  236. xor_(src, src);
  237. }
  238. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  239. }
  240. void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
  241. auto dst = Compile_GetRegister(opcode.src_a, eax);
  242. auto src = Compile_GetRegister(opcode.src_b, RESULT);
  243. if (opcode.bf_src_bit != 0) {
  244. shr(src, opcode.bf_src_bit);
  245. }
  246. if (opcode.bf_size != 31) {
  247. and_(src, opcode.GetBitfieldMask());
  248. }
  249. shl(src, al);
  250. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  251. }
  252. static u32 Read(Engines::Maxwell3D* maxwell3d, u32 method) {
  253. return maxwell3d->GetRegisterValue(method);
  254. }
  255. static void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
  256. maxwell3d->CallMethodFromMME(method_address.address, value);
  257. }
  258. void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
  259. if (optimizer.zero_reg_skip && opcode.src_a == 0) {
  260. if (opcode.immediate == 0) {
  261. xor_(RESULT, RESULT);
  262. } else {
  263. mov(RESULT, opcode.immediate);
  264. }
  265. } else {
  266. auto result = Compile_GetRegister(opcode.src_a, RESULT);
  267. if (opcode.immediate > 2) {
  268. add(result, opcode.immediate);
  269. } else if (opcode.immediate == 1) {
  270. inc(result);
  271. } else if (opcode.immediate < 0) {
  272. sub(result, opcode.immediate * -1);
  273. }
  274. }
  275. Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0);
  276. mov(Common::X64::ABI_PARAM1, qword[STATE]);
  277. mov(Common::X64::ABI_PARAM2, RESULT);
  278. Common::X64::CallFarFunction(*this, &Read);
  279. Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0);
  280. mov(RESULT, Common::X64::ABI_RETURN.cvt32());
  281. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  282. }
  283. void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
  284. Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0);
  285. mov(Common::X64::ABI_PARAM1, qword[STATE]);
  286. mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS);
  287. mov(Common::X64::ABI_PARAM3, value);
  288. Common::X64::CallFarFunction(*this, &Send);
  289. Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0);
  290. Xbyak::Label dont_process{};
  291. // Get increment
  292. test(METHOD_ADDRESS, 0x3f000);
  293. // If zero, method address doesn't update
  294. je(dont_process);
  295. mov(ecx, METHOD_ADDRESS);
  296. and_(METHOD_ADDRESS, 0xfff);
  297. shr(ecx, 12);
  298. and_(ecx, 0x3f);
  299. lea(eax, ptr[rcx + METHOD_ADDRESS.cvt64()]);
  300. sal(ecx, 12);
  301. or_(eax, ecx);
  302. mov(METHOD_ADDRESS, eax);
  303. L(dont_process);
  304. }
  305. void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
  306. ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
  307. const s32 jump_address =
  308. static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
  309. Xbyak::Label end;
  310. auto value = Compile_GetRegister(opcode.src_a, eax);
  311. test(value, value);
  312. if (optimizer.has_delayed_pc) {
  313. switch (opcode.branch_condition) {
  314. case Macro::BranchCondition::Zero:
  315. jne(end, T_NEAR);
  316. break;
  317. case Macro::BranchCondition::NotZero:
  318. je(end, T_NEAR);
  319. break;
  320. }
  321. if (opcode.branch_annul) {
  322. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  323. jmp(labels[jump_address], T_NEAR);
  324. } else {
  325. Xbyak::Label handle_post_exit{};
  326. Xbyak::Label skip{};
  327. jmp(skip, T_NEAR);
  328. if (opcode.is_exit) {
  329. L(handle_post_exit);
  330. // Execute 1 instruction
  331. mov(BRANCH_HOLDER, end_of_code);
  332. // Jump to next instruction to skip delay slot check
  333. jmp(labels[jump_address], T_NEAR);
  334. } else {
  335. L(handle_post_exit);
  336. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  337. jmp(labels[jump_address], T_NEAR);
  338. }
  339. L(skip);
  340. mov(BRANCH_HOLDER, handle_post_exit);
  341. jmp(delay_skip[pc], T_NEAR);
  342. }
  343. } else {
  344. switch (opcode.branch_condition) {
  345. case Macro::BranchCondition::Zero:
  346. je(labels[jump_address], T_NEAR);
  347. break;
  348. case Macro::BranchCondition::NotZero:
  349. jne(labels[jump_address], T_NEAR);
  350. break;
  351. }
  352. }
  353. L(end);
  354. }
  355. void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() {
  356. optimizer.can_skip_carry = true;
  357. optimizer.has_delayed_pc = false;
  358. for (auto raw_op : code) {
  359. Macro::Opcode op{};
  360. op.raw = raw_op;
  361. if (op.operation == Macro::Operation::ALU) {
  362. // Scan for any ALU operations which actually use the carry flag, if they don't exist in
  363. // our current code we can skip emitting the carry flag handling operations
  364. if (op.alu_operation == Macro::ALUOperation::AddWithCarry ||
  365. op.alu_operation == Macro::ALUOperation::SubtractWithBorrow) {
  366. optimizer.can_skip_carry = false;
  367. }
  368. }
  369. if (op.operation == Macro::Operation::Branch) {
  370. if (!op.branch_annul) {
  371. optimizer.has_delayed_pc = true;
  372. }
  373. }
  374. }
  375. }
  376. void MacroJITx64Impl::Compile() {
  377. MICROPROFILE_SCOPE(MacroJitCompile);
  378. bool keep_executing = true;
  379. labels.fill(Xbyak::Label());
  380. Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
  381. // JIT state
  382. mov(STATE, Common::X64::ABI_PARAM1);
  383. mov(PARAMETERS, qword[Common::X64::ABI_PARAM1 +
  384. static_cast<Xbyak::uint32>(offsetof(JITState, parameters))]);
  385. mov(REGISTERS, Common::X64::ABI_PARAM1);
  386. add(REGISTERS, static_cast<Xbyak::uint32>(offsetof(JITState, registers)));
  387. xor_(RESULT, RESULT);
  388. xor_(METHOD_ADDRESS, METHOD_ADDRESS);
  389. xor_(NEXT_PARAMETER, NEXT_PARAMETER);
  390. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  391. mov(dword[REGISTERS + 4], Compile_FetchParameter());
  392. // Track get register for zero registers and mark it as no-op
  393. optimizer.zero_reg_skip = true;
  394. // AddImmediate tends to be used as a NOP instruction, if we detect this we can
  395. // completely skip the entire code path and no emit anything
  396. optimizer.skip_dummy_addimmediate = true;
  397. // SMO tends to emit a lot of unnecessary method moves, we can mitigate this by only emitting
  398. // one if our register isn't "dirty"
  399. optimizer.optimize_for_method_move = true;
  400. // Check to see if we can skip emitting certain instructions
  401. Optimizer_ScanFlags();
  402. const u32 op_count = static_cast<u32>(code.size());
  403. for (u32 i = 0; i < op_count; i++) {
  404. if (i < op_count - 1) {
  405. pc = i + 1;
  406. next_opcode = GetOpCode();
  407. } else {
  408. next_opcode = {};
  409. }
  410. pc = i;
  411. Compile_NextInstruction();
  412. }
  413. L(end_of_code);
  414. Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
  415. ret();
  416. ready();
  417. program = getCode<ProgramType>();
  418. }
  419. bool MacroJITx64Impl::Compile_NextInstruction() {
  420. const auto opcode = GetOpCode();
  421. if (labels[pc].getAddress()) {
  422. return false;
  423. }
  424. L(labels[pc]);
  425. switch (opcode.operation) {
  426. case Macro::Operation::ALU:
  427. Compile_ALU(opcode);
  428. break;
  429. case Macro::Operation::AddImmediate:
  430. Compile_AddImmediate(opcode);
  431. break;
  432. case Macro::Operation::ExtractInsert:
  433. Compile_ExtractInsert(opcode);
  434. break;
  435. case Macro::Operation::ExtractShiftLeftImmediate:
  436. Compile_ExtractShiftLeftImmediate(opcode);
  437. break;
  438. case Macro::Operation::ExtractShiftLeftRegister:
  439. Compile_ExtractShiftLeftRegister(opcode);
  440. break;
  441. case Macro::Operation::Read:
  442. Compile_Read(opcode);
  443. break;
  444. case Macro::Operation::Branch:
  445. Compile_Branch(opcode);
  446. break;
  447. default:
  448. UNIMPLEMENTED_MSG("Unimplemented opcode {}", opcode.operation.Value());
  449. break;
  450. }
  451. if (optimizer.has_delayed_pc) {
  452. if (opcode.is_exit) {
  453. mov(rax, end_of_code);
  454. test(BRANCH_HOLDER, BRANCH_HOLDER);
  455. cmove(BRANCH_HOLDER, rax);
  456. // Jump to next instruction to skip delay slot check
  457. je(labels[pc + 1], T_NEAR);
  458. } else {
  459. // TODO(ogniK): Optimize delay slot branching
  460. Xbyak::Label no_delay_slot{};
  461. test(BRANCH_HOLDER, BRANCH_HOLDER);
  462. je(no_delay_slot, T_NEAR);
  463. mov(rax, BRANCH_HOLDER);
  464. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  465. jmp(rax);
  466. L(no_delay_slot);
  467. }
  468. L(delay_skip[pc]);
  469. if (opcode.is_exit) {
  470. return false;
  471. }
  472. } else {
  473. test(BRANCH_HOLDER, BRANCH_HOLDER);
  474. jne(end_of_code, T_NEAR);
  475. if (opcode.is_exit) {
  476. inc(BRANCH_HOLDER);
  477. return false;
  478. }
  479. }
  480. return true;
  481. }
  482. Xbyak::Reg32 Tegra::MacroJITx64Impl::Compile_FetchParameter() {
  483. mov(eax, dword[PARAMETERS + NEXT_PARAMETER * sizeof(u32)]);
  484. inc(NEXT_PARAMETER);
  485. return eax;
  486. }
  487. Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
  488. if (index == 0) {
  489. // Register 0 is always zero
  490. xor_(dst, dst);
  491. } else {
  492. mov(dst, dword[REGISTERS + index * sizeof(u32)]);
  493. }
  494. return dst;
  495. }
  496. void Tegra::MacroJITx64Impl::Compile_WriteCarry(Xbyak::Reg64 dst) {
  497. Xbyak::Label zero{}, end{};
  498. xor_(ecx, ecx);
  499. shr(dst, 32);
  500. setne(cl);
  501. mov(dword[STATE + offsetof(JITState, carry_flag)], ecx);
  502. }
  503. void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) {
  504. auto SetRegister = [=](u32 reg, Xbyak::Reg32 result) {
  505. // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
  506. // register.
  507. if (reg == 0) {
  508. return;
  509. }
  510. mov(dword[REGISTERS + reg * sizeof(u32)], result);
  511. };
  512. auto SetMethodAddress = [=](Xbyak::Reg32 reg) { mov(METHOD_ADDRESS, reg); };
  513. switch (operation) {
  514. case Macro::ResultOperation::IgnoreAndFetch:
  515. SetRegister(reg, Compile_FetchParameter());
  516. break;
  517. case Macro::ResultOperation::Move:
  518. SetRegister(reg, RESULT);
  519. break;
  520. case Macro::ResultOperation::MoveAndSetMethod:
  521. SetRegister(reg, RESULT);
  522. SetMethodAddress(RESULT);
  523. break;
  524. case Macro::ResultOperation::FetchAndSend:
  525. // Fetch parameter and send result.
  526. SetRegister(reg, Compile_FetchParameter());
  527. Compile_Send(RESULT);
  528. break;
  529. case Macro::ResultOperation::MoveAndSend:
  530. // Move and send result.
  531. SetRegister(reg, RESULT);
  532. Compile_Send(RESULT);
  533. break;
  534. case Macro::ResultOperation::FetchAndSetMethod:
  535. // Fetch parameter and use result as Method Address.
  536. SetRegister(reg, Compile_FetchParameter());
  537. SetMethodAddress(RESULT);
  538. break;
  539. case Macro::ResultOperation::MoveAndSetMethodFetchAndSend:
  540. // Move result and use as Method Address, then fetch and send parameter.
  541. SetRegister(reg, RESULT);
  542. SetMethodAddress(RESULT);
  543. Compile_Send(Compile_FetchParameter());
  544. break;
  545. case Macro::ResultOperation::MoveAndSetMethodSend:
  546. // Move result and use as Method Address, then send bits 12:17 of result.
  547. SetRegister(reg, RESULT);
  548. SetMethodAddress(RESULT);
  549. shr(RESULT, 12);
  550. and_(RESULT, 0b111111);
  551. Compile_Send(RESULT);
  552. break;
  553. default:
  554. UNIMPLEMENTED_MSG("Unimplemented macro operation {}", static_cast<std::size_t>(operation));
  555. }
  556. }
  557. Macro::Opcode MacroJITx64Impl::GetOpCode() const {
  558. ASSERT(pc < code.size());
  559. return {code[pc]};
  560. }
  561. std::bitset<32> MacroJITx64Impl::PersistentCallerSavedRegs() const {
  562. return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED;
  563. }
  564. } // namespace Tegra