odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

asmjit_test_emitters.cpp (10089B)


      1 // This file is part of AsmJit project <https://asmjit.com>
      2 //
      3 // See <asmjit/core.h> or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include <asmjit/core.h>
     11 #include "../commons/asmjitutils.h"
     12 
     13 #if ASMJIT_ARCH_X86 != 0
     14   #include <asmjit/x86.h>
     15 #endif
     16 
     17 #if ASMJIT_ARCH_ARM == 64
     18   #include <asmjit/a64.h>
     19 #endif
     20 
     21 using namespace asmjit;
     22 
     23 static void print_app_info() noexcept {
     24   printf("AsmJit Emitters Test-Suite v%u.%u.%u [Arch=%s] [Mode=%s]\n\n",
     25     unsigned((ASMJIT_LIBRARY_VERSION >> 16)       ),
     26     unsigned((ASMJIT_LIBRARY_VERSION >>  8) & 0xFF),
     27     unsigned((ASMJIT_LIBRARY_VERSION      ) & 0xFF),
     28     asmjit_arch_as_string(Arch::kHost),
     29     asmjit_build_type()
     30   );
     31 }
     32 
     33 #if !defined(ASMJIT_NO_JIT) && ((ASMJIT_ARCH_X86 != 0  && !defined(ASMJIT_NO_X86    )) || \
     34                                 (ASMJIT_ARCH_ARM == 64 && !defined(ASMJIT_NO_AARCH64)) )
     35 
     36 // Signature of the generated function.
     37 using SumIntsFunc = void (*)(int* dst, const int* a, const int* b);
     38 
     39 // X86 Backend
     40 // -----------
     41 
     42 #if ASMJIT_ARCH_X86 != 0
     43 // This function works with both x86::Assembler and x86::Builder. It shows how
     44 // `x86::Emitter` can be used to make your code more generic.
     45 static void generate_func_with_emitter(x86::Emitter* emitter) noexcept {
     46   // Decide which registers will be mapped to function arguments. Try changing
     47   // registers of `dst`, `src_a`, and `src_b` and see what happens in function's
     48   // prolog and epilog.
     49   x86::Gp dst   = emitter->zax();
     50   x86::Gp src_a = emitter->zcx();
     51   x86::Gp src_b = emitter->zdx();
     52 
     53   // Decide which vector registers to use. We use these to keep the code generic,
     54   // you can switch to any other registers when needed.
     55   x86::Vec vec0 = x86::xmm0;
     56   x86::Vec vec1 = x86::xmm1;
     57 
     58   // Create and initialize `FuncDetail` and `FuncFrame`.
     59   FuncDetail func;
     60   func.init(FuncSignature::build<void, int*, const int*, const int*>(), emitter->environment());
     61 
     62   FuncFrame frame;
     63   frame.init(func);
     64 
     65   // Make or registers dirty.
     66   frame.add_dirty_regs(vec0, vec1);
     67 
     68   FuncArgsAssignment args(&func);         // Create arguments assignment context.
     69   args.assign_all(dst, src_a, src_b);      // Assign our registers to arguments.
     70   args.update_func_frame(frame);            // Reflect our args in FuncFrame.
     71   frame.finalize();
     72 
     73   // Emit prolog and allocate arguments to registers.
     74   emitter->emit_prolog(frame);
     75   emitter->emit_args_assignment(frame, args);
     76 
     77   emitter->movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0.
     78   emitter->movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1.
     79 
     80   emitter->paddd(vec0, vec1);             // Add 4 ints in XMM1 to XMM0.
     81   emitter->movdqu(x86::ptr(dst), vec0);   // Store the result to [dst].
     82 
     83   // Emit epilog and return.
     84   emitter->emit_epilog(frame);
     85 }
     86 
     87 #ifndef ASMJIT_NO_COMPILER
     88 // This function works with x86::Compiler, provided for comparison.
     89 static void generate_func_with_compiler(x86::Compiler* cc) noexcept {
     90   x86::Gp dst = cc->new_gp_ptr("dst");
     91   x86::Gp src_a = cc->new_gp_ptr("src_a");
     92   x86::Gp src_b = cc->new_gp_ptr("src_b");
     93   x86::Vec vec0 = cc->new_xmm("vec0");
     94   x86::Vec vec1 = cc->new_xmm("vec1");
     95 
     96   FuncNode* func_node = cc->add_func(FuncSignature::build<void, int*, const int*, const int*>());
     97   func_node->set_arg(0, dst);
     98   func_node->set_arg(1, src_a);
     99   func_node->set_arg(2, src_b);
    100 
    101   cc->movdqu(vec0, x86::ptr(src_a));
    102   cc->movdqu(vec1, x86::ptr(src_b));
    103   cc->paddd(vec0, vec1);
    104   cc->movdqu(x86::ptr(dst), vec0);
    105   cc->end_func();
    106 }
    107 #endif
    108 
    109 static Error generate_func(CodeHolder& code, EmitterType emitter_type) noexcept {
    110   switch (emitter_type) {
    111     case EmitterType::kAssembler: {
    112       printf("Using x86::Assembler:\n");
    113       x86::Assembler a(&code);
    114       generate_func_with_emitter(a.as<x86::Emitter>());
    115       return Error::kOk;
    116     }
    117 
    118 #ifndef ASMJIT_NO_BUILDER
    119     case EmitterType::kBuilder: {
    120       printf("Using x86::Builder:\n");
    121       x86::Builder cb(&code);
    122       generate_func_with_emitter(cb.as<x86::Emitter>());
    123 
    124       return cb.finalize();
    125     }
    126 #endif
    127 
    128 #ifndef ASMJIT_NO_COMPILER
    129     case EmitterType::kCompiler: {
    130       printf("Using x86::Compiler:\n");
    131       x86::Compiler cc(&code);
    132       generate_func_with_compiler(&cc);
    133 
    134       return cc.finalize();
    135     }
    136 #endif
    137 
    138     default: {
    139       printf("** FAILURE: No emitter to use **\n");
    140       exit(1);
    141     }
    142   }
    143 }
    144 #endif
    145 
    146 // AArch64 Backend
    147 // ---------------
    148 
    149 #if ASMJIT_ARCH_ARM == 64
    150 // This function works with both a64::Assembler and a64::Builder. It shows how
    151 // `a64::Emitter` can be used to make your code more generic.
    152 static void generate_func_with_emitter(a64::Emitter* emitter) noexcept {
    153   // Decide which registers will be mapped to function arguments. Try changing
    154   // registers of `dst`, `src_a`, and `src_b` and see what happens in function's
    155   // prolog and epilog.
    156   a64::Gp dst   = a64::x0;
    157   a64::Gp src_a = a64::x1;
    158   a64::Gp src_b = a64::x2;
    159 
    160   // Decide which vector registers to use. We use these to keep the code generic,
    161   // you can switch to any other registers when needed.
    162   a64::Vec vec0 = a64::v0;
    163   a64::Vec vec1 = a64::v1;
    164   a64::Vec vec2 = a64::v2;
    165 
    166   // Create and initialize `FuncDetail` and `FuncFrame`.
    167   FuncDetail func;
    168   func.init(FuncSignature::build<void, int*, const int*, const int*>(), emitter->environment());
    169 
    170   FuncFrame frame;
    171   frame.init(func);
    172 
    173   // Make XMM0 and XMM1 dirty. VEC group includes XMM|YMM|ZMM registers.
    174   frame.add_dirty_regs(vec0, vec1, vec2);
    175 
    176   FuncArgsAssignment args(&func);                // Create arguments assignment context.
    177   args.assign_all(dst, src_a, src_b);             // Assign our registers to arguments.
    178   args.update_func_frame(frame);                   // Reflect our args in FuncFrame.
    179   frame.finalize();
    180 
    181   // Emit prolog and allocate arguments to registers.
    182   emitter->emit_prolog(frame);
    183   emitter->emit_args_assignment(frame, args);
    184 
    185   emitter->ld1(vec0.b16(), a64::ptr(src_a));     // Load 4 ints from [src_a] to vec0.
    186   emitter->ld1(vec1.b16(), a64::ptr(src_b));     // Load 4 ints from [src_b] to vec1.
    187   emitter->add(vec2.s4(), vec0.s4(), vec1.s4()); // Add 4 ints of vec0 and vec1 and store to vec2.
    188   emitter->st1(vec2.b16(), a64::ptr(dst));       // Store the result (vec2) to [dst].
    189 
    190   // Emit epilog and return.
    191   emitter->emit_epilog(frame);
    192 }
    193 
    194 #ifndef ASMJIT_NO_COMPILER
    195 // This function works with x86::Compiler, provided for comparison.
    196 static void generate_func_with_compiler(a64::Compiler* cc) noexcept {
    197   a64::Gp dst = cc->new_gp_ptr("dst");
    198   a64::Gp src_a = cc->new_gp_ptr("src_a");
    199   a64::Gp src_b = cc->new_gp_ptr("src_b");
    200   a64::Vec vec0 = cc->new_vec_q("vec0");
    201   a64::Vec vec1 = cc->new_vec_q("vec1");
    202   a64::Vec vec2 = cc->new_vec_q("vec2");
    203 
    204   FuncNode* func_node = cc->add_func(FuncSignature::build<void, int*, const int*, const int*>());
    205   func_node->set_arg(0, dst);
    206   func_node->set_arg(1, src_a);
    207   func_node->set_arg(2, src_b);
    208 
    209   cc->ld1(vec0.b16(), a64::ptr(src_a));          // Load 4 ints from [src_a] to vec0.
    210   cc->ld1(vec1.b16(), a64::ptr(src_b));          // Load 4 ints from [src_b] to vec1.
    211   cc->add(vec2.s4(), vec0.s4(), vec1.s4());      // Add 4 ints of vec0 and vec1 and store to vec2.
    212   cc->st1(vec2.b16(), a64::ptr(dst));            // Store the result (vec2) to [dst].
    213   cc->end_func();
    214 }
    215 #endif
    216 
    217 static Error generate_func(CodeHolder& code, EmitterType emitter_type) noexcept {
    218   switch (emitter_type) {
    219     case EmitterType::kAssembler: {
    220       printf("Using a64::Assembler:\n");
    221       a64::Assembler a(&code);
    222       generate_func_with_emitter(a.as<a64::Emitter>());
    223       return Error::kOk;
    224     }
    225 
    226 #ifndef ASMJIT_NO_BUILDER
    227     case EmitterType::kBuilder: {
    228       printf("Using a64::Builder:\n");
    229       a64::Builder cb(&code);
    230       generate_func_with_emitter(cb.as<a64::Emitter>());
    231 
    232       return cb.finalize();
    233     }
    234 #endif
    235 
    236 #ifndef ASMJIT_NO_COMPILER
    237     case EmitterType::kCompiler: {
    238       printf("Using a64::Compiler:\n");
    239       a64::Compiler cc(&code);
    240       generate_func_with_compiler(&cc);
    241 
    242       return cc.finalize();
    243     }
    244 #endif
    245 
    246     default: {
    247       printf("** FAILURE: No emitter to use **\n");
    248       exit(1);
    249     }
    250   }
    251 }
    252 #endif
    253 
    254 // Testing
    255 // -------
    256 
    257 static uint32_t test_func(JitRuntime& rt, EmitterType emitter_type) noexcept {
    258 #ifndef ASMJIT_NO_LOGGING
    259   FileLogger logger(stdout);
    260   logger.set_indentation(FormatIndentationGroup::kCode, 2);
    261 #endif
    262 
    263   CodeHolder code;
    264   code.init(rt.environment(), rt.cpu_features());
    265 
    266 #ifndef ASMJIT_NO_LOGGING
    267   code.set_logger(&logger);
    268 #endif
    269 
    270   Error err = generate_func(code, emitter_type);
    271   if (err != Error::kOk) {
    272     printf("** FAILURE: Failed to generate a function: %s **\n", DebugUtils::error_as_string(err));
    273     return 1;
    274   }
    275 
    276   // Add the code generated to the runtime.
    277   SumIntsFunc fn;
    278   err = rt.add(&fn, &code);
    279 
    280   if (err != Error::kOk) {
    281     printf("** FAILURE: JitRuntime::add() failed: %s **\n", DebugUtils::error_as_string(err));
    282     return 1;
    283   }
    284 
    285   // Execute the generated function.
    286   static const int in_a[4] = { 4, 3, 2, 1 };
    287   static const int in_b[4] = { 1, 5, 2, 8 };
    288   int out[4] {};
    289   fn(out, in_a, in_b);
    290 
    291   // Should print {5 8 4 9}.
    292   printf("Result = { %d %d %d %d }\n\n", out[0], out[1], out[2], out[3]);
    293 
    294   rt.release(fn);
    295   return out[0] == 5 && out[1] == 8 && out[2] == 4 && out[3] == 9;
    296 }
    297 
    298 int main() {
    299   print_app_info();
    300 
    301   JitRuntime rt;
    302   unsigned failed_count = 0;
    303 
    304   failed_count += !test_func(rt, EmitterType::kAssembler);
    305 
    306 #ifndef ASMJIT_NO_BUILDER
    307   failed_count += !test_func(rt, EmitterType::kBuilder);
    308 #endif
    309 
    310 #ifndef ASMJIT_NO_COMPILER
    311   failed_count += !test_func(rt, EmitterType::kCompiler);
    312 #endif
    313 
    314   if (!failed_count)
    315     printf("** SUCCESS **\n");
    316   else
    317     printf("** FAILURE - %u %s failed ** \n", failed_count, failed_count == 1 ? "test" : "tests");
    318 
    319   return failed_count ? 1 : 0;
    320 }
    321 #else
    322 int main() {
    323   print_app_info();
    324   printf("!! This test is disabled: <ASMJIT_NO_JIT> or unsuitable target architecture !!\n");
    325   return 0;
    326 }
    327 #endif // ASMJIT_ARCH_X86 && !ASMJIT_NO_X86 && !ASMJIT_NO_JIT