macro_jit_x64.cpp 23 KB

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