Files
retrobsd/src/cmd/smlrc/cgmips.c
Alexey Frunze bfc3125556 Floats in Smaller C!
double is an alias for float. IOW, only 32-bit
single precision floats are supported. This isn't
conformant, but OK for some embedded systems (e.g.
RetroBSD) and simple compilers like this.

Also, the following operators are not supported with
floats at the moment: ++, --, +=, -=, *=, /=.
But +, -, *, /, =, ||, &&, ?:, !, comparison, casts,
if/while/for are OK.
2016-01-16 22:45:42 -08:00

2265 lines
61 KiB
C

/*
Copyright (c) 2012-2015, Alexey Frunze
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*****************************************************************************/
/* */
/* Smaller C */
/* */
/* A simple and small single-pass C compiler ("small C" class). */
/* */
/* MIPS code generator */
/* */
/*****************************************************************************/
// Works around bugs in RetroBSD's as instruction reordering
//#define REORDER_WORKAROUND
STATIC
void GenInit(void)
{
// initialization of target-specific code generator
SizeOfWord = 4;
OutputFormat = FormatSegmented;
CodeHeaderFooter[0] = "\t.text";
DataHeaderFooter[0] = "\t.data";
RoDataHeaderFooter[0] = "\t.rdata";
BssHeaderFooter[0] = "\t.bss";
UseLeadingUnderscores = 0;
#ifdef REORDER_WORKAROUND
FileHeader = "\t.set\tnoreorder";
#else
FileHeader = "\t.set\treorder";
#endif
}
STATIC
int GenInitParams(int argc, char** argv, int* idx)
{
(void)argc;
// initialization of target-specific code generator with parameters
if (!strcmp(argv[*idx], "-v"))
{
// RetroBSD's cc may supply this parameter. Just need to consume it.
return 1;
}
return 0;
}
STATIC
void GenInitFinalize(void)
{
// finalization of initialization of target-specific code generator
}
STATIC
void GenStartCommentLine(void)
{
printf2(" # ");
}
STATIC
void GenWordAlignment(int bss)
{
(void)bss;
printf2("\t.align 2\n");
}
STATIC
void GenLabel(char* Label, int Static)
{
{
if (!Static && GenExterns)
printf2("\t.globl\t%s\n", Label);
printf2("%s:\n", Label);
}
}
STATIC
void GenPrintLabel(char* Label)
{
{
if (isdigit(*Label))
printf2("$L%s", Label);
else
printf2("%s", Label);
}
}
STATIC
void GenNumLabel(int Label)
{
printf2("$L%d:\n", Label);
}
STATIC
void GenPrintNumLabel(int label)
{
printf2("$L%d", label);
}
STATIC
void GenZeroData(unsigned Size, int bss)
{
(void)bss;
printf2("\t.space\t%u\n", truncUint(Size)); // or ".fill size"
}
STATIC
void GenIntData(int Size, int Val)
{
Val = truncInt(Val);
if (Size == 1)
printf2("\t.byte\t%d\n", Val);
else if (Size == 2)
printf2("\t.half\t%d\n", Val);
else if (Size == 4)
printf2("\t.word\t%d\n", Val);
}
STATIC
void GenStartAsciiString(void)
{
printf2("\t.ascii\t");
}
STATIC
void GenAddrData(int Size, char* Label, int ofs)
{
ofs = truncInt(ofs);
if (Size == 1)
printf2("\t.byte\t");
else if (Size == 2)
printf2("\t.half\t");
else if (Size == 4)
printf2("\t.word\t");
GenPrintLabel(Label);
if (ofs)
printf2(" %+d", ofs);
puts2("");
}
#define MipsInstrNop 0x00
#define MipsInstrMov 0x01
#define MipsInstrMfLo 0x02
#define MipsInstrMfHi 0x03
#define MipsInstrLA 0x06
#define MipsInstrLI 0x07
//#define MipsInstrLUI
#define MipsInstrLB 0x08
#define MipsInstrLBU 0x09
#define MipsInstrLH 0x0A
#define MipsInstrLHU 0x0B
#define MipsInstrLW 0x0C
#define MipsInstrSB 0x0D
#define MipsInstrSH 0x0E
#define MipsInstrSW 0x0F
#define MipsInstrAddU 0x10
#define MipsInstrSubU 0x11
#define MipsInstrAnd 0x12
#define MipsInstrOr 0x13
#define MipsInstrXor 0x14
#define MipsInstrNor 0x15
#define MipsInstrSLL 0x16
#define MipsInstrSRL 0x17
#define MipsInstrSRA 0x18
#define MipsInstrMul 0x19
#define MipsInstrDiv 0x1A
#define MipsInstrDivU 0x1B
#define MipsInstrSLT 0x1C
#define MipsInstrSLTU 0x1D
#define MipsInstrJ 0x1E
#define MipsInstrJAL 0x1F
#define MipsInstrBEQ 0x20
#define MipsInstrBNE 0x21
#define MipsInstrBLTZ 0x22
#define MipsInstrBGEZ 0x23
#define MipsInstrBLEZ 0x24
#define MipsInstrBGTZ 0x25
#define MipsInstrSeb 0x26
#define MipsInstrSeh 0x27
STATIC
void GenPrintInstr(int instr, int val)
{
char* p = "";
(void)val;
switch (instr)
{
case MipsInstrNop : p = "nop"; break;
case MipsInstrMov : p = "move"; break;
case MipsInstrMfLo : p = "mflo"; break;
case MipsInstrMfHi : p = "mfhi"; break;
case MipsInstrLA : p = "la"; break;
case MipsInstrLI : p = "li"; break;
// case MipsInstrLUI : p = "lui"; break;
case MipsInstrLB : p = "lb"; break;
case MipsInstrLBU : p = "lbu"; break;
case MipsInstrLH : p = "lh"; break;
case MipsInstrLHU : p = "lhu"; break;
case MipsInstrLW : p = "lw"; break;
case MipsInstrSB : p = "sb"; break;
case MipsInstrSH : p = "sh"; break;
case MipsInstrSW : p = "sw"; break;
case MipsInstrAddU : p = "addu"; break;
case MipsInstrSubU : p = "subu"; break;
case MipsInstrAnd : p = "and"; break;
case MipsInstrOr : p = "or"; break;
case MipsInstrXor : p = "xor"; break;
case MipsInstrNor : p = "nor"; break;
case MipsInstrSLL : p = "sll"; break;
case MipsInstrSRL : p = "srl"; break;
case MipsInstrSRA : p = "sra"; break;
case MipsInstrMul : p = "mul"; break;
case MipsInstrDiv : p = "div"; break;
case MipsInstrDivU : p = "divu"; break;
case MipsInstrSLT : p = "slt"; break;
case MipsInstrSLTU : p = "sltu"; break;
case MipsInstrJ : p = "j"; break;
case MipsInstrJAL : p = "jal"; break;
case MipsInstrBEQ : p = "beq"; break;
case MipsInstrBNE : p = "bne"; break;
case MipsInstrBLTZ : p = "bltz"; break;
case MipsInstrBGEZ : p = "bgez"; break;
case MipsInstrBLEZ : p = "blez"; break;
case MipsInstrBGTZ : p = "bgtz"; break;
case MipsInstrSeb : p = "seb"; break;
case MipsInstrSeh : p = "seh"; break;
}
printf2("\t%s\t", p);
}
#define MipsOpRegZero 0x00
#define MipsOpRegAt 0x01
#define MipsOpRegV0 0x02
#define MipsOpRegV1 0x03
#define MipsOpRegA0 0x04
#define MipsOpRegA1 0x05
#define MipsOpRegA2 0x06
#define MipsOpRegA3 0x07
#define MipsOpRegT0 0x08
#define MipsOpRegT1 0x09
#define MipsOpRegT2 0x0A
#define MipsOpRegT3 0x0B
#define MipsOpRegT4 0x0C
#define MipsOpRegT5 0x0D
#define MipsOpRegT6 0x0E
#define MipsOpRegT7 0x0F
#define MipsOpRegS0 0x10
#define MipsOpRegS1 0x11
#define MipsOpRegS2 0x12
#define MipsOpRegS3 0x13
#define MipsOpRegS4 0x14
#define MipsOpRegS5 0x15
#define MipsOpRegS6 0x16
#define MipsOpRegS7 0x17
#define MipsOpRegT8 0x18
#define MipsOpRegT9 0x19
#define MipsOpRegSp 0x1D
#define MipsOpRegFp 0x1E
#define MipsOpRegRa 0x1F
#define MipsOpIndRegZero 0x20
#define MipsOpIndRegAt 0x21
#define MipsOpIndRegV0 0x22
#define MipsOpIndRegV1 0x23
#define MipsOpIndRegA0 0x24
#define MipsOpIndRegA1 0x25
#define MipsOpIndRegA2 0x26
#define MipsOpIndRegA3 0x27
#define MipsOpIndRegT0 0x28
#define MipsOpIndRegT1 0x29
#define MipsOpIndRegSp 0x3D
#define MipsOpIndRegFp 0x3E
#define MipsOpIndRegRa 0x3F
#define MipsOpConst 0x80
#define MipsOpLabel 0x81
#define MipsOpNumLabel 0x82
#define MipsOpLabelLo 0x83
#define MipsOpIndLocal MipsOpIndRegFp
#define MAX_TEMP_REGS 8 // this many temp registers used beginning with T0 to hold subexpression results
#define TEMP_REG_A MipsOpRegT8 // two temporary registers used for momentary operations, similarly to the AT register
#define TEMP_REG_B MipsOpRegT9
#ifdef REORDER_WORKAROUND
STATIC
void GenNop(void)
{
puts2("\tnop");
}
#endif
STATIC
void GenPrintOperand(int op, int val)
{
if (op >= MipsOpRegZero && op <= MipsOpRegRa)
{
printf2("$%d", op);
}
else if (op >= MipsOpIndRegZero && op <= MipsOpIndRegRa)
{
printf2("%d($%d)", truncInt(val), op - MipsOpIndRegZero);
}
else
{
switch (op)
{
case MipsOpConst: printf2("%d", truncInt(val)); break;
case MipsOpLabelLo:
printf2("%%lo(");
GenPrintLabel(IdentTable + val);
printf2(")($1)");
break;
case MipsOpLabel: GenPrintLabel(IdentTable + val); break;
case MipsOpNumLabel: GenPrintNumLabel(val); break;
default:
//error("WTF!\n");
errorInternal(100);
break;
}
}
}
STATIC
void GenPrintOperandSeparator(void)
{
printf2(", ");
}
STATIC
void GenPrintNewLine(void)
{
puts2("");
}
STATIC
void GenPrintInstr1Operand(int instr, int instrval, int operand, int operandval)
{
GenPrintInstr(instr, instrval);
GenPrintOperand(operand, operandval);
GenPrintNewLine();
#ifdef REORDER_WORKAROUND
if (instr == MipsInstrJ || instr == MipsInstrJAL)
GenNop();
#endif
}
STATIC
void GenPrintInstr2Operands(int instr, int instrval, int operand1, int operand1val, int operand2, int operand2val)
{
if (operand2 == MipsOpConst && operand2val == 0 &&
(instr == MipsInstrAddU || instr == MipsInstrSubU))
return;
GenPrintInstr(instr, instrval);
GenPrintOperand(operand1, operand1val);
GenPrintOperandSeparator();
GenPrintOperand(operand2, operand2val);
GenPrintNewLine();
#ifdef REORDER_WORKAROUND
if (instr == MipsInstrBLTZ || instr == MipsInstrBGEZ ||
instr == MipsInstrBGTZ || instr == MipsInstrBLEZ)
GenNop();
#endif
}
STATIC
void GenPrintInstr3Operands(int instr, int instrval,
int operand1, int operand1val,
int operand2, int operand2val,
int operand3, int operand3val)
{
if (operand3 == MipsOpConst && operand3val == 0 &&
(instr == MipsInstrAddU || instr == MipsInstrSubU) &&
operand1 == operand2)
return;
GenPrintInstr(instr, instrval);
GenPrintOperand(operand1, operand1val);
GenPrintOperandSeparator();
GenPrintOperand(operand2, operand2val);
GenPrintOperandSeparator();
GenPrintOperand(operand3, operand3val);
GenPrintNewLine();
#ifdef REORDER_WORKAROUND
if (instr == MipsInstrBEQ || instr == MipsInstrBNE)
GenNop();
#endif
}
STATIC
void GenExtendRegIfNeeded(int reg, int opSz)
{
if (opSz == -1)
{
#ifdef DONT_USE_SEH
GenPrintInstr3Operands(MipsInstrSLL, 0,
reg, 0,
reg, 0,
MipsOpConst, 24);
GenPrintInstr3Operands(MipsInstrSRA, 0,
reg, 0,
reg, 0,
MipsOpConst, 24);
#else
GenPrintInstr2Operands(MipsInstrSeb, 0,
reg, 0,
reg, 0);
#endif
}
else if (opSz == 1)
{
GenPrintInstr3Operands(MipsInstrAnd, 0,
reg, 0,
reg, 0,
MipsOpConst, 0xFF);
}
else if (opSz == -2)
{
#ifdef DONT_USE_SEH
GenPrintInstr3Operands(MipsInstrSLL, 0,
reg, 0,
reg, 0,
MipsOpConst, 16);
GenPrintInstr3Operands(MipsInstrSRA, 0,
reg, 0,
reg, 0,
MipsOpConst, 16);
#else
GenPrintInstr2Operands(MipsInstrSeh, 0,
reg, 0,
reg, 0);
#endif
}
else if (opSz == 2)
{
GenPrintInstr3Operands(MipsInstrAnd, 0,
reg, 0,
reg, 0,
MipsOpConst, 0xFFFF);
}
}
STATIC
void GenJumpUncond(int label)
{
GenPrintInstr1Operand(MipsInstrJ, 0,
MipsOpNumLabel, label);
}
extern int GenWreg; // GenWreg is defined below
STATIC
void GenJumpIfEqual(int val, int label)
{
GenPrintInstr2Operands(MipsInstrLI, 0,
TEMP_REG_B, 0,
MipsOpConst, val);
GenPrintInstr3Operands(MipsInstrBEQ, 0,
GenWreg, 0,
TEMP_REG_B, 0,
MipsOpNumLabel, label);
}
STATIC
void GenJumpIfZero(int label)
{
#ifndef NO_ANNOTATIONS
printf2(" # JumpIfZero\n");
#endif
GenPrintInstr3Operands(MipsInstrBEQ, 0,
GenWreg, 0,
MipsOpRegZero, 0,
MipsOpNumLabel, label);
}
STATIC
void GenJumpIfNotZero(int label)
{
#ifndef NO_ANNOTATIONS
printf2(" # JumpIfNotZero\n");
#endif
GenPrintInstr3Operands(MipsInstrBNE, 0,
GenWreg, 0,
MipsOpRegZero, 0,
MipsOpNumLabel, label);
}
fpos_t GenPrologPos;
int GenLeaf;
STATIC
void GenWriteFrameSize(void)
{
unsigned size = 8/*RA + FP*/ - CurFxnMinLocalOfs;
printf2("\tsubu\t$29, $29, %10u\n", size); // 10 chars are enough for 32-bit unsigned ints
printf2("\tsw\t$30, %10u($29)\n", size - 8);
printf2("\taddu\t$30, $29, %10u\n", size - 8);
printf2("\t%csw\t$31, 4($30)\n", GenLeaf ? '#' : ' ');
}
STATIC
void GenUpdateFrameSize(void)
{
fpos_t pos;
fgetpos(OutFile, &pos);
fsetpos(OutFile, &GenPrologPos);
GenWriteFrameSize();
fsetpos(OutFile, &pos);
}
STATIC
void GenFxnProlog(void)
{
if (CurFxnParamCntMin && CurFxnParamCntMax)
{
int i, cnt = CurFxnParamCntMax;
if (cnt > 4)
cnt = 4;
// TBD!!! for structure passing use the cumulative parameter size
// instead of the number of parameters. Currently this bug is masked
// by the subroutine that pushes structures on the stack (it copies
// all words except the first to the stack). But passing structures
// in registers from assembly code won't always work.
for (i = 0; i < cnt; i++)
GenPrintInstr2Operands(MipsInstrSW, 0,
MipsOpRegA0 + i, 0,
MipsOpIndRegSp, 4 * i);
}
GenLeaf = 1; // will be reset to 0 if a call is generated
fgetpos(OutFile, &GenPrologPos);
GenWriteFrameSize();
}
STATIC
void GenGrowStack(int size)
{
if (!size)
return;
GenPrintInstr3Operands(MipsInstrSubU, 0,
MipsOpRegSp, 0,
MipsOpRegSp, 0,
MipsOpConst, size);
}
STATIC
void GenFxnEpilog(void)
{
GenUpdateFrameSize();
if (!GenLeaf)
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegRa, 0,
MipsOpIndRegFp, 4);
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegFp, 0,
MipsOpIndRegFp, 0);
GenPrintInstr3Operands(MipsInstrAddU, 0,
MipsOpRegSp, 0,
MipsOpRegSp, 0,
MipsOpConst, 8/*RA + FP*/ - CurFxnMinLocalOfs);
GenPrintInstr1Operand(MipsInstrJ, 0,
MipsOpRegRa, 0);
}
STATIC
int GenMaxLocalsSize(void)
{
return 0x7FFFFFFF;
}
STATIC
int GenGetBinaryOperatorInstr(int tok)
{
switch (tok)
{
case tokPostAdd:
case tokAssignAdd:
case '+':
return MipsInstrAddU;
case tokPostSub:
case tokAssignSub:
case '-':
return MipsInstrSubU;
case '&':
case tokAssignAnd:
return MipsInstrAnd;
case '^':
case tokAssignXor:
return MipsInstrXor;
case '|':
case tokAssignOr:
return MipsInstrOr;
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokEQ:
case tokNEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
return MipsInstrNop;
case '*':
case tokAssignMul:
return MipsInstrMul;
case '/':
case '%':
case tokAssignDiv:
case tokAssignMod:
return MipsInstrDiv;
case tokUDiv:
case tokUMod:
case tokAssignUDiv:
case tokAssignUMod:
return MipsInstrDivU;
case tokLShift:
case tokAssignLSh:
return MipsInstrSLL;
case tokRShift:
case tokAssignRSh:
return MipsInstrSRA;
case tokURShift:
case tokAssignURSh:
return MipsInstrSRL;
default:
//error("Error: Invalid operator\n");
errorInternal(101);
return 0;
}
}
STATIC
void GenPreIdentAccess(int label)
{
printf2("\t.set\tnoat\n\tlui\t$1, %%hi(");
GenPrintLabel(IdentTable + label);
puts2(")");
}
STATIC
void GenPostIdentAccess(void)
{
puts2("\t.set\tat");
}
STATIC
void GenReadIdent(int regDst, int opSz, int label)
{
int instr = MipsInstrLW;
GenPreIdentAccess(label);
if (opSz == -1)
{
instr = MipsInstrLB;
}
else if (opSz == 1)
{
instr = MipsInstrLBU;
}
else if (opSz == -2)
{
instr = MipsInstrLH;
}
else if (opSz == 2)
{
instr = MipsInstrLHU;
}
GenPrintInstr2Operands(instr, 0,
regDst, 0,
MipsOpLabelLo, label);
GenPostIdentAccess();
}
STATIC
void GenReadLocal(int regDst, int opSz, int ofs)
{
int instr = MipsInstrLW;
if (opSz == -1)
{
instr = MipsInstrLB;
}
else if (opSz == 1)
{
instr = MipsInstrLBU;
}
else if (opSz == -2)
{
instr = MipsInstrLH;
}
else if (opSz == 2)
{
instr = MipsInstrLHU;
}
GenPrintInstr2Operands(instr, 0,
regDst, 0,
MipsOpIndRegFp, ofs);
}
STATIC
void GenReadIndirect(int regDst, int regSrc, int opSz)
{
int instr = MipsInstrLW;
if (opSz == -1)
{
instr = MipsInstrLB;
}
else if (opSz == 1)
{
instr = MipsInstrLBU;
}
else if (opSz == -2)
{
instr = MipsInstrLH;
}
else if (opSz == 2)
{
instr = MipsInstrLHU;
}
GenPrintInstr2Operands(instr, 0,
regDst, 0,
regSrc + MipsOpIndRegZero, 0);
}
STATIC
void GenWriteIdent(int regSrc, int opSz, int label)
{
int instr = MipsInstrSW;
GenPreIdentAccess(label);
if (opSz == -1 || opSz == 1)
{
instr = MipsInstrSB;
}
else if (opSz == -2 || opSz == 2)
{
instr = MipsInstrSH;
}
GenPrintInstr2Operands(instr, 0,
regSrc, 0,
MipsOpLabelLo, label);
GenPostIdentAccess();
}
STATIC
void GenWriteLocal(int regSrc, int opSz, int ofs)
{
int instr = MipsInstrSW;
if (opSz == -1 || opSz == 1)
{
instr = MipsInstrSB;
}
else if (opSz == -2 || opSz == 2)
{
instr = MipsInstrSH;
}
GenPrintInstr2Operands(instr, 0,
regSrc, 0,
MipsOpIndRegFp, ofs);
}
STATIC
void GenWriteIndirect(int regDst, int regSrc, int opSz)
{
int instr = MipsInstrSW;
if (opSz == -1 || opSz == 1)
{
instr = MipsInstrSB;
}
else if (opSz == -2 || opSz == 2)
{
instr = MipsInstrSH;
}
GenPrintInstr2Operands(instr, 0,
regSrc, 0,
regDst + MipsOpIndRegZero, 0);
}
STATIC
void GenIncDecIdent(int regDst, int opSz, int label, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokInc)
instr = MipsInstrSubU;
GenReadIdent(regDst, opSz, label);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteIdent(regDst, opSz, label);
GenExtendRegIfNeeded(regDst, opSz);
}
STATIC
void GenIncDecLocal(int regDst, int opSz, int ofs, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokInc)
instr = MipsInstrSubU;
GenReadLocal(regDst, opSz, ofs);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteLocal(regDst, opSz, ofs);
GenExtendRegIfNeeded(regDst, opSz);
}
STATIC
void GenIncDecIndirect(int regDst, int regSrc, int opSz, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokInc)
instr = MipsInstrSubU;
GenReadIndirect(regDst, regSrc, opSz);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteIndirect(regSrc, regDst, opSz);
GenExtendRegIfNeeded(regDst, opSz);
}
STATIC
void GenPostIncDecIdent(int regDst, int opSz, int label, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokPostInc)
instr = MipsInstrSubU;
GenReadIdent(regDst, opSz, label);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteIdent(regDst, opSz, label);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, -1);
GenExtendRegIfNeeded(regDst, opSz);
}
STATIC
void GenPostIncDecLocal(int regDst, int opSz, int ofs, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokPostInc)
instr = MipsInstrSubU;
GenReadLocal(regDst, opSz, ofs);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteLocal(regDst, opSz, ofs);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, -1);
GenExtendRegIfNeeded(regDst, opSz);
}
STATIC
void GenPostIncDecIndirect(int regDst, int regSrc, int opSz, int tok)
{
int instr = MipsInstrAddU;
if (tok != tokPostInc)
instr = MipsInstrSubU;
GenReadIndirect(regDst, regSrc, opSz);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, 1);
GenWriteIndirect(regSrc, regDst, opSz);
GenPrintInstr3Operands(instr, 0,
regDst, 0,
regDst, 0,
MipsOpConst, -1);
GenExtendRegIfNeeded(regDst, opSz);
}
int CanUseTempRegs;
int TempsUsed;
int GenWreg = MipsOpRegV0; // current working register (V0 or Tn or An)
int GenLreg, GenRreg; // left operand register and right operand register after GenPopReg()
/*
General idea behind GenWreg, GenLreg, GenRreg:
- In expressions w/o function calls:
Subexpressions are evaluated in V0, T0, T1, ..., T<MAX_TEMP_REGS-1>. If those registers
aren't enough, the stack is used additionally.
The expression result ends up in V0, which is handy for returning from
functions.
In the process, GenWreg is the current working register and is one of: V0, T0, T1, ... .
All unary operators are evaluated in the current working register.
GenPushReg() and GenPopReg() advance GenWreg as needed when handling binary operators.
GenPopReg() sets GenWreg, GenLreg and GenRreg. GenLreg and GenRreg are the registers
where the left and right operands of a binary operator are.
When the exression runs out of the temporary registers, the stack is used. While it is being
used, GenWreg remains equal to the last temporary register, and GenPopReg() sets GenLreg = TEMP_REG_A.
Hence, after GenPopReg() the operands of the binary operator are always in registers and can be
directly manipulated with.
Following GenPopReg(), binary operator evaluation must take the left and right operands from
GenLreg and GenRreg and write the evaluated result into GenWreg. Care must be taken as GenWreg
will be the same as either GenLreg (when the popped operand comes from T0-T<MAX_TEMP_REGS-1>)
or GenRreg (when the popped operand comes from the stack in TEMP_REG_A).
- In expressions with function calls:
GenWreg is always V0 in subexpressions that aren't function parameters. These subexpressions
get automatically pushed onto the stack as necessary.
GenWreg is always V0 in expressions, where return values from function calls are used as parameters
into other called functions. IOW, this is the case when the function call depth is greater than 1.
Subexpressions in such expressions get automatically pushed onto the stack as necessary.
GenWreg is A0-A3 in subexpressions that are function parameters when the function call depth is 1.
Basically, while a function parameter is evaluated, it's evaluated in the register from where
the called function will take it. This avoids some of unnecessary register copies and stack
manipulations in the most simple and very common cases of function calls.
*/
STATIC
void GenWregInc(int inc)
{
if (inc > 0)
{
// Advance the current working register to the next available temporary register
if (GenWreg == MipsOpRegV0)
GenWreg = MipsOpRegT0;
else
GenWreg++;
}
else
{
// Return to the previous current working register
if (GenWreg == MipsOpRegT0)
GenWreg = MipsOpRegV0;
else
GenWreg--;
}
}
STATIC
void GenPushReg(void)
{
if (CanUseTempRegs && TempsUsed < MAX_TEMP_REGS)
{
GenWregInc(1);
TempsUsed++;
return;
}
GenPrintInstr3Operands(MipsInstrSubU, 0,
MipsOpRegSp, 0,
MipsOpRegSp, 0,
MipsOpConst, 4);
GenPrintInstr2Operands(MipsInstrSW, 0,
GenWreg, 0,
MipsOpIndRegSp, 0);
TempsUsed++;
}
STATIC
void GenPopReg(void)
{
TempsUsed--;
if (CanUseTempRegs && TempsUsed < MAX_TEMP_REGS)
{
GenRreg = GenWreg;
GenWregInc(-1);
GenLreg = GenWreg;
return;
}
GenPrintInstr2Operands(MipsInstrLW, 0,
TEMP_REG_A, 0,
MipsOpIndRegSp, 0);
GenPrintInstr3Operands(MipsInstrAddU, 0,
MipsOpRegSp, 0,
MipsOpRegSp, 0,
MipsOpConst, 4);
GenLreg = TEMP_REG_A;
GenRreg = GenWreg;
}
#define tokRevIdent 0x100
#define tokRevLocalOfs 0x101
#define tokAssign0 0x102
#define tokNum0 0x103
STATIC
void GenPrep(int* idx)
{
int tok;
int oldIdxRight, oldIdxLeft, t0, t1;
if (*idx < 0)
//error("GenFuse(): idx < 0\n");
errorInternal(100);
tok = stack[*idx][0];
oldIdxRight = --*idx;
switch (tok)
{
case tokUDiv:
case tokUMod:
case tokAssignUDiv:
case tokAssignUMod:
if (stack[oldIdxRight][0] == tokNumInt || stack[oldIdxRight][0] == tokNumUint)
{
// Change unsigned division to right shift and unsigned modulo to bitwise and
unsigned m = truncUint(stack[oldIdxRight][1]);
if (m && !(m & (m - 1)))
{
if (tok == tokUMod || tok == tokAssignUMod)
{
stack[oldIdxRight][1] = (int)(m - 1);
tok = (tok == tokUMod) ? '&' : tokAssignAnd;
}
else
{
t1 = 0;
while (m >>= 1) t1++;
stack[oldIdxRight][1] = t1;
tok = (tok == tokUDiv) ? tokURShift : tokAssignURSh;
}
stack[oldIdxRight + 1][0] = tok;
}
}
}
switch (tok)
{
case tokNumUint:
stack[oldIdxRight + 1][0] = tokNumInt; // reduce the number of cases since tokNumInt and tokNumUint are handled the same way
// fallthrough
case tokNumInt:
case tokNum0:
case tokIdent:
case tokLocalOfs:
break;
case tokPostAdd:
case tokPostSub:
case '-':
case '/':
case '%':
case tokUDiv:
case tokUMod:
case tokLShift:
case tokRShift:
case tokURShift:
case tokLogAnd:
case tokLogOr:
case tokComma:
GenPrep(idx);
// fallthrough
case tokShortCirc:
case tokGoto:
case tokUnaryStar:
case tokInc:
case tokDec:
case tokPostInc:
case tokPostDec:
case '~':
case tokUnaryPlus:
case tokUnaryMinus:
case tok_Bool:
case tokVoid:
case tokUChar:
case tokSChar:
case tokShort:
case tokUShort:
GenPrep(idx);
break;
case '=':
if (oldIdxRight + 1 == sp - 1 &&
(stack[oldIdxRight][0] == tokNumInt || stack[oldIdxRight][0] == tokNumUint) &&
truncUint(stack[oldIdxRight][1]) == 0)
{
// Special case for assigning 0 while throwing away the expression result value
// TBD??? ,
stack[oldIdxRight][0] = tokNum0; // this zero constant will not be loaded into a register
stack[oldIdxRight + 1][0] = tokAssign0; // change '=' to tokAssign0
}
// fallthrough
case tokAssignAdd:
case tokAssignSub:
case tokAssignMul:
case tokAssignDiv:
case tokAssignUDiv:
case tokAssignMod:
case tokAssignUMod:
case tokAssignLSh:
case tokAssignRSh:
case tokAssignURSh:
case tokAssignAnd:
case tokAssignXor:
case tokAssignOr:
GenPrep(idx);
oldIdxLeft = *idx;
GenPrep(idx);
// If the left operand is an identifier (with static or auto storage), swap it with the right operand
// and mark it specially, so it can be used directly
if ((t0 = stack[oldIdxLeft][0]) == tokIdent || t0 == tokLocalOfs)
{
t1 = stack[oldIdxLeft][1];
memmove(stack[oldIdxLeft], stack[oldIdxLeft + 1], (oldIdxRight - oldIdxLeft) * sizeof(stack[0]));
stack[oldIdxRight][0] = (t0 == tokIdent) ? tokRevIdent : tokRevLocalOfs;
stack[oldIdxRight][1] = t1;
}
break;
case '+':
case '*':
case '&':
case '^':
case '|':
case tokEQ:
case tokNEQ:
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
GenPrep(idx);
oldIdxLeft = *idx;
GenPrep(idx);
// If the right operand isn't a constant, but the left operand is, swap the operands
// so the constant can become an immediate right operand in the instruction
t1 = stack[oldIdxRight][0];
t0 = stack[oldIdxLeft][0];
if (t1 != tokNumInt && t0 == tokNumInt)
{
int xor;
t1 = stack[oldIdxLeft][1];
memmove(stack[oldIdxLeft], stack[oldIdxLeft + 1], (oldIdxRight - oldIdxLeft) * sizeof(stack[0]));
stack[oldIdxRight][0] = t0;
stack[oldIdxRight][1] = t1;
switch (tok)
{
case '<':
case '>':
xor = '<' ^ '>'; break;
case tokLEQ:
case tokGEQ:
xor = tokLEQ ^ tokGEQ; break;
case tokULess:
case tokUGreater:
xor = tokULess ^ tokUGreater; break;
case tokULEQ:
case tokUGEQ:
xor = tokULEQ ^ tokUGEQ; break;
default:
xor = 0; break;
}
tok ^= xor;
}
// Handle a few special cases and transform the instruction
if (stack[oldIdxRight][0] == tokNumInt)
{
unsigned m = truncUint(stack[oldIdxRight][1]);
switch (tok)
{
case '*':
// Change multiplication to left shift, this helps indexing arrays of ints/pointers/etc
if (m && !(m & (m - 1)))
{
t1 = 0;
while (m >>= 1) t1++;
stack[oldIdxRight][1] = t1;
tok = tokLShift;
}
break;
case tokLEQ:
// left <= const will later change to left < const+1, but const+1 must be <=0x7FFFFFFF
if (m == 0x7FFFFFFF)
{
// left <= 0x7FFFFFFF is always true, change to the equivalent left >= 0u
stack[oldIdxRight][1] = 0;
tok = tokUGEQ;
}
break;
case tokULEQ:
// left <= const will later change to left < const+1, but const+1 must be <=0xFFFFFFFFu
if (m == 0xFFFFFFFF)
{
// left <= 0xFFFFFFFFu is always true, change to the equivalent left >= 0u
stack[oldIdxRight][1] = 0;
tok = tokUGEQ;
}
break;
case '>':
// left > const will later change to !(left < const+1), but const+1 must be <=0x7FFFFFFF
if (m == 0x7FFFFFFF)
{
// left > 0x7FFFFFFF is always false, change to the equivalent left & 0
stack[oldIdxRight][1] = 0;
tok = '&';
}
break;
case tokUGreater:
// left > const will later change to !(left < const+1), but const+1 must be <=0xFFFFFFFFu
if (m == 0xFFFFFFFF)
{
// left > 0xFFFFFFFFu is always false, change to the equivalent left & 0
stack[oldIdxRight][1] = 0;
tok = '&';
}
break;
}
}
stack[oldIdxRight + 1][0] = tok;
break;
case ')':
while (stack[*idx][0] != '(')
{
GenPrep(idx);
if (stack[*idx][0] == ',')
--*idx;
}
--*idx;
break;
default:
//error("GenPrep: unexpected token %s\n", GetTokenName(tok));
errorInternal(101);
}
}
/*
; l <[u] 0 // slt[u] w, w, 0 "k"
l <[u] const // slt[u] w, w, const "m"
l <[u] r // slt[u] w, l, r "i"
* if (l < 0) // bgez w, Lskip "f"
if (l <[u] const) // slt[u] w, w, const; beq w, $0, Lskip "mc"
if (l <[u] r) // slt[u] w, l, r; beq w, $0, Lskip "ic"
; l <=[u] 0 // slt[u] w, w, 1 "l"
l <=[u] const // slt[u] w, w, const + 1 "n"
l <=[u] r // slt[u] w, r, l; xor w, w, 1 "js"
* if (l <= 0) // bgtz w, Lskip "g"
if (l <=[u] const) // slt[u] w, w, const + 1; beq w, $0, Lskip "nc"
if (l <=[u] r) // slt[u] w, r, l; bne w, $0, Lskip "jd"
l >[u] 0 // slt[u] w, $0, w "o"
l >[u] const // slt[u] w, w, const + 1; xor w, w, 1 "ns"
l >[u] r // slt[u] w, r, l "j"
* if (l > 0) // blez w, Lskip "h"
**if (l >u 0) // beq w, $0, Lskip
if (l >[u] const) // slt[u] w, w, const + 1; bne w, $0, Lskip "nd"
if (l >[u] r) // slt[u] w, r, l; beq w, $0, Lskip "jc"
; l >=[u] 0 // slt[u] w, w, 0; xor w, w, 1 "ks"
l >=[u] const // slt[u] w, w, const; xor w, w, 1 "ms"
l >=[u] r // slt[u] w, l, r; xor w, w, 1 "is"
* if (l >= 0) // bltz w, Lskip "e"
if (l >=[u] const) // slt[u] w, w, const; bne w, $0, Lskip "md"
if (l >=[u] r) // slt[u] w, l, r; bne w, $0, Lskip "id"
l == 0 // sltu w, w, 1 "q"
l == const // xor w, w, const; sltu w, w, 1 "tq"
l == r // xor w, l, r; sltu w, w, 1 "rq"
if (l == 0) // bne w, $0, Lskip "d"
if (l == const) // xor w, w, const; bne w, $0, Lskip "td"
if (l == r) // bne l, r, Lskip "b"
l != 0 // sltu w, $0, w "p"
l != const // xor w, w, const; sltu w, $0, w "tp"
l != r // xor w, l, r; sltu w, $0, w "rp"
if (l != 0) // beq w, $0, Lskip "c"
if (l != const) // xor w, w, const; beq w, $0, Lskip "tc"
if (l != r) // beq l, r, Lskip "a"
*/
char CmpBlocks[6/*op*/][2/*condbranch*/][3/*constness*/][2] =
{
{
{ "k", "m", "i" },
{ "f", "mc", "ic" }
},
{
{ "l", "n", "js" },
{ "g", "nc", "jd" }
},
{
{ "o", "ns", "j" },
{ "h", "nd", "jc" }
},
{
{ "ks", "ms", "is" },
{ "e", "md", "id" }
},
{
{ "q", "tq", "rq" },
{ "d", "td", "b" }
},
{
{ "p", "tp", "rp" },
{ "c", "tc", "a" }
}
};
STATIC
void GenCmp(int* idx, int op)
{
// constness: 0 = zero const, 1 = non-zero const, 2 = non-const
int constness = (stack[*idx - 1][0] == tokNumInt) ? (stack[*idx - 1][1] != 0) : 2;
int constval = (constness == 1) ? truncInt(stack[*idx - 1][1]) : 0;
// condbranch: 0 = no conditional branch, 1 = branch if true, 2 = branch if false
int condbranch = (*idx + 1 < sp) ? (stack[*idx + 1][0] == tokIf) + (stack[*idx + 1][0] == tokIfNot) * 2 : 0;
int unsign = op >> 4;
int slt = unsign ? MipsInstrSLTU : MipsInstrSLT;
int label = condbranch ? stack[*idx + 1][1] : 0;
char* p;
int i;
op &= 0xF;
if (constness == 2)
GenPopReg();
// bltz, blez, bgez, bgtz are for signed comparison with 0 only,
// so for conditional branches on <0u, <=0u, >0u, >=0u use the general method instead
if (condbranch && op < 4 && constness == 0 && unsign)
{
// Except, >0u is more optimal as !=0
if (op == 2)
op = 5;
else
constness = 1;
}
p = CmpBlocks[op][condbranch != 0][constness];
for (i = 0; i < 2; i++)
{
switch (p[i])
{
case 'a':
condbranch ^= 3;
// fallthrough
case 'b':
GenPrintInstr3Operands((condbranch == 1) ? MipsInstrBEQ : MipsInstrBNE, 0,
GenLreg, 0,
GenRreg, 0,
MipsOpNumLabel, label);
break;
case 'c':
condbranch ^= 3;
// fallthrough
case 'd':
GenPrintInstr3Operands((condbranch == 1) ? MipsInstrBEQ : MipsInstrBNE, 0,
GenWreg, 0,
MipsOpRegZero, 0,
MipsOpNumLabel, label);
break;
case 'e':
condbranch ^= 3;
// fallthrough
case 'f':
GenPrintInstr2Operands((condbranch == 1) ? MipsInstrBLTZ : MipsInstrBGEZ, 0,
GenWreg, 0,
MipsOpNumLabel, label);
break;
case 'g':
condbranch ^= 3;
// fallthrough
case 'h':
GenPrintInstr2Operands((condbranch == 1) ? MipsInstrBGTZ : MipsInstrBLEZ, 0,
GenWreg, 0,
MipsOpNumLabel, label);
break;
case 'i':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
GenLreg, 0,
GenRreg, 0);
break;
case 'j':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
GenRreg, 0,
GenLreg, 0);
break;
case 'k':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpRegZero, 0);
break;
case 'l':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 1);
break;
case 'n':
constval++;
// fallthrough
case 'm':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, constval);
break;
case 'o':
GenPrintInstr3Operands(slt, 0,
GenWreg, 0,
MipsOpRegZero, 0,
GenWreg, 0);
break;
case 'p':
GenPrintInstr3Operands(MipsInstrSLTU, 0,
GenWreg, 0,
MipsOpRegZero, 0,
GenWreg, 0);
break;
case 'q':
GenPrintInstr3Operands(MipsInstrSLTU, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 1);
break;
case 'r':
GenPrintInstr3Operands(MipsInstrXor, 0,
GenWreg, 0,
GenLreg, 0,
GenRreg, 0);
break;
case 's':
GenPrintInstr3Operands(MipsInstrXor, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 1);
break;
case 't':
GenPrintInstr3Operands(MipsInstrXor, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, constval);
break;
}
}
*idx += condbranch != 0;
}
STATIC
int GenIsCmp(int t)
{
return
t == '<' ||
t == '>' ||
t == tokGEQ ||
t == tokLEQ ||
t == tokULess ||
t == tokUGreater ||
t == tokUGEQ ||
t == tokULEQ ||
t == tokEQ ||
t == tokNEQ;
}
// Improved register/stack-based code generator
// DONE: test 32-bit code generation
STATIC
void GenExpr0(void)
{
int i;
int gotUnary = 0;
int maxCallDepth = 0;
int callDepth = 0;
int paramOfs = 0;
int t = sp - 1;
if (stack[t][0] == tokIf || stack[t][0] == tokIfNot || stack[t][0] == tokReturn)
t--;
GenPrep(&t);
for (i = 0; i < sp; i++)
if (stack[i][0] == '(')
{
if (++callDepth > maxCallDepth)
maxCallDepth = callDepth;
}
else if (stack[i][0] == ')')
{
callDepth--;
}
CanUseTempRegs = maxCallDepth == 0;
TempsUsed = 0;
if (GenWreg != MipsOpRegV0)
errorInternal(102);
for (i = 0; i < sp; i++)
{
int tok = stack[i][0];
int v = stack[i][1];
#ifndef NO_ANNOTATIONS
switch (tok)
{
case tokNumInt: printf2(" # %d\n", truncInt(v)); break;
//case tokNumUint: printf2(" # %uu\n", truncUint(v)); break;
case tokIdent: case tokRevIdent: printf2(" # %s\n", IdentTable + v); break;
case tokLocalOfs: case tokRevLocalOfs: printf2(" # local ofs\n"); break;
case ')': printf2(" # ) fxn call\n"); break;
case tokUnaryStar: printf2(" # * (read dereference)\n"); break;
case '=': printf2(" # = (write dereference)\n"); break;
case tokShortCirc: printf2(" # short-circuit "); break;
case tokGoto: printf2(" # sh-circ-goto "); break;
case tokLogAnd: printf2(" # short-circuit && target\n"); break;
case tokLogOr: printf2(" # short-circuit || target\n"); break;
case tokIf: case tokIfNot: case tokReturn: break;
default: printf2(" # %s\n", GetTokenName(tok)); break;
}
#endif
switch (tok)
{
case tokNumInt:
if (!(i + 1 < sp && ((t = stack[i + 1][0]) == '+' ||
t == '-' ||
t == '&' ||
t == '^' ||
t == '|' ||
t == tokLShift ||
t == tokRShift ||
t == tokURShift ||
GenIsCmp(t))))
{
if (gotUnary)
GenPushReg();
GenPrintInstr2Operands(MipsInstrLI, 0,
GenWreg, 0,
MipsOpConst, v);
}
gotUnary = 1;
break;
case tokIdent:
if (gotUnary)
GenPushReg();
if (!(i + 1 < sp && ((t = stack[i + 1][0]) == ')' ||
t == tokUnaryStar ||
t == tokInc ||
t == tokDec ||
t == tokPostInc ||
t == tokPostDec)))
{
GenPrintInstr2Operands(MipsInstrLA, 0,
GenWreg, 0,
MipsOpLabel, v);
}
gotUnary = 1;
break;
case tokLocalOfs:
if (gotUnary)
GenPushReg();
if (!(i + 1 < sp && ((t = stack[i + 1][0]) == tokUnaryStar ||
t == tokInc ||
t == tokDec ||
t == tokPostInc ||
t == tokPostDec)))
{
GenPrintInstr3Operands(MipsInstrAddU, 0,
GenWreg, 0,
MipsOpRegFp, 0,
MipsOpConst, v);
}
gotUnary = 1;
break;
case '(':
if (gotUnary)
GenPushReg();
gotUnary = 0;
if (maxCallDepth != 1 && v < 16)
GenGrowStack(16 - v);
paramOfs = v - 4;
if (maxCallDepth == 1 && paramOfs >= 0 && paramOfs <= 12)
{
// Work directly in A0-A3 instead of working in V0 and avoid copying V0 to A0-A3
GenWreg = MipsOpRegA0 + paramOfs / 4;
}
break;
case ',':
if (maxCallDepth == 1)
{
if (paramOfs == 16)
{
// Got the last on-stack parameter, the rest will go in A0-A3
GenPushReg();
gotUnary = 0;
GenWreg = MipsOpRegA3;
}
if (paramOfs >= 0 && paramOfs <= 12)
{
// Advance to the next An reg or revert to V0
if (paramOfs)
GenWreg--;
else
GenWreg = MipsOpRegV0;
gotUnary = 0;
}
paramOfs -= 4;
}
break;
case ')':
GenLeaf = 0;
if (maxCallDepth != 1)
{
if (v >= 4)
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegA0, 0,
MipsOpIndRegSp, 0);
if (v >= 8)
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegA1, 0,
MipsOpIndRegSp, 4);
if (v >= 12)
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegA2, 0,
MipsOpIndRegSp, 8);
if (v >= 16)
GenPrintInstr2Operands(MipsInstrLW, 0,
MipsOpRegA3, 0,
MipsOpIndRegSp, 12);
}
else
{
GenGrowStack(16);
}
if (stack[i - 1][0] == tokIdent)
{
GenPrintInstr1Operand(MipsInstrJAL, 0,
MipsOpLabel, stack[i - 1][1]);
}
else
{
GenPrintInstr1Operand(MipsInstrJAL, 0,
GenWreg, 0);
}
if (v < 16)
v = 16;
GenGrowStack(-v);
break;
case tokUnaryStar:
if (stack[i - 1][0] == tokIdent)
GenReadIdent(GenWreg, v, stack[i - 1][1]);
else if (stack[i - 1][0] == tokLocalOfs)
GenReadLocal(GenWreg, v, stack[i - 1][1]);
else
GenReadIndirect(GenWreg, GenWreg, v);
break;
case tokUnaryPlus:
break;
case '~':
GenPrintInstr3Operands(MipsInstrNor, 0,
GenWreg, 0,
GenWreg, 0,
GenWreg, 0);
break;
case tokUnaryMinus:
GenPrintInstr3Operands(MipsInstrSubU, 0,
GenWreg, 0,
MipsOpRegZero, 0,
GenWreg, 0);
break;
case '+':
case '-':
case '*':
case '&':
case '^':
case '|':
case tokLShift:
case tokRShift:
case tokURShift:
if (stack[i - 1][0] == tokNumInt && tok != '*')
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr3Operands(instr, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, stack[i - 1][1]);
}
else
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPopReg();
GenPrintInstr3Operands(instr, 0,
GenWreg, 0,
GenLreg, 0,
GenRreg, 0);
}
break;
case '/':
case tokUDiv:
case '%':
case tokUMod:
{
GenPopReg();
if (tok == '/' || tok == '%')
GenPrintInstr3Operands(MipsInstrDiv, 0,
MipsOpRegZero, 0,
GenLreg, 0,
GenRreg, 0);
else
GenPrintInstr3Operands(MipsInstrDivU, 0,
MipsOpRegZero, 0,
GenLreg, 0,
GenRreg, 0);
if (tok == '%' || tok == tokUMod)
GenPrintInstr1Operand(MipsInstrMfHi, 0,
GenWreg, 0);
else
GenPrintInstr1Operand(MipsInstrMfLo, 0,
GenWreg, 0);
}
break;
case tokInc:
case tokDec:
if (stack[i - 1][0] == tokIdent)
{
GenIncDecIdent(GenWreg, v, stack[i - 1][1], tok);
}
else if (stack[i - 1][0] == tokLocalOfs)
{
GenIncDecLocal(GenWreg, v, stack[i - 1][1], tok);
}
else
{
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_A, 0,
GenWreg, 0);
GenIncDecIndirect(GenWreg, TEMP_REG_A, v, tok);
}
break;
case tokPostInc:
case tokPostDec:
if (stack[i - 1][0] == tokIdent)
{
GenPostIncDecIdent(GenWreg, v, stack[i - 1][1], tok);
}
else if (stack[i - 1][0] == tokLocalOfs)
{
GenPostIncDecLocal(GenWreg, v, stack[i - 1][1], tok);
}
else
{
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_A, 0,
GenWreg, 0);
GenPostIncDecIndirect(GenWreg, TEMP_REG_A, v, tok);
}
break;
case tokPostAdd:
case tokPostSub:
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPopReg();
if (GenWreg == GenLreg)
{
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenLreg, 0);
GenReadIndirect(GenWreg, TEMP_REG_B, v);
GenPrintInstr3Operands(instr, 0,
TEMP_REG_A, 0,
GenWreg, 0,
GenRreg, 0);
GenWriteIndirect(TEMP_REG_B, TEMP_REG_A, v);
}
else
{
// GenWreg == GenRreg here
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenRreg, 0);
GenReadIndirect(GenWreg, GenLreg, v);
GenPrintInstr3Operands(instr, 0,
TEMP_REG_B, 0,
GenWreg, 0,
TEMP_REG_B, 0);
GenWriteIndirect(GenLreg, TEMP_REG_B, v);
}
}
break;
case tokAssignAdd:
case tokAssignSub:
case tokAssignMul:
case tokAssignAnd:
case tokAssignXor:
case tokAssignOr:
case tokAssignLSh:
case tokAssignRSh:
case tokAssignURSh:
if (stack[i - 1][0] == tokRevLocalOfs || stack[i - 1][0] == tokRevIdent)
{
int instr = GenGetBinaryOperatorInstr(tok);
if (stack[i - 1][0] == tokRevLocalOfs)
GenReadLocal(TEMP_REG_B, v, stack[i - 1][1]);
else
GenReadIdent(TEMP_REG_B, v, stack[i - 1][1]);
GenPrintInstr3Operands(instr, 0,
GenWreg, 0,
TEMP_REG_B, 0,
GenWreg, 0);
if (stack[i - 1][0] == tokRevLocalOfs)
GenWriteLocal(GenWreg, v, stack[i - 1][1]);
else
GenWriteIdent(GenWreg, v, stack[i - 1][1]);
}
else
{
int instr = GenGetBinaryOperatorInstr(tok);
int lsaved, rsaved;
GenPopReg();
if (GenWreg == GenLreg)
{
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenLreg, 0);
lsaved = TEMP_REG_B;
rsaved = GenRreg;
}
else
{
// GenWreg == GenRreg here
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenRreg, 0);
rsaved = TEMP_REG_B;
lsaved = GenLreg;
}
GenReadIndirect(GenWreg, GenLreg, v); // destroys either GenLreg or GenRreg because GenWreg coincides with one of them
GenPrintInstr3Operands(instr, 0,
GenWreg, 0,
GenWreg, 0,
rsaved, 0);
GenWriteIndirect(lsaved, GenWreg, v);
}
GenExtendRegIfNeeded(GenWreg, v);
break;
case tokAssignDiv:
case tokAssignUDiv:
case tokAssignMod:
case tokAssignUMod:
if (stack[i - 1][0] == tokRevLocalOfs || stack[i - 1][0] == tokRevIdent)
{
if (stack[i - 1][0] == tokRevLocalOfs)
GenReadLocal(TEMP_REG_B, v, stack[i - 1][1]);
else
GenReadIdent(TEMP_REG_B, v, stack[i - 1][1]);
if (tok == tokAssignDiv || tok == tokAssignMod)
GenPrintInstr3Operands(MipsInstrDiv, 0,
MipsOpRegZero, 0,
TEMP_REG_B, 0,
GenWreg, 0);
else
GenPrintInstr3Operands(MipsInstrDivU, 0,
MipsOpRegZero, 0,
TEMP_REG_B, 0,
GenWreg, 0);
if (tok == tokAssignMod || tok == tokAssignUMod)
GenPrintInstr1Operand(MipsInstrMfHi, 0,
GenWreg, 0);
else
GenPrintInstr1Operand(MipsInstrMfLo, 0,
GenWreg, 0);
if (stack[i - 1][0] == tokRevLocalOfs)
GenWriteLocal(GenWreg, v, stack[i - 1][1]);
else
GenWriteIdent(GenWreg, v, stack[i - 1][1]);
}
else
{
int lsaved, rsaved;
GenPopReg();
if (GenWreg == GenLreg)
{
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenLreg, 0);
lsaved = TEMP_REG_B;
rsaved = GenRreg;
}
else
{
// GenWreg == GenRreg here
GenPrintInstr2Operands(MipsInstrMov, 0,
TEMP_REG_B, 0,
GenRreg, 0);
rsaved = TEMP_REG_B;
lsaved = GenLreg;
}
GenReadIndirect(GenWreg, GenLreg, v); // destroys either GenLreg or GenRreg because GenWreg coincides with one of them
if (tok == tokAssignDiv || tok == tokAssignMod)
GenPrintInstr3Operands(MipsInstrDiv, 0,
MipsOpRegZero, 0,
GenWreg, 0,
rsaved, 0);
else
GenPrintInstr3Operands(MipsInstrDivU, 0,
MipsOpRegZero, 0,
GenWreg, 0,
rsaved, 0);
if (tok == tokAssignMod || tok == tokAssignUMod)
GenPrintInstr1Operand(MipsInstrMfHi, 0,
GenWreg, 0);
else
GenPrintInstr1Operand(MipsInstrMfLo, 0,
GenWreg, 0);
GenWriteIndirect(lsaved, GenWreg, v);
}
GenExtendRegIfNeeded(GenWreg, v);
break;
case '=':
if (stack[i - 1][0] == tokRevLocalOfs)
{
GenWriteLocal(GenWreg, v, stack[i - 1][1]);
}
else if (stack[i - 1][0] == tokRevIdent)
{
GenWriteIdent(GenWreg, v, stack[i - 1][1]);
}
else
{
GenPopReg();
GenWriteIndirect(GenLreg, GenRreg, v);
if (GenWreg != GenRreg)
GenPrintInstr2Operands(MipsInstrMov, 0,
GenWreg, 0,
GenRreg, 0);
}
GenExtendRegIfNeeded(GenWreg, v);
break;
case tokAssign0: // assignment of 0, while throwing away the expression result value
if (stack[i - 1][0] == tokRevLocalOfs)
{
GenWriteLocal(MipsOpRegZero, v, stack[i - 1][1]);
}
else if (stack[i - 1][0] == tokRevIdent)
{
GenWriteIdent(MipsOpRegZero, v, stack[i - 1][1]);
}
else
{
GenWriteIndirect(GenWreg, MipsOpRegZero, v);
}
break;
case '<': GenCmp(&i, 0x00); break;
case tokLEQ: GenCmp(&i, 0x01); break;
case '>': GenCmp(&i, 0x02); break;
case tokGEQ: GenCmp(&i, 0x03); break;
case tokULess: GenCmp(&i, 0x10); break;
case tokULEQ: GenCmp(&i, 0x11); break;
case tokUGreater: GenCmp(&i, 0x12); break;
case tokUGEQ: GenCmp(&i, 0x13); break;
case tokEQ: GenCmp(&i, 0x04); break;
case tokNEQ: GenCmp(&i, 0x05); break;
case tok_Bool:
GenPrintInstr3Operands(MipsInstrSLTU, 0,
GenWreg, 0,
MipsOpRegZero, 0,
GenWreg, 0);
break;
case tokSChar:
#ifdef DONT_USE_SEH
GenPrintInstr3Operands(MipsInstrSLL, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 24);
GenPrintInstr3Operands(MipsInstrSRA, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 24);
#else
GenPrintInstr2Operands(MipsInstrSeb, 0,
GenWreg, 0,
GenWreg, 0);
#endif
break;
case tokUChar:
GenPrintInstr3Operands(MipsInstrAnd, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 0xFF);
break;
case tokShort:
#ifdef DONT_USE_SEH
GenPrintInstr3Operands(MipsInstrSLL, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 16);
GenPrintInstr3Operands(MipsInstrSRA, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 16);
#else
GenPrintInstr2Operands(MipsInstrSeh, 0,
GenWreg, 0,
GenWreg, 0);
#endif
break;
case tokUShort:
GenPrintInstr3Operands(MipsInstrAnd, 0,
GenWreg, 0,
GenWreg, 0,
MipsOpConst, 0xFFFF);
break;
case tokShortCirc:
#ifndef NO_ANNOTATIONS
if (v >= 0)
printf2("&&\n");
else
printf2("||\n");
#endif
if (v >= 0)
GenJumpIfZero(v); // &&
else
GenJumpIfNotZero(-v); // ||
gotUnary = 0;
break;
case tokGoto:
#ifndef NO_ANNOTATIONS
printf2("goto\n");
#endif
GenJumpUncond(v);
gotUnary = 0;
break;
case tokLogAnd:
case tokLogOr:
GenNumLabel(v);
break;
case tokVoid:
gotUnary = 0;
break;
case tokRevIdent:
case tokRevLocalOfs:
case tokComma:
case tokReturn:
case tokNum0:
break;
case tokIf:
GenJumpIfNotZero(stack[i][1]);
break;
case tokIfNot:
GenJumpIfZero(stack[i][1]);
break;
default:
//error("Error: Internal Error: GenExpr0(): unexpected token %s\n", GetTokenName(tok));
errorInternal(103);
break;
}
}
if (GenWreg != MipsOpRegV0)
errorInternal(104);
}
STATIC
void GenDumpChar(int ch)
{
if (ch < 0)
{
if (TokenStringLen)
printf2("\"\n");
return;
}
if (TokenStringLen == 0)
{
GenStartAsciiString();
printf2("\"");
}
if (ch >= 0x20 && ch <= 0x7E)
{
if (ch == '"' || ch == '\\')
printf2("\\");
printf2("%c", ch);
}
else
{
printf2("\\%03o", ch);
}
}
STATIC
void GenExpr(void)
{
GenExpr0();
}
STATIC
void GenFin(void)
{
if (StructCpyLabel)
{
int lbl = LabelCnt++;
puts2(CodeHeaderFooter[0]);
GenNumLabel(StructCpyLabel);
puts2("\tmove\t$2, $6\n"
"\tmove\t$3, $6");
GenNumLabel(lbl);
puts2("\tlbu\t$6, 0($5)\n"
"\taddiu\t$5, $5, 1\n"
"\taddiu\t$4, $4, -1\n"
"\tsb\t$6, 0($3)\n"
"\taddiu\t$3, $3, 1");
printf2("\tbne\t$4, $0, "); GenPrintNumLabel(lbl);
puts2("");
#ifdef REORDER_WORKAROUND
GenNop();
#endif
puts2("\tj\t$31");
#ifdef REORDER_WORKAROUND
GenNop();
#endif
puts2(CodeHeaderFooter[1]);
}
#ifndef NO_STRUCT_BY_VAL
if (StructPushLabel)
{
int lbl = LabelCnt++;
puts2(CodeHeaderFooter[0]);
GenNumLabel(StructPushLabel);
puts2("\tmove\t$6, $5\n"
"\taddiu\t$6, $6, 3\n"
"\tli\t$3, -4\n"
"\tand\t$6, $6, $3\n"
"\tsubu\t$29, $29, $6\n"
"\taddiu\t$3, $29, 16\n"
"\tmove\t$2, $3");
GenNumLabel(lbl);
puts2("\tlbu\t$6, 0($4)\n"
"\taddiu\t$4, $4, 1\n"
"\taddiu\t$5, $5, -1\n"
"\tsb\t$6, 0($3)\n"
"\taddiu\t$3, $3, 1");
printf2("\tbne\t$5, $0, "); GenPrintNumLabel(lbl);
puts2("");
#ifdef REORDER_WORKAROUND
GenNop();
#endif
puts2("\tlw\t$2, 0($2)\n"
"\taddiu\t$29, $29, 4\n"
"\tj\t$31");
#ifdef REORDER_WORKAROUND
GenNop();
#endif
puts2(CodeHeaderFooter[1]);
}
#endif
}