1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#include "stdafx.h"
#include "PosixLayout.h"
#include "Code/Layout.h"
namespace code {
namespace x64 {
PosixLayout::PosixLayout(const Arena *arena) : Layout(arena) {}
Offset PosixLayout::resultParam() {
Nat count = 1 + toPreserve->count();
return -(Offset::sPtr * count);
}
void PosixLayout::saveResult(Listing *dest) {
const Result &result = params->result();
if (result.memoryRegister() != noReg) {
// We need to save ptrA that contains the return address. Twice to keep the stack aligned.
*dest << push(ptrA);
*dest << push(ptrA);
} else if (result.registerCount() > 0) {
Size sz = Size::sPtr * roundUp(result.registerCount(), Nat(2));
*dest << sub(ptrStack, ptrConst(sz));
for (Nat i = 0; i < result.registerCount(); i++) {
*dest << mov(ptrRel(ptrStack, Offset::sPtr * i), asSize(result.registerAt(i), Size::sPtr));
}
}
}
void PosixLayout::restoreResult(Listing *dest) {
const Result &result = params->result();
if (result.memoryRegister() != noReg) {
*dest << pop(ptrA);
*dest << pop(ptrA);
} else if (result.registerCount() > 0) {
Size sz = Size::sPtr * roundUp(result.registerCount(), Nat(2));
for (Nat i = 0; i < result.registerCount(); i++) {
*dest << mov(asSize(result.registerAt(i), Size::sPtr), ptrRel(ptrStack, Offset::sPtr * i));
}
*dest << add(ptrStack, ptrConst(sz));
}
}
void PosixLayout::emitProlog(Listing *dest) {
*dest << prolog();
// Allocate stack space.
if (layout->last() != Offset())
*dest << sub(ptrStack, ptrConst(layout->last()));
// Keep track of offsets.
Offset offset = -Offset::sPtr;
// Save registers we need to preserve.
for (RegSet::Iter i = toPreserve->begin(); i != toPreserve->end(); ++i) {
*dest << mov(ptrRel(ptrFrame, offset), asSize(i.v(), Size::sPtr));
*dest << preserve(ptrRel(ptrFrame, offset), asSize(i.v(), Size::sPtr));
offset -= Offset::sPtr;
}
// Spill parameters to the stack.
spillParams(dest);
}
void PosixLayout::emitEpilog(Listing *dest) {
// Restore preserved registers.
Offset offset = -Offset::sPtr;
for (RegSet::Iter i = toPreserve->begin(); i != toPreserve->end(); ++i) {
*dest << mov(asSize(i.v(), Size::sPtr), ptrRel(ptrFrame, offset));
offset -= Offset::sPtr;
}
// The "epilog" pseudo-op generates a LEAVE instruction, which corresponds to these instructions:
// *dest << mov(ptrStack, ptrFrame);
// *dest << pop(ptrFrame);
*dest << code::epilog();
}
static Size spillParams(Array<Offset> *out, Listing *l, Params *params, Offset varOffset) {
// NOTE: We could avoid spilling primitives to memory, as we do not generally use those
// registers from code generated for the generic platform. However, we would need to
// save as soon as we perform a function call anyway, which is probably more expensive
// than just spilling them unconditionally in the function prolog.
Array<Var> *all = l->allParams();
{
// Start by computing the locations of parameters passed on the stack.
Offset stackOffset = Offset::sPtr * 2; // return addr, rbp
for (Nat i = 0; i < params->stackCount(); i++) {
Nat paramId = params->stackParam(i).id(); // Should never be 'Param::returnId'
Var var = all->at(paramId);
out->at(var.key()) = stackOffset + Offset(params->stackOffset(i));
}
}
Size used;
// Then, compute where to spill the remaining registers. Keep track if they are passed
// in memory or not, since that affects their size!
for (Nat i = 0; i < params->registerCount(); i++) {
Param p = params->registerParam(i);
if (p.empty())
continue;
if (p.id() == Param::returnId())
continue;
Var var = all->at(p.id());
Offset &to = out->at(var.key());
// Already computed -> no need to spill.
if (to != Offset())
continue;
// Try to squeeze the preserved registers as tightly as possible while keeping alignment.
// This is more concentrated than when pushing registers to the stack. On the stack, all
// parameters are aligned to 8 bytes, while we do not need that here. We only need to keep
// the natural alignment of the parameters to keep the CPU happy.
Size sz = var.size();
if (l->freeOpt(var) & freeIndirection)
sz = Size::sPtr;
// However, since we don't want to use multiple instructions to spill a single
// parameter, we increase the size of some parameters a bit.
if (asSize(ptrA, sz) == noReg)
sz = sz + Size::sInt.alignment();
used = (used + sz).alignedAs(sz);
to = -(varOffset + used);
}
return used.aligned();
}
Array<Offset> *PosixLayout::computeLayout(Listing *l, Params *params, Nat spilled) {
Array<Offset> *result = code::layout(l);
// Check if we need to spill the result parameter as well!
if (params->result().memoryRegister() != noReg)
spilled++;
// Saved registers and other things:
Offset varOffset = Offset::sPtr * spilled;
// Figure out which parameters we need to spill into memory.
varOffset += code::x64::spillParams(result, l, params, varOffset);
varOffset = varOffset.alignAs(Size::sPtr);
// Update the layout of the other variables according to the size we needed for
// parameters and spilled registers.
Array<Var> *all = l->allVars();
for (Nat i = 0; i < all->count(); i++) {
Var var = all->at(i);
Nat id = var.key();
if (!l->isParam(var)) {
Size s = var.size();
if (l->freeOpt(var) & freeIndirection)
s = Size::sPtr;
result->at(id) = -(result->at(id) + s.aligned() + varOffset);
}
}
result->last() += varOffset;
if (result->last().v64() & 0xF) {
// Need to be aligned to 64 bits. Otherwise, the SIMD operations used widely on
// X86-64 will not work properly.
result->last() = Offset(roundUp(result->last().v64(), 0x10));
// result->last() += Offset::sPtr;
}
return result;
}
}
}
|