odin-blend2d

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

asmjit_test_compiler_a64.cpp (18245B)


      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 <asmjit/core.h>
      7 #if !defined(ASMJIT_NO_COMPILER) && !defined(ASMJIT_NO_AARCH64)
      8 
      9 #include <asmjit/a64.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 
     14 #include "asmjit_test_compiler.h"
     15 
     16 using namespace asmjit;
     17 
     18 // a64::Compiler - A64TestCase
     19 // ===========================
     20 
     21 class A64TestCase : public TestCase {
     22 public:
     23   A64TestCase(const char* name = nullptr)
     24     : TestCase(name, Arch::kAArch64) {}
     25 
     26   void compile(BaseCompiler& cc) override {
     27     compile(static_cast<a64::Compiler&>(cc));
     28   }
     29 
     30   virtual void compile(a64::Compiler& cc) = 0;
     31 };
     32 
     33 // a64::Compiler - A64Test_GpArgs
     34 // ==============================
     35 
     36 class A64Test_GpArgs : public A64TestCase {
     37 public:
     38   uint32_t _arg_count;
     39   bool _preserve_fp;
     40 
     41   A64Test_GpArgs(uint32_t arg_count, bool preserve_fp)
     42     : _arg_count(arg_count),
     43       _preserve_fp(preserve_fp) {
     44     _name.assign_format("GpArgs {NumArgs=%u PreserveFP=%c}", arg_count, preserve_fp ? 'Y' : 'N');
     45   }
     46 
     47   static void add(TestApp& app) {
     48     for (uint32_t i = 0; i <= 16; i++) {
     49       app.add(new A64Test_GpArgs(i, true));
     50       app.add(new A64Test_GpArgs(i, false));
     51     }
     52   }
     53 
     54   void compile(a64::Compiler& cc) override {
     55     uint32_t arg_count = _arg_count;
     56     FuncSignature signature;
     57 
     58     signature.set_ret_t<int>();
     59     for (uint32_t i = 0; i < arg_count; i++) {
     60       signature.add_arg_t<int>();
     61     }
     62 
     63     FuncNode* func_node = cc.add_func(signature);
     64     if (_preserve_fp)
     65       func_node->frame().set_preserved_fp();
     66 
     67     a64::Gp sum;
     68 
     69     if (arg_count) {
     70       for (uint32_t i = 0; i < arg_count; i++) {
     71         a64::Gp i_reg = cc.new_gp32("i%u", i);
     72         func_node->set_arg(i, i_reg);
     73 
     74         if (i == 0)
     75           sum = i_reg;
     76         else
     77           cc.add(sum, sum, i_reg);
     78       }
     79     }
     80     else {
     81       sum = cc.new_gp32("i");
     82       cc.mov(sum, 0);
     83     }
     84 
     85     cc.ret(sum);
     86     cc.end_func();
     87   }
     88 
     89   bool run(void* _func, String& result, String& expect) override {
     90     using U = unsigned int;
     91 
     92     using Func0 = U (*)();
     93     using Func1 = U (*)(U);
     94     using Func2 = U (*)(U, U);
     95     using Func3 = U (*)(U, U, U);
     96     using Func4 = U (*)(U, U, U, U);
     97     using Func5 = U (*)(U, U, U, U, U);
     98     using Func6 = U (*)(U, U, U, U, U, U);
     99     using Func7 = U (*)(U, U, U, U, U, U, U);
    100     using Func8 = U (*)(U, U, U, U, U, U, U, U);
    101     using Func9 = U (*)(U, U, U, U, U, U, U, U, U);
    102     using Func10 = U (*)(U, U, U, U, U, U, U, U, U, U);
    103     using Func11 = U (*)(U, U, U, U, U, U, U, U, U, U, U);
    104     using Func12 = U (*)(U, U, U, U, U, U, U, U, U, U, U, U);
    105     using Func13 = U (*)(U, U, U, U, U, U, U, U, U, U, U, U, U);
    106     using Func14 = U (*)(U, U, U, U, U, U, U, U, U, U, U, U, U, U);
    107     using Func15 = U (*)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U);
    108     using Func16 = U (*)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U);
    109 
    110     unsigned int result_ret = 0;
    111     unsigned int expect_ret = 0;
    112 
    113     switch (_arg_count) {
    114       case 0:
    115         result_ret = ptr_as_func<Func0>(_func)();
    116         expect_ret = 0;
    117         break;
    118       case 1:
    119         result_ret = ptr_as_func<Func1>(_func)(1);
    120         expect_ret = 1;
    121         break;
    122       case 2:
    123         result_ret = ptr_as_func<Func2>(_func)(1, 2);
    124         expect_ret = 1 + 2;
    125         break;
    126       case 3:
    127         result_ret = ptr_as_func<Func3>(_func)(1, 2, 3);
    128         expect_ret = 1 + 2 + 3;
    129         break;
    130       case 4:
    131         result_ret = ptr_as_func<Func4>(_func)(1, 2, 3, 4);
    132         expect_ret = 1 + 2 + 3 + 4;
    133         break;
    134       case 5:
    135         result_ret = ptr_as_func<Func5>(_func)(1, 2, 3, 4, 5);
    136         expect_ret = 1 + 2 + 3 + 4 + 5;
    137         break;
    138       case 6:
    139         result_ret = ptr_as_func<Func6>(_func)(1, 2, 3, 4, 5, 6);
    140         expect_ret = 1 + 2 + 3 + 4 + 5 + 6;
    141         break;
    142       case 7:
    143         result_ret = ptr_as_func<Func7>(_func)(1, 2, 3, 4, 5, 6, 7);
    144         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7;
    145         break;
    146       case 8:
    147         result_ret = ptr_as_func<Func8>(_func)(1, 2, 3, 4, 5, 6, 7, 8);
    148         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
    149         break;
    150       case 9:
    151         result_ret = ptr_as_func<Func9>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9);
    152         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9;
    153         break;
    154       case 10:
    155         result_ret = ptr_as_func<Func10>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    156         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;
    157         break;
    158       case 11:
    159         result_ret = ptr_as_func<Func11>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
    160         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11;
    161         break;
    162       case 12:
    163         result_ret = ptr_as_func<Func12>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
    164         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12;
    165         break;
    166       case 13:
    167         result_ret = ptr_as_func<Func13>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
    168         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13;
    169         break;
    170       case 14:
    171         result_ret = ptr_as_func<Func14>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
    172         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14;
    173         break;
    174       case 15:
    175         result_ret = ptr_as_func<Func15>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
    176         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15;
    177         break;
    178       case 16:
    179         result_ret = ptr_as_func<Func16>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    180         expect_ret = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16;
    181         break;
    182     }
    183 
    184     result.assign_format("ret={%u, %u}", result_ret >> 28, result_ret & 0x0FFFFFFFu);
    185     expect.assign_format("ret={%u, %u}", expect_ret >> 28, expect_ret & 0x0FFFFFFFu);
    186 
    187     return result == expect;
    188   }
    189 };
    190 
    191 // a64::Compiler - A64Test_Simd1
    192 // =============================
    193 
    194 class A64Test_Simd1 : public A64TestCase {
    195 public:
    196   A64Test_Simd1()
    197     : A64TestCase("Simd1") {}
    198 
    199   static void add(TestApp& app) {
    200     app.add(new A64Test_Simd1());
    201   }
    202 
    203   void compile(a64::Compiler& cc) override {
    204     FuncNode* func_node = cc.add_func(FuncSignature::build<void, void*, const void*, const void*>());
    205 
    206     a64::Gp dst = cc.new_gp_ptr("dst");
    207     a64::Gp src1 = cc.new_gp_ptr("src1");
    208     a64::Gp src2 = cc.new_gp_ptr("src2");
    209 
    210     func_node->set_arg(0, dst);
    211     func_node->set_arg(1, src1);
    212     func_node->set_arg(2, src2);
    213 
    214     a64::Vec v1 = cc.new_vec_q("vec1");
    215     a64::Vec v2 = cc.new_vec_q("vec2");
    216     a64::Vec v3 = cc.new_vec_q("vec3");
    217 
    218     cc.ldr(v2, a64::ptr(src1));
    219     cc.ldr(v3, a64::ptr(src2));
    220     cc.add(v1.b16(), v2.b16(), v3.b16());
    221     cc.str(v1, a64::ptr(dst));
    222 
    223     cc.end_func();
    224   }
    225 
    226   bool run(void* _func, String& result, String& expect) override {
    227     using Func = void (*)(void*, const void*, const void*);
    228 
    229     uint32_t dst[4];
    230     uint32_t a_src[4] = { 0 , 1 , 2 , 255 };
    231     uint32_t b_src[4] = { 99, 17, 33, 1   };
    232 
    233     // NOTE: It's a byte-add, so uint8_t(255+1) == 0.
    234     uint32_t ref[4] = { 99, 18, 35, 0 };
    235 
    236     ptr_as_func<Func>(_func)(dst, a_src, b_src);
    237 
    238     result.assign_format("ret={%u, %u, %u, %u}", dst[0], dst[1], dst[2], dst[3]);
    239     expect.assign_format("ret={%u, %u, %u, %u}", ref[0], ref[1], ref[2], ref[3]);
    240 
    241     return result == expect;
    242   }
    243 };
    244 
    245 // a64::Compiler - A64Test_ManyRegs
    246 // ================================
    247 
    248 class A64Test_ManyRegs : public A64TestCase {
    249 public:
    250   uint32_t _reg_count;
    251 
    252   A64Test_ManyRegs(uint32_t n)
    253     : A64TestCase(),
    254       _reg_count(n) {
    255     _name.assign_format("GpRegs {NumRegs=%u}", n);
    256   }
    257 
    258   static void add(TestApp& app) {
    259     for (uint32_t i = 2; i < 64; i++)
    260       app.add(new A64Test_ManyRegs(i));
    261   }
    262 
    263   void compile(a64::Compiler& cc) override {
    264     cc.add_func(FuncSignature::build<int>());
    265 
    266     a64::Gp* regs = static_cast<a64::Gp*>(malloc(_reg_count * sizeof(a64::Gp)));
    267 
    268     for (uint32_t i = 0; i < _reg_count; i++) {
    269       regs[i] = cc.new_gp32("reg%u", i);
    270       cc.mov(regs[i], i + 1);
    271     }
    272 
    273     a64::Gp sum = cc.new_gp32("sum");
    274     cc.mov(sum, 0);
    275 
    276     for (uint32_t i = 0; i < _reg_count; i++) {
    277       cc.add(sum, sum, regs[i]);
    278     }
    279 
    280     cc.ret(sum);
    281     cc.end_func();
    282 
    283     free(regs);
    284   }
    285 
    286   bool run(void* _func, String& result, String& expect) override {
    287     using Func = int (*)(void);
    288     Func func = ptr_as_func<Func>(_func);
    289 
    290     result.assign_format("ret={%d}", func());
    291     expect.assign_format("ret={%d}", calc_sum());
    292 
    293     return result == expect;
    294   }
    295 
    296   uint32_t calc_sum() const {
    297     return (_reg_count | 1) * ((_reg_count + 1) / 2);
    298   }
    299 };
    300 
    301 // a64::Compiler - A64Test_Adr
    302 // ===========================
    303 
    304 class A64Test_Adr : public A64TestCase {
    305 public:
    306   A64Test_Adr()
    307     : A64TestCase("Adr") {}
    308 
    309   static void add(TestApp& app) {
    310     app.add(new A64Test_Adr());
    311   }
    312 
    313   void compile(a64::Compiler& cc) override {
    314     cc.add_func(FuncSignature::build<int>());
    315 
    316     a64::Gp addr = cc.new_gp_ptr("addr");
    317     a64::Gp val = cc.new_gp_ptr("val");
    318 
    319     Label L_Table = cc.new_label();
    320 
    321     cc.adr(addr, L_Table);
    322     cc.ldrsw(val, a64::ptr(addr, 8));
    323     cc.ret(val);
    324     cc.end_func();
    325 
    326     cc.bind(L_Table);
    327     cc.embed_int32(1);
    328     cc.embed_int32(2);
    329     cc.embed_int32(3);
    330     cc.embed_int32(4);
    331     cc.embed_int32(5);
    332   }
    333 
    334   bool run(void* _func, String& result, String& expect) override {
    335     using Func = int (*)(void);
    336     Func func = ptr_as_func<Func>(_func);
    337 
    338     result.assign_format("ret={%d}", func());
    339     expect.assign_format("ret={%d}", 3);
    340 
    341     return result == expect;
    342   }
    343 };
    344 
    345 // a64::Compiler - A64Test_Branch1
    346 // ===============================
    347 
    348 class A64Test_Branch1 : public A64TestCase {
    349 public:
    350   A64Test_Branch1()
    351     : A64TestCase("Branch1") {}
    352 
    353   static void add(TestApp& app) {
    354     app.add(new A64Test_Branch1());
    355   }
    356 
    357   void compile(a64::Compiler& cc) override {
    358     FuncNode* func_node = cc.add_func(FuncSignature::build<void, void*, size_t>());
    359 
    360     a64::Gp p = cc.new_gp_ptr("p");
    361     a64::Gp count = cc.new_gp_ptr("count");
    362     a64::Gp i = cc.new_gp_ptr("i");
    363     Label L = cc.new_label();
    364 
    365     func_node->set_arg(0, p);
    366     func_node->set_arg(1, count);
    367 
    368     cc.mov(i, 0);
    369 
    370     cc.bind(L);
    371     cc.strb(i.w(), a64::ptr(p, i));
    372     cc.add(i, i, 1);
    373     cc.cmp(i, count);
    374     cc.b_ne(L);
    375 
    376     cc.end_func();
    377   }
    378 
    379   bool run(void* _func, String& result, String& expect) override {
    380     using Func = void (*)(void* p, size_t n);
    381     Func func = ptr_as_func<Func>(_func);
    382 
    383     uint8_t array[16];
    384     func(array, 16);
    385 
    386     expect.assign("ret={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}");
    387 
    388     result.assign("ret={");
    389     for (size_t i = 0; i < 16; i++) {
    390       if (i)
    391         result.append(", ");
    392       result.append_format("%d", int(array[i]));
    393     }
    394     result.append("}");
    395 
    396     return result == expect;
    397   }
    398 };
    399 
    400 // a64::Compiler - A64Test_Invoke1
    401 // ===============================
    402 
    403 class A64Test_Invoke1 : public A64TestCase {
    404 public:
    405   A64Test_Invoke1()
    406     : A64TestCase("Invoke1") {}
    407 
    408   static void add(TestApp& app) {
    409     app.add(new A64Test_Invoke1());
    410   }
    411 
    412   void compile(a64::Compiler& cc) override {
    413     FuncNode* func_node = cc.add_func(FuncSignature::build<uint32_t, uint32_t, uint32_t>());
    414 
    415     a64::Gp x = cc.new_gp32("x");
    416     a64::Gp y = cc.new_gp32("y");
    417     a64::Gp r = cc.new_gp32("r");
    418     a64::Gp fn = cc.new_gp_ptr("fn");
    419 
    420     func_node->set_arg(0, x);
    421     func_node->set_arg(1, y);
    422 
    423     cc.mov(fn, (uint64_t)called_fn);
    424 
    425     InvokeNode* invoke_node;
    426     cc.invoke(Out(invoke_node), fn, FuncSignature::build<uint32_t, uint32_t, uint32_t>());
    427     invoke_node->set_arg(0, x);
    428     invoke_node->set_arg(1, y);
    429     invoke_node->set_ret(0, r);
    430 
    431     cc.ret(r);
    432     cc.end_func();
    433   }
    434 
    435   bool run(void* _func, String& result, String& expect) override {
    436     using Func = uint32_t (*)(uint32_t, uint32_t);
    437     Func func = ptr_as_func<Func>(_func);
    438 
    439     uint32_t x = 49;
    440     uint32_t y = 7;
    441 
    442     result.assign_format("ret={%u}", func(x, y));
    443     expect.assign_format("ret={%u}", x - y);
    444 
    445     return result == expect;
    446   }
    447 
    448   static uint32_t called_fn(uint32_t x, uint32_t y) {
    449     return x - y;
    450   }
    451 };
    452 
    453 // a64::Compiler - A64Test_Invoke2
    454 // ===============================
    455 
    456 class A64Test_Invoke2 : public A64TestCase {
    457 public:
    458   A64Test_Invoke2()
    459     : A64TestCase("Invoke2") {}
    460 
    461   static void add(TestApp& app) {
    462     app.add(new A64Test_Invoke2());
    463   }
    464 
    465   void compile(a64::Compiler& cc) override {
    466     FuncNode* func_node = cc.add_func(FuncSignature::build<double, double, double>());
    467 
    468     a64::Vec x = cc.new_vec_d("x");
    469     a64::Vec y = cc.new_vec_d("y");
    470     a64::Vec r = cc.new_vec_d("r");
    471     a64::Gp fn = cc.new_gp_ptr("fn");
    472 
    473     func_node->set_arg(0, x);
    474     func_node->set_arg(1, y);
    475     cc.mov(fn, (uint64_t)called_fn);
    476 
    477     InvokeNode* invoke_node;
    478     cc.invoke(Out(invoke_node), fn, FuncSignature::build<double, double, double>());
    479     invoke_node->set_arg(0, x);
    480     invoke_node->set_arg(1, y);
    481     invoke_node->set_ret(0, r);
    482 
    483     cc.ret(r);
    484     cc.end_func();
    485   }
    486 
    487   bool run(void* _func, String& result, String& expect) override {
    488     using Func = double (*)(double, double);
    489     Func func = ptr_as_func<Func>(_func);
    490 
    491     double x = 49;
    492     double y = 7;
    493 
    494     result.assign_format("ret={%f}", func(x, y));
    495     expect.assign_format("ret={%f}", called_fn(x, y));
    496 
    497     return result == expect;
    498   }
    499 
    500   static double called_fn(double x, double y) {
    501     return x - y;
    502   }
    503 };
    504 
    505 // a64::Compiler - A64Test_Invoke3
    506 // ===============================
    507 
    508 class A64Test_Invoke3 : public A64TestCase {
    509 public:
    510   A64Test_Invoke3()
    511     : A64TestCase("Invoke3") {}
    512 
    513   static void add(TestApp& app) {
    514     app.add(new A64Test_Invoke3());
    515   }
    516 
    517   void compile(a64::Compiler& cc) override {
    518     FuncNode* func_node = cc.add_func(FuncSignature::build<double, double, double>());
    519 
    520     a64::Vec x = cc.new_vec_d("x");
    521     a64::Vec y = cc.new_vec_d("y");
    522     a64::Vec r = cc.new_vec_d("r");
    523     a64::Gp fn = cc.new_gp_ptr("fn");
    524 
    525     func_node->set_arg(0, x);
    526     func_node->set_arg(1, y);
    527     cc.mov(fn, (uint64_t)called_fn);
    528 
    529     InvokeNode* invoke_node;
    530     cc.invoke(Out(invoke_node), fn, FuncSignature::build<double, double, double>());
    531     invoke_node->set_arg(0, y);
    532     invoke_node->set_arg(1, x);
    533     invoke_node->set_ret(0, r);
    534 
    535     cc.ret(r);
    536     cc.end_func();
    537   }
    538 
    539   bool run(void* _func, String& result, String& expect) override {
    540     using Func = double (*)(double, double);
    541     Func func = ptr_as_func<Func>(_func);
    542 
    543     double x = 49;
    544     double y = 7;
    545 
    546     result.assign_format("ret={%f}", func(x, y));
    547     expect.assign_format("ret={%f}", called_fn(y, x));
    548 
    549     return result == expect;
    550   }
    551 
    552   static double called_fn(double x, double y) {
    553     return x - y;
    554   }
    555 };
    556 
    557 // a64::Compiler - A64Test_JumpTable
    558 // =================================
    559 
    560 class A64Test_JumpTable : public A64TestCase {
    561 public:
    562   bool _annotated;
    563 
    564   A64Test_JumpTable(bool annotated)
    565     : A64TestCase("A64Test_JumpTable"),
    566       _annotated(annotated) {
    567     _name.assign_format("JumpTable {%s}", annotated ? "Annotated" : "Unknown Target");
    568   }
    569 
    570   enum Operator {
    571     kOperatorAdd = 0,
    572     kOperatorSub = 1,
    573     kOperatorMul = 2,
    574     kOperatorDiv = 3
    575   };
    576 
    577   static void add(TestApp& app) {
    578     app.add(new A64Test_JumpTable(false));
    579     app.add(new A64Test_JumpTable(true));
    580   }
    581 
    582   void compile(a64::Compiler& cc) override {
    583     FuncNode* func_node = cc.add_func(FuncSignature::build<float, float, float, uint32_t>());
    584 
    585     a64::Vec a = cc.new_vec_s("a");
    586     a64::Vec b = cc.new_vec_s("b");
    587     a64::Gp op = cc.new_gp32("op");
    588 
    589     a64::Gp target = cc.new_gp_ptr("target");
    590     a64::Gp offset = cc.new_gp_ptr("offset");
    591 
    592     Label L_End = cc.new_label();
    593 
    594     Label L_Table = cc.new_label();
    595     Label L_Add = cc.new_label();
    596     Label L_Sub = cc.new_label();
    597     Label L_Mul = cc.new_label();
    598     Label L_Div = cc.new_label();
    599 
    600     func_node->set_arg(0, a);
    601     func_node->set_arg(1, b);
    602     func_node->set_arg(2, op);
    603 
    604     cc.adr(target, L_Table);
    605     cc.ldrsw(offset, a64::ptr(target, op, a64::sxtw(2)));
    606     cc.add(target, target, offset);
    607 
    608     // JumpAnnotation allows to annotate all possible jump targets of
    609     // instructions where it cannot be deduced from operands.
    610     if (_annotated) {
    611       JumpAnnotation* annotation = cc.new_jump_annotation();
    612       annotation->add_label(L_Add);
    613       annotation->add_label(L_Sub);
    614       annotation->add_label(L_Mul);
    615       annotation->add_label(L_Div);
    616       cc.br(target, annotation);
    617     }
    618     else {
    619       cc.br(target);
    620     }
    621 
    622     cc.bind(L_Add);
    623     cc.fadd(a, a, b);
    624     cc.b(L_End);
    625 
    626     cc.bind(L_Sub);
    627     cc.fsub(a, a, b);
    628     cc.b(L_End);
    629 
    630     cc.bind(L_Mul);
    631     cc.fmul(a, a, b);
    632     cc.b(L_End);
    633 
    634     cc.bind(L_Div);
    635     cc.fdiv(a, a, b);
    636 
    637     cc.bind(L_End);
    638     cc.ret(a);
    639     cc.end_func();
    640 
    641     cc.bind(L_Table);
    642     cc.embed_label_delta(L_Add, L_Table, 4);
    643     cc.embed_label_delta(L_Sub, L_Table, 4);
    644     cc.embed_label_delta(L_Mul, L_Table, 4);
    645     cc.embed_label_delta(L_Div, L_Table, 4);
    646   }
    647 
    648   bool run(void* _func, String& result, String& expect) override {
    649     using Func = float (*)(float, float, uint32_t);
    650     Func func = ptr_as_func<Func>(_func);
    651 
    652     float dst[4];
    653     float ref[4];
    654 
    655     dst[0] = func(33.0f, 14.0f, kOperatorAdd);
    656     dst[1] = func(33.0f, 14.0f, kOperatorSub);
    657     dst[2] = func(10.0f, 6.0f, kOperatorMul);
    658     dst[3] = func(80.0f, 8.0f, kOperatorDiv);
    659 
    660     ref[0] = 47.0f;
    661     ref[1] = 19.0f;
    662     ref[2] = 60.0f;
    663     ref[3] = 10.0f;
    664 
    665     result.assign_format("ret={%f, %f, %f, %f}", double(dst[0]), double(dst[1]), double(dst[2]), double(dst[3]));
    666     expect.assign_format("ret={%f, %f, %f, %f}", double(ref[0]), double(ref[1]), double(ref[2]), double(ref[3]));
    667 
    668     return result == expect;
    669   }
    670 };
    671 
    672 // a64::Compiler - Export
    673 // ======================
    674 
    675 void compiler_add_a64_tests(TestApp& app) {
    676   app.add_t<A64Test_GpArgs>();
    677   app.add_t<A64Test_ManyRegs>();
    678   app.add_t<A64Test_Simd1>();
    679   app.add_t<A64Test_Adr>();
    680   app.add_t<A64Test_Branch1>();
    681   app.add_t<A64Test_Invoke1>();
    682   app.add_t<A64Test_Invoke2>();
    683   app.add_t<A64Test_Invoke3>();
    684   app.add_t<A64Test_JumpTable>();
    685 }
    686 
    687 #endif // !ASMJIT_NO_COMPILER && !ASMJIT_NO_AARCH64