Каким образом значения регистров передаются в трансляторах? Как происходит преобразование данных из виртуальных регистров в машинные?

Как можно прекратить поддержку структуры виртуальных регистров при выполнении клиентского кода в ходе динамической трансляции? Необходимо перестать работать с виртуальными регистрами и начать использовать реальные регистры непосредственно в коде трансляции. Какие методы можно применить для этого?
  • 29 февраля 2024 г. 23:38
Ответы на вопрос 2
Значения регистров передаются в трансляторах обычно с помощью внутренних структур данных, которые отображают виртуальные регистры на их машинные эквиваленты. Во время преобразования данных из виртуальных регистров в машинные, транслятор использует эти структуры данных для определения соответствия и переноса значений.

Для прекращения поддержки виртуальных регистров при выполнении клиентского кода в ходе динамической трансляции и использования реальных регистров непосредственно в коде трансляции, можно применить следующие методы:
1. Изменение структуры данных: изменить внутренние структуры данных транслятора таким образом, чтобы вместо виртуальных регистров использовались реальные регистры.
2. Пересмотр алгоритмов: пересмотреть алгоритмы трансляции так, чтобы они работали с реальными регистрами напрямую, без промежуточного использования виртуальных регистров.
3. Переписать код транслятора: переписать код транслятора таким образом, чтобы он напрямую работал с реальными регистрами вместо виртуальных.

Выбор конкретного метода зависит от специфики используемого транслятора и требований к производительности и эффективности работы.
Как перестать поддерживать ее, при выполнении клиентского кода в ходе динамической трансляции.

Просто иметь оптимизации, которые знают, какой регистр какое название имеет.

Раз уж ты поставил тег .NET, то у платформы есть свой JIT компилятор, который пишется под каждую платформу (CPU) и имеет знание о ее регистрах.
Вот пример того, как это делается в исходном коде:
1. Каждая платформа регистрирует свои регистры и информацию о них.
#if defined(TARGET_XARCH)

#if defined(TARGET_X86)
/*
REGDEF(name, rnum,   mask, sname) */
REGDEF(EAX,     0,   0x01, "eax"   )
REGDEF(ECX,     1,   0x02, "ecx"   )
REGDEF(EDX,     2,   0x04, "edx"   )
REGDEF(EBX,     3,   0x08, "ebx"   )
REGDEF(ESP,     4,   0x10, "esp"   )
REGDEF(EBP,     5,   0x20, "ebp"   )
REGDEF(ESI,     6,   0x40, "esi"   )
REGDEF(EDI,     7,   0x80, "edi"   )
REGALIAS(RAX, EAX)
REGALIAS(RCX, ECX)
REGALIAS(RDX, EDX)
REGALIAS(RBX, EBX)
REGALIAS(RSP, ESP)
REGALIAS(RBP, EBP)
REGALIAS(RSI, ESI)
REGALIAS(RDI, EDI)

#else // !defined(TARGET_X86)

/*
REGDEF(name, rnum,   mask, sname) */
REGDEF(RAX,     0, 0x0001, "rax"   )
REGDEF(RCX,     1, 0x0002, "rcx"   )
REGDEF(RDX,     2, 0x0004, "rdx"   )
REGDEF(RBX,     3, 0x0008, "rbx"   )
REGDEF(RSP,     4, 0x0010, "rsp"   )
REGDEF(RBP,     5, 0x0020, "rbp"   )
REGDEF(RSI,     6, 0x0040, "rsi"   )
REGDEF(RDI,     7, 0x0080, "rdi"   )
REGDEF(R8,      8, 0x0100, "r8"    )
REGDEF(R9,      9, 0x0200, "r9"    )
REGDEF(R10,    10, 0x0400, "r10"   )
REGDEF(R11,    11, 0x0800, "r11"   )
REGDEF(R12,    12, 0x1000, "r12"   )
REGDEF(R13,    13, 0x2000, "r13"   )
REGDEF(R14,    14, 0x4000, "r14"   )
REGDEF(R15,    15, 0x8000, "r15"   )

