1
0

asmjit_test_x86_asm.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // [AsmJit]
  2. // Machine Code Generation for C++.
  3. //
  4. // [License]
  5. // Zlib - See LICENSE.md file in the package.
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include "./asmjit.h"
  10. using namespace asmjit;
  11. // Signature of the generated function.
  12. typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
  13. // This function works with both x86::Assembler and x86::Builder. It shows how
  14. // `x86::Emitter` can be used to make your code more generic.
  15. static void makeRawFunc(x86::Emitter* emitter) noexcept {
  16. // Decide which registers will be mapped to function arguments. Try changing
  17. // registers of `dst`, `src_a`, and `src_b` and see what happens in function's
  18. // prolog and epilog.
  19. x86::Gp dst = emitter->zax();
  20. x86::Gp src_a = emitter->zcx();
  21. x86::Gp src_b = emitter->zdx();
  22. // Decide which vector registers to use. We use these to keep the code generic,
  23. // you can switch to any other registers when needed.
  24. x86::Xmm vec0 = x86::xmm0;
  25. x86::Xmm vec1 = x86::xmm1;
  26. // Create and initialize `FuncDetail` and `FuncFrame`.
  27. FuncDetail func;
  28. func.init(FuncSignatureT<void, int*, const int*, const int*>(CallConv::kIdHost));
  29. FuncFrame frame;
  30. frame.init(func);
  31. // Make XMM0 and XMM1 dirty. VEC group includes XMM|YMM|ZMM registers.
  32. frame.addDirtyRegs(x86::xmm0, x86::xmm1);
  33. FuncArgsAssignment args(&func); // Create arguments assignment context.
  34. args.assignAll(dst, src_a, src_b); // Assign our registers to arguments.
  35. args.updateFuncFrame(frame); // Reflect our args in FuncFrame.
  36. frame.finalize();
  37. // Emit prolog and allocate arguments to registers.
  38. emitter->emitProlog(frame);
  39. emitter->emitArgsAssignment(frame, args);
  40. emitter->movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0.
  41. emitter->movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1.
  42. emitter->paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0.
  43. emitter->movdqu(x86::ptr(dst), vec0); // Store the result to [dst].
  44. // Emit epilog and return.
  45. emitter->emitEpilog(frame);
  46. }
  47. // This function works with x86::Compiler, provided for comparison.
  48. static void makeCompiledFunc(x86::Compiler* cc) noexcept {
  49. x86::Gp dst = cc->newIntPtr();
  50. x86::Gp src_a = cc->newIntPtr();
  51. x86::Gp src_b = cc->newIntPtr();
  52. x86::Xmm vec0 = cc->newXmm();
  53. x86::Xmm vec1 = cc->newXmm();
  54. cc->addFunc(FuncSignatureT<void, int*, const int*, const int*>(CallConv::kIdHost));
  55. cc->setArg(0, dst);
  56. cc->setArg(1, src_a);
  57. cc->setArg(2, src_b);
  58. cc->movdqu(vec0, x86::ptr(src_a));
  59. cc->movdqu(vec1, x86::ptr(src_b));
  60. cc->paddd(vec0, vec1);
  61. cc->movdqu(x86::ptr(dst), vec0);
  62. cc->endFunc();
  63. }
  64. static uint32_t testFunc(JitRuntime& rt, uint32_t emitterType) noexcept {
  65. FileLogger logger(stdout);
  66. CodeHolder code;
  67. code.init(rt.codeInfo());
  68. code.setLogger(&logger);
  69. Error err = kErrorOk;
  70. switch (emitterType) {
  71. case BaseEmitter::kTypeAssembler: {
  72. printf("Using x86::Assembler:\n");
  73. x86::Assembler a(&code);
  74. makeRawFunc(a.as<x86::Emitter>());
  75. break;
  76. }
  77. case BaseEmitter::kTypeBuilder: {
  78. printf("Using x86::Builder:\n");
  79. x86::Builder cb(&code);
  80. makeRawFunc(cb.as<x86::Emitter>());
  81. err = cb.finalize();
  82. if (err) {
  83. printf("x86::Builder::finalize() failed: %s\n", DebugUtils::errorAsString(err));
  84. return 1;
  85. }
  86. break;
  87. }
  88. case BaseEmitter::kTypeCompiler: {
  89. printf("Using x86::Compiler:\n");
  90. x86::Compiler cc(&code);
  91. makeCompiledFunc(&cc);
  92. err = cc.finalize();
  93. if (err) {
  94. printf("x86::Compiler::finalize() failed: %s\n", DebugUtils::errorAsString(err));
  95. return 1;
  96. }
  97. break;
  98. }
  99. }
  100. // Add the code generated to the runtime.
  101. SumIntsFunc fn;
  102. err = rt.add(&fn, &code);
  103. if (err) {
  104. printf("JitRuntime::add() failed: %s\n", DebugUtils::errorAsString(err));
  105. return 1;
  106. }
  107. // Execute the generated function.
  108. int inA[4] = { 4, 3, 2, 1 };
  109. int inB[4] = { 1, 5, 2, 8 };
  110. int out[4];
  111. fn(out, inA, inB);
  112. // Should print {5 8 4 9}.
  113. printf("Result = { %d %d %d %d }\n\n", out[0], out[1], out[2], out[3]);
  114. rt.release(fn);
  115. return !(out[0] == 5 && out[1] == 8 && out[2] == 4 && out[3] == 9);
  116. }
  117. int main(int argc, char* argv[]) {
  118. ASMJIT_UNUSED(argc);
  119. ASMJIT_UNUSED(argv);
  120. unsigned nFailed = 0;
  121. JitRuntime rt;
  122. nFailed += testFunc(rt, BaseEmitter::kTypeAssembler);
  123. nFailed += testFunc(rt, BaseEmitter::kTypeBuilder);
  124. nFailed += testFunc(rt, BaseEmitter::kTypeCompiler);
  125. if (!nFailed)
  126. printf("[PASSED] All tests passed\n");
  127. else
  128. printf("[FAILED] %u %s failed\n", nFailed, nFailed == 1 ? "test" : "tests");
  129. return nFailed ? 1 : 0;
  130. }