Files
retrobsd/src/cmd/smlrc/cgx86.c
Alexey Frunze ae75f1cecc Smaller C: improvements
All architectures:
- "return" statement at function's end doesn't jump to immediately
  following epilog anymore
- zero and non-zero constants are recognized in conditional
  expressions of "if", "do/while" and "for" statements, no code
  generated to evaluate these constant conditions and related
  unnecessary jumps aren't generated anymore either
- in "for (clause-1; expr-2; expr-3) body", "expr-3" and "body" are
  now reordered to the more natural code flow "body expr-3", thereby
  getting rid of unnecessary jumps
MIPS code generator:
- function prologue/epilogue shortened further
- RA is not explicitly saved/restored in leaf functions
- assignment of 0 (e.g. "int a = 0;") is done from register 0
  directly, avoiding a load of a constant
2015-08-29 02:27:19 -07:00

3409 lines
93 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). */
/* */
/* x86 code generator */
/* */
/*****************************************************************************/
// TBD!!! compress/clean up
#ifdef CAN_COMPILE_32BIT
int WindowsStack = 0;
int WinChkStkLabel = 0;
#endif
#define MAX_GLOBALS_TABLE_LEN MAX_IDENT_TABLE_LEN
/*
Globals table entry format:
use char: use: bit 0 = defined, bit 1 = used
idlen char: string length (<= 127)
id char[idlen]: string (ASCIIZ)
*/
char GlobalsTable[MAX_GLOBALS_TABLE_LEN];
int GlobalsTableLen = 0;
STATIC
void GenAddGlobal(char* s, int use)
{
int i = 0;
int l;
if (GenExterns)
{
while (i < GlobalsTableLen)
{
if (!strcmp(GlobalsTable + i + 2, s))
{
GlobalsTable[i] |= use;
return;
}
i += GlobalsTable[i + 1] + 2;
}
l = strlen(s) + 1;
if (GlobalsTableLen + l + 2 > MAX_GLOBALS_TABLE_LEN)
error("Table of globals exhausted\n");
GlobalsTable[GlobalsTableLen++] = use;
GlobalsTable[GlobalsTableLen++] = l;
memcpy(GlobalsTable + GlobalsTableLen, s, l);
GlobalsTableLen += l;
}
}
STATIC
void GenInit(void)
{
// initialization of target-specific code generator
SizeOfWord = 2;
OutputFormat = FormatSegmented;
UseLeadingUnderscores = 1;
}
STATIC
int GenInitParams(int argc, char** argv, int* idx)
{
(void)argc;
// initialization of target-specific code generator with parameters
if (!strcmp(argv[*idx], "-nobss"))
{
UseBss = 0;
return 1;
}
else if (!strcmp(argv[*idx], "-seg16"))
{
// this is the default option for x86
OutputFormat = FormatSegmented; SizeOfWord = 2;
return 1;
}
#ifdef CAN_COMPILE_32BIT
else if (!strcmp(argv[*idx], "-seg32"))
{
OutputFormat = FormatSegmented; SizeOfWord = 4;
return 1;
}
else if (!strcmp(argv[*idx], "-huge"))
{
OutputFormat = FormatSegHuge; SizeOfWord = 4;
return 1;
}
else if (!strcmp(argv[*idx], "-winstack"))
{
WindowsStack = 1;
return 1;
}
#endif
return 0;
}
STATIC
void GenInitFinalize(void)
{
// finalization of initialization of target-specific code generator
// Change the output assembly format/content according to the options
CodeHeaderFooter[0] = "section .text";
DataHeaderFooter[0] = "section .data";
RoDataHeaderFooter[0] = "section .rodata";
BssHeaderFooter[0] = "section .bss";
if (SizeOfWord == 2 || OutputFormat == FormatSegHuge)
FileHeader = "bits 16\n";
else
FileHeader = "bits 32\n";
}
STATIC
void GenStartCommentLine(void)
{
printf2("; ");
}
STATIC
void GenWordAlignment(int bss)
{
printf2(bss ? "\talignb %d\n" : "\talign %d\n", SizeOfWord);
}
STATIC
void GenLabel(char* Label, int Static)
{
if (UseLeadingUnderscores)
{
if (!Static && GenExterns)
printf2("\tglobal\t_%s\n", Label);
printf2("_%s:\n", Label);
}
else
{
if (!Static && GenExterns)
printf2("\tglobal\t$%s\n", Label);
printf2("$%s:\n", Label);
}
GenAddGlobal(Label, 1);
}
STATIC
void GenPrintLabel(char* Label)
{
if (UseLeadingUnderscores)
{
if (isdigit(*Label))
printf2("L%s", Label);
else
printf2("_%s", Label);
}
else
{
if (isdigit(*Label))
printf2("..@L%s", Label);
else
printf2("$%s", Label);
}
}
STATIC
void GenNumLabel(int Label)
{
if (UseLeadingUnderscores)
printf2("L%d:\n", Label);
else
printf2("..@L%d:\n", Label);
}
STATIC
void GenPrintNumLabel(int label)
{
if (UseLeadingUnderscores)
printf2("L%d", label);
else
printf2("..@L%d", label);
}
STATIC
void GenZeroData(unsigned Size, int bss)
{
printf2(bss ? "\tresb\t%u\n" : "\ttimes\t%u db 0\n", truncUint(Size));
}
STATIC
void GenIntData(int Size, int Val)
{
Val = truncInt(Val);
if (Size == 1)
printf2("\tdb\t%d\n", Val);
else if (Size == 2)
printf2("\tdw\t%d\n", Val);
#ifdef CAN_COMPILE_32BIT
else if (Size == 4)
printf2("\tdd\t%d\n", Val);
#endif
}
STATIC
void GenStartAsciiString(void)
{
printf2("\tdb\t");
}
STATIC
void GenAddrData(int Size, char* Label, int ofs)
{
ofs = truncInt(ofs);
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
{
int lab = LabelCnt++;
printf2("section .relod\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(DataHeaderFooter[0]);
GenNumLabel(lab);
}
#endif
if (Size == 1)
printf2("\tdb\t");
else if (Size == 2)
printf2("\tdw\t");
#ifdef CAN_COMPILE_32BIT
else if (Size == 4)
printf2("\tdd\t");
#endif
GenPrintLabel(Label);
if (ofs)
printf2(" %+d", ofs);
puts2("");
if (!isdigit(*Label))
GenAddGlobal(Label, 2);
}
#define X86InstrMov 0x00
#define X86InstrMovSx 0x01
#define X86InstrMovZx 0x02
#define X86InstrXchg 0x03
#define X86InstrLea 0x04
#define X86InstrPush 0x05
#define X86InstrPop 0x06
#define X86InstrInc 0x07
#define X86InstrDec 0x08
#define X86InstrAdd 0x09
#define X86InstrSub 0x0A
#define X86InstrAnd 0x0B
#define X86InstrXor 0x0C
#define X86InstrOr 0x0D
#define X86InstrCmp 0x0E
#define X86InstrTest 0x0F
#define X86InstrMul 0x10
#define X86InstrImul 0x11
#define X86InstrIdiv 0x12
#define X86InstrDiv 0x13
#define X86InstrShl 0x14
#define X86InstrSar 0x15
#define X86InstrShr 0x16
#define X86InstrNeg 0x17
#define X86InstrNot 0x18
#define X86InstrCbw 0x19
#define X86InstrCwd 0x1A
#define X86InstrCdq 0x1B
#define X86InstrSetCc 0x1C
#define X86InstrJcc 0x1D
#define X86InstrJNotCc 0x1E
#define X86InstrLeave 0x1F
#define X86InstrCall 0x20
#define X86InstrRet 0x21
#define X86InstrJmp 0x22
char* winstrs[] =
{
"mov",
"movsx",
"movzx",
"xchg",
"lea",
"push",
"pop",
"inc",
"dec",
"add",
"sub",
"and",
"xor",
"or",
"cmp",
"test",
"mul",
"imul",
"idiv",
"div",
"shl",
"sar",
"shr",
"neg",
"not",
"cbw",
"cwd",
"cdq",
0, // setcc
0, // jcc
0, // j!cc
0, // leave
"call",
0, // ret
"jmp",
};
STATIC
void GenPrintInstr(int instr, int val)
{
char* p = "";
switch (instr)
{
case X86InstrLeave: p = (OutputFormat != FormatSegHuge) ? "leave" : "db\t0x66\n\tleave"; break;
case X86InstrRet: p = (OutputFormat != FormatSegHuge) ? "ret" : "retf"; break;
case X86InstrJcc:
switch (val)
{
case '<': p = "jl"; break;
case tokULess: p = "jb"; break;
case '>': p = "jg"; break;
case tokUGreater: p = "ja"; break;
case tokLEQ: p = "jle"; break;
case tokULEQ: p = "jbe"; break;
case tokGEQ: p = "jge"; break;
case tokUGEQ: p = "jae"; break;
case tokEQ: p = "je"; break;
case tokNEQ: p = "jne"; break;
}
break;
case X86InstrJNotCc:
switch (val)
{
case '<': p = "jge"; break;
case tokULess: p = "jae"; break;
case '>': p = "jle"; break;
case tokUGreater: p = "jbe"; break;
case tokLEQ: p = "jg"; break;
case tokULEQ: p = "ja"; break;
case tokGEQ: p = "jl"; break;
case tokUGEQ: p = "jb"; break;
case tokEQ: p = "jne"; break;
case tokNEQ: p = "je"; break;
}
break;
case X86InstrSetCc:
switch (val)
{
case '<': p = "setl"; break;
case tokULess: p = "setb"; break;
case '>': p = "setg"; break;
case tokUGreater: p = "seta"; break;
case tokLEQ: p = "setle"; break;
case tokULEQ: p = "setbe"; break;
case tokGEQ: p = "setge"; break;
case tokUGEQ: p = "setae"; break;
case tokEQ: p = "sete"; break;
case tokNEQ: p = "setne"; break;
}
break;
default:
p = winstrs[instr];
break;
}
switch (instr)
{
case X86InstrCbw:
case X86InstrCwd:
#ifdef CAN_COMPILE_32BIT
case X86InstrCdq:
#endif
case X86InstrLeave:
case X86InstrRet:
printf2("\t%s", p);
break;
default:
printf2("\t%s\t", p);
break;
}
}
#define X86OpRegAByte 0x00
#define X86OpRegAByteHigh 0x01
#define X86OpRegCByte 0x02
#define X86OpRegAWord 0x03
#define X86OpRegBWord 0x04
#define X86OpRegCWord 0x05
#define X86OpRegDWord 0x06
#define X86OpRegAHalfWord 0x07
#define X86OpRegCHalfWord 0x08
#define X86OpRegBpWord 0x09
#define X86OpRegSpWord 0x0A
#define X86OpRegAByteOrWord 0x0B
#define X86OpRegCByteOrWord 0x0C
#define X86OpConst 0x0D
#define X86OpLabel 0x0E
#define X86OpNumLabel 0x0F
#define X86OpIndLabel 0x10
#define X86OpIndLabelExplicitByte 0x11
#define X86OpIndLabelExplicitWord 0x12
#define X86OpIndLabelExplicitHalfWord 0x13
#define X86OpIndLabelExplicitByteOrWord 0x14
#define X86OpIndLocal 0x15
#define X86OpIndLocalExplicitByte 0x16
#define X86OpIndLocalExplicitWord 0x17
#define X86OpIndLocalExplicitHalfWord 0x18
#define X86OpIndLocalExplicitByteOrWord 0x19
#define X86OpIndRegB 0x1A
#define X86OpIndRegBExplicitByte 0x1B
#define X86OpIndRegBExplicitWord 0x1C
#define X86OpIndRegBExplicitHalfWord 0x1D
#define X86OpIndRegBExplicitByteOrWord 0x1E
STATIC
int GenSelectByteOrWord(int op, int opSz)
{
switch (op)
{
case X86OpRegAByteOrWord:
op = X86OpRegAByte;
if (opSz == SizeOfWord)
op = X86OpRegAWord;
#ifdef CAN_COMPILE_32BIT
else if (opSz == 2 || opSz == -2)
op = X86OpRegAHalfWord;
#endif
break;
case X86OpRegCByteOrWord:
op = X86OpRegCByte;
if (opSz == SizeOfWord)
op = X86OpRegCWord;
#ifdef CAN_COMPILE_32BIT
else if (opSz == 2 || opSz == -2)
op = X86OpRegCHalfWord;
#endif
break;
case X86OpIndLabelExplicitByteOrWord:
op = X86OpIndLabelExplicitByte;
if (opSz == SizeOfWord)
op = X86OpIndLabelExplicitWord;
#ifdef CAN_COMPILE_32BIT
else if (opSz == 2 || opSz == -2)
op = X86OpIndLabelExplicitHalfWord;
#endif
break;
case X86OpIndLocalExplicitByteOrWord:
op = X86OpIndLocalExplicitByte;
if (opSz == SizeOfWord)
op = X86OpIndLocalExplicitWord;
#ifdef CAN_COMPILE_32BIT
else if (opSz == 2 || opSz == -2)
op = X86OpIndLocalExplicitHalfWord;
#endif
break;
case X86OpIndRegBExplicitByteOrWord:
op = X86OpIndRegBExplicitByte;
if (opSz == SizeOfWord)
op = X86OpIndRegBExplicitWord;
#ifdef CAN_COMPILE_32BIT
else if (opSz == 2 || opSz == -2)
op = X86OpIndRegBExplicitHalfWord;
#endif
break;
}
return op;
}
STATIC
void GenPrintOperand(int op, int val)
{
if (SizeOfWord == 2)
{
switch (op)
{
case X86OpRegAByte: printf2("al"); break;
case X86OpRegAByteHigh: printf2("ah"); break;
case X86OpRegCByte: printf2("cl"); break;
case X86OpRegAWord: printf2("ax"); break;
case X86OpRegBWord: printf2("bx"); break;
case X86OpRegCWord: printf2("cx"); break;
case X86OpRegDWord: printf2("dx"); break;
case X86OpRegBpWord: printf2("bp"); break;
case X86OpRegSpWord: printf2("sp"); break;
case X86OpConst: printf2("%d", truncInt(val)); break;
case X86OpLabel: GenPrintLabel(IdentTable + val); break;
case X86OpNumLabel: GenPrintNumLabel(val); break;
case X86OpIndLabel: printf2("["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLabelExplicitByte: printf2("byte ["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLabelExplicitWord: printf2("word ["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLocal: printf2("[bp%+d]", truncInt(val)); break;
case X86OpIndLocalExplicitByte: printf2("byte [bp%+d]", truncInt(val)); break;
case X86OpIndLocalExplicitWord: printf2("word [bp%+d]", truncInt(val)); break;
case X86OpIndRegB: printf2("[bx]"); break;
case X86OpIndRegBExplicitByte: printf2("byte [bx]"); break;
case X86OpIndRegBExplicitWord: printf2("word [bx]"); break;
}
}
#ifdef CAN_COMPILE_32BIT
else
{
char* frame = (OutputFormat == FormatSegHuge) ? "bp" : "ebp";
char* base = (OutputFormat == FormatSegHuge) ? "si" : "ebx";
switch (op)
{
case X86OpRegAByte: printf2("al"); break;
case X86OpRegAByteHigh: printf2("ah"); break;
case X86OpRegCByte: printf2("cl"); break;
case X86OpRegAWord: printf2("eax"); break;
case X86OpRegBWord: printf2("ebx"); break;
case X86OpRegCWord: printf2("ecx"); break;
case X86OpRegDWord: printf2("edx"); break;
case X86OpRegAHalfWord: printf2("ax"); break;
case X86OpRegCHalfWord: printf2("cx"); break;
case X86OpRegBpWord: printf2("ebp"); break;
case X86OpRegSpWord: printf2("esp"); break;
case X86OpConst: printf2("%d", truncInt(val)); break;
case X86OpLabel: GenPrintLabel(IdentTable + val); break;
case X86OpNumLabel: GenPrintNumLabel(val); break;
case X86OpIndLabel: printf2("["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLabelExplicitByte: printf2("byte ["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLabelExplicitWord: printf2("dword ["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLabelExplicitHalfWord: printf2("word ["); GenPrintLabel(IdentTable + val); printf2("]"); break;
case X86OpIndLocal: printf2("[%s%+d]", frame, truncInt(val)); break;
case X86OpIndLocalExplicitByte: printf2("byte [%s%+d]", frame, truncInt(val)); break;
case X86OpIndLocalExplicitWord: printf2("dword [%s%+d]", frame, truncInt(val)); break;
case X86OpIndLocalExplicitHalfWord: printf2("word [%s%+d]", frame, truncInt(val)); break;
case X86OpIndRegB: printf2("[%s]", base); break;
case X86OpIndRegBExplicitByte: printf2("byte [%s]", base); break;
case X86OpIndRegBExplicitWord: printf2("dword [%s]", base); break;
case X86OpIndRegBExplicitHalfWord: printf2("word [%s]", base); break;
}
}
#endif
}
STATIC
void GenPrintOperandSeparator(void)
{
printf2(", ");
}
STATIC
void GenPrintNewLine(void)
{
puts2("");
}
STATIC
void GenPrintInstrNoOperand(int instr)
{
GenPrintInstr(instr, 0);
GenPrintNewLine();
}
#ifdef CAN_COMPILE_32BIT
STATIC
void GenRegB2Seg(void)
{
if (OutputFormat == FormatSegHuge)
puts2("\tmov\tesi, ebx\n\tror\tesi, 4\n\tmov\tds, si\n\tshr\tesi, 28");
}
#endif
STATIC
void GenPrintInstr1Operand(int instr, int instrval, int operand, int operandval)
{
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge && instr == X86InstrPush)
{
if (operand == X86OpConst)
{
printf2("\tpush\tdword %d\n", truncInt(operandval));
return;
}
else if (operand == X86OpLabel)
{
int lab = LabelCnt++;
printf2("section .relod\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(CodeHeaderFooter[0]);
puts2("\tdb\t0x66, 0x68"); // push dword const
GenNumLabel(lab);
printf2("\tdd\t"); GenPrintLabel(IdentTable + operandval); puts2("");
return;
}
}
#endif
GenPrintInstr(instr, instrval);
GenPrintOperand(operand, operandval);
GenPrintNewLine();
}
STATIC
void GenPrintInstr2Operands(int instr, int instrval, int operand1, int operand1val, int operand2, int operand2val)
{
if (operand2 == X86OpConst && truncUint(operand2val) == 0 &&
(instr == X86InstrAdd || instr == X86InstrSub))
return;
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
{
if (instr == X86InstrLea)
{
if (operand2 == X86OpIndLocal)
{
if (operand1 == X86OpRegAWord)
{
puts2("\txor\teax, eax\n\tmov\tax, ss"); // mov r32, sreg leaves top 16 bits undefined on pre-Pentium CPUs
printf2("\tshl\teax, 4\n\tlea\teax, [ebp+eax%+d]\n", truncInt(operand2val));
return;
}
else if (operand1 == X86OpRegCWord)
{
puts2("\txor\tecx, ecx\n\tmov\tcx, ss"); // mov r32, sreg leaves top 16 bits undefined on pre-Pentium CPUs
printf2("\tshl\tecx, 4\n\tlea\tecx, [ebp+ecx%+d]\n", truncInt(operand2val));
return;
}
}
errorInternal(106);
}
if (instr == X86InstrMov)
{
if (operand1 == X86OpRegAWord && operand2 == X86OpLabel)
{
int lab = LabelCnt++;
printf2("section .relod\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(CodeHeaderFooter[0]);
puts2("\tdb\t0x66, 0xB8"); // mov eax, const
GenNumLabel(lab);
printf2("\tdd\t"); GenPrintLabel(IdentTable + operand2val); puts2("");
return;
}
}
}
#endif
if (operand2 == X86OpConst &&
(operand2val == 1 || operand2val == -1) &&
(instr == X86InstrAdd || instr == X86InstrSub))
{
if ((operand2val == 1 && instr == X86InstrAdd) ||
(operand2val == -1 && instr == X86InstrSub))
GenPrintInstr(X86InstrInc, 0);
else
GenPrintInstr(X86InstrDec, 0);
GenPrintOperand(operand1, operand1val);
GenPrintNewLine();
return;
}
GenPrintInstr(instr, instrval);
GenPrintOperand(operand1, operand1val);
GenPrintOperandSeparator();
GenPrintOperand(operand2, operand2val);
GenPrintNewLine();
}
STATIC
void GenPrintInstr3Operands(int instr, int instrval,
int operand1, int operand1val,
int operand2, int operand2val,
int operand3, int operand3val)
{
GenPrintInstr(instr, instrval);
GenPrintOperand(operand1, operand1val);
GenPrintOperandSeparator();
GenPrintOperand(operand2, operand2val);
GenPrintOperandSeparator();
GenPrintOperand(operand3, operand3val);
GenPrintNewLine();
}
STATIC
void GenExtendRegAIfNeeded(int opSz)
{
if (SizeOfWord == 2)
{
if (opSz == -1)
GenPrintInstrNoOperand(X86InstrCbw);
else if (opSz == 1)
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAByteHigh, 0,
X86OpConst, 0);
}
#ifdef CAN_COMPILE_32BIT
else
{
if (opSz == -1)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
else if (opSz == 1)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
else if (opSz == -2)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
else if (opSz == 2)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
}
#endif
}
STATIC
void GenJumpUncond(int label)
{
GenPrintInstr1Operand(X86InstrJmp, 0,
X86OpNumLabel, label);
}
STATIC
void GenJumpIfEqual(int val, int label)
{
GenPrintInstr2Operands(X86InstrCmp, 0,
X86OpRegAWord, 0,
X86OpConst, val);
GenPrintInstr1Operand(X86InstrJcc, tokEQ,
X86OpNumLabel, label);
}
STATIC
void GenJumpIfZero(int label)
{
#ifndef NO_ANNOTATIONS
GenStartCommentLine(); printf2("JumpIfZero\n");
#endif
GenPrintInstr2Operands(X86InstrTest, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0);
GenPrintInstr1Operand(X86InstrJcc, tokEQ,
X86OpNumLabel, label);
}
STATIC
void GenJumpIfNotZero(int label)
{
#ifndef NO_ANNOTATIONS
GenStartCommentLine(); printf2("JumpIfNotZero\n");
#endif
GenPrintInstr2Operands(X86InstrTest, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0);
GenPrintInstr1Operand(X86InstrJcc, tokNEQ,
X86OpNumLabel, label);
}
fpos_t GenPrologPos;
STATIC
void GenWriteFrameSize(void)
{
unsigned size = -CurFxnMinLocalOfs;
int pfx = size ? ' ' : ';';
#ifdef CAN_COMPILE_32BIT
if (SizeOfWord == 4 &&
OutputFormat != FormatSegHuge &&
WindowsStack)
{
int pfx = (size >= 4096) ? ' ' : ';';
// When targeting Windows, call equivalent of _chkstk() to
// correctly grow the stack page by page by probing it
if (!WinChkStkLabel)
WinChkStkLabel = -LabelCnt++; // reserve a label for _chkstk() and mark unused
if (WinChkStkLabel < 0 && pfx == ' ')
WinChkStkLabel = -WinChkStkLabel; // _chkstk() has been used at least once
printf2("\t%cmov\teax, %10u\n", pfx, size); // 10 chars are enough for 32-bit unsigned ints
printf2("\t%ccall\t", pfx);
GenPrintNumLabel((WinChkStkLabel < 0) ? -WinChkStkLabel : WinChkStkLabel);
puts2("");
}
#endif
if (SizeOfWord == 2)
printf2("\t%csub\tsp, %10u\n", pfx, size); // 10 chars are enough for 32-bit unsigned ints
#ifdef CAN_COMPILE_32BIT
else
printf2("\t%csub\tesp, %10u\n", pfx, size); // 10 chars are enough for 32-bit unsigned ints
#endif
}
STATIC
void GenUpdateFrameSize(void)
{
fpos_t pos;
fgetpos(OutFile, &pos);
fsetpos(OutFile, &GenPrologPos);
GenWriteFrameSize();
fsetpos(OutFile, &pos);
}
STATIC
void GenFxnProlog(void)
{
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegBpWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBpWord, 0,
X86OpRegSpWord, 0);
fgetpos(OutFile, &GenPrologPos);
GenWriteFrameSize();
}
STATIC
void GenGrowStack(int size)
{
if (!size)
return;
GenPrintInstr2Operands(X86InstrSub, 0,
X86OpRegSpWord, 0,
X86OpConst, size);
}
STATIC
void GenFxnEpilog(void)
{
GenUpdateFrameSize();
GenPrintInstrNoOperand(X86InstrLeave);
GenPrintInstrNoOperand(X86InstrRet);
}
STATIC
int GenMaxLocalsSize(void)
{
#ifdef CAN_COMPILE_32BIT
if (SizeOfWord == 4 && OutputFormat != FormatSegHuge)
return 0x7FFFFFFF;
#endif
return 0x7FFF;
}
#ifdef CAN_COMPILE_32BIT
/*
struct INTREGS
{
unsigned short gs, fs, es, ds;
unsigned edi, esi, ebp, esp, ebx, edx, ecx, eax;
unsigned short ss, ip, cs, flags;
};
void __interrupt isr(struct INTREGS** ppRegs)
{
// **ppRegs (input/output values of registers) can be modified to
// handle software interrupts requested via the int instruction and
// communicate data via registers
// *ppRegs (directly related to the stack pointer) can be modified to
// return to a different location & implement preemptive scheduling,
// e.g. save *ppRegs of the interrupted task somewhere, update *ppRegs
// with a value from another interrupted task.
}
*/
void GenIsrProlog(void)
{
// The CPU has already done these:
// push flags
// push cs
// push ip
puts2("\tpush\tss");
puts2("\tpushad");
puts2("\tpush\tds\n"
"\tpush\tes\n"
"\tpush\tfs\n"
"\tpush\tgs");
// The context has been saved
puts2("\txor\teax, eax\n\tmov\tax, ss"); // mov r32, sreg leaves top 16 bits undefined on pre-Pentium CPUs
puts2("\txor\tebx, ebx\n\tmov\tbx, sp"); // top 16 bits of esp can contain garbage as well
puts2("\tshl\teax, 4\n\tadd\teax, ebx");
puts2("\tpush\teax"); // pointer to the structure with register values
puts2("\tsub\teax, 4\n\tpush\teax"); // pointer to the pointer to the structure with register values
puts2("\tpush\teax"); // fake return address allowing to use the existing bp-relative addressing of locals and params
puts2("\tpush\tebp\n"
"\tmov\tebp, esp");
fgetpos(OutFile, &GenPrologPos);
GenWriteFrameSize();
}
void GenIsrEpilog(void)
{
GenUpdateFrameSize();
puts2("\tdb\t0x66\n\tleave");
puts2("\tpop\teax"); // fake return address
puts2("\tpop\teax"); // pointer to the pointer to the structure with register values
puts2("\tpop\tebx"); // pointer to the structure with register values
puts2("\tror\tebx, 4\n\tmov\tds, bx\n\tshr\tebx, 28"); // ds:bx = pointer to the structure with register values
puts2("\tmov\tax, [bx+4*10]\n\tmov\tbx, [bx+4*5]\n\tsub\tbx, 4*10"); // ax:bx = proper pointer (with original segment) to the struct...
puts2("\tmov\tss, ax\n\tmov\tsp, bx"); // restore ss:sp that we had after push gs
// The context is now going to be restored
puts2("\tpop\tgs\n"
"\tpop\tfs\n"
"\tpop\tes\n"
"\tpop\tds");
puts2("\tpopad");
puts2("\tpop\tss");
puts2("\tiret");
}
#endif
STATIC
void GenReadIdent(int opSz, int label)
{
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLabel, label);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenReadLocal(int opSz, int ofs)
{
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLocal, ofs);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenReadIndirect(int opSz)
{
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenReadCRegIdent(int opSz, int label)
{
if (opSz == -1)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndLabelExplicitByte, label);
else if (opSz == 1)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndLabelExplicitByte, label);
#ifdef CAN_COMPILE_32BIT
else if (opSz != SizeOfWord && -opSz != SizeOfWord)
{
if (opSz == -2)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndLabelExplicitHalfWord, label);
else if (opSz == 2)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndLabelExplicitHalfWord, label);
}
#endif
else
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpIndLabel, label);
}
STATIC
void GenReadCRegLocal(int opSz, int ofs)
{
if (opSz == -1)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndLocalExplicitByte, ofs);
else if (opSz == 1)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndLocalExplicitByte, ofs);
#ifdef CAN_COMPILE_32BIT
else if (opSz != SizeOfWord && -opSz != SizeOfWord)
{
if (opSz == -2)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndLocalExplicitHalfWord, ofs);
else if (opSz == 2)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndLocalExplicitHalfWord, ofs);
}
#endif
else
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpIndLocal, ofs);
}
STATIC
void GenReadCRegIndirect(int opSz)
{
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
if (opSz == -1)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndRegBExplicitByte, 0);
else if (opSz == 1)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndRegBExplicitByte, 0);
#ifdef CAN_COMPILE_32BIT
else if (opSz != SizeOfWord && -opSz != SizeOfWord)
{
if (opSz == -2)
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegCWord, 0,
X86OpIndRegBExplicitHalfWord, 0);
else if (opSz == 2)
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegCWord, 0,
X86OpIndRegBExplicitHalfWord, 0);
}
#endif
else
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpIndRegB, 0);
}
STATIC
void GenIncDecIdent(int opSz, int label, int tok)
{
int instr = X86InstrInc;
if (tok != tokInc)
instr = X86InstrDec;
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndLabelExplicitByteOrWord, opSz), label);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLabel, label);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenIncDecLocal(int opSz, int ofs, int tok)
{
int instr = X86InstrInc;
if (tok != tokInc)
instr = X86InstrDec;
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndLocalExplicitByteOrWord, opSz), ofs);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLocal, ofs);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenIncDecIndirect(int opSz, int tok)
{
int instr = X86InstrInc;
if (tok != tokInc)
instr = X86InstrDec;
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndRegBExplicitByteOrWord, opSz), 0);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(opSz);
}
STATIC
void GenPostIncDecIdent(int opSz, int label, int tok)
{
int instr = X86InstrInc;
if (tok != tokPostInc)
instr = X86InstrDec;
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLabel, label);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndLabelExplicitByteOrWord, opSz), label);
}
STATIC
void GenPostIncDecLocal(int opSz, int ofs, int tok)
{
int instr = X86InstrInc;
if (tok != tokPostInc)
instr = X86InstrDec;
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLocal, ofs);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndLocalExplicitByteOrWord, opSz), ofs);
}
STATIC
void GenPostIncDecIndirect(int opSz, int tok)
{
int instr = X86InstrInc;
if (tok != tokPostInc)
instr = X86InstrDec;
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndRegBExplicitByteOrWord, opSz), 0);
}
STATIC
void GenPostAddSubIdent(int opSz, int val, int label, int tok)
{
int instr = X86InstrAdd;
if (tok != tokPostAdd)
instr = X86InstrSub;
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLabel, label);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr2Operands(instr, 0,
GenSelectByteOrWord(X86OpIndLabelExplicitByteOrWord, opSz), label,
X86OpConst, val);
}
STATIC
void GenPostAddSubLocal(int opSz, int val, int ofs, int tok)
{
int instr = X86InstrAdd;
if (tok != tokPostAdd)
instr = X86InstrSub;
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndLocal, ofs);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr2Operands(instr, 0,
GenSelectByteOrWord(X86OpIndLocalExplicitByteOrWord, opSz), ofs,
X86OpConst, val);
}
STATIC
void GenPostAddSubIndirect(int opSz, int val, int tok)
{
int instr = X86InstrAdd;
if (tok != tokPostAdd)
instr = X86InstrSub;
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, opSz), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(opSz);
GenPrintInstr2Operands(instr, 0,
GenSelectByteOrWord(X86OpIndRegBExplicitByteOrWord, opSz), 0,
X86OpConst, val);
}
#define tokOpNumInt 0x100
#define tokOpNumUint 0x101
#define tokOpIdent 0x102
#define tokOpLocalOfs 0x103
#define tokOpAcc 0x104
#define tokOpIndIdent 0x105
#define tokOpIndLocalOfs 0x106
#define tokOpIndAcc 0x107
#define tokOpStack 0x108
#define tokOpIndStack 0x109
#define tokPushAcc 0x200
STATIC
int GetOperandInfo(int idx, int lvalSize, int* val, int* size, int* delDeref)
{
int idx0 = idx;
*delDeref = 0;
while (stack[idx][0] >= tokOpNumInt && stack[idx][0] <= tokOpIndAcc)
idx--;
if (stack[idx][0] == tokUnaryStar)
{
if (lvalSize)
{
// lvalue dereference is implied for the left operand of =
// and for operands of ++/--, these operands contain the
// lvalue address
*size = lvalSize;
*val = 0;
return tokOpIndAcc;
}
*size = stack[idx][1]; // take size from tokUnaryStar
*delDeref = 1;
*val = stack[idx + 1][1]; // operand "value" is in tokUnaryStar's operand
return stack[idx + 1][0] + tokOpIndIdent - tokOpIdent; // add indirection
}
idx = idx0;
if (lvalSize)
{
// lvalue dereference is implied for the left operand of =
// and for operands of ++/--
*size = lvalSize;
*val = stack[idx][1];
switch (stack[idx][0])
{
case tokIdent:
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
goto l1;
#endif
return tokOpIndIdent;
case tokLocalOfs:
return tokOpIndLocalOfs;
default:
#ifdef CAN_COMPILE_32BIT
l1:
#endif
*val = 0;
return tokOpIndAcc;
}
}
*size = SizeOfWord;
*val = stack[idx][1];
switch (stack[idx][0])
{
case tokNumInt:
return tokOpNumInt;
case tokNumUint:
return tokOpNumUint;
case tokIdent:
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
goto l2;
#endif
return tokOpIdent;
case tokLocalOfs:
return tokOpLocalOfs;
default:
#ifdef CAN_COMPILE_32BIT
l2:
#endif
*val = 0;
return tokOpAcc;
}
}
STATIC
void GenFuse(int* idx)
{
int tok;
int oldIdxRight, oldSpRight;
int oldIdxLeft, oldSpLeft;
int opSzRight, opSzLeft;
int opTypRight, opTypLeft;
int opValRight, opValLeft;
int delDerefRight, delDerefLeft;
int num, lvalSize;
if (*idx < 0)
//error("GenFuse(): idx < 0\n");
errorInternal(100);
tok = stack[*idx][0];
--*idx;
oldIdxRight = *idx;
oldSpRight = sp;
switch (tok)
{
case tokNumInt:
case tokNumUint:
case tokIdent:
case tokLocalOfs:
break;
case tokShortCirc:
case tokGoto:
GenFuse(idx);
break;
case tokUnaryStar:
opSzRight = stack[*idx + 1][1];
GenFuse(idx);
oldIdxRight -= oldSpRight - sp;
switch (stack[oldIdxRight][0])
{
case tokIdent:
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
goto l1;
#endif
case tokLocalOfs:
if (stack[oldIdxRight][0] == tokIdent)
stack[oldIdxRight + 1][0] = tokOpIdent;
else
stack[oldIdxRight + 1][0] = tokOpLocalOfs;
stack[oldIdxRight + 1][1] = stack[oldIdxRight][1];
stack[oldIdxRight][0] = tok;
stack[oldIdxRight][1] = opSzRight;
break;
default:
#ifdef CAN_COMPILE_32BIT
l1:
#endif
ins(oldIdxRight + 2, tokOpAcc);
break;
}
break;
case tokInc:
case tokDec:
case tokPostInc:
case tokPostDec:
opSzRight = stack[*idx + 1][1];
GenFuse(idx);
oldIdxRight -= oldSpRight - sp;
switch (stack[oldIdxRight][0])
{
case tokIdent:
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
goto l2;
#endif
case tokLocalOfs:
if (stack[oldIdxRight][0] == tokIdent)
stack[oldIdxRight + 1][0] = tokOpIndIdent;
else
stack[oldIdxRight + 1][0] = tokOpIndLocalOfs;
stack[oldIdxRight + 1][1] = stack[oldIdxRight][1];
stack[oldIdxRight][0] = tok;
stack[oldIdxRight][1] = opSzRight;
break;
default:
#ifdef CAN_COMPILE_32BIT
l2:
#endif
ins(oldIdxRight + 2, tokOpIndAcc);
break;
}
break;
case '~':
case tokUnaryPlus:
case tokUnaryMinus:
case tok_Bool:
case tokVoid:
case tokUChar:
case tokSChar:
#ifdef CAN_COMPILE_32BIT
case tokShort:
case tokUShort:
#endif
GenFuse(idx);
oldIdxRight -= oldSpRight - sp;
if (tok == tokUnaryPlus)
del(oldIdxRight + 1, 1);
break;
case tokPostAdd:
case tokPostSub:
opSzRight = stack[*idx + 1][1];
num = stack[*idx][1];
oldIdxRight = --*idx; // skip tokNum
GenFuse(idx);
oldIdxRight -= oldSpRight - sp;
switch (stack[oldIdxRight][0])
{
case tokIdent:
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
goto l3;
#endif
case tokLocalOfs:
stack[oldIdxRight + 2][0] = tokOpNumInt;
stack[oldIdxRight + 2][1] = num;
if (stack[oldIdxRight][0] == tokIdent)
stack[oldIdxRight + 1][0] = tokOpIndIdent;
else
stack[oldIdxRight + 1][0] = tokOpIndLocalOfs;
stack[oldIdxRight + 1][1] = stack[oldIdxRight][1];
stack[oldIdxRight][0] = tok;
stack[oldIdxRight][1] = opSzRight;
break;
default:
#ifdef CAN_COMPILE_32BIT
l3:
#endif
stack[oldIdxRight + 1][0] = tok;
stack[oldIdxRight + 1][1] = opSzRight;
stack[oldIdxRight + 2][0] = tokOpIndAcc;
ins2(oldIdxRight + 3, tokOpNumInt, num);
break;
}
break;
/*
Operator-operand fusion:
ac = lft: ac op= rht: lft = ac:
(load) ("execute") (store)
*(id/l) *(id/l) *(id/l)
mov a?,mlft op a?,mrht mov mlft,a?
---
mov cl,mrht
shift ax,cl
---
mov c?,mrht
cwd
idiv cx
opt: mov ax,dx
*ac *ac *ac
mov bx,ax < mov bx,ax ; bx preserved
mov a?,[bx] < mov c?,[bx] mov [bx],a?
op ax,cx(cl)
*ac-stack n/a *ac-stack
pop bx ; bx preserved
mov a?,[bx] mov [bx],a?
id/num id/num
mov ax,ilft op ax,irht
---
mov cx,irht
op ax,cx
l l
lea ax,llft lea cx,lrht
op ax,cx
ac ac
nop < mov cx,ax
op ax,cx
ac-stack n/a
pop ax
lft (*)ac -> lft (*)ac-stack IFF rht is (*)ac
Legend:
- lft/rht - left/right operand
- num - number
- id - global/static identifier/location
- l - local variable location
- * - dereference operator
- m - memory operand at address id/l
- i - immediate/number/constant operand
- ac - accumulator (al or ax)
- a? - accumulator (al or ax), depending on operand size
- b? - bl or bx, depending on operand size
- >push axlft - need to insert "push ax" at the end of the left operand evaluation
instruction operand combinations (dst/lft, src/rht):
- r/m, r/imm
- r, m
special instructions:
- lea r, m
- shl/sar
- mul/imul/idiv
- cbw/cwd
- movsx/movzx
*/
case '=':
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:
case '+':
case '-':
case '*':
case '/':
case tokUDiv:
case '%':
case tokUMod:
case tokLShift:
case tokRShift:
case tokURShift:
case '&':
case '^':
case '|':
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokEQ:
case tokNEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
case tokLogAnd:
case tokLogOr:
case tokComma:
switch (tok)
{
case '=':
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:
lvalSize = stack[*idx + 1][1];
break;
default:
lvalSize = 0;
break;
}
GenFuse(idx);
oldIdxRight -= oldSpRight - sp;
opTypRight = GetOperandInfo(oldIdxRight, 0, &opValRight, &opSzRight, &delDerefRight);
oldIdxLeft = *idx; oldSpLeft = sp;
GenFuse(idx);
oldIdxLeft -= oldSpLeft - sp;
oldIdxRight -= oldSpLeft - sp;
opTypLeft = GetOperandInfo(oldIdxLeft, lvalSize, &opValLeft, &opSzLeft, &delDerefLeft);
// operands of &&, || and comma aren't to be fused into &&, || and comma
if (tok == tokLogAnd || tok == tokLogOr || tok == tokComma)
break;
if (opTypLeft != tokOpAcc && opTypLeft != tokOpIndAcc)
{
// the left operand will be fully fused into the operator, remove it
int cnt = oldIdxLeft - *idx;
del(*idx + 1, cnt);
oldIdxLeft -= cnt;
oldIdxRight -= cnt;
}
else if (opTypRight == tokOpAcc || opTypRight == tokOpIndAcc)
{
// preserve ax after the evaluation of the left operand
// because the right operand's value ends up in ax as well
ins(++oldIdxLeft, tokPushAcc);
oldIdxRight++;
// adjust the left operand "type"/location
if (opTypLeft == tokOpAcc)
opTypLeft = tokOpStack;
else
opTypLeft = tokOpIndStack;
if (delDerefLeft)
{
// remove the dereference, fusing will take care of it
del(oldIdxLeft -= 2, 2);
oldIdxRight -= 2;
}
}
else if (delDerefLeft)
{
// remove the dereference, fusing will take care of it
del(oldIdxLeft - 1, 2);
oldIdxLeft -= 2;
oldIdxRight -= 2;
}
if (opTypRight != tokOpAcc && opTypRight != tokOpIndAcc)
{
// the right operand will be fully fused into the operator, remove it
int cnt = oldIdxRight - oldIdxLeft;
del(oldIdxLeft + 1, cnt);
oldIdxRight -= cnt;
}
else if (delDerefRight)
{
// remove the dereference, fusing will take care of it
del(oldIdxRight - 1, 2);
oldIdxRight -= 2;
}
// store the operand sizes into the operator
stack[oldIdxRight + 1][1] = (opSzLeft + 8) * 16 + (opSzRight + 8);
// fuse the operands into the operator
ins2(oldIdxRight + 2, opTypRight, opValRight);
ins2(oldIdxRight + 2, opTypLeft, opValLeft);
break;
case ')':
while (stack[*idx][0] != '(')
{
GenFuse(idx);
if (stack[*idx][0] == ',')
--*idx;
}
--*idx;
break;
default:
//error("GenFuse: unexpected token %s\n", GetTokenName(tok));
errorInternal(101);
}
}
STATIC
int GenGetBinaryOperatorInstr(int tok)
{
switch (tok)
{
case tokPostAdd:
case tokAssignAdd:
case '+':
return X86InstrAdd;
case tokPostSub:
case tokAssignSub:
case '-':
return X86InstrSub;
case '&':
case tokAssignAnd:
return X86InstrAnd;
case '^':
case tokAssignXor:
return X86InstrXor;
case '|':
case tokAssignOr:
return X86InstrOr;
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokEQ:
case tokNEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
return X86InstrCmp;
case '*':
case tokAssignMul:
return X86InstrMul;
case '/':
case '%':
case tokAssignDiv:
case tokAssignMod:
return X86InstrIdiv;
case tokUDiv:
case tokUMod:
case tokAssignUDiv:
case tokAssignUMod:
return X86InstrDiv;
case tokLShift:
case tokAssignLSh:
return X86InstrShl;
case tokRShift:
case tokAssignRSh:
return X86InstrSar;
case tokURShift:
case tokAssignURSh:
return X86InstrShr;
default:
//error("Error: Invalid operator\n");
errorInternal(102);
return 0;
}
}
// Newer, less stack-dependent code generator,
// generates more compact code (~30% less) than the stack-based generator
#ifndef CG_STACK_BASED
STATIC
void GenExpr1(void)
{
int s = sp - 1;
int i;
if (stack[s][0] == tokIf || stack[s][0] == tokIfNot || stack[s][0] == tokReturn)
s--;
GenFuse(&s);
#ifndef NO_ANNOTATIONS
printf2("; Fused expression: \"");
for (i = 0; i < sp; i++)
{
int tok = stack[i][0];
switch (tok)
{
case tokNumInt:
case tokOpNumInt:
printf2("%d", truncInt(stack[i][1]));
break;
case tokNumUint:
case tokOpNumUint:
printf2("%uu", truncUint(stack[i][1]));
break;
case tokIdent:
case tokOpIdent:
{
char* p = IdentTable + stack[i][1];
if (isdigit(*p))
printf2("L");
printf2("%s", p);
}
break;
case tokOpIndIdent:
printf2("*%s", IdentTable + stack[i][1]);
break;
case tokShortCirc:
if (stack[i][1] >= 0)
printf2("[sh&&->%d]", stack[i][1]);
else
printf2("[sh||->%d]", -stack[i][1]);
break;
case tokGoto:
printf2("[goto->%d]", stack[i][1]);
break;
case tokLocalOfs:
case tokOpLocalOfs:
printf2("(@%d)", truncInt(stack[i][1]));
break;
case tokOpIndLocalOfs:
printf2("*(@%d)", truncInt(stack[i][1]));
break;
case tokUnaryStar:
printf2("*(%d)", stack[i][1]);
break;
case '(': case ',':
printf2("%c", tok);
break;
case ')':
printf2(")%d", stack[i][1]);
break;
case tokOpAcc:
printf2("ax");
break;
case tokOpIndAcc:
printf2("*ax");
break;
case tokOpStack:
printf2("*sp");
break;
case tokOpIndStack:
printf2("**sp");
break;
case tokPushAcc:
printf2("push-ax");
break;
case tokIf:
printf2("IF");
break;
case tokIfNot:
printf2("IF!");
break;
case tokReturn:
break;
default:
printf2("%s", GetTokenName(tok));
switch (tok)
{
case tokLogOr: case tokLogAnd:
printf2("[%d]", stack[i][1]);
break;
case '=':
case tokInc: case tokDec:
case tokPostInc: case tokPostDec:
case tokAssignAdd: case tokAssignSub:
case tokPostAdd: case tokPostSub:
case tokAssignMul: case tokAssignDiv: case tokAssignMod:
case tokAssignUDiv: case tokAssignUMod:
case tokAssignLSh: case tokAssignRSh: case tokAssignURSh:
case tokAssignAnd: case tokAssignXor: case tokAssignOr:
printf2("(%d)", stack[i][1]);
break;
}
break;
}
printf2(" ");
}
printf2("\"\n");
#endif
for (i = 0; i < sp; i++)
{
int tok = stack[i][0];
int v = stack[i][1];
int instr;
switch (tok)
{
case tokNumInt:
case tokNumUint:
// Don't load operand into ax when ax is going to be pushed next, push it directly
if (!(i + 1 < sp && stack[i + 1][0] == ','))
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpConst, v);
break;
case tokIdent:
// Don't load operand into ax when ax is going to be pushed next, push it directly
if (!(i + 1 < sp && (stack[i + 1][0] == ',' || stack[i + 1][0] == ')')))
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpLabel, v);
break;
case tokLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegAWord, 0,
X86OpIndLocal, v);
break;
case '~':
GenPrintInstr1Operand(X86InstrNot, 0,
X86OpRegAWord, 0);
break;
case tokUnaryMinus:
GenPrintInstr1Operand(X86InstrNeg, 0,
X86OpRegAWord, 0);
break;
case tok_Bool:
GenPrintInstr2Operands(X86InstrTest, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0);
GenPrintInstr1Operand(X86InstrSetCc, tokNEQ,
X86OpRegAByte, 0);
// fallthrough
case tokSChar:
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCbw);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
#endif
break;
case tokUChar:
GenPrintInstr2Operands(X86InstrAnd, 0,
X86OpRegAWord, 0,
X86OpConst, 0xFF);
break;
#ifdef CAN_COMPILE_32BIT
case tokShort:
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
break;
case tokUShort:
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
break;
#endif
case tokShortCirc:
if (v >= 0)
GenJumpIfZero(v); // &&
else
GenJumpIfNotZero(-v); // ||
break;
case tokGoto:
GenJumpUncond(v);
break;
case tokLogAnd:
case tokLogOr:
GenNumLabel(v);
break;
case tokPushAcc:
// TBD??? handle similarly to ','???
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegAWord, 0);
break;
case ',':
// push operand directly if it hasn't been loaded into ax
if (stack[i - 2][0] == tokUnaryStar && stack[i - 2][1] == SizeOfWord)
{
switch (stack[i - 1][0])
{
case tokOpIdent:
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpIndLabelExplicitWord, stack[i - 1][1]);
break;
case tokOpLocalOfs:
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpIndLocalExplicitWord, stack[i - 1][1]);
break;
case tokOpAcc:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpIndRegBExplicitWord, 0);
break;
}
}
else
{
switch (stack[i - 1][0])
{
case tokNumInt:
case tokNumUint:
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpConst, stack[i - 1][1]);
break;
case tokIdent:
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpLabel, stack[i - 1][1]);
break;
default:
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegAWord, 0);
break;
}
}
break;
case tokUnaryStar:
// Don't load operand into ax when ax is going to be pushed next, push it directly
if (!(v == SizeOfWord && i + 2 < sp && stack[i + 2][0] == ','))
{
switch (stack[i + 1][0])
{
case tokOpIdent:
GenReadIdent(v, stack[i + 1][1]);
break;
case tokOpLocalOfs:
GenReadLocal(v, stack[i + 1][1]);
break;
case tokOpAcc:
GenReadIndirect(v);
break;
}
}
i++;
break;
case tokInc:
case tokDec:
switch (stack[i + 1][0])
{
case tokOpIndIdent:
GenIncDecIdent(v, stack[i + 1][1], tok);
break;
case tokOpIndLocalOfs:
GenIncDecLocal(v, stack[i + 1][1], tok);
break;
case tokOpIndAcc:
GenIncDecIndirect(v, tok);
break;
}
i++;
break;
case tokPostInc:
case tokPostDec:
switch (stack[i + 1][0])
{
case tokOpIndIdent:
GenPostIncDecIdent(v, stack[i + 1][1], tok);
break;
case tokOpIndLocalOfs:
GenPostIncDecLocal(v, stack[i + 1][1], tok);
break;
case tokOpIndAcc:
GenPostIncDecIndirect(v, tok);
break;
}
i++;
break;
case tokPostAdd:
case tokPostSub:
switch (stack[i + 1][0])
{
case tokOpIndIdent:
GenPostAddSubIdent(v, stack[i + 2][1], stack[i + 1][1], tok);
break;
case tokOpIndLocalOfs:
GenPostAddSubLocal(v, stack[i + 2][1], stack[i + 1][1], tok);
break;
case tokOpIndAcc:
GenPostAddSubIndirect(v, stack[i + 2][1], tok);
break;
}
i += 2;
break;
case '=':
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:
case '+':
case '-':
case '*':
case '/':
case tokUDiv:
case '%':
case tokUMod:
case tokLShift:
case tokRShift:
case tokURShift:
case '&':
case '^':
case '|':
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokEQ:
case tokNEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
// save the right operand from ax in cx, so it's not
// overwritten by the left operand in ax
if (tok != '=')
{
if (stack[i + 2][0] == tokOpAcc)
{
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpRegAWord, 0);
}
else if (stack[i + 2][0] == tokOpIndAcc)
{
GenReadCRegIndirect(v % 16 - 8);
}
}
// load the left operand into ax (or the right operand if it's '=')
if (tok == '=')
{
if (stack[i + 1][0] == tokOpIndAcc)
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
// "swap" left and right operands
i++;
v = v / 16 + v % 16 * 16;
}
switch (stack[i + 1][0])
{
case tokOpNumInt:
case tokOpNumUint:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpConst, stack[i + 1][1]);
break;
case tokOpIdent:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpLabel, stack[i + 1][1]);
break;
case tokOpLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegAWord, 0,
X86OpIndLocal, stack[i + 1][1]);
break;
case tokOpAcc:
break;
case tokOpIndIdent:
GenReadIdent(v / 16 - 8, stack[i + 1][1]);
break;
case tokOpIndLocalOfs:
GenReadLocal(v / 16 - 8, stack[i + 1][1]);
break;
case tokOpIndAcc:
GenReadIndirect(v / 16 - 8);
break;
case tokOpStack:
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegAWord, 0);
break;
case tokOpIndStack:
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v / 16 - 8), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(v / 16 - 8);
break;
}
if (tok == '=')
{
// "unswap" left and right operands
i--;
v = v / 16 + v % 16 * 16;
if (stack[i + 1][0] == tokOpIndStack)
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
}
// operator
switch (tok)
{
case tokAssignAdd:
case tokAssignSub:
case tokAssignAnd:
case tokAssignXor:
case tokAssignOr:
case '+':
case '-':
case '&':
case '^':
case '|':
case '<':
case '>':
case tokLEQ:
case tokGEQ:
case tokEQ:
case tokNEQ:
case tokULess:
case tokUGreater:
case tokULEQ:
case tokUGEQ:
instr = GenGetBinaryOperatorInstr(tok);
switch (stack[i + 2][0])
{
case tokOpNumInt:
case tokOpNumUint:
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpConst, stack[i + 2][1]);
break;
case tokOpIdent:
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpLabel, stack[i + 2][1]);
break;
case tokOpLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegCWord, 0,
X86OpIndLocal, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCWord, 0);
break;
case tokOpAcc:
case tokOpIndAcc:
// right operand in cx already
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCWord, 0);
break;
case tokOpIndIdent:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegIdent(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpIndLabel, stack[i + 2][1]);
}
break;
case tokOpIndLocalOfs:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegLocal(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpIndLocal, stack[i + 2][1]);
}
break;
}
if (i + 3 < sp && (stack[i + 3][0] == tokIf || stack[i + 3][0] == tokIfNot))
{
switch (tok)
{
case '<':
case tokULess:
case '>':
case tokUGreater:
case tokLEQ:
case tokULEQ:
case tokGEQ:
case tokUGEQ:
case tokEQ:
case tokNEQ:
if (stack[i + 3][0] == tokIf)
GenPrintInstr1Operand(X86InstrJcc, tok,
X86OpNumLabel, stack[i + 3][1]);
else
GenPrintInstr1Operand(X86InstrJNotCc, tok,
X86OpNumLabel, stack[i + 3][1]);
break;
}
}
else
{
switch (tok)
{
case '<':
case tokULess:
case '>':
case tokUGreater:
case tokLEQ:
case tokULEQ:
case tokGEQ:
case tokUGEQ:
case tokEQ:
case tokNEQ:
GenPrintInstr1Operand(X86InstrSetCc, tok,
X86OpRegAByte, 0);
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCbw);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
#endif
break;
}
}
break;
case '*':
case tokAssignMul:
instr = GenGetBinaryOperatorInstr(tok);
switch (stack[i + 2][0])
{
case tokOpNumInt:
case tokOpNumUint:
GenPrintInstr3Operands(X86InstrImul, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0,
X86OpConst, stack[i + 2][1]);
break;
case tokOpIdent:
GenPrintInstr3Operands(X86InstrImul, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0,
X86OpLabel, stack[i + 2][1]);
break;
case tokOpLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegCWord, 0,
X86OpIndLocal, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpAcc:
case tokOpIndAcc:
// right operand in cx already
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpIndIdent:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegIdent(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr1Operand(instr, 0,
X86OpIndLabelExplicitWord, stack[i + 2][1]);
}
break;
case tokOpIndLocalOfs:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegLocal(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr1Operand(instr, 0,
X86OpIndLocalExplicitWord, stack[i + 2][1]);
}
break;
}
break;
case '/':
case tokUDiv:
case '%':
case tokUMod:
case tokAssignDiv:
case tokAssignUDiv:
case tokAssignMod:
case tokAssignUMod:
instr = GenGetBinaryOperatorInstr(tok);
switch (tok)
{
case '/':
case '%':
case tokAssignDiv:
case tokAssignMod:
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCwd);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstrNoOperand(X86InstrCdq);
#endif
break;
default:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegDWord, 0,
X86OpConst, 0);
break;
}
switch (stack[i + 2][0])
{
case tokOpNumInt:
case tokOpNumUint:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpConst, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpIdent:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpLabel, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegCWord, 0,
X86OpIndLocal, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpAcc:
case tokOpIndAcc:
// right operand in cx already
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
break;
case tokOpIndIdent:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegIdent(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr1Operand(instr, 0,
X86OpIndLabelExplicitWord, stack[i + 2][1]);
}
break;
case tokOpIndLocalOfs:
if (v % 16 - 8 != SizeOfWord)
{
GenReadCRegLocal(v % 16 - 8, stack[i + 2][1]);
GenPrintInstr1Operand(instr, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr1Operand(instr, 0,
X86OpIndLocalExplicitWord, stack[i + 2][1]);
}
}
if (tok == '%' || tok == tokAssignMod ||
tok == tokUMod || tok == tokAssignUMod)
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpRegDWord, 0);
break;
case tokLShift:
case tokRShift:
case tokURShift:
case tokAssignLSh:
case tokAssignRSh:
case tokAssignURSh:
instr = GenGetBinaryOperatorInstr(tok);
switch (stack[i + 2][0])
{
case tokOpNumInt:
case tokOpNumUint:
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpConst, stack[i + 2][1]);
break;
case tokOpIdent:
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpLabel, stack[i + 2][1]);
break;
case tokOpLocalOfs:
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegCWord, 0,
X86OpIndLocal, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
break;
case tokOpAcc:
case tokOpIndAcc:
// right operand in cx already
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
break;
case tokOpIndIdent:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCByte, 0,
X86OpIndLabel, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
break;
case tokOpIndLocalOfs:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCByte, 0,
X86OpIndLocal, stack[i + 2][1]);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
break;
}
break;
case '=':
break;
default:
//error("Error: Internal Error: GenExpr1() a: unexpected token %s\n", GetTokenName(tok));
errorInternal(103);
break;
}
// store ax into the left operand, if needed
switch (tok)
{
case '=':
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:
switch (stack[i + 1][0])
{
case tokOpIndIdent:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndLabel, stack[i + 1][1],
GenSelectByteOrWord(X86OpRegAByteOrWord, v / 16 - 8), 0);
break;
case tokOpIndLocalOfs:
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndLocal, stack[i + 1][1],
GenSelectByteOrWord(X86OpRegAByteOrWord, v / 16 - 8), 0);
break;
case tokOpIndAcc:
case tokOpIndStack:
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v / 16 - 8), 0);
break;
}
// the result of the expression is of type of the
// left lvalue operand, so, "truncate" it if needed
GenExtendRegAIfNeeded(v / 16 - 8);
}
i += 2;
break;
case ')':
// DONE: "call ident"
if (stack[i - 1][0] == tokIdent)
{
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
{
int lab = LabelCnt++;
puts2("\tdb\t0x9A"); // call far seg:ofs
printf2("section .relot\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(CodeHeaderFooter[0]);
GenNumLabel(lab);
printf2("\tdd\t"); GenPrintLabel(IdentTable + stack[i - 1][1]); puts2("");
}
else // fallthrough
#endif
GenPrintInstr1Operand(X86InstrCall, 0,
X86OpLabel, stack[i - 1][1]);
}
else
{
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
{
int lab = (LabelCnt += 3) - 3;
puts2("\tdb\t0x9A"); // call far seg:ofs (only to generate return address)
printf2("section .relot\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(CodeHeaderFooter[0]);
GenNumLabel(lab);
printf2("\tdd\t"); GenPrintNumLabel(lab + 1); puts2("");
GenNumLabel(lab + 1);
printf2("\tadd\tword [esp], "); GenPrintNumLabel(lab + 2); printf2(" - "); GenPrintNumLabel(lab + 1); puts2(""); // adjust return address
puts2("\tshl\teax, 12\n\trol\tax, 4\n\tpush\teax\n\tretf");
GenNumLabel(lab + 2);
}
else // fallthrough
#endif
GenPrintInstr1Operand(X86InstrCall, 0,
X86OpRegAWord, 0);
}
GenGrowStack(-v);
break;
case '(':
case tokIf:
case tokIfNot:
case tokReturn:
break;
case tokVoid:
case tokComma:
break;
default:
//error("Error: Internal Error: GenExpr1() b: unexpected token %s\n", GetTokenName(tok));
errorInternal(104);
break;
}
}
}
#else // #ifndef CG_STACK_BASED
// Original, primitive stack-based code generator
// DONE: test 32-bit code generation
STATIC
void GenExpr0(void)
{
int i;
int gotUnary = 0;
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: printf2("; %s\n", IdentTable + v); break;
case tokLocalOfs: 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:
case tokNumUint:
if (gotUnary)
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpConst, v);
gotUnary = 1;
break;
case tokIdent:
if (gotUnary)
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpLabel, v);
gotUnary = 1;
break;
case tokLocalOfs:
if (gotUnary)
GenPrintInstr1Operand(X86InstrPush, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrLea, 0,
X86OpRegAWord, 0,
X86OpIndLocal, v);
gotUnary = 1;
break;
case ')':
#ifdef CAN_COMPILE_32BIT
if (OutputFormat == FormatSegHuge)
{
int lab = (LabelCnt += 3) - 3;
puts2("\tdb\t0x9A"); // call far seg:ofs (only to generate return address)
printf2("section .relot\n\tdd\t"); GenPrintNumLabel(lab); puts2("");
puts2(CodeHeaderFooter[0]);
GenNumLabel(lab);
printf2("\tdd\t"); GenPrintNumLabel(lab + 1); puts2("");
GenNumLabel(lab + 1);
printf2("\tadd\tword [esp], "); GenPrintNumLabel(lab + 2); printf2(" - "); GenPrintNumLabel(lab + 1); puts2(""); // adjust return address
puts2("\tshl\teax, 12\n\trol\tax, 4\n\tpush\teax\n\tretf");
GenNumLabel(lab + 2);
}
else // fallthrough
#endif
GenPrintInstr1Operand(X86InstrCall, 0,
X86OpRegAWord, 0);
GenGrowStack(-v);
break;
case tokUnaryStar:
GenReadIndirect(v);
break;
case tokUnaryPlus:
break;
case '~':
GenPrintInstr1Operand(X86InstrNot, 0,
X86OpRegAWord, 0);
break;
case tokUnaryMinus:
GenPrintInstr1Operand(X86InstrNeg, 0,
X86OpRegAWord, 0);
break;
case '+':
case '-':
case '*':
case '&':
case '^':
case '|':
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
if (tok == '-')
GenPrintInstr2Operands(X86InstrXchg, 0,
X86OpRegAWord, 0,
X86OpRegBWord, 0);
if (tok != '*')
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegBWord, 0);
else
GenPrintInstr1Operand(instr, 0,
X86OpRegBWord, 0);
}
break;
case '/':
case tokUDiv:
case '%':
case tokUMod:
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
GenPrintInstr2Operands(X86InstrXchg, 0,
X86OpRegAWord, 0,
X86OpRegBWord, 0);
if (tok == '/' || tok == '%')
{
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCwd);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstrNoOperand(X86InstrCdq);
#endif
GenPrintInstr1Operand(X86InstrIdiv, 0,
X86OpRegBWord, 0);
}
else
{
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegDWord, 0,
X86OpConst, 0);
GenPrintInstr1Operand(X86InstrDiv, 0,
X86OpRegBWord, 0);
}
if (tok == '%' || tok == tokUMod)
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpRegDWord, 0);
break;
case tokLShift:
case tokRShift:
case tokURShift:
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegCWord, 0);
GenPrintInstr2Operands(X86InstrXchg, 0,
X86OpRegAWord, 0,
X86OpRegCWord, 0);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
}
break;
case tokInc:
GenIncDecIndirect(v, tok);
break;
case tokDec:
GenIncDecIndirect(v, tok);
break;
case tokPostInc:
GenPostIncDecIndirect(v, tok);
break;
case tokPostDec:
GenPostIncDecIndirect(v, tok);
break;
case tokPostAdd:
case tokPostSub:
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0,
X86OpIndRegB, 0);
GenPrintInstr2Operands(instr, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegCByteOrWord, v), 0);
GenExtendRegAIfNeeded(v);
}
break;
case tokAssignAdd:
case tokAssignSub:
case tokAssignMul:
case tokAssignAnd:
case tokAssignXor:
case tokAssignOr:
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
if (tok != tokAssignMul)
{
GenPrintInstr2Operands(instr, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0,
X86OpIndRegB, 0);
}
else
{
GenPrintInstr1Operand(instr, 0,
GenSelectByteOrWord(X86OpIndRegBExplicitByteOrWord, v), 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0);
}
GenExtendRegAIfNeeded(v);
}
break;
case tokAssignDiv:
case tokAssignUDiv:
case tokAssignMod:
case tokAssignUMod:
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(v);
if (tok == tokAssignDiv || tok == tokAssignMod)
{
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCwd);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstrNoOperand(X86InstrCdq);
#endif
GenPrintInstr1Operand(X86InstrIdiv, 0,
X86OpRegCWord, 0);
}
else
{
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegDWord, 0,
X86OpConst, 0);
GenPrintInstr1Operand(X86InstrDiv, 0,
X86OpRegCWord, 0);
}
if (tok == tokAssignMod || tok == tokAssignUMod)
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegAWord, 0,
X86OpRegDWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0);
GenExtendRegAIfNeeded(v);
break;
case tokAssignLSh:
case tokAssignRSh:
case tokAssignURSh:
{
int instr = GenGetBinaryOperatorInstr(tok);
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpRegCWord, 0,
X86OpRegAWord, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0,
X86OpIndRegB, 0);
GenExtendRegAIfNeeded(v);
GenPrintInstr2Operands(instr, 0,
X86OpRegAWord, 0,
X86OpRegCByte, 0);
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0);
GenExtendRegAIfNeeded(v);
}
break;
case '=':
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
#ifdef CAN_COMPILE_32BIT
GenRegB2Seg();
#endif
GenPrintInstr2Operands(X86InstrMov, 0,
X86OpIndRegB, 0,
GenSelectByteOrWord(X86OpRegAByteOrWord, v), 0);
GenExtendRegAIfNeeded(v);
break;
case '<':
case tokULess:
case '>':
case tokUGreater:
case tokLEQ:
case tokULEQ:
case tokGEQ:
case tokUGEQ:
case tokEQ:
case tokNEQ:
GenPrintInstr1Operand(X86InstrPop, 0,
X86OpRegBWord, 0);
GenPrintInstr2Operands(X86InstrCmp, 0,
X86OpRegBWord, 0,
X86OpRegAWord, 0);
GenPrintInstr1Operand(X86InstrSetCc, tok,
X86OpRegAByte, 0);
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCbw);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
#endif
break;
case tok_Bool:
GenPrintInstr2Operands(X86InstrTest, 0,
X86OpRegAWord, 0,
X86OpRegAWord, 0);
GenPrintInstr1Operand(X86InstrSetCc, tokNEQ,
X86OpRegAByte, 0);
// fallthrough
case tokSChar:
if (SizeOfWord == 2)
GenPrintInstrNoOperand(X86InstrCbw);
#ifdef CAN_COMPILE_32BIT
else
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAByte, 0);
#endif
break;
case tokUChar:
GenPrintInstr2Operands(X86InstrAnd, 0,
X86OpRegAWord, 0,
X86OpConst, 0xFF);
break;
#ifdef CAN_COMPILE_32BIT
case tokShort:
GenPrintInstr2Operands(X86InstrMovSx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
break;
case tokUShort:
GenPrintInstr2Operands(X86InstrMovZx, 0,
X86OpRegAWord, 0,
X86OpRegAHalfWord, 0);
break;
#endif
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 tokComma:
case ',':
case '(':
case tokReturn:
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(105);
break;
}
}
}
#endif // #ifndef CG_STACK_BASED
STATIC
void GenDumpChar(int ch)
{
static int quot = 0;
if (ch < 0)
{
if (quot)
{
printf2("\"");
quot = 0;
}
if (TokenStringLen)
printf2("\n");
return;
}
if (TokenStringLen == 0)
GenStartAsciiString();
// quote ASCII chars for better readability
if (ch >= 0x20 && ch <= 0x7E && ch != '"')
{
if (!quot)
{
quot = 1;
if (TokenStringLen)
printf2(",");
printf2("\"");
}
printf2("%c", ch);
}
else
{
if (quot)
{
quot = 0;
printf2("\"");
}
if (TokenStringLen)
printf2(",");
printf2("%u", ch & 0xFFu);
}
}
STATIC
void GenExpr(void)
{
if (GenExterns)
{
int i;
for (i = 0; i < sp; i++)
if (stack[i][0] == tokIdent && !isdigit(IdentTable[stack[i][1]]))
GenAddGlobal(IdentTable + stack[i][1], 2);
}
#ifndef CG_STACK_BASED
GenExpr1();
#else
GenExpr0();
#endif
}
STATIC
void GenFin(void)
{
if (StructCpyLabel)
{
puts2(CodeHeaderFooter[0]);
GenNumLabel(StructCpyLabel);
CurFxnMinLocalOfs = 0;
GenFxnProlog();
if (SizeOfWord == 2)
{
puts2("\tmov\tdi, [bp+8]\n"
"\tmov\tsi, [bp+6]\n"
"\tmov\tcx, [bp+4]\n"
"\tcld\n"
"\trep\tmovsb\n"
"\tmov\tax, [bp+8]");
}
#ifdef CAN_COMPILE_32BIT
else if (OutputFormat != FormatSegHuge)
{
puts2("\tmov\tedi, [ebp+16]\n"
"\tmov\tesi, [ebp+12]\n"
"\tmov\tecx, [ebp+8]\n"
"\tcld\n"
"\trep\tmovsb\n"
"\tmov\teax, [ebp+16]");
}
else
{
int lbl = (LabelCnt += 2) - 2;
puts2("\tmov\tedi, [ebp+16]\n"
"\tror\tedi, 4\n"
"\tmov\tes, di\n"
"\tshr\tedi, 28\n"
"\tmov\tesi, [ebp+12]\n"
"\tror\tesi, 4\n"
"\tmov\tds, si\n"
"\tshr\tesi, 28");
puts2("\tmov\tebx, [ebp+8]\n"
"\tcld");
GenNumLabel(lbl); // L1:
puts2("\tmov\tecx, 32768\n"
"\tcmp\tebx, ecx");
printf2("\tjc\t"); GenPrintNumLabel(lbl + 1); // jc L2
puts2("\n"
"\tsub\tebx, ecx\n"
"\trep\tmovsb\n"
"\tand\tdi, 15\n"
"\tmov\tax, es\n"
"\tadd\tax, 2048\n"
"\tmov\tes, ax\n"
"\tand\tsi, 15\n"
"\tmov\tax, ds\n"
"\tadd\tax, 2048\n"
"\tmov\tds, ax");
printf2("\tjmp\t"); GenPrintNumLabel(lbl); // jmp L1
puts2("");
GenNumLabel(lbl + 1); // L2:
puts2("\tmov\tcx, bx\n"
"\trep\tmovsb\n"
"\tmov\teax, [ebp+16]");
}
#endif
GenFxnEpilog();
puts2(CodeHeaderFooter[1]);
}
#ifndef NO_STRUCT_BY_VAL
if (StructPushLabel)
{
puts2(CodeHeaderFooter[0]);
GenNumLabel(StructPushLabel);
CurFxnMinLocalOfs = 0;
GenFxnProlog();
if (SizeOfWord == 2)
{
puts2("\tmov\tdx, [bp+2]\n" // dx = return address
"\tmov\tsi, [bp+4]\n" // si = &struct
"\tmov\tcx, [bp+6]\n" // cx = sizeof(struct)
"\tmov\tbp, [bp]\n" // restore bp
"\tmov\tax, cx\n" // ax = sizeof(struct)
"\tinc\tax\n" // ax = sizeof(struct) + 1
"\tand\tax, -2\n" // ax = sizeof(struct) rounded up to multiple of 2 bytes
"\tadd\tsp, 4*2\n" // remove bp, return address and 2 args from stack
"\tsub\tsp, ax"); // allocate stack space for struct
puts2("\tmov\tdi, sp\n" // di = where struct should be copied to
"\tcld\n"
"\trep\tmovsb\n" // copy
"\tpop\tax\n" // return first 2 bytes of struct in ax
"\tpush\tax\n"
"\tpush\tbyte 0\n" // caller will remove this 0 and first 2 bytes of struct from stack (as 2 args)
"\tpush\tdx\n" // and then it will push ax (first 2 bytes of struct) back
"\tret"); // actually return to return address saved in dx
}
#ifdef CAN_COMPILE_32BIT
else if (OutputFormat != FormatSegHuge)
{
// Copying the pushed structure to the stack backwards
// (from higher to lower addresses) in order to correctly
// grow the stack on Windows, page by page
puts2("\tmov\tedx, [ebp+4]\n" // edx = return address
"\tmov\tesi, [ebp+8]\n" // esi = &struct
"\tmov\tecx, [ebp+12]\n" // ecx = sizeof(struct)
"\tmov\tebp, [ebp]\n" // restore ebp
"\tlea\teax, [ecx + 3]\n" // eax = sizeof(struct) + 3
"\tand\teax, -4\n" // eax = sizeof(struct) rounded up to multiple of 4 bytes
"\tadd\tesp, 4*4\n" // remove ebp, return address and 2 args from stack
"\tsub\tesp, eax"); // allocate stack space for struct
puts2("\tlea\tesi, [esi + ecx - 1]\n" // esi = &last byte of struct
"\tlea\tedi, [esp + ecx - 1]\n" // edi = where it should be copied to
"\tstd\n"
"\trep\tmovsb\n" // copy
"\tcld\n"
"\tmov\teax, [esp]\n" // return first 4 bytes of struct in eax
"\tpush\t0\n" // caller will remove this 0 and first 4 bytes of struct from stack (as 2 args)
"\tpush\tedx\n" // and then it will push eax (first 4 bytes of struct) back
"\tret"); // actually return to return address saved in edx
}
else
{
puts2("\tmov\tedx, [ebp+4]\n" // edx = return address (seg:ofs)
"\tmov\tesi, [ebp+8]\n" // esi = &struct (phys)
"\tror\tesi, 4\n"
"\tmov\tds, si\n"
"\tshr\tesi, 28\n" // ds:si = &struct (seg:ofs)
"\tmov\tecx, [ebp+12]\n" // ecx = sizeof(struct)
"\tmov\tebp, [ebp]\n" // restore ebp
"\tlea\teax, [ecx + 3]\n" // eax = sizeof(struct) + 3
"\tand\teax, -4\n" // eax = sizeof(struct) rounded up to multiple of 4 bytes
"\tadd\tsp, 4*4\n" // remove ebp, return address and 2 args from stack
"\tsub\tsp, ax"); // allocate stack space for struct
puts2("\tmov\tax, ss\n"
"\tmov\tes, ax\n" // es = ss
"\tmov\tdi, sp\n" // es:di = where struct should be copied to (seg:ofs)
"\tcld\n"
"\trep\tmovsb\n" // copy; limit to ~64KB since stack size itself is ~64KB max
"\tpop\teax\n" // return first 4 bytes of struct in eax
"\tpush\teax\n"
"\tpush\teax\n" // caller will remove this and first 4 bytes of struct from stack (as 2 args)
"\tpush\tedx\n" // and then it will push eax (first 4 bytes of struct) back
"\tretf"); // actually return to return address saved in edx
}
#endif
// GenFxnEpilog();
puts2(CodeHeaderFooter[1]);
}
#endif
#ifdef CAN_COMPILE_32BIT
if (WinChkStkLabel > 0) // if _chkstk() has been used at least once
{
// When targeting Windows, simulate _chkstk() to
// correctly grow the stack page by page by probing it
int lbl = LabelCnt++;
puts2(CodeHeaderFooter[0]);
GenNumLabel(WinChkStkLabel);
puts2("\tlea\tebx, [esp+4]\n"
"\tmov\tecx, ebx\n"
"\tsub\tecx, eax\n"
"\tand\tebx, -4096\n"
"\tand\tecx, -4096");
GenNumLabel(lbl); // L1:
puts2("\tsub\tebx, 4096\n"
"\tmov\tal, [ebx]\n"
"\tcmp\tebx, ecx");
printf2("\tjne\t"); GenPrintNumLabel(lbl); // jne L1
puts2("\n\tret");
puts2(CodeHeaderFooter[1]);
}
#endif
if (GenExterns)
{
int i = 0;
puts2("");
while (i < GlobalsTableLen)
{
if (GlobalsTable[i] == 2)
{
printf2("\textern\t");
GenPrintLabel(GlobalsTable + i + 2);
puts2("");
}
i += GlobalsTable[i + 1] + 2;
}
}
}