odin-blend2d

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

emithelper.cpp (11600B)


      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 "../core/api-build_p.h"
      7 #include "../core/archtraits.h"
      8 #include "../core/emithelper_p.h"
      9 #include "../core/formatter.h"
     10 #include "../core/funcargscontext_p.h"
     11 #include "../core/radefs_p.h"
     12 
     13 // Can be used for debugging...
     14 // #define ASMJIT_DUMP_ARGS_ASSIGNMENT
     15 
     16 ASMJIT_BEGIN_NAMESPACE
     17 
     18 // BaseEmitHelper - Formatting
     19 // ===========================
     20 
     21 #ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
     22 static void dump_func_value(String& sb, Arch arch, const FuncValue& value) noexcept {
     23   Formatter::format_type_id(sb, value.type_id());
     24   sb.append('@');
     25 
     26   if (value.is_indirect()) {
     27     sb.append('[');
     28   }
     29 
     30   if (value.is_reg()) {
     31     Formatter::format_register(sb, 0, nullptr, arch, value.reg_type(), value.reg_id());
     32   }
     33   else if (value.is_stack()) {
     34     sb.append_format("[%d]", value.stack_offset());
     35   }
     36   else {
     37     sb.append("<none>");
     38   }
     39 
     40   if (value.is_indirect()) {
     41     sb.append(']');
     42   }
     43 }
     44 
     45 static void dump_assignment(String& sb, const FuncArgsContext& ctx) noexcept {
     46   using Var = FuncArgsContext::Var;
     47 
     48   Arch arch = ctx.arch();
     49   uint32_t var_count = ctx.var_count();
     50 
     51   for (uint32_t i = 0; i < var_count; i++) {
     52     const Var& var = ctx.var(i);
     53     const FuncValue& dst = var.out;
     54     const FuncValue& cur = var.cur;
     55 
     56     sb.append_format("Var%u: ", i);
     57     dump_func_value(sb, arch, dst);
     58     sb.append(" <- ");
     59     dump_func_value(sb, arch, cur);
     60 
     61     if (var.is_done()) {
     62       sb.append(" {Done}");
     63     }
     64 
     65     sb.append('\n');
     66   }
     67 }
     68 #endif
     69 
     70 // BaseEmitHelper - Abstract
     71 // =========================
     72 
     73 Error BaseEmitHelper::emit_reg_move(const Operand_& dst_, const Operand_& src_, TypeId type_id, const char* comment) {
     74   Support::maybe_unused(dst_, src_, type_id, comment);
     75   return make_error(Error::kInvalidState);
     76 }
     77 
     78 Error BaseEmitHelper::emit_reg_swap(const Reg& a, const Reg& b, const char* comment) {
     79   Support::maybe_unused(a, b, comment);
     80   return make_error(Error::kInvalidState);
     81 }
     82 
     83 Error BaseEmitHelper::emit_arg_move(const Reg& dst_, TypeId dst_type_id, const Operand_& src_, TypeId src_type_id, const char* comment) {
     84   Support::maybe_unused(dst_, dst_type_id, src_, src_type_id, comment);
     85   return make_error(Error::kInvalidState);
     86 }
     87 
     88 // BaseEmitHelper - EmitArgsAssignment
     89 // ===================================
     90 
     91 ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emit_args_assignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
     92   using Var = FuncArgsContext::Var;
     93   using WorkData = FuncArgsContext::WorkData;
     94 
     95   enum WorkFlags : uint32_t {
     96     kWorkNone      = 0x00,
     97     kWorkDidSome   = 0x01,
     98     kWorkPending   = 0x02,
     99     kWorkPostponed = 0x04
    100   };
    101 
    102   Arch arch = frame.arch();
    103   const ArchTraits& arch_traits = ArchTraits::by_arch(arch);
    104 
    105   RAConstraints constraints;
    106   FuncArgsContext ctx;
    107 
    108   ASMJIT_PROPAGATE(constraints.init(arch));
    109   ASMJIT_PROPAGATE(ctx.init_work_data(frame, args, &constraints));
    110 
    111 #ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
    112   {
    113     String sb;
    114     dump_assignment(sb, ctx);
    115     printf("%s\n", sb.data());
    116   }
    117 #endif
    118 
    119   auto& work_data = ctx._work_data;
    120   uint32_t var_count = ctx._var_count;
    121   uint32_t sa_var_id = ctx._sa_var_id;
    122 
    123   Reg sp = Reg(_emitter->_gp_signature, arch_traits.sp_reg_id());
    124   Reg sa = sp;
    125 
    126   if (frame.has_dynamic_alignment()) {
    127     if (frame.has_preserved_fp()) {
    128       sa.set_id(arch_traits.fp_reg_id());
    129     }
    130     else {
    131       sa.set_id(sa_var_id < var_count ? ctx._vars[sa_var_id].cur.reg_id() : frame.sa_reg_id());
    132     }
    133   }
    134 
    135   // Register to stack and stack to stack moves must be first as now we have
    136   // the biggest chance of having as many as possible unassigned registers.
    137 
    138   if (ctx._stack_dst_mask) {
    139     // Base address of all arguments passed by stack.
    140     BaseMem base_arg_ptr(sa, int32_t(frame.sa_offset(sa.id())));
    141     BaseMem base_stack_ptr(sp, 0);
    142 
    143     for (uint32_t var_id = 0; var_id < var_count; var_id++) {
    144       Var& var = ctx._vars[var_id];
    145 
    146       if (!var.out.is_stack()) {
    147         continue;
    148       }
    149 
    150       FuncValue& cur = var.cur;
    151       FuncValue& out = var.out;
    152 
    153       ASMJIT_ASSERT(cur.is_reg() || cur.is_stack());
    154       Reg reg;
    155 
    156       BaseMem dst_stack_ptr = base_stack_ptr.clone_adjusted(out.stack_offset());
    157       BaseMem src_stack_ptr = base_arg_ptr.clone_adjusted(cur.stack_offset());
    158 
    159       if (cur.is_indirect()) {
    160         if (cur.is_stack()) {
    161           // TODO: Indirect stack.
    162           return make_error(Error::kInvalidAssignment);
    163         }
    164         else {
    165           src_stack_ptr.set_base_id(cur.reg_id());
    166         }
    167       }
    168 
    169       if (cur.is_reg() && !cur.is_indirect()) {
    170         WorkData& wd = work_data[RegUtils::group_of(cur.reg_type())];
    171         uint32_t reg_id = cur.reg_id();
    172 
    173         reg.set_signature_and_id(RegUtils::signature_of(cur.reg_type()), reg_id);
    174         wd.unassign(var_id, reg_id);
    175       }
    176       else {
    177         // Stack to reg move - tricky since we move stack to stack we can decide which register to use. In general
    178         // we follow the rule that IntToInt moves will use GP regs with possibility to signature or zero extend,
    179         // and all other moves will either use GP or VEC regs depending on the size of the move.
    180         OperandSignature signature = get_suitable_reg_for_mem_to_mem_move(arch, out.type_id(), cur.type_id());
    181         if (ASMJIT_UNLIKELY(!signature.is_valid())) {
    182           return make_error(Error::kInvalidState);
    183         }
    184 
    185         WorkData& wd = work_data[signature.reg_group()];
    186         RegMask available_regs = wd.available_regs();
    187         if (ASMJIT_UNLIKELY(!available_regs)) {
    188           return make_error(Error::kInvalidState);
    189         }
    190 
    191         uint32_t available_id = Support::ctz(available_regs);
    192         reg.set_signature_and_id(signature, available_id);
    193 
    194         ASMJIT_PROPAGATE(emit_arg_move(reg, out.type_id(), src_stack_ptr, cur.type_id()));
    195       }
    196 
    197       if (cur.is_indirect() && cur.is_reg()) {
    198         work_data[RegGroup::kGp].unassign(var_id, cur.reg_id());
    199       }
    200 
    201       // Register to stack move.
    202       ASMJIT_PROPAGATE(emit_reg_move(dst_stack_ptr, reg, cur.type_id()));
    203       var.mark_done();
    204     }
    205   }
    206 
    207   // Shuffle all registers that are currently assigned accordingly to target assignment.
    208 
    209   uint32_t work_flags = kWorkNone;
    210   for (;;) {
    211     for (uint32_t var_id = 0; var_id < var_count; var_id++) {
    212       Var& var = ctx._vars[var_id];
    213       if (var.is_done() || !var.cur.is_reg()) {
    214         continue;
    215       }
    216 
    217       FuncValue& cur = var.cur;
    218       FuncValue& out = var.out;
    219 
    220       RegGroup cur_group = RegUtils::group_of(cur.reg_type());
    221       RegGroup out_group = RegUtils::group_of(out.reg_type());
    222 
    223       uint32_t cur_id = cur.reg_id();
    224       uint32_t out_id = out.reg_id();
    225 
    226       if (cur_group != out_group) {
    227         // TODO: Conversion is not supported.
    228         return make_error(Error::kInvalidAssignment);
    229       }
    230       else {
    231         WorkData& wd = work_data[out_group];
    232         if (!wd.is_assigned(out_id) || cur_id == out_id) {
    233 EmitMove:
    234           ASMJIT_PROPAGATE(
    235             emit_arg_move(
    236               Reg(RegUtils::signature_of(out.reg_type()), out_id), out.type_id(),
    237               Reg(RegUtils::signature_of(cur.reg_type()), cur_id), cur.type_id()));
    238 
    239           // Only reassign if this is not a sign/zero extension that happens on the same in/out register.
    240           if (cur_id != out_id) {
    241             wd.reassign(var_id, out_id, cur_id);
    242           }
    243 
    244           cur.init_reg(out.reg_type(), out_id, out.type_id());
    245 
    246           if (out_id == out.reg_id()) {
    247             var.mark_done();
    248           }
    249           work_flags |= kWorkDidSome | kWorkPending;
    250         }
    251         else {
    252           uint32_t alt_id = wd._phys_to_var_id[out_id];
    253           Var& alt_var = ctx._vars[alt_id];
    254 
    255           if (!alt_var.out.is_initialized() || (alt_var.out.is_reg() && alt_var.out.reg_id() == cur_id)) {
    256             // Only few architectures provide swap operations, and only for few register groups.
    257             if (arch_traits.has_inst_reg_swap(cur_group)) {
    258               RegType highest_type = Support::max(cur.reg_type(), alt_var.cur.reg_type());
    259               if (Support::is_between(highest_type, RegType::kGp8Lo, RegType::kGp16)) {
    260                 highest_type = RegType::kGp32;
    261               }
    262 
    263               OperandSignature signature = RegUtils::signature_of(highest_type);
    264               ASMJIT_PROPAGATE(emit_reg_swap(Reg(signature, out_id), Reg(signature, cur_id)));
    265 
    266               wd.swap(var_id, cur_id, alt_id, out_id);
    267               cur.set_reg_id(out_id);
    268               var.mark_done();
    269               alt_var.cur.set_reg_id(cur_id);
    270 
    271               if (alt_var.out.is_initialized()) {
    272                 alt_var.mark_done();
    273               }
    274               work_flags |= kWorkDidSome;
    275             }
    276             else {
    277               // If there is a scratch register it can be used to perform the swap.
    278               RegMask available_regs = wd.available_regs();
    279               if (available_regs) {
    280                 RegMask in_out_regs = wd.dst_regs();
    281                 if (available_regs & ~in_out_regs) {
    282                   available_regs &= ~in_out_regs;
    283                 }
    284                 out_id = Support::ctz(available_regs);
    285                 goto EmitMove;
    286               }
    287               else {
    288                 work_flags |= kWorkPending;
    289               }
    290             }
    291           }
    292           else {
    293             work_flags |= kWorkPending;
    294           }
    295         }
    296       }
    297     }
    298 
    299     if (!(work_flags & kWorkPending)) {
    300       break;
    301     }
    302 
    303     // If we did nothing twice it means that something is really broken.
    304     if ((work_flags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed) {
    305       return make_error(Error::kInvalidState);
    306     }
    307 
    308     work_flags = (work_flags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
    309   }
    310 
    311   // Load arguments passed by stack into registers. This is pretty simple and
    312   // it never requires multiple iterations like the previous phase.
    313 
    314   if (ctx._has_stack_src) {
    315     uint32_t iter_count = 1;
    316     if (frame.has_dynamic_alignment() && !frame.has_preserved_fp()) {
    317       sa.set_id(sa_var_id < var_count ? ctx._vars[sa_var_id].cur.reg_id() : frame.sa_reg_id());
    318     }
    319 
    320     // Base address of all arguments passed by stack.
    321     BaseMem base_arg_ptr(sa, int32_t(frame.sa_offset(sa.id())));
    322 
    323     for (uint32_t iter = 0; iter < iter_count; iter++) {
    324       for (uint32_t var_id = 0; var_id < var_count; var_id++) {
    325         Var& var = ctx._vars[var_id];
    326         if (var.is_done()) {
    327           continue;
    328         }
    329 
    330         if (var.cur.is_stack()) {
    331           ASMJIT_ASSERT(var.out.is_reg());
    332 
    333           uint32_t out_id = var.out.reg_id();
    334           RegType out_type = var.out.reg_type();
    335 
    336           RegGroup group = RegUtils::group_of(out_type);
    337           WorkData& wd = work_data[group];
    338 
    339           if (out_id == sa.id() && group == RegGroup::kGp) {
    340             // This register will be processed last as we still need `sa_reg_id`.
    341             if (iter_count == 1) {
    342               iter_count++;
    343               continue;
    344             }
    345             wd.unassign(wd._phys_to_var_id[out_id], out_id);
    346           }
    347 
    348           Reg dst_reg = Reg(RegUtils::signature_of(out_type), out_id);
    349           BaseMem src_mem = base_arg_ptr.clone_adjusted(var.cur.stack_offset());
    350 
    351           ASMJIT_PROPAGATE(emit_arg_move(
    352             dst_reg, var.out.type_id(),
    353             src_mem, var.cur.type_id()));
    354 
    355           wd.assign(var_id, out_id);
    356           var.cur.init_reg(out_type, out_id, var.cur.type_id(), FuncValue::kFlagIsDone);
    357         }
    358       }
    359     }
    360   }
    361 
    362   return Error::kOk;
    363 }
    364 
    365 ASMJIT_END_NAMESPACE