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