342 lines
6.2 KiB
C++
342 lines
6.2 KiB
C++
|
|
#include "tile.h"
|
|
#include "../std.h"
|
|
#include "codegen_x86.h"
|
|
|
|
//reduce to 3 for stress test
|
|
static const int NUM_REGS = 6;
|
|
|
|
static const string regs[] = {"???", "eax", "ecx", "edx", "edi", "esi", "ebx"};
|
|
|
|
//array of 'used' flags
|
|
static bool regUsed[NUM_REGS + 1];
|
|
|
|
//size of locals in function
|
|
static int frameSize, maxFrameSize;
|
|
|
|
//code fragments
|
|
static vector<string> codeFrags, dataFrags;
|
|
|
|
//name of function
|
|
static string funcLabel;
|
|
|
|
static void resetRegs()
|
|
{
|
|
for (int n = 1; n <= NUM_REGS; ++n)
|
|
regUsed[n] = false;
|
|
}
|
|
|
|
static int allocReg(int n)
|
|
{
|
|
if (!n || regUsed[n]) {
|
|
for (n = NUM_REGS; n >= 1 && regUsed[n]; --n) {
|
|
}
|
|
if (!n)
|
|
return 0;
|
|
}
|
|
regUsed[n] = true;
|
|
return n;
|
|
}
|
|
|
|
static void freeReg(int n)
|
|
{
|
|
regUsed[n] = false;
|
|
}
|
|
|
|
static void pushReg(int n)
|
|
{
|
|
frameSize += 4;
|
|
if (frameSize > maxFrameSize)
|
|
maxFrameSize = frameSize;
|
|
char buff[32];
|
|
_itoa(frameSize, buff, 10);
|
|
string s = "\tmov\t[ebp-";
|
|
s += buff;
|
|
s += "],";
|
|
s += regs[n];
|
|
s += '\n';
|
|
codeFrags.push_back(s);
|
|
}
|
|
|
|
static void popReg(int n)
|
|
{
|
|
char buff[32];
|
|
_itoa(frameSize, buff, 10);
|
|
string s = "\tmov\t";
|
|
s += regs[n];
|
|
s += ",[ebp-";
|
|
s += buff;
|
|
s += "]\n";
|
|
codeFrags.push_back(s);
|
|
frameSize -= 4;
|
|
}
|
|
|
|
static void moveReg(int d, int s)
|
|
{
|
|
string t = "\tmov\t" + regs[d] + ',' + regs[s] + '\n';
|
|
codeFrags.push_back(t);
|
|
}
|
|
|
|
static void swapRegs(int d, int s)
|
|
{
|
|
string t = "\txchg\t" + regs[d] + ',' + regs[s] + '\n';
|
|
codeFrags.push_back(t);
|
|
}
|
|
|
|
Tile::Tile(const string& a, Tile* l, Tile* r)
|
|
: assem(a), l(l), r(r), want_l(0), want_r(0), hits(0), need(0), argFrame(0)
|
|
{}
|
|
|
|
Tile::Tile(const string& a, const string& a2, Tile* l, Tile* r)
|
|
: assem(a), assem2(a2), l(l), r(r), want_l(0), want_r(0), hits(0), need(0), argFrame(0)
|
|
{}
|
|
|
|
Tile::~Tile()
|
|
{
|
|
delete l;
|
|
delete r;
|
|
}
|
|
|
|
void Tile::label()
|
|
{
|
|
if (!l) {
|
|
need = 1;
|
|
} else if (!r) {
|
|
l->label();
|
|
need = l->need;
|
|
} else {
|
|
l->label();
|
|
r->label();
|
|
if (l->need == r->need)
|
|
need = l->need + 1;
|
|
else if (l->need > r->need)
|
|
need = l->need;
|
|
else
|
|
need = r->need;
|
|
}
|
|
}
|
|
|
|
int Tile::eval(int want)
|
|
{
|
|
//save any hit registers
|
|
int spill = hits;
|
|
if (want_l)
|
|
spill |= 1 << want_l;
|
|
if (want_r)
|
|
spill |= 1 << want_r;
|
|
if (spill) {
|
|
for (int n = 1; n <= NUM_REGS; ++n) {
|
|
if (spill & (1 << n)) {
|
|
if (regUsed[n])
|
|
pushReg(n);
|
|
else
|
|
spill &= ~(1 << n);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if tile needs an argFrame...
|
|
if (argFrame) {
|
|
codeFrags.push_back("-" + itoa(argFrame));
|
|
}
|
|
|
|
int got_l = 0, got_r = 0;
|
|
if (want_l)
|
|
want = want_l;
|
|
|
|
string* as = &assem;
|
|
|
|
if (!l) {
|
|
got_l = allocReg(want);
|
|
} else if (!r) {
|
|
got_l = l->eval(want);
|
|
} else {
|
|
if (l->need >= NUM_REGS && r->need >= NUM_REGS) {
|
|
got_r = r->eval(0);
|
|
pushReg(got_r);
|
|
freeReg(got_r);
|
|
got_l = l->eval(want);
|
|
got_r = allocReg(want_r);
|
|
popReg(got_r);
|
|
} else if (r->need > l->need) {
|
|
got_r = r->eval(want_r);
|
|
got_l = l->eval(want);
|
|
} else {
|
|
got_l = l->eval(want);
|
|
got_r = r->eval(want_r);
|
|
if (assem2.size())
|
|
as = &assem2;
|
|
}
|
|
if (want_l == got_r || want_r == got_l) {
|
|
swapRegs(got_l, got_r);
|
|
int t = got_l;
|
|
got_l = got_r;
|
|
got_r = t;
|
|
}
|
|
}
|
|
|
|
if (!want_l)
|
|
want_l = got_l;
|
|
else if (want_l != got_l)
|
|
moveReg(want_l, got_l);
|
|
|
|
if (!want_r)
|
|
want_r = got_r;
|
|
else if (want_r != got_r)
|
|
moveReg(want_r, got_r);
|
|
|
|
int i;
|
|
while ((i = as->find("%l")) != string::npos)
|
|
as->replace(i, 2, regs[want_l]);
|
|
while ((i = as->find("%r")) != string::npos)
|
|
as->replace(i, 2, regs[want_r]);
|
|
|
|
codeFrags.push_back(*as);
|
|
|
|
freeReg(got_r);
|
|
if (want_l != got_l)
|
|
moveReg(got_l, want_l);
|
|
|
|
//cleanup argFrame
|
|
if (argFrame) {
|
|
//***** Not needed for STDCALL *****
|
|
// codeFrags.push_back( "+"+itoa(argFrame) );
|
|
}
|
|
|
|
//restore spilled regs
|
|
if (spill) {
|
|
for (int n = NUM_REGS; n >= 1; --n) {
|
|
if (spill & (1 << n))
|
|
popReg(n);
|
|
}
|
|
}
|
|
return got_l;
|
|
}
|
|
|
|
void Codegen_x86::flush()
|
|
{
|
|
vector<string>::iterator it;
|
|
for (it = dataFrags.begin(); it != dataFrags.end(); ++it)
|
|
out << *it;
|
|
dataFrags.clear();
|
|
}
|
|
|
|
void Codegen_x86::enter(const string& l, int frameSize)
|
|
{
|
|
inCode = true;
|
|
::frameSize = maxFrameSize = frameSize;
|
|
codeFrags.clear();
|
|
funcLabel = l;
|
|
}
|
|
|
|
void Codegen_x86::code(TNode* stmt)
|
|
{
|
|
resetRegs();
|
|
Tile* q = munch(stmt);
|
|
q->label();
|
|
q->eval(0);
|
|
delete q;
|
|
delete stmt;
|
|
}
|
|
|
|
static string fixEsp(int esp_off)
|
|
{
|
|
if (esp_off < 0)
|
|
return "\tsub\tesp," + itoa(-esp_off) + "\n";
|
|
return "\tadd\tesp," + itoa(esp_off) + "\n";
|
|
}
|
|
|
|
void Codegen_x86::leave(TNode* cleanup, int pop_sz)
|
|
{
|
|
if (cleanup) {
|
|
resetRegs();
|
|
allocReg(EAX);
|
|
Tile* q = munch(cleanup);
|
|
q->label();
|
|
q->eval(0);
|
|
delete q;
|
|
}
|
|
|
|
out << "\t.align\t16\n";
|
|
|
|
if (funcLabel.size())
|
|
out << funcLabel << '\n';
|
|
|
|
out << "\tpush\tebx\n";
|
|
out << "\tpush\tesi\n";
|
|
out << "\tpush\tedi\n";
|
|
out << "\tpush\tebp\n";
|
|
out << "\tmov\tebp,esp\n";
|
|
if (maxFrameSize)
|
|
out << "\tsub\tesp," << maxFrameSize << '\n';
|
|
|
|
int esp_off = 0;
|
|
vector<string>::iterator it = codeFrags.begin();
|
|
for (it = codeFrags.begin(); it != codeFrags.end(); ++it) {
|
|
const string& t = *it;
|
|
if (t[0] == '+') {
|
|
esp_off += atoi(t.substr(1));
|
|
} else if (t[0] == '-') {
|
|
//***** Still needed for STDCALL *****
|
|
esp_off -= atoi(t.substr(1));
|
|
} else {
|
|
if (esp_off) {
|
|
out << fixEsp(esp_off);
|
|
esp_off = 0;
|
|
}
|
|
out << *it;
|
|
}
|
|
}
|
|
if (esp_off)
|
|
out << fixEsp(esp_off);
|
|
|
|
out << "\tmov\tesp,ebp\n";
|
|
out << "\tpop\tebp\n";
|
|
out << "\tpop\tedi\n";
|
|
out << "\tpop\tesi\n";
|
|
out << "\tpop\tebx\n";
|
|
out << "\tret\tword " << pop_sz << "\n";
|
|
|
|
delete cleanup;
|
|
inCode = false;
|
|
}
|
|
|
|
void Codegen_x86::label(const string& l)
|
|
{
|
|
string t = l + '\n';
|
|
if (inCode)
|
|
codeFrags.push_back(t);
|
|
else
|
|
dataFrags.push_back(t);
|
|
}
|
|
|
|
void Codegen_x86::align_data(int n)
|
|
{
|
|
char buff[32];
|
|
_itoa(n, buff, 10);
|
|
dataFrags.push_back(string("\t.align\t") + buff + '\n');
|
|
}
|
|
|
|
void Codegen_x86::i_data(int i, const string& l)
|
|
{
|
|
if (l.size())
|
|
dataFrags.push_back(l);
|
|
char buff[32];
|
|
_itoa(i, buff, 10);
|
|
dataFrags.push_back(string("\t.dd\t") + buff + '\n');
|
|
}
|
|
|
|
void Codegen_x86::s_data(const string& s, const string& l)
|
|
{
|
|
if (l.size())
|
|
dataFrags.push_back(l);
|
|
dataFrags.push_back(string("\t.db\t\"") + s + "\",0\n");
|
|
}
|
|
|
|
void Codegen_x86::p_data(const string& p, const string& l)
|
|
{
|
|
if (l.size())
|
|
dataFrags.push_back(l);
|
|
dataFrags.push_back(string("\t.dd\t") + p + '\n');
|
|
}
|