REGALIAS(EAX, RAX)
REGALIAS(ECX, RCX)
REGALIAS(EDX, RDX)
REGALIAS(EBX, RBX)
REGALIAS(ESP, RSP)
REGALIAS(EBP, RBP)
REGALIAS(ESI, RSI)
REGALIAS(EDI, RDI)

#endif // !defined(TARGET_X86)

2. Для каждой платформы реализуются своя пара кодогенератор / эмиттер

// Кодогенератор
void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
{
    GenTree* op1 = treeNode->gtGetOp1();
    GenTree* op2 = treeNode->gtGetOp2();

    instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);

    regNumber op1reg = op1->isUsedFromReg() ? op1->GetRegNum() : REG_NA;
    regNumber op2reg = op2->isUsedFromReg() ? op2->GetRegNum() : REG_NA;

    GenTree* dst;
    GenTree* src;

    // This is the case of reg1 = reg1 op reg2
    // We're ready to emit the instruction without any moves
    if (op1reg == targetReg)
    {
        dst = op1;
        src = op2;
    }
    // We have reg1 = reg2 op reg1
    // In order for this operation to be correct
    // we need that op is a commutative operation so
    // we can convert it into reg1 = reg1 op reg2 and emit
    // the same code as above
    else if (op2reg == targetReg)
    {
        dst = op2;
        src = op1;
    }
    // dest, op1 and op2 registers are different:
    // reg3 = reg1 op reg2
    // We can implement this by issuing a mov:
    // reg3 = reg1
    // reg3 = reg3 op reg2
    else
    {
        var_types op1Type = op1->TypeGet();
        inst_Mov(op1Type, targetReg, op1reg, /* canSkip */ false);
        regSet.verifyRegUsed(targetReg);
        gcInfo.gcMarkRegPtrVal(targetReg, op1Type);
        dst = treeNode;
        src = op2;
    }
    // try to use an inc or dec
    if (oper == GT_ADD && !varTypeIsFloating(treeNode) && src->isContainedIntOrIImmed() && !treeNode->gtOverflowEx())
    {
        if (src->IsIntegralConst(1))
        {
            emit->emitIns_R(INS_inc, emitTypeSize(treeNode), targetReg);
            genProduceReg(treeNode);
            return;
        }
        else if (src->IsIntegralConst(-1))
        {
            emit->emitIns_R(INS_dec, emitTypeSize(treeNode), targetReg);
            genProduceReg(treeNode);
            return;
        }
    }
    regNumber r = emit->emitInsBinary(ins, emitTypeSize(treeNode), dst, src);
}

// Эммитер
/*****************************************************************************
 *
 *  Add an instruction with two register operands.
 */

void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts instOptions)
{
    if (IsMovInstruction(ins))
    {
        assert(!"Please use emitIns_Mov() to correctly handle move elision");
        emitIns_Mov(ins, attr, reg1, reg2, /* canSkip */ false);
    }

    emitAttr size = EA_SIZE(attr);

    assert(size <= EA_64BYTE);
    noway_assert(emitVerifyEncodable(ins, size, reg1, reg2));

    /* Special case: "XCHG" uses a different format */
    insFormat fmt = (ins == INS_xchg) ? IF_RRW_RRW : emitInsModeFormat(ins, IF_RRD_RRD);

    instrDesc* id = emitNewInstrSmall(attr);
    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idReg1(reg1);
    id->idReg2(reg2);

    if ((instOptions & INS_OPTS_EVEX_b_MASK) != INS_OPTS_NONE)
    {
        // if EVEX.b needs to be set in this path, then it should be embedded rounding.
        assert(UseEvexEncoding());
        id->idSetEvexbContext(instOptions);
    }

    UNATIVE_OFFSET sz = emitInsSizeRR(id);
    id->idCodeSize(sz);

    dispIns(id);
    emitCurIGsize += sz;
}


Дальше нам остается просто получить название регистра по его числу - это мы сделали на 1 шаге при их регистрации.

Здесь применяется условная компиляция. Но в рантайме (динамически) это тоже можно реализовать - просто кидаешь везде простые массивы и индексы.
Похожие вопросы