macro_jit_x64.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  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. const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
  251. and_(dst, mask);
  252. shr(src, opcode.bf_src_bit);
  253. and_(src, opcode.GetBitfieldMask());
  254. shl(src, opcode.bf_dst_bit);
  255. or_(dst, src);
  256. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  257. }
  258. void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
  259. const auto dst = Compile_GetRegister(opcode.src_a, ecx);
  260. const auto src = Compile_GetRegister(opcode.src_b, RESULT);
  261. shr(src, dst.cvt8());
  262. and_(src, opcode.GetBitfieldMask());
  263. shl(src, opcode.bf_dst_bit);
  264. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  265. }
  266. void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
  267. const auto dst = Compile_GetRegister(opcode.src_a, ecx);
  268. const auto src = Compile_GetRegister(opcode.src_b, RESULT);
  269. shr(src, opcode.bf_src_bit);
  270. and_(src, opcode.GetBitfieldMask());
  271. shl(src, dst.cvt8());
  272. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  273. }
  274. void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
  275. if (optimizer.zero_reg_skip && opcode.src_a == 0) {
  276. if (opcode.immediate == 0) {
  277. xor_(RESULT, RESULT);
  278. } else {
  279. mov(RESULT, opcode.immediate);
  280. }
  281. } else {
  282. auto result = Compile_GetRegister(opcode.src_a, RESULT);
  283. if (opcode.immediate > 2) {
  284. add(result, opcode.immediate);
  285. } else if (opcode.immediate == 1) {
  286. inc(result);
  287. } else if (opcode.immediate < 0) {
  288. sub(result, opcode.immediate * -1);
  289. }
  290. }
  291. // Equivalent to Engines::Maxwell3D::GetRegisterValue:
  292. if (optimizer.enable_asserts) {
  293. Xbyak::Label pass_range_check;
  294. cmp(RESULT, static_cast<u32>(Engines::Maxwell3D::Regs::NUM_REGS));
  295. jb(pass_range_check);
  296. int3();
  297. L(pass_range_check);
  298. }
  299. mov(rax, qword[STATE]);
  300. mov(RESULT,
  301. dword[rax + offsetof(Engines::Maxwell3D, regs) +
  302. offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
  303. Compile_ProcessResult(opcode.result_operation, opcode.dst);
  304. }
  305. void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
  306. maxwell3d->CallMethod(method_address.address, value, true);
  307. }
  308. void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
  309. Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
  310. mov(Common::X64::ABI_PARAM1, qword[STATE]);
  311. mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS);
  312. mov(Common::X64::ABI_PARAM3, value);
  313. Common::X64::CallFarFunction(*this, &Send);
  314. Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
  315. Xbyak::Label dont_process{};
  316. // Get increment
  317. test(METHOD_ADDRESS, 0x3f000);
  318. // If zero, method address doesn't update
  319. je(dont_process);
  320. mov(ecx, METHOD_ADDRESS);
  321. and_(METHOD_ADDRESS, 0xfff);
  322. shr(ecx, 12);
  323. and_(ecx, 0x3f);
  324. lea(eax, ptr[rcx + METHOD_ADDRESS.cvt64()]);
  325. sal(ecx, 12);
  326. or_(eax, ecx);
  327. mov(METHOD_ADDRESS, eax);
  328. L(dont_process);
  329. }
  330. void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
  331. ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
  332. const s32 jump_address =
  333. static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
  334. Xbyak::Label end;
  335. auto value = Compile_GetRegister(opcode.src_a, eax);
  336. cmp(value, 0); // test(value, value);
  337. if (optimizer.has_delayed_pc) {
  338. switch (opcode.branch_condition) {
  339. case Macro::BranchCondition::Zero:
  340. jne(end, T_NEAR);
  341. break;
  342. case Macro::BranchCondition::NotZero:
  343. je(end, T_NEAR);
  344. break;
  345. }
  346. if (opcode.branch_annul) {
  347. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  348. jmp(labels[jump_address], T_NEAR);
  349. } else {
  350. Xbyak::Label handle_post_exit{};
  351. Xbyak::Label skip{};
  352. jmp(skip, T_NEAR);
  353. L(handle_post_exit);
  354. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  355. jmp(labels[jump_address], T_NEAR);
  356. L(skip);
  357. mov(BRANCH_HOLDER, handle_post_exit);
  358. jmp(delay_skip[pc], T_NEAR);
  359. }
  360. } else {
  361. switch (opcode.branch_condition) {
  362. case Macro::BranchCondition::Zero:
  363. je(labels[jump_address], T_NEAR);
  364. break;
  365. case Macro::BranchCondition::NotZero:
  366. jne(labels[jump_address], T_NEAR);
  367. break;
  368. }
  369. }
  370. L(end);
  371. }
  372. void MacroJITx64Impl::Optimizer_ScanFlags() {
  373. optimizer.can_skip_carry = true;
  374. optimizer.has_delayed_pc = false;
  375. for (auto raw_op : code) {
  376. Macro::Opcode op{};
  377. op.raw = raw_op;
  378. if (op.operation == Macro::Operation::ALU) {
  379. // Scan for any ALU operations which actually use the carry flag, if they don't exist in
  380. // our current code we can skip emitting the carry flag handling operations
  381. if (op.alu_operation == Macro::ALUOperation::AddWithCarry ||
  382. op.alu_operation == Macro::ALUOperation::SubtractWithBorrow) {
  383. optimizer.can_skip_carry = false;
  384. }
  385. }
  386. if (op.operation == Macro::Operation::Branch) {
  387. if (!op.branch_annul) {
  388. optimizer.has_delayed_pc = true;
  389. }
  390. }
  391. }
  392. }
  393. void MacroJITx64Impl::Compile() {
  394. MICROPROFILE_SCOPE(MacroJitCompile);
  395. labels.fill(Xbyak::Label());
  396. Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
  397. // JIT state
  398. mov(STATE, Common::X64::ABI_PARAM1);
  399. mov(PARAMETERS, Common::X64::ABI_PARAM2);
  400. mov(MAX_PARAMETER, Common::X64::ABI_PARAM3);
  401. xor_(RESULT, RESULT);
  402. xor_(METHOD_ADDRESS, METHOD_ADDRESS);
  403. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  404. mov(dword[STATE + offsetof(JITState, registers) + 4], Compile_FetchParameter());
  405. // Track get register for zero registers and mark it as no-op
  406. optimizer.zero_reg_skip = true;
  407. // AddImmediate tends to be used as a NOP instruction, if we detect this we can
  408. // completely skip the entire code path and no emit anything
  409. optimizer.skip_dummy_addimmediate = true;
  410. // SMO tends to emit a lot of unnecessary method moves, we can mitigate this by only emitting
  411. // one if our register isn't "dirty"
  412. optimizer.optimize_for_method_move = true;
  413. // Enable run-time assertions in JITted code
  414. optimizer.enable_asserts = false;
  415. // Check to see if we can skip emitting certain instructions
  416. Optimizer_ScanFlags();
  417. const u32 op_count = static_cast<u32>(code.size());
  418. for (u32 i = 0; i < op_count; i++) {
  419. if (i < op_count - 1) {
  420. pc = i + 1;
  421. next_opcode = GetOpCode();
  422. } else {
  423. next_opcode = {};
  424. }
  425. pc = i;
  426. Compile_NextInstruction();
  427. }
  428. L(end_of_code);
  429. Common::X64::ABI_PopRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
  430. ret();
  431. ready();
  432. program = getCode<ProgramType>();
  433. }
  434. bool MacroJITx64Impl::Compile_NextInstruction() {
  435. const auto opcode = GetOpCode();
  436. if (labels[pc].getAddress()) {
  437. return false;
  438. }
  439. L(labels[pc]);
  440. switch (opcode.operation) {
  441. case Macro::Operation::ALU:
  442. Compile_ALU(opcode);
  443. break;
  444. case Macro::Operation::AddImmediate:
  445. Compile_AddImmediate(opcode);
  446. break;
  447. case Macro::Operation::ExtractInsert:
  448. Compile_ExtractInsert(opcode);
  449. break;
  450. case Macro::Operation::ExtractShiftLeftImmediate:
  451. Compile_ExtractShiftLeftImmediate(opcode);
  452. break;
  453. case Macro::Operation::ExtractShiftLeftRegister:
  454. Compile_ExtractShiftLeftRegister(opcode);
  455. break;
  456. case Macro::Operation::Read:
  457. Compile_Read(opcode);
  458. break;
  459. case Macro::Operation::Branch:
  460. Compile_Branch(opcode);
  461. break;
  462. default:
  463. UNIMPLEMENTED_MSG("Unimplemented opcode {}", opcode.operation.Value());
  464. break;
  465. }
  466. if (optimizer.has_delayed_pc) {
  467. if (opcode.is_exit) {
  468. mov(rax, end_of_code);
  469. test(BRANCH_HOLDER, BRANCH_HOLDER);
  470. cmove(BRANCH_HOLDER, rax);
  471. // Jump to next instruction to skip delay slot check
  472. je(labels[pc + 1], T_NEAR);
  473. } else {
  474. // TODO(ogniK): Optimize delay slot branching
  475. Xbyak::Label no_delay_slot{};
  476. test(BRANCH_HOLDER, BRANCH_HOLDER);
  477. je(no_delay_slot, T_NEAR);
  478. mov(rax, BRANCH_HOLDER);
  479. xor_(BRANCH_HOLDER, BRANCH_HOLDER);
  480. jmp(rax);
  481. L(no_delay_slot);
  482. }
  483. L(delay_skip[pc]);
  484. if (opcode.is_exit) {
  485. return false;
  486. }
  487. } else {
  488. test(BRANCH_HOLDER, BRANCH_HOLDER);
  489. jne(end_of_code, T_NEAR);
  490. if (opcode.is_exit) {
  491. inc(BRANCH_HOLDER);
  492. return false;
  493. }
  494. }
  495. return true;
  496. }
  497. static void WarnInvalidParameter(uintptr_t parameter, uintptr_t max_parameter) {
  498. LOG_CRITICAL(HW_GPU,
  499. "Macro JIT: invalid parameter access 0x{:x} (0x{:x} is the last parameter)",
  500. parameter, max_parameter - sizeof(u32));
  501. }
  502. Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() {
  503. Xbyak::Label parameter_ok{};
  504. cmp(PARAMETERS, MAX_PARAMETER);
  505. jb(parameter_ok, T_NEAR);
  506. Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
  507. mov(Common::X64::ABI_PARAM1, PARAMETERS);
  508. mov(Common::X64::ABI_PARAM2, MAX_PARAMETER);
  509. Common::X64::CallFarFunction(*this, &WarnInvalidParameter);
  510. Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
  511. L(parameter_ok);
  512. mov(eax, dword[PARAMETERS]);
  513. add(PARAMETERS, sizeof(u32));
  514. return eax;
  515. }
  516. Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
  517. if (index == 0) {
  518. // Register 0 is always zero
  519. xor_(dst, dst);
  520. } else {
  521. mov(dst, dword[STATE + offsetof(JITState, registers) + index * sizeof(u32)]);
  522. }
  523. return dst;
  524. }
  525. void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) {
  526. const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) {
  527. // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
  528. // register.
  529. if (reg_index == 0) {
  530. return;
  531. }
  532. mov(dword[STATE + offsetof(JITState, registers) + reg_index * sizeof(u32)], result);
  533. };
  534. const auto SetMethodAddress = [this](const Xbyak::Reg32& reg32) { mov(METHOD_ADDRESS, reg32); };
  535. switch (operation) {
  536. case Macro::ResultOperation::IgnoreAndFetch:
  537. SetRegister(reg, Compile_FetchParameter());
  538. break;
  539. case Macro::ResultOperation::Move:
  540. SetRegister(reg, RESULT);
  541. break;
  542. case Macro::ResultOperation::MoveAndSetMethod:
  543. SetRegister(reg, RESULT);
  544. SetMethodAddress(RESULT);
  545. break;
  546. case Macro::ResultOperation::FetchAndSend:
  547. // Fetch parameter and send result.
  548. SetRegister(reg, Compile_FetchParameter());
  549. Compile_Send(RESULT);
  550. break;
  551. case Macro::ResultOperation::MoveAndSend:
  552. // Move and send result.
  553. SetRegister(reg, RESULT);
  554. Compile_Send(RESULT);
  555. break;
  556. case Macro::ResultOperation::FetchAndSetMethod:
  557. // Fetch parameter and use result as Method Address.
  558. SetRegister(reg, Compile_FetchParameter());
  559. SetMethodAddress(RESULT);
  560. break;
  561. case Macro::ResultOperation::MoveAndSetMethodFetchAndSend:
  562. // Move result and use as Method Address, then fetch and send parameter.
  563. SetRegister(reg, RESULT);
  564. SetMethodAddress(RESULT);
  565. Compile_Send(Compile_FetchParameter());
  566. break;
  567. case Macro::ResultOperation::MoveAndSetMethodSend:
  568. // Move result and use as Method Address, then send bits 12:17 of result.
  569. SetRegister(reg, RESULT);
  570. SetMethodAddress(RESULT);
  571. shr(RESULT, 12);
  572. and_(RESULT, 0b111111);
  573. Compile_Send(RESULT);
  574. break;
  575. default:
  576. UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
  577. break;
  578. }
  579. }
  580. Macro::Opcode MacroJITx64Impl::GetOpCode() const {
  581. ASSERT(pc < code.size());
  582. return {code[pc]};
  583. }
  584. } // Anonymous namespace
  585. MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d_)
  586. : MacroEngine{maxwell3d_}, maxwell3d{maxwell3d_} {}
  587. std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) {
  588. return std::make_unique<MacroJITx64Impl>(maxwell3d, code);
  589. }
  590. } // namespace Tegra