mirror of
https://github.com/asmjit/asmjit
synced 2026-06-08 13:13:30 +00:00
b56f4176cb
* Denested src folder to root, renamed testing to asmjit-testing
* Refactored how headers are included into <asmjit/...> form. This
is necessary as compilers would never simplify a path once a ..
appears in include directory - then paths such as ../core/../core
appeared in asserts, which was ugly
* Moved support utilities into asmjit/support/... (still included
by asmjit/core.h for convenience and compatibility)
* Added CMakePresets.json for making it easy to develop AsmJit
* Reworked CMakeLists to be shorter and use CMake option(),
etc... This simplifies it and makes it using more standard
features
* ASMJIT_EMBED now creates asmjit_embed INTERFACE library,
which is accessible via asmjit::asmjit target - this simplifies
embedding and makes it the same as library targets from a CMake
perspective
* Removed ASMJIT_DEPS - this is now provided by cmake target
aliases - 'asmjit::asmjit' so users should not need this variable
* Changed meaning of ASMJIT_LIBS - this now contains only AsmJit
dependencies without asmjit::asmjit target alias. Don't rely on
ASMJIT_LIBS anymore as it's only used internally
* Removed ASMJIT_NO_DEPRECATED option - AsmJit is not going
to provide controllable deprecations in the future
* Removed ASMJIT_NO_VALIDATION in favor of ASMJIT_NO_INTROSPECTION,
which now controls query, features, and validation API presence
* Removed ASMJIT_DIR option - it was never really needed
* Removed AMX_TRANSPOSE feature from instruction database (X86).
Intel has removed it as well, so it's a feature that won't
be siliconized
341 lines
11 KiB
C++
341 lines
11 KiB
C++
// This file is part of AsmJit project <https://asmjit.com>
|
|
//
|
|
// See <asmjit/core.h> or LICENSE.md for license and copyright information
|
|
// SPDX-License-Identifier: Zlib
|
|
|
|
#include <asmjit/core/api-build_p.h>
|
|
#include <asmjit/core/funcargscontext_p.h>
|
|
|
|
ASMJIT_BEGIN_NAMESPACE
|
|
|
|
//! \cond INTERNAL
|
|
//! \addtogroup asmjit_core
|
|
//! \{
|
|
|
|
FuncArgsContext::FuncArgsContext() noexcept {
|
|
for (WorkData& wd : _work_data) {
|
|
wd.reset();
|
|
}
|
|
}
|
|
|
|
ASMJIT_FAVOR_SIZE Error FuncArgsContext::init_work_data(const FuncFrame& frame, const FuncArgsAssignment& args, const RAConstraints* constraints) noexcept {
|
|
Arch arch = frame.arch();
|
|
const FuncDetail& func = *args.func_detail();
|
|
|
|
_arch_traits = &ArchTraits::by_arch(arch);
|
|
_constraints = constraints;
|
|
_arch = arch;
|
|
|
|
// Initialize `_arch_regs`.
|
|
for (RegGroup group : Support::enumerate(RegGroup::kMaxVirt)) {
|
|
_work_data[group]._arch_regs = _constraints->available_regs(group);
|
|
}
|
|
|
|
if (frame.has_preserved_fp()) {
|
|
_work_data[size_t(RegGroup::kGp)]._arch_regs &= ~Support::bit_mask<RegMask>(arch_traits().fp_reg_id());
|
|
}
|
|
|
|
uint32_t reassignment_flag_mask = 0;
|
|
|
|
// Extract information from all function arguments/assignments and build Var[] array.
|
|
uint32_t var_id = 0;
|
|
uint32_t arg_count = args.func_detail()->arg_count();
|
|
|
|
for (uint32_t arg_index = 0; arg_index < arg_count; arg_index++) {
|
|
for (uint32_t value_index = 0; value_index < Globals::kMaxValuePack; value_index++) {
|
|
const FuncValue& dst_ = args.arg(arg_index, value_index);
|
|
if (!dst_.is_assigned()) {
|
|
continue;
|
|
}
|
|
|
|
const FuncValue& src_ = func.arg(arg_index, value_index);
|
|
if (ASMJIT_UNLIKELY(!src_.is_assigned())) {
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
Var& var = _vars[var_id];
|
|
var.init(src_, dst_);
|
|
|
|
FuncValue& src = var.cur;
|
|
FuncValue& dst = var.out;
|
|
|
|
RegGroup dst_group = RegGroup::kMaxValue;
|
|
uint32_t dst_id = Reg::kIdBad;
|
|
WorkData* dst_wd = nullptr;
|
|
|
|
// Not supported.
|
|
if (src.is_indirect()) {
|
|
return make_error(Error::kInvalidAssignment);
|
|
}
|
|
|
|
if (dst.is_reg()) {
|
|
RegType dst_type = dst.reg_type();
|
|
if (ASMJIT_UNLIKELY(!arch_traits().has_reg_type(dst_type))) {
|
|
return make_error(Error::kInvalidRegType);
|
|
}
|
|
|
|
// Copy TypeId from source if the destination doesn't have it. The RA used by BaseCompiler would never
|
|
// leave TypeId undefined, but users of FuncAPI can just assign phys regs without specifying their types.
|
|
if (!dst.has_type_id()) {
|
|
dst.set_type_id(RegUtils::type_id_of(dst.reg_type()));
|
|
}
|
|
|
|
dst_group = RegUtils::group_of(dst_type);
|
|
if (ASMJIT_UNLIKELY(dst_group > RegGroup::kMaxVirt)) {
|
|
return make_error(Error::kInvalidRegGroup);
|
|
}
|
|
|
|
dst_wd = &_work_data[dst_group];
|
|
dst_id = dst.reg_id();
|
|
|
|
if (ASMJIT_UNLIKELY(dst_id >= 32 || !Support::bit_test(dst_wd->arch_regs(), dst_id))) {
|
|
return make_error(Error::kInvalidPhysId);
|
|
}
|
|
|
|
if (ASMJIT_UNLIKELY(Support::bit_test(dst_wd->dst_regs(), dst_id))) {
|
|
return make_error(Error::kOverlappedRegs);
|
|
}
|
|
|
|
dst_wd->_dst_regs |= Support::bit_mask<RegMask>(dst_id);
|
|
dst_wd->_dst_shuf |= Support::bit_mask<RegMask>(dst_id);
|
|
dst_wd->_used_regs |= Support::bit_mask<RegMask>(dst_id);
|
|
}
|
|
else {
|
|
if (!dst.has_type_id()) {
|
|
dst.set_type_id(src.type_id());
|
|
}
|
|
|
|
OperandSignature signature = get_suitable_reg_for_mem_to_mem_move(arch, dst.type_id(), src.type_id());
|
|
if (ASMJIT_UNLIKELY(!signature.is_valid())) {
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
_stack_dst_mask = uint8_t(_stack_dst_mask | Support::bit_mask<uint32_t>(signature.reg_group()));
|
|
}
|
|
|
|
if (src.is_reg()) {
|
|
uint32_t src_id = src.reg_id();
|
|
RegGroup src_group = RegUtils::group_of(src.reg_type());
|
|
|
|
if (dst_group == src_group) {
|
|
ASMJIT_ASSERT(dst_wd != nullptr);
|
|
dst_wd->assign(var_id, src_id);
|
|
|
|
reassignment_flag_mask |= uint32_t(dst_id != src_id) << uint32_t(dst_group);
|
|
|
|
if (dst_id == src_id) {
|
|
// The best case, register is allocated where it is expected to be. However, we should
|
|
// not mark this as done if both registers are GP and sign or zero extension is required.
|
|
if (dst_group != RegGroup::kGp) {
|
|
var.mark_done();
|
|
}
|
|
else {
|
|
TypeId dt = dst.type_id();
|
|
TypeId st = src.type_id();
|
|
|
|
uint32_t dst_size = TypeUtils::size_of(dt);
|
|
uint32_t src_size = TypeUtils::size_of(st);
|
|
|
|
if (dt == TypeId::kVoid || st == TypeId::kVoid || dst_size <= src_size) {
|
|
var.mark_done();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (ASMJIT_UNLIKELY(src_group > RegGroup::kMaxVirt)) {
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
WorkData& src_data = _work_data[size_t(src_group)];
|
|
src_data.assign(var_id, src_id);
|
|
reassignment_flag_mask |= 1u << uint32_t(dst_group);
|
|
}
|
|
}
|
|
else {
|
|
if (dst_wd)
|
|
dst_wd->_num_stack_args++;
|
|
_has_stack_src = true;
|
|
}
|
|
|
|
var_id++;
|
|
}
|
|
}
|
|
|
|
// Initialize WorkData::work_regs.
|
|
for (RegGroup group : Support::enumerate(RegGroup::kMaxVirt)) {
|
|
_work_data[group]._work_regs =
|
|
(_work_data[group].arch_regs() & (frame.dirty_regs(group) | ~frame.preserved_regs(group))) | _work_data[group].dst_regs() | _work_data[group].assigned_regs();
|
|
_work_data[group]._needs_scratch = (reassignment_flag_mask >> uint32_t(group)) & 1u;
|
|
}
|
|
|
|
// Create a variable that represents `SARegId` if necessary.
|
|
bool sa_reg_required = _has_stack_src && frame.has_dynamic_alignment() && !frame.has_preserved_fp();
|
|
|
|
WorkData& gp_regs = _work_data[RegGroup::kGp];
|
|
uint32_t sa_cur_reg_id = frame.sa_reg_id();
|
|
uint32_t sa_out_reg_id = args.sa_reg_id();
|
|
|
|
if (sa_cur_reg_id != Reg::kIdBad) {
|
|
// Check if the provided `SARegId` doesn't collide with input registers.
|
|
if (ASMJIT_UNLIKELY(gp_regs.is_assigned(sa_cur_reg_id))) {
|
|
return make_error(Error::kOverlappedRegs);
|
|
}
|
|
}
|
|
|
|
if (sa_out_reg_id != Reg::kIdBad) {
|
|
// Check if the provided `SARegId` doesn't collide with argument assignments.
|
|
if (ASMJIT_UNLIKELY(Support::bit_test(gp_regs.dst_regs(), sa_out_reg_id))) {
|
|
return make_error(Error::kOverlappedRegs);
|
|
}
|
|
sa_reg_required = true;
|
|
}
|
|
|
|
if (sa_reg_required) {
|
|
TypeId ptr_type_id = Environment::is_32bit(arch) ? TypeId::kUInt32 : TypeId::kUInt64;
|
|
RegType ptr_reg_type = Environment::is_32bit(arch) ? RegType::kGp32 : RegType::kGp64;
|
|
|
|
_sa_var_id = uint8_t(var_id);
|
|
_has_preserved_fp = frame.has_preserved_fp();
|
|
|
|
Var& var = _vars[var_id];
|
|
var.reset();
|
|
|
|
if (sa_cur_reg_id == Reg::kIdBad) {
|
|
if (sa_out_reg_id != Reg::kIdBad && !gp_regs.is_assigned(sa_out_reg_id)) {
|
|
sa_cur_reg_id = sa_out_reg_id;
|
|
}
|
|
else {
|
|
RegMask available_regs = gp_regs.available_regs();
|
|
if (!available_regs) {
|
|
available_regs = gp_regs.arch_regs() & ~gp_regs.work_regs();
|
|
}
|
|
|
|
if (ASMJIT_UNLIKELY(!available_regs)) {
|
|
return make_error(Error::kNoMorePhysRegs);
|
|
}
|
|
|
|
sa_cur_reg_id = Support::ctz(available_regs);
|
|
}
|
|
}
|
|
|
|
var.cur.init_reg(ptr_reg_type, sa_cur_reg_id, ptr_type_id);
|
|
gp_regs.assign(var_id, sa_cur_reg_id);
|
|
gp_regs._work_regs |= Support::bit_mask<RegMask>(sa_cur_reg_id);
|
|
|
|
if (sa_out_reg_id != Reg::kIdBad) {
|
|
var.out.init_reg(ptr_reg_type, sa_out_reg_id, ptr_type_id);
|
|
gp_regs._dst_regs |= Support::bit_mask<RegMask>(sa_out_reg_id);
|
|
gp_regs._work_regs |= Support::bit_mask<RegMask>(sa_out_reg_id);
|
|
}
|
|
else {
|
|
var.mark_done();
|
|
}
|
|
|
|
var_id++;
|
|
}
|
|
|
|
_var_count = var_id;
|
|
|
|
// Detect register swaps.
|
|
for (var_id = 0; var_id < _var_count; var_id++) {
|
|
Var& var = _vars[var_id];
|
|
if (var.cur.is_reg() && var.out.is_reg()) {
|
|
uint32_t src_id = var.cur.reg_id();
|
|
uint32_t dst_id = var.out.reg_id();
|
|
|
|
RegGroup group = RegUtils::group_of(var.cur.reg_type());
|
|
if (group != RegUtils::group_of(var.out.reg_type())) {
|
|
continue;
|
|
}
|
|
|
|
WorkData& wd = _work_data[group];
|
|
if (wd.is_assigned(dst_id)) {
|
|
Var& other = _vars[wd._phys_to_var_id[dst_id]];
|
|
if (RegUtils::group_of(other.out.reg_type()) == group && other.out.reg_id() == src_id) {
|
|
wd._num_swaps++;
|
|
_reg_swaps_mask = uint8_t(_reg_swaps_mask | Support::bit_mask<uint32_t>(group));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
ASMJIT_FAVOR_SIZE Error FuncArgsContext::mark_dst_regs_dirty(FuncFrame& frame) noexcept {
|
|
for (RegGroup group : Support::enumerate(RegGroup::kMaxVirt)) {
|
|
WorkData& wd = _work_data[group];
|
|
uint32_t regs = wd.used_regs() | wd._dst_shuf;
|
|
|
|
wd._work_regs |= regs;
|
|
frame.add_dirty_regs(group, regs);
|
|
}
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
ASMJIT_FAVOR_SIZE Error FuncArgsContext::mark_scratch_regs(FuncFrame& frame) noexcept {
|
|
uint32_t group_mask = 0;
|
|
|
|
// Handle stack to stack moves.
|
|
group_mask |= _stack_dst_mask;
|
|
|
|
// Handle register swaps.
|
|
group_mask |= _reg_swaps_mask & ~Support::bit_mask<uint32_t>(RegGroup::kGp);
|
|
|
|
if (!group_mask)
|
|
return Error::kOk;
|
|
|
|
// Selects one dirty register per affected group that can be used as a scratch register.
|
|
for (RegGroup group : Support::enumerate(RegGroup::kMaxVirt)) {
|
|
if (Support::bit_test(group_mask, group)) {
|
|
WorkData& wd = _work_data[group];
|
|
if (wd._needs_scratch) {
|
|
// Initially, pick some clobbered or dirty register.
|
|
RegMask work_regs = wd.work_regs();
|
|
RegMask regs = work_regs & ~(wd.used_regs() | wd._dst_shuf);
|
|
|
|
// If that didn't work out pick some register which is not in 'used'.
|
|
if (!regs) {
|
|
regs = work_regs & ~wd.used_regs();
|
|
}
|
|
|
|
// If that didn't work out pick any other register that is allocable.
|
|
// This last resort case will, however, result in marking one more
|
|
// register dirty.
|
|
if (!regs) {
|
|
regs = wd.arch_regs() & ~work_regs;
|
|
}
|
|
|
|
// If that didn't work out we will have to use XORs instead of MOVs.
|
|
if (!regs) {
|
|
continue;
|
|
}
|
|
|
|
RegMask reg_mask = Support::blsi(regs);
|
|
wd._work_regs |= reg_mask;
|
|
frame.add_dirty_regs(group, reg_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
ASMJIT_FAVOR_SIZE Error FuncArgsContext::mark_stack_args_reg(FuncFrame& frame) noexcept {
|
|
if (_sa_var_id != kVarIdNone) {
|
|
const Var& var = _vars[_sa_var_id];
|
|
frame.set_sa_reg_id(var.cur.reg_id());
|
|
}
|
|
else if (frame.has_preserved_fp()) {
|
|
frame.set_sa_reg_id(arch_traits().fp_reg_id());
|
|
}
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
//! \}
|
|
//! \endcond
|
|
|
|
ASMJIT_END_NAMESPACE
|