You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
12 KiB
352 lines
12 KiB
4 years ago
|
// AsmJit - Machine code generation for C++
|
||
|
//
|
||
|
// * Official AsmJit Home Page: https://asmjit.com
|
||
|
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||
|
//
|
||
|
// Copyright (c) 2008-2020 The AsmJit Authors
|
||
|
//
|
||
|
// This software is provided 'as-is', without any express or implied
|
||
|
// warranty. In no event will the authors be held liable for any damages
|
||
|
// arising from the use of this software.
|
||
|
//
|
||
|
// Permission is granted to anyone to use this software for any purpose,
|
||
|
// including commercial applications, and to alter it and redistribute it
|
||
|
// freely, subject to the following restrictions:
|
||
|
//
|
||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||
|
// claim that you wrote the original software. If you use this software
|
||
|
// in a product, an acknowledgment in the product documentation would be
|
||
|
// appreciated but is not required.
|
||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||
|
// misrepresented as being the original software.
|
||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||
|
|
||
|
#include "../core/api-build_p.h"
|
||
|
#include "../core/archtraits.h"
|
||
|
#include "../core/emithelper_p.h"
|
||
|
#include "../core/formatter.h"
|
||
|
#include "../core/funcargscontext_p.h"
|
||
|
#include "../core/radefs_p.h"
|
||
|
|
||
|
// Can be used for debugging...
|
||
|
// #define ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||
|
|
||
|
ASMJIT_BEGIN_NAMESPACE
|
||
|
|
||
|
// ============================================================================
|
||
|
// [asmjit::BaseEmitHelper - Formatting]
|
||
|
// ============================================================================
|
||
|
|
||
|
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||
|
static void dumpFuncValue(String& sb, uint32_t arch, const FuncValue& value) noexcept {
|
||
|
Formatter::formatTypeId(sb, value.typeId());
|
||
|
sb.append('@');
|
||
|
|
||
|
if (value.isIndirect())
|
||
|
sb.append('[');
|
||
|
|
||
|
if (value.isReg())
|
||
|
Formatter::formatRegister(sb, 0, nullptr, arch, value.regType(), value.regId());
|
||
|
else if (value.isStack())
|
||
|
sb.appendFormat("[%d]", value.stackOffset());
|
||
|
else
|
||
|
sb.append("<none>");
|
||
|
|
||
|
if (value.isIndirect())
|
||
|
sb.append(']');
|
||
|
}
|
||
|
|
||
|
static void dumpAssignment(String& sb, const FuncArgsContext& ctx) noexcept {
|
||
|
typedef FuncArgsContext::Var Var;
|
||
|
|
||
|
uint32_t arch = ctx.arch();
|
||
|
uint32_t varCount = ctx.varCount();
|
||
|
|
||
|
for (uint32_t i = 0; i < varCount; i++) {
|
||
|
const Var& var = ctx.var(i);
|
||
|
const FuncValue& dst = var.out;
|
||
|
const FuncValue& cur = var.cur;
|
||
|
|
||
|
sb.appendFormat("Var%u: ", i);
|
||
|
dumpFuncValue(sb, arch, dst);
|
||
|
sb.append(" <- ");
|
||
|
dumpFuncValue(sb, arch, cur);
|
||
|
|
||
|
if (var.isDone())
|
||
|
sb.append(" {Done}");
|
||
|
|
||
|
sb.append('\n');
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// ============================================================================
|
||
|
// [asmjit::BaseEmitHelper - EmitArgsAssignment]
|
||
|
// ============================================================================
|
||
|
|
||
|
ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
|
||
|
typedef FuncArgsContext::Var Var;
|
||
|
typedef FuncArgsContext::WorkData WorkData;
|
||
|
|
||
|
enum WorkFlags : uint32_t {
|
||
|
kWorkNone = 0x00,
|
||
|
kWorkDidSome = 0x01,
|
||
|
kWorkPending = 0x02,
|
||
|
kWorkPostponed = 0x04
|
||
|
};
|
||
|
|
||
|
uint32_t arch = frame.arch();
|
||
|
const ArchTraits& archTraits = ArchTraits::byArch(arch);
|
||
|
|
||
|
RAConstraints constraints;
|
||
|
FuncArgsContext ctx;
|
||
|
|
||
|
ASMJIT_PROPAGATE(constraints.init(arch));
|
||
|
ASMJIT_PROPAGATE(ctx.initWorkData(frame, args, &constraints));
|
||
|
|
||
|
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||
|
{
|
||
|
String sb;
|
||
|
dumpAssignment(sb, ctx);
|
||
|
printf("%s\n", sb.data());
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
uint32_t varCount = ctx._varCount;
|
||
|
WorkData* workData = ctx._workData;
|
||
|
|
||
|
uint32_t saVarId = ctx._saVarId;
|
||
|
BaseReg sp = BaseReg::fromSignatureAndId(_emitter->_gpRegInfo.signature(), archTraits.spRegId());
|
||
|
BaseReg sa = sp;
|
||
|
|
||
|
if (frame.hasDynamicAlignment()) {
|
||
|
if (frame.hasPreservedFP())
|
||
|
sa.setId(archTraits.fpRegId());
|
||
|
else
|
||
|
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Register to stack and stack to stack moves must be first as now we have
|
||
|
// the biggest chance of having as many as possible unassigned registers.
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
if (ctx._stackDstMask) {
|
||
|
// Base address of all arguments passed by stack.
|
||
|
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
|
||
|
BaseMem baseStackPtr(sp, 0);
|
||
|
|
||
|
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||
|
Var& var = ctx._vars[varId];
|
||
|
|
||
|
if (!var.out.isStack())
|
||
|
continue;
|
||
|
|
||
|
FuncValue& cur = var.cur;
|
||
|
FuncValue& out = var.out;
|
||
|
|
||
|
ASMJIT_ASSERT(cur.isReg() || cur.isStack());
|
||
|
BaseReg reg;
|
||
|
|
||
|
BaseMem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset());
|
||
|
BaseMem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset());
|
||
|
|
||
|
if (cur.isIndirect()) {
|
||
|
if (cur.isStack()) {
|
||
|
// TODO: Indirect stack.
|
||
|
return DebugUtils::errored(kErrorInvalidAssignment);
|
||
|
}
|
||
|
else {
|
||
|
srcStackPtr.setBaseId(cur.regId());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cur.isReg() && !cur.isIndirect()) {
|
||
|
WorkData& wd = workData[archTraits.regTypeToGroup(cur.regType())];
|
||
|
uint32_t rId = cur.regId();
|
||
|
|
||
|
reg.setSignatureAndId(archTraits.regTypeToSignature(cur.regType()), rId);
|
||
|
wd.unassign(varId, rId);
|
||
|
}
|
||
|
else {
|
||
|
// Stack to reg move - tricky since we move stack to stack we can decide which
|
||
|
// register to use. In general we follow the rule that IntToInt moves will use
|
||
|
// GP regs with possibility to signature or zero extend, and all other moves will
|
||
|
// either use GP or VEC regs depending on the size of the move.
|
||
|
RegInfo rInfo = getSuitableRegForMemToMemMove(arch, out.typeId(), cur.typeId());
|
||
|
if (ASMJIT_UNLIKELY(!rInfo.isValid()))
|
||
|
return DebugUtils::errored(kErrorInvalidState);
|
||
|
|
||
|
WorkData& wd = workData[rInfo.group()];
|
||
|
uint32_t availableRegs = wd.availableRegs();
|
||
|
if (ASMJIT_UNLIKELY(!availableRegs))
|
||
|
return DebugUtils::errored(kErrorInvalidState);
|
||
|
|
||
|
uint32_t rId = Support::ctz(availableRegs);
|
||
|
reg.setSignatureAndId(rInfo.signature(), rId);
|
||
|
|
||
|
ASMJIT_PROPAGATE(emitArgMove(reg, out.typeId(), srcStackPtr, cur.typeId()));
|
||
|
}
|
||
|
|
||
|
if (cur.isIndirect() && cur.isReg())
|
||
|
workData[BaseReg::kGroupGp].unassign(varId, cur.regId());
|
||
|
|
||
|
// Register to stack move.
|
||
|
ASMJIT_PROPAGATE(emitRegMove(dstStackPtr, reg, cur.typeId()));
|
||
|
var.markDone();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Shuffle all registers that are currently assigned accordingly to target
|
||
|
// assignment.
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
uint32_t workFlags = kWorkNone;
|
||
|
for (;;) {
|
||
|
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||
|
Var& var = ctx._vars[varId];
|
||
|
if (var.isDone() || !var.cur.isReg())
|
||
|
continue;
|
||
|
|
||
|
FuncValue& cur = var.cur;
|
||
|
FuncValue& out = var.out;
|
||
|
|
||
|
uint32_t curGroup = archTraits.regTypeToGroup(cur.regType());
|
||
|
uint32_t outGroup = archTraits.regTypeToGroup(out.regType());
|
||
|
|
||
|
uint32_t curId = cur.regId();
|
||
|
uint32_t outId = out.regId();
|
||
|
|
||
|
if (curGroup != outGroup) {
|
||
|
// TODO: Conversion is not supported.
|
||
|
return DebugUtils::errored(kErrorInvalidAssignment);
|
||
|
}
|
||
|
else {
|
||
|
WorkData& wd = workData[outGroup];
|
||
|
if (!wd.isAssigned(outId)) {
|
||
|
EmitMove:
|
||
|
ASMJIT_PROPAGATE(
|
||
|
emitArgMove(
|
||
|
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
|
||
|
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
|
||
|
|
||
|
wd.reassign(varId, outId, curId);
|
||
|
cur.initReg(out.regType(), outId, out.typeId());
|
||
|
|
||
|
if (outId == out.regId())
|
||
|
var.markDone();
|
||
|
workFlags |= kWorkDidSome | kWorkPending;
|
||
|
}
|
||
|
else {
|
||
|
uint32_t altId = wd._physToVarId[outId];
|
||
|
Var& altVar = ctx._vars[altId];
|
||
|
|
||
|
if (!altVar.out.isInitialized() || (altVar.out.isReg() && altVar.out.regId() == curId)) {
|
||
|
// Only few architectures provide swap operations, and only for few register groups.
|
||
|
if (archTraits.hasSwap(curGroup)) {
|
||
|
uint32_t highestType = Support::max(cur.regType(), altVar.cur.regType());
|
||
|
if (Support::isBetween<uint32_t>(highestType, BaseReg::kTypeGp8Lo, BaseReg::kTypeGp16))
|
||
|
highestType = BaseReg::kTypeGp32;
|
||
|
|
||
|
uint32_t signature = archTraits.regTypeToSignature(highestType);
|
||
|
ASMJIT_PROPAGATE(
|
||
|
emitRegSwap(BaseReg::fromSignatureAndId(signature, outId),
|
||
|
BaseReg::fromSignatureAndId(signature, curId)));
|
||
|
wd.swap(varId, curId, altId, outId);
|
||
|
cur.setRegId(outId);
|
||
|
var.markDone();
|
||
|
altVar.cur.setRegId(curId);
|
||
|
|
||
|
if (altVar.out.isInitialized())
|
||
|
altVar.markDone();
|
||
|
workFlags |= kWorkDidSome;
|
||
|
}
|
||
|
else {
|
||
|
// If there is a scratch register it can be used to perform the swap.
|
||
|
uint32_t availableRegs = wd.availableRegs();
|
||
|
if (availableRegs) {
|
||
|
uint32_t inOutRegs = wd.dstRegs();
|
||
|
if (availableRegs & ~inOutRegs)
|
||
|
availableRegs &= ~inOutRegs;
|
||
|
outId = Support::ctz(availableRegs);
|
||
|
goto EmitMove;
|
||
|
}
|
||
|
else {
|
||
|
workFlags |= kWorkPending;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
workFlags |= kWorkPending;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(workFlags & kWorkPending))
|
||
|
break;
|
||
|
|
||
|
// If we did nothing twice it means that something is really broken.
|
||
|
if ((workFlags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed)
|
||
|
return DebugUtils::errored(kErrorInvalidState);
|
||
|
|
||
|
workFlags = (workFlags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Load arguments passed by stack into registers. This is pretty simple and
|
||
|
// it never requires multiple iterations like the previous phase.
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
if (ctx._hasStackSrc) {
|
||
|
uint32_t iterCount = 1;
|
||
|
if (frame.hasDynamicAlignment() && !frame.hasPreservedFP())
|
||
|
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
|
||
|
|
||
|
// Base address of all arguments passed by stack.
|
||
|
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
|
||
|
|
||
|
for (uint32_t iter = 0; iter < iterCount; iter++) {
|
||
|
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||
|
Var& var = ctx._vars[varId];
|
||
|
if (var.isDone())
|
||
|
continue;
|
||
|
|
||
|
if (var.cur.isStack()) {
|
||
|
ASMJIT_ASSERT(var.out.isReg());
|
||
|
|
||
|
uint32_t outId = var.out.regId();
|
||
|
uint32_t outType = var.out.regType();
|
||
|
|
||
|
uint32_t group = archTraits.regTypeToGroup(outType);
|
||
|
WorkData& wd = ctx._workData[group];
|
||
|
|
||
|
if (outId == sa.id() && group == BaseReg::kGroupGp) {
|
||
|
// This register will be processed last as we still need `saRegId`.
|
||
|
if (iterCount == 1) {
|
||
|
iterCount++;
|
||
|
continue;
|
||
|
}
|
||
|
wd.unassign(wd._physToVarId[outId], outId);
|
||
|
}
|
||
|
|
||
|
BaseReg dstReg = BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(outType), outId);
|
||
|
BaseMem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset());
|
||
|
|
||
|
ASMJIT_PROPAGATE(emitArgMove(
|
||
|
dstReg, var.out.typeId(),
|
||
|
srcMem, var.cur.typeId()));
|
||
|
|
||
|
wd.assign(varId, outId);
|
||
|
var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return kErrorOk;
|
||
|
}
|
||
|
|
||
|
ASMJIT_END_NAMESPACE
|