android_system_core/libacc/acc.cpp
Jack Palevich b13d4e857a Improve error-handling when an expected token is missing.
We had been arbitrarily swallowing the next token, even if it wasn't
the one we were expecting. Now we only swallow it if it _is_ the one
we were expecting.
2009-09-18 16:26:05 -07:00

6239 lines
194 KiB
C++

/*
* Android "Almost" C Compiler.
* This is a compiler for a small subset of the C language, intended for use
* in scripting environments where speed and memory footprint are important.
*
* This code is based upon the "unobfuscated" version of the
* Obfuscated Tiny C compiler, see the file LICENSE for details.
*
*/
#define LOG_TAG "acc"
#include <cutils/log.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cutils/hashmap.h>
#if defined(__i386__)
#include <sys/mman.h>
#endif
#if defined(__arm__)
#define DEFAULT_ARM_CODEGEN
#define PROVIDE_ARM_CODEGEN
#elif defined(__i386__)
#define DEFAULT_X86_CODEGEN
#define PROVIDE_X86_CODEGEN
#elif defined(__x86_64__)
#define DEFAULT_X64_CODEGEN
#define PROVIDE_X64_CODEGEN
#endif
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
#define ARM_USE_VFP
#endif
#include <acc/acc.h>
#define LOG_API(...) do {} while(0)
// #define LOG_API(...) fprintf (stderr, __VA_ARGS__)
#define LOG_STACK(...) do {} while(0)
// #define LOG_STACK(...) fprintf (stderr, __VA_ARGS__)
// #define PROVIDE_TRACE_CODEGEN
// Uncomment to disable ARM peephole optimizations
// #define DISABLE_ARM_PEEPHOLE
// Uncomment to save input to a text file in DEBUG_DUMP_PATTERN
// #define DEBUG_SAVE_INPUT_TO_FILE
#ifdef DEBUG_SAVE_INPUT_TO_FILE
#ifdef ARM_USE_VFP
#define DEBUG_DUMP_PATTERN "/data/misc/acc_dump/%d.c"
#else
#define DEBUG_DUMP_PATTERN "/tmp/acc_dump/%d.c"
#endif
#endif
#define assert(b) assertImpl(b, __LINE__)
namespace acc {
// Subset of STL vector.
template<class E> class Vector {
public:
Vector() {
mpBase = 0;
mUsed = 0;
mSize = 0;
}
~Vector() {
if (mpBase) {
for(size_t i = 0; i < mUsed; i++) {
mpBase[mUsed].~E();
}
free(mpBase);
}
}
inline E& operator[](size_t i) {
return mpBase[i];
}
inline E& front() {
return mpBase[0];
}
inline E& back() {
return mpBase[mUsed - 1];
}
void pop_back() {
mUsed -= 1;
mpBase[mUsed].~E();
}
void push_back(const E& item) {
* ensure(1) = item;
}
size_t size() {
return mUsed;
}
private:
E* ensure(int n) {
size_t newUsed = mUsed + n;
if (newUsed > mSize) {
size_t newSize = mSize * 2 + 10;
if (newSize < newUsed) {
newSize = newUsed;
}
mpBase = (E*) realloc(mpBase, sizeof(E) * newSize);
mSize = newSize;
}
E* result = mpBase + mUsed;
mUsed = newUsed;
return result;
}
E* mpBase;
size_t mUsed;
size_t mSize;
};
class ErrorSink {
public:
void error(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
verror(fmt, ap);
va_end(ap);
}
virtual ~ErrorSink() {}
virtual void verror(const char* fmt, va_list ap) = 0;
};
class Compiler : public ErrorSink {
typedef int tokenid_t;
enum TypeTag {
TY_INT, // 0
TY_CHAR, // 1
TY_SHORT, // 2
TY_VOID, // 3
TY_FLOAT, // 4
TY_DOUBLE, // 5
TY_POINTER, // 6
TY_ARRAY, // 7
TY_STRUCT, // 8
TY_FUNC, // 9
TY_PARAM // 10
};
struct Type {
TypeTag tag;
tokenid_t id; // For function arguments, global vars, local vars, struct elements
tokenid_t structTag; // For structs the name of the struct
int length; // length of array, offset of struct element. -1 means struct is forward defined
int alignment; // for structs only
Type* pHead; // For a struct this is the prototype struct.
Type* pTail;
};
enum ExpressionType {
ET_RVALUE,
ET_LVALUE
};
struct ExpressionValue {
ExpressionValue() {
et = ET_RVALUE;
pType = NULL;
}
ExpressionType et;
Type* pType;
};
class ICodeBuf {
public:
virtual ~ICodeBuf() {}
virtual void init(int size) = 0;
virtual void setErrorSink(ErrorSink* pErrorSink) = 0;
virtual void o4(int n) = 0;
virtual void ob(int n) = 0;
virtual void* getBase() = 0;
virtual intptr_t getSize() = 0;
virtual intptr_t getPC() = 0;
// Call this before trying to modify code in the buffer.
virtual void flush() = 0;
};
class CodeBuf : public ICodeBuf {
char* ind; // Output code pointer
char* pProgramBase;
ErrorSink* mErrorSink;
int mSize;
bool mOverflowed;
void release() {
if (pProgramBase != 0) {
free(pProgramBase);
pProgramBase = 0;
}
}
bool check(int n) {
int newSize = ind - pProgramBase + n;
bool overflow = newSize > mSize;
if (overflow && !mOverflowed) {
mOverflowed = true;
if (mErrorSink) {
mErrorSink->error("Code too large: %d bytes", newSize);
}
}
return overflow;
}
public:
CodeBuf() {
pProgramBase = 0;
ind = 0;
mErrorSink = 0;
mSize = 0;
mOverflowed = false;
}
virtual ~CodeBuf() {
release();
}
virtual void init(int size) {
release();
mSize = size;
pProgramBase = (char*) calloc(1, size);
ind = pProgramBase;
}
virtual void setErrorSink(ErrorSink* pErrorSink) {
mErrorSink = pErrorSink;
}
virtual void o4(int n) {
if(check(4)) {
return;
}
* (int*) ind = n;
ind += 4;
}
/*
* Output a byte. Handles all values, 0..ff.
*/
virtual void ob(int n) {
if(check(1)) {
return;
}
*ind++ = n;
}
virtual void* getBase() {
return (void*) pProgramBase;
}
virtual intptr_t getSize() {
return ind - pProgramBase;
}
virtual intptr_t getPC() {
return (intptr_t) ind;
}
virtual void flush() {}
};
/**
* A code generator creates an in-memory program, generating the code on
* the fly. There is one code generator implementation for each supported
* architecture.
*
* The code generator implements the following abstract machine:
* R0 - the accumulator.
* FP - a frame pointer for accessing function arguments and local
* variables.
* SP - a stack pointer for storing intermediate results while evaluating
* expressions. The stack pointer grows downwards.
*
* The function calling convention is that all arguments are placed on the
* stack such that the first argument has the lowest address.
* After the call, the result is in R0. The caller is responsible for
* removing the arguments from the stack.
* The R0 register is not saved across function calls. The
* FP and SP registers are saved.
*/
class CodeGenerator {
public:
CodeGenerator() {
mErrorSink = 0;
pCodeBuf = 0;
pushType();
}
virtual ~CodeGenerator() {}
virtual void init(ICodeBuf* pCodeBuf) {
this->pCodeBuf = pCodeBuf;
pCodeBuf->setErrorSink(mErrorSink);
}
virtual void setErrorSink(ErrorSink* pErrorSink) {
mErrorSink = pErrorSink;
if (pCodeBuf) {
pCodeBuf->setErrorSink(mErrorSink);
}
}
/* Give the code generator some utility types so it can
* use its own types as needed for the results of some
* operations like gcmp.
*/
void setTypes(Type* pInt) {
mkpInt = pInt;
}
/* Emit a function prolog.
* pDecl is the function declaration, which gives the arguments.
* Save the old value of the FP.
* Set the new value of the FP.
* Convert from the native platform calling convention to
* our stack-based calling convention. This may require
* pushing arguments from registers to the stack.
* Allocate "N" bytes of stack space. N isn't known yet, so
* just emit the instructions for adjusting the stack, and return
* the address to patch up. The patching will be done in
* functionExit().
* returns address to patch with local variable size.
*/
virtual int functionEntry(Type* pDecl) = 0;
/* Emit a function epilog.
* Restore the old SP and FP register values.
* Return to the calling function.
* argCount - the number of arguments to the function.
* localVariableAddress - returned from functionEntry()
* localVariableSize - the size in bytes of the local variables.
*/
virtual void functionExit(Type* pDecl, int localVariableAddress,
int localVariableSize) = 0;
/* load immediate value to R0 */
virtual void li(int i) = 0;
/* Load floating point value from global address. */
virtual void loadFloat(int address, Type* pType) = 0;
/* Add the struct offset in bytes to R0, change the type to pType */
virtual void addStructOffsetR0(int offset, Type* pType) = 0;
/* Jump to a target, and return the address of the word that
* holds the target data, in case it needs to be fixed up later.
*/
virtual int gjmp(int t) = 0;
/* Test R0 and jump to a target if the test succeeds.
* l = 0: je, l == 1: jne
* Return the address of the word that holds the targed data, in
* case it needs to be fixed up later.
*/
virtual int gtst(bool l, int t) = 0;
/* Compare TOS against R0, and store the boolean result in R0.
* Pops TOS.
* op specifies the comparison.
*/
virtual void gcmp(int op) = 0;
/* Perform the arithmetic op specified by op. TOS is the
* left argument, R0 is the right argument.
* Pops TOS.
*/
virtual void genOp(int op) = 0;
/* Compare 0 against R0, and store the boolean result in R0.
* op specifies the comparison.
*/
virtual void gUnaryCmp(int op) = 0;
/* Perform the arithmetic op specified by op. 0 is the
* left argument, R0 is the right argument.
*/
virtual void genUnaryOp(int op) = 0;
/* Push R0 onto the stack. (Also known as "dup" for duplicate.)
*/
virtual void pushR0() = 0;
/* Turn R0, TOS into R0 TOS R0 */
virtual void over() = 0;
/* Pop R0 from the stack. (Also known as "drop")
*/
virtual void popR0() = 0;
/* Store R0 to the address stored in TOS.
* The TOS is popped.
*/
virtual void storeR0ToTOS() = 0;
/* Load R0 from the address stored in R0.
*/
virtual void loadR0FromR0() = 0;
/* Load the absolute address of a variable to R0.
* If ea <= LOCAL, then this is a local variable, or an
* argument, addressed relative to FP.
* else it is an absolute global address.
*
* et is ET_RVALUE for things like string constants, ET_LVALUE for
* variables.
*/
virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) = 0;
/* Load the pc-relative address of a forward-referenced variable to R0.
* Return the address of the 4-byte constant so that it can be filled
* in later.
*/
virtual int leaForward(int ea, Type* pPointerType) = 0;
/**
* Convert R0 to the given type.
*/
void convertR0(Type* pType) {
convertR0Imp(pType, false);
}
void castR0(Type* pType) {
convertR0Imp(pType, true);
}
virtual void convertR0Imp(Type* pType, bool isCast) = 0;
/* Emit code to adjust the stack for a function call. Return the
* label for the address of the instruction that adjusts the
* stack size. This will be passed as argument "a" to
* endFunctionCallArguments.
*/
virtual int beginFunctionCallArguments() = 0;
/* Emit code to store R0 to the stack at byte offset l.
* Returns stack size of object (typically 4 or 8 bytes)
*/
virtual size_t storeR0ToArg(int l, Type* pArgType) = 0;
/* Patch the function call preamble.
* a is the address returned from beginFunctionCallArguments
* l is the number of bytes the arguments took on the stack.
* Typically you would also emit code to convert the argument
* list into whatever the native function calling convention is.
* On ARM for example you would pop the first 5 arguments into
* R0..R4
*/
virtual void endFunctionCallArguments(Type* pDecl, int a, int l) = 0;
/* Emit a call to an unknown function. The argument "symbol" needs to
* be stored in the location where the address should go. It forms
* a chain. The address will be patched later.
* Return the address of the word that has to be patched.
*/
virtual int callForward(int symbol, Type* pFunc) = 0;
/* Call a function pointer. L is the number of bytes the arguments
* take on the stack. The address of the function is stored at
* location SP + l.
*/
virtual void callIndirect(int l, Type* pFunc) = 0;
/* Adjust SP after returning from a function call. l is the
* number of bytes of arguments stored on the stack. isIndirect
* is true if this was an indirect call. (In which case the
* address of the function is stored at location SP + l.)
*/
virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) = 0;
/* Generate a symbol at the current PC. t is the head of a
* linked list of addresses to patch.
*/
virtual void gsym(int t) = 0;
/* Resolve a forward reference function at the current PC.
* t is the head of a
* linked list of addresses to patch.
* (Like gsym, but using absolute address, not PC relative address.)
*/
virtual void resolveForward(int t) = 0;
/*
* Do any cleanup work required at the end of a compile.
* For example, an instruction cache might need to be
* invalidated.
* Return non-zero if there is an error.
*/
virtual int finishCompile() = 0;
/**
* Adjust relative branches by this amount.
*/
virtual int jumpOffset() = 0;
/**
* Memory alignment (in bytes) for this type of data
*/
virtual size_t alignmentOf(Type* type) = 0;
/**
* Array element alignment (in bytes) for this type of data.
*/
virtual size_t sizeOf(Type* type) = 0;
virtual Type* getR0Type() {
return mExpressionStack.back().pType;
}
virtual ExpressionType getR0ExpressionType() {
return mExpressionStack.back().et;
}
virtual void setR0ExpressionType(ExpressionType et) {
mExpressionStack.back().et = et;
}
virtual size_t getExpressionStackDepth() {
return mExpressionStack.size();
}
virtual void forceR0RVal() {
if (getR0ExpressionType() == ET_LVALUE) {
loadR0FromR0();
}
}
protected:
/*
* Output a byte. Handles all values, 0..ff.
*/
void ob(int n) {
pCodeBuf->ob(n);
}
void o4(int data) {
pCodeBuf->o4(data);
}
intptr_t getBase() {
return (intptr_t) pCodeBuf->getBase();
}
intptr_t getPC() {
return pCodeBuf->getPC();
}
intptr_t getSize() {
return pCodeBuf->getSize();
}
void flush() {
pCodeBuf->flush();
}
void error(const char* fmt,...) {
va_list ap;
va_start(ap, fmt);
mErrorSink->verror(fmt, ap);
va_end(ap);
}
void assertImpl(bool test, int line) {
if (!test) {
error("code generator assertion failed at line %s:%d.", __FILE__, line);
LOGD("code generator assertion failed at line %s:%d.", __FILE__, line);
* (char*) 0 = 0;
}
}
void setR0Type(Type* pType) {
assert(pType != NULL);
mExpressionStack.back().pType = pType;
mExpressionStack.back().et = ET_RVALUE;
}
void setR0Type(Type* pType, ExpressionType et) {
assert(pType != NULL);
mExpressionStack.back().pType = pType;
mExpressionStack.back().et = et;
}
Type* getTOSType() {
return mExpressionStack[mExpressionStack.size()-2].pType;
}
void pushType() {
if (mExpressionStack.size()) {
mExpressionStack.push_back(mExpressionStack.back());
} else {
mExpressionStack.push_back(ExpressionValue());
}
}
void overType() {
size_t size = mExpressionStack.size();
if (size >= 2) {
mExpressionStack.push_back(mExpressionStack.back());
mExpressionStack[size-1] = mExpressionStack[size-2];
mExpressionStack[size-2] = mExpressionStack[size];
}
}
void popType() {
mExpressionStack.pop_back();
}
bool bitsSame(Type* pA, Type* pB) {
return collapseType(pA->tag) == collapseType(pB->tag);
}
TypeTag collapseType(TypeTag tag) {
static const TypeTag collapsedTag[] = {
TY_INT,
TY_INT,
TY_INT,
TY_VOID,
TY_FLOAT,
TY_DOUBLE,
TY_INT,
TY_INT,
TY_VOID,
TY_VOID,
TY_VOID
};
return collapsedTag[tag];
}
TypeTag collapseTypeR0() {
return collapseType(getR0Type()->tag);
}
static bool isFloatType(Type* pType) {
return isFloatTag(pType->tag);
}
static bool isFloatTag(TypeTag tag) {
return tag == TY_FLOAT || tag == TY_DOUBLE;
}
static bool isPointerType(Type* pType) {
return isPointerTag(pType->tag);
}
static bool isPointerTag(TypeTag tag) {
return tag == TY_POINTER || tag == TY_ARRAY;
}
Type* getPointerArithmeticResultType(Type* a, Type* b) {
TypeTag aTag = a->tag;
TypeTag bTag = b->tag;
if (aTag == TY_POINTER) {
return a;
}
if (bTag == TY_POINTER) {
return b;
}
if (aTag == TY_ARRAY) {
return a->pTail;
}
if (bTag == TY_ARRAY) {
return b->pTail;
}
return NULL;
}
Type* mkpInt;
private:
Vector<ExpressionValue> mExpressionStack;
ICodeBuf* pCodeBuf;
ErrorSink* mErrorSink;
};
#ifdef PROVIDE_ARM_CODEGEN
static size_t rotateRight(size_t n, size_t rotate) {
return (n >> rotate) | (n << (32 - rotate));
}
static size_t rotateLeft(size_t n, size_t rotate) {
return (n << rotate) | (n >> (32 - rotate));
}
static bool encode12BitImmediate(size_t immediate, size_t* pResult) {
for(size_t i = 0; i < 16; i++) {
size_t rotate = i * 2;
size_t mask = rotateRight(0xff, rotate);
if ((immediate | mask) == mask) {
size_t bits8 = rotateLeft(immediate, rotate);
// assert(bits8 <= 0xff);
*pResult = (i << 8) | bits8;
return true;
}
}
return false;
}
static size_t decode12BitImmediate(size_t immediate) {
size_t data = immediate & 0xff;
size_t rotate = 2 * ((immediate >> 8) & 0xf);
return rotateRight(data, rotate);
}
static bool isPowerOfTwo(size_t n) {
return (n != 0) & ((n & (n-1)) == 0);
}
static size_t log2(size_t n) {
int result = 0;
while (n >>= 1) {
result++;
}
return result;
}
class ARMCodeBuf : public ICodeBuf {
ICodeBuf* mpBase;
ErrorSink* mErrorSink;
class CircularQueue {
static const int SIZE = 16; // Must be power of 2
static const int MASK = SIZE-1;
unsigned int mBuf[SIZE];
int mHead;
int mCount;
public:
CircularQueue() {
mHead = 0;
mCount = 0;
}
void pushBack(unsigned int data) {
mBuf[(mHead + mCount) & MASK] = data;
mCount += 1;
}
unsigned int popFront() {
unsigned int result = mBuf[mHead];
mHead = (mHead + 1) & MASK;
mCount -= 1;
return result;
}
void popBack(int n) {
mCount -= n;
}
inline int count() {
return mCount;
}
bool empty() {
return mCount == 0;
}
bool full() {
return mCount == SIZE;
}
// The valid indexes are 1 - count() to 0
unsigned int operator[](int i) {
return mBuf[(mHead + mCount + i) & MASK];
}
};
CircularQueue mQ;
void error(const char* fmt,...) {
va_list ap;
va_start(ap, fmt);
mErrorSink->verror(fmt, ap);
va_end(ap);
}
void flush() {
while (!mQ.empty()) {
mpBase->o4(mQ.popFront());
}
mpBase->flush();
}
public:
ARMCodeBuf(ICodeBuf* pBase) {
mpBase = pBase;
}
virtual ~ARMCodeBuf() {
delete mpBase;
}
void init(int size) {
mpBase->init(size);
}
void setErrorSink(ErrorSink* pErrorSink) {
mErrorSink = pErrorSink;
mpBase->setErrorSink(pErrorSink);
}
void o4(int n) {
if (mQ.full()) {
mpBase->o4(mQ.popFront());
}
mQ.pushBack(n);
#ifndef DISABLE_ARM_PEEPHOLE
// Peephole check
bool didPeep;
do {
static const unsigned int opMask = 0x01e00000;
static const unsigned int immediateMask = 0x00000fff;
static const unsigned int BMask = 0x00400000;
didPeep = false;
if (mQ.count() >= 4) {
// Operand by a small constant
// push;mov #imm;pop;op ==> op #imm
if (mQ[-4] == 0xe92d0001 && // stmfd r13!, {r0}
(mQ[-3] & ~immediateMask) == 0xe3a00000 && // mov r0, #X
mQ[-2] == 0xe8bd0002 && // ldmea r13!, {r1}
(mQ[-1] & ~opMask) == (0xe0810000 & ~opMask)) { // OP r0, r1, r0
unsigned int movConst = mQ[-3];
unsigned int op = mQ[-1];
unsigned int combined = 0xe2000000 | (op & opMask) | (movConst & immediateMask);
// fprintf(stderr, "op %x movConst %x combined %x\n", op, movConst, combined);
if (! (combined == 0xe2800000 || combined == 0xe2400000)) { // add/sub #0
mQ.popBack(4);
mQ.pushBack(combined);
didPeep = true;
} else {
mQ.popBack(4);
didPeep = true;
}
}
}
// Load local variable
// sub r0,r11,#imm;ldr/ldrb r0,[r0] ==> ldr/ldrb r0, [r11,#-imm]
if (mQ.count() >= 2) {
if ((mQ[-2] & ~immediateMask) == 0xe24b0000) { // sub r0,r11,#imm
const unsigned int encodedImmediate = mQ[-2] & immediateMask;
const unsigned int ld = mQ[-1];
if ((ld & ~BMask) == 0xe5900000) { // ldr{b} r0, [r0]
unsigned int combined = encodedImmediate | (0xE51B0000 | (ld & BMask)); // ldr r0, [r11, #-0]
mQ.popBack(2);
mQ.pushBack(combined);
didPeep = true;
} else if (ld == 0xedd07a00) { // ldcl p10, c7, [r0, #0x000]
unsigned int decodedImmediate = decode12BitImmediate(encodedImmediate);
if (decodedImmediate <= 1020 && ((decodedImmediate & 3) == 0)) {
unsigned int combined = (decodedImmediate >> 2) | 0xed5b7a00; // ldcl p10, c7, [r11, #-0]
mQ.popBack(2);
mQ.pushBack(combined);
didPeep = true;
}
}
}
}
// Constant array lookup
if (mQ.count() >= 6 &&
mQ[-6] == 0xe92d0001 && // stmfd r13!, {r0}
(mQ[-5] & ~immediateMask)== 0xe3a00000 && // mov r0, #0x00000001
mQ[-4] == 0xe8bd0002 && // ldmea r13!, {r1}
(mQ[-3] & ~immediateMask)== 0xe3a02000 && // mov r2, #0x00000004
mQ[-2] == 0xe0000092 && // mul r0, r2, r0
mQ[-1] == 0xe0810000) { // add r0, r1, r0
unsigned int mov1 = mQ[-5];
unsigned int mov2 = mQ[-3];
unsigned int const1 = decode12BitImmediate(mov1);
unsigned int const2 = decode12BitImmediate(mov2);
unsigned int comboConst = const1 * const2;
size_t immediate = 0;
if (encode12BitImmediate(comboConst, &immediate)) {
mQ.popBack(6);
unsigned int add = immediate | 0xE2800000; // add r0, r0, #n
if (comboConst) {
mQ.pushBack(add);
}
didPeep = true;
}
}
// Pointer arithmetic with a stride that is a power of two
if (mQ.count() >= 3 &&
(mQ[-3] & ~ immediateMask) == 0xe3a02000 && // mov r2, #stride
mQ[-2] == 0xe0000092 && // mul r0, r2, r0
mQ[-1] == 0xe0810000) { // add r0, r1, r0
int stride = decode12BitImmediate(mQ[-3]);
if (isPowerOfTwo(stride)) {
mQ.popBack(3);
unsigned int add = 0xe0810000 | (log2(stride) << 7); // add r0, r1, r0, LSL #log2(stride)
mQ.pushBack(add);
didPeep = true;
}
}
} while (didPeep);
#endif
}
void ob(int n) {
error("ob() not supported.");
}
void* getBase() {
flush();
return mpBase->getBase();
}
intptr_t getSize() {
flush();
return mpBase->getSize();
}
intptr_t getPC() {
flush();
return mpBase->getPC();
}
};
class ARMCodeGenerator : public CodeGenerator {
public:
ARMCodeGenerator() {
#ifdef ARM_USE_VFP
// LOGD("Using ARM VFP hardware floating point.");
#else
// LOGD("Using ARM soft floating point.");
#endif
}
virtual ~ARMCodeGenerator() {}
/* returns address to patch with local variable size
*/
virtual int functionEntry(Type* pDecl) {
mStackUse = 0;
// sp -> arg4 arg5 ...
// Push our register-based arguments back on the stack
int regArgCount = calcRegArgCount(pDecl);
if (regArgCount > 0) {
mStackUse += regArgCount * 4;
o4(0xE92D0000 | ((1 << regArgCount) - 1)); // stmfd sp!, {}
}
// sp -> arg0 arg1 ...
o4(0xE92D4800); // stmfd sp!, {fp, lr}
mStackUse += 2 * 4;
// sp, fp -> oldfp, retadr, arg0 arg1 ....
o4(0xE1A0B00D); // mov fp, sp
LOG_STACK("functionEntry: %d\n", mStackUse);
int pc = getPC();
o4(0xE24DD000); // sub sp, sp, # <local variables>
// We don't know how many local variables we are going to use,
// but we will round the allocation up to a multiple of
// STACK_ALIGNMENT, so it won't affect the stack alignment.
return pc;
}
virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) {
// Round local variable size up to a multiple of stack alignment
localVariableSize = ((localVariableSize + STACK_ALIGNMENT - 1) /
STACK_ALIGNMENT) * STACK_ALIGNMENT;
// Patch local variable allocation code:
if (localVariableSize < 0 || localVariableSize > 255) {
error("localVariables out of range: %d", localVariableSize);
}
*(char*) (localVariableAddress) = localVariableSize;
#ifdef ARM_USE_VFP
{
Type* pReturnType = pDecl->pHead;
switch(pReturnType->tag) {
case TY_FLOAT:
o4(0xEE170A90); // fmrs r0, s15
break;
case TY_DOUBLE:
o4(0xEC510B17); // fmrrd r0, r1, d7
break;
default:
break;
}
}
#endif
// sp -> locals .... fp -> oldfp, retadr, arg0, arg1, ...
o4(0xE1A0E00B); // mov lr, fp
o4(0xE59BB000); // ldr fp, [fp]
o4(0xE28ED004); // add sp, lr, #4
// sp -> retadr, arg0, ...
o4(0xE8BD4000); // ldmfd sp!, {lr}
// sp -> arg0 ....
// We store the PC into the lr so we can adjust the sp before
// returning. We need to pull off the registers we pushed
// earlier. We don't need to actually store them anywhere,
// just adjust the stack.
int regArgCount = calcRegArgCount(pDecl);
if (regArgCount) {
o4(0xE28DD000 | (regArgCount << 2)); // add sp, sp, #argCount << 2
}
o4(0xE12FFF1E); // bx lr
}
/* load immediate value */
virtual void li(int t) {
liReg(t, 0);
setR0Type(mkpInt);
}
virtual void loadFloat(int address, Type* pType) {
setR0Type(pType);
// Global, absolute address
o4(0xE59F0000); // ldr r0, .L1
o4(0xEA000000); // b .L99
o4(address); // .L1: .word ea
// .L99:
switch (pType->tag) {
case TY_FLOAT:
#ifdef ARM_USE_VFP
o4(0xEDD07A00); // flds s15, [r0]
#else
o4(0xE5900000); // ldr r0, [r0]
#endif
break;
case TY_DOUBLE:
#ifdef ARM_USE_VFP
o4(0xED907B00); // fldd d7, [r0]
#else
o4(0xE1C000D0); // ldrd r0, [r0]
#endif
break;
default:
assert(false);
break;
}
}
virtual void addStructOffsetR0(int offset, Type* pType) {
if (offset) {
size_t immediate = 0;
if (encode12BitImmediate(offset, &immediate)) {
o4(0xE2800000 | immediate); // add r0, r0, #offset
} else {
error("structure offset out of range: %d", offset);
}
}
setR0Type(pType, ET_LVALUE);
}
virtual int gjmp(int t) {
int pc = getPC();
o4(0xEA000000 | encodeAddress(t)); // b .L33
return pc;
}
/* l = 0: je, l == 1: jne */
virtual int gtst(bool l, int t) {
Type* pR0Type = getR0Type();
TypeTag tagR0 = pR0Type->tag;
switch(tagR0) {
case TY_FLOAT:
#ifdef ARM_USE_VFP
o4(0xEEF57A40); // fcmpzs s15
o4(0xEEF1FA10); // fmstat
#else
callRuntime((void*) runtime_is_non_zero_f);
o4(0xE3500000); // cmp r0,#0
#endif
break;
case TY_DOUBLE:
#ifdef ARM_USE_VFP
o4(0xEEB57B40); // fcmpzd d7
o4(0xEEF1FA10); // fmstat
#else
callRuntime((void*) runtime_is_non_zero_d);
o4(0xE3500000); // cmp r0,#0
#endif
break;
default:
o4(0xE3500000); // cmp r0,#0
break;
}
int branch = l ? 0x1A000000 : 0x0A000000; // bne : beq
int pc = getPC();
o4(branch | encodeAddress(t));
return pc;
}
virtual void gcmp(int op) {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = collapseType(pR0Type->tag);
TypeTag tagTOS = collapseType(pTOSType->tag);
if (tagR0 == TY_INT && tagTOS == TY_INT) {
setupIntPtrArgs();
o4(0xE1510000); // cmp r1, r1
switch(op) {
case OP_EQUALS:
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
break;
case OP_NOT_EQUALS:
o4(0x03A00000); // moveq r0,#0
o4(0x13A00001); // movne r0,#1
break;
case OP_LESS_EQUAL:
o4(0xD3A00001); // movle r0,#1
o4(0xC3A00000); // movgt r0,#0
break;
case OP_GREATER:
o4(0xD3A00000); // movle r0,#0
o4(0xC3A00001); // movgt r0,#1
break;
case OP_GREATER_EQUAL:
o4(0xA3A00001); // movge r0,#1
o4(0xB3A00000); // movlt r0,#0
break;
case OP_LESS:
o4(0xA3A00000); // movge r0,#0
o4(0xB3A00001); // movlt r0,#1
break;
default:
error("Unknown comparison op %d", op);
break;
}
} else if (tagR0 == TY_DOUBLE || tagTOS == TY_DOUBLE) {
setupDoubleArgs();
#ifdef ARM_USE_VFP
o4(0xEEB46BC7); // fcmped d6, d7
o4(0xEEF1FA10); // fmstat
switch(op) {
case OP_EQUALS:
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
break;
case OP_NOT_EQUALS:
o4(0x03A00000); // moveq r0,#0
o4(0x13A00001); // movne r0,#1
break;
case OP_LESS_EQUAL:
o4(0xD3A00001); // movle r0,#1
o4(0xC3A00000); // movgt r0,#0
break;
case OP_GREATER:
o4(0xD3A00000); // movle r0,#0
o4(0xC3A00001); // movgt r0,#1
break;
case OP_GREATER_EQUAL:
o4(0xA3A00001); // movge r0,#1
o4(0xB3A00000); // movlt r0,#0
break;
case OP_LESS:
o4(0xA3A00000); // movge r0,#0
o4(0xB3A00001); // movlt r0,#1
break;
default:
error("Unknown comparison op %d", op);
break;
}
#else
switch(op) {
case OP_EQUALS:
callRuntime((void*) runtime_cmp_eq_dd);
break;
case OP_NOT_EQUALS:
callRuntime((void*) runtime_cmp_ne_dd);
break;
case OP_LESS_EQUAL:
callRuntime((void*) runtime_cmp_le_dd);
break;
case OP_GREATER:
callRuntime((void*) runtime_cmp_gt_dd);
break;
case OP_GREATER_EQUAL:
callRuntime((void*) runtime_cmp_ge_dd);
break;
case OP_LESS:
callRuntime((void*) runtime_cmp_lt_dd);
break;
default:
error("Unknown comparison op %d", op);
break;
}
#endif
} else {
setupFloatArgs();
#ifdef ARM_USE_VFP
o4(0xEEB47AE7); // fcmpes s14, s15
o4(0xEEF1FA10); // fmstat
switch(op) {
case OP_EQUALS:
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
break;
case OP_NOT_EQUALS:
o4(0x03A00000); // moveq r0,#0
o4(0x13A00001); // movne r0,#1
break;
case OP_LESS_EQUAL:
o4(0xD3A00001); // movle r0,#1
o4(0xC3A00000); // movgt r0,#0
break;
case OP_GREATER:
o4(0xD3A00000); // movle r0,#0
o4(0xC3A00001); // movgt r0,#1
break;
case OP_GREATER_EQUAL:
o4(0xA3A00001); // movge r0,#1
o4(0xB3A00000); // movlt r0,#0
break;
case OP_LESS:
o4(0xA3A00000); // movge r0,#0
o4(0xB3A00001); // movlt r0,#1
break;
default:
error("Unknown comparison op %d", op);
break;
}
#else
switch(op) {
case OP_EQUALS:
callRuntime((void*) runtime_cmp_eq_ff);
break;
case OP_NOT_EQUALS:
callRuntime((void*) runtime_cmp_ne_ff);
break;
case OP_LESS_EQUAL:
callRuntime((void*) runtime_cmp_le_ff);
break;
case OP_GREATER:
callRuntime((void*) runtime_cmp_gt_ff);
break;
case OP_GREATER_EQUAL:
callRuntime((void*) runtime_cmp_ge_ff);
break;
case OP_LESS:
callRuntime((void*) runtime_cmp_lt_ff);
break;
default:
error("Unknown comparison op %d", op);
break;
}
#endif
}
setR0Type(mkpInt);
}
virtual void genOp(int op) {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = pR0Type->tag;
TypeTag tagTOS = pTOSType->tag;
bool isFloatR0 = isFloatTag(tagR0);
bool isFloatTOS = isFloatTag(tagTOS);
if (!isFloatR0 && !isFloatTOS) {
setupIntPtrArgs();
bool isPtrR0 = isPointerTag(tagR0);
bool isPtrTOS = isPointerTag(tagTOS);
if (isPtrR0 || isPtrTOS) {
if (isPtrR0 && isPtrTOS) {
if (op != OP_MINUS) {
error("Unsupported pointer-pointer operation %d.", op);
}
if (! typeEqual(pR0Type, pTOSType)) {
error("Incompatible pointer types for subtraction.");
}
o4(0xE0410000); // sub r0,r1,r0
setR0Type(mkpInt);
int size = sizeOf(pR0Type->pHead);
if (size != 1) {
pushR0();
li(size);
// TODO: Optimize for power-of-two.
genOp(OP_DIV);
}
} else {
if (! (op == OP_PLUS || (op == OP_MINUS && isPtrR0))) {
error("Unsupported pointer-scalar operation %d", op);
}
Type* pPtrType = getPointerArithmeticResultType(
pR0Type, pTOSType);
int size = sizeOf(pPtrType->pHead);
if (size != 1) {
// TODO: Optimize for power-of-two.
liReg(size, 2);
if (isPtrR0) {
o4(0x0E0010192); // mul r1,r2,r1
} else {
o4(0x0E0000092); // mul r0,r2,r0
}
}
switch(op) {
case OP_PLUS:
o4(0xE0810000); // add r0,r1,r0
break;
case OP_MINUS:
o4(0xE0410000); // sub r0,r1,r0
break;
}
setR0Type(pPtrType);
}
} else {
switch(op) {
case OP_MUL:
o4(0x0E0000091); // mul r0,r1,r0
break;
case OP_DIV:
callRuntime((void*) runtime_DIV);
break;
case OP_MOD:
callRuntime((void*) runtime_MOD);
break;
case OP_PLUS:
o4(0xE0810000); // add r0,r1,r0
break;
case OP_MINUS:
o4(0xE0410000); // sub r0,r1,r0
break;
case OP_SHIFT_LEFT:
o4(0xE1A00011); // lsl r0,r1,r0
break;
case OP_SHIFT_RIGHT:
o4(0xE1A00051); // asr r0,r1,r0
break;
case OP_BIT_AND:
o4(0xE0010000); // and r0,r1,r0
break;
case OP_BIT_XOR:
o4(0xE0210000); // eor r0,r1,r0
break;
case OP_BIT_OR:
o4(0xE1810000); // orr r0,r1,r0
break;
case OP_BIT_NOT:
o4(0xE1E00000); // mvn r0, r0
break;
default:
error("Unimplemented op %d\n", op);
break;
}
}
} else {
Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType;
if (pResultType->tag == TY_DOUBLE) {
setupDoubleArgs();
switch(op) {
case OP_MUL:
#ifdef ARM_USE_VFP
o4(0xEE267B07); // fmuld d7, d6, d7
#else
callRuntime((void*) runtime_op_mul_dd);
#endif
break;
case OP_DIV:
#ifdef ARM_USE_VFP
o4(0xEE867B07); // fdivd d7, d6, d7
#else
callRuntime((void*) runtime_op_div_dd);
#endif
break;
case OP_PLUS:
#ifdef ARM_USE_VFP
o4(0xEE367B07); // faddd d7, d6, d7
#else
callRuntime((void*) runtime_op_add_dd);
#endif
break;
case OP_MINUS:
#ifdef ARM_USE_VFP
o4(0xEE367B47); // fsubd d7, d6, d7
#else
callRuntime((void*) runtime_op_sub_dd);
#endif
break;
default:
error("Unsupported binary floating operation %d\n", op);
break;
}
} else {
setupFloatArgs();
switch(op) {
case OP_MUL:
#ifdef ARM_USE_VFP
o4(0xEE677A27); // fmuls s15, s14, s15
#else
callRuntime((void*) runtime_op_mul_ff);
#endif
break;
case OP_DIV:
#ifdef ARM_USE_VFP
o4(0xEEC77A27); // fdivs s15, s14, s15
#else
callRuntime((void*) runtime_op_div_ff);
#endif
break;
case OP_PLUS:
#ifdef ARM_USE_VFP
o4(0xEE777A27); // fadds s15, s14, s15
#else
callRuntime((void*) runtime_op_add_ff);
#endif
break;
case OP_MINUS:
#ifdef ARM_USE_VFP
o4(0xEE777A67); // fsubs s15, s14, s15
#else
callRuntime((void*) runtime_op_sub_ff);
#endif
break;
default:
error("Unsupported binary floating operation %d\n", op);
break;
}
}
setR0Type(pResultType);
}
}
virtual void gUnaryCmp(int op) {
if (op != OP_LOGICAL_NOT) {
error("Unknown unary cmp %d", op);
} else {
Type* pR0Type = getR0Type();
TypeTag tag = collapseType(pR0Type->tag);
switch(tag) {
case TY_INT:
o4(0xE3A01000); // mov r1, #0
o4(0xE1510000); // cmp r1, r0
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
break;
case TY_FLOAT:
#ifdef ARM_USE_VFP
o4(0xEEF57A40); // fcmpzs s15
o4(0xEEF1FA10); // fmstat
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
#else
callRuntime((void*) runtime_is_zero_f);
#endif
break;
case TY_DOUBLE:
#ifdef ARM_USE_VFP
o4(0xEEB57B40); // fcmpzd d7
o4(0xEEF1FA10); // fmstat
o4(0x03A00001); // moveq r0,#1
o4(0x13A00000); // movne r0,#0
#else
callRuntime((void*) runtime_is_zero_d);
#endif
break;
default:
error("gUnaryCmp unsupported type");
break;
}
}
setR0Type(mkpInt);
}
virtual void genUnaryOp(int op) {
Type* pR0Type = getR0Type();
TypeTag tag = collapseType(pR0Type->tag);
switch(tag) {
case TY_INT:
switch(op) {
case OP_MINUS:
o4(0xE3A01000); // mov r1, #0
o4(0xE0410000); // sub r0,r1,r0
break;
case OP_BIT_NOT:
o4(0xE1E00000); // mvn r0, r0
break;
default:
error("Unknown unary op %d\n", op);
break;
}
break;
case TY_FLOAT:
case TY_DOUBLE:
switch (op) {
case OP_MINUS:
if (tag == TY_FLOAT) {
#ifdef ARM_USE_VFP
o4(0xEEF17A67); // fnegs s15, s15
#else
callRuntime((void*) runtime_op_neg_f);
#endif
} else {
#ifdef ARM_USE_VFP
o4(0xEEB17B47); // fnegd d7, d7
#else
callRuntime((void*) runtime_op_neg_d);
#endif
}
break;
case OP_BIT_NOT:
error("Can't apply '~' operator to a float or double.");
break;
default:
error("Unknown unary op %d\n", op);
break;
}
break;
default:
error("genUnaryOp unsupported type");
break;
}
}
virtual void pushR0() {
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
#ifdef ARM_USE_VFP
switch (r0ct ) {
case TY_FLOAT:
o4(0xED6D7A01); // fstmfds sp!,{s15}
mStackUse += 4;
break;
case TY_DOUBLE:
o4(0xED2D7B02); // fstmfdd sp!,{d7}
mStackUse += 8;
break;
default:
o4(0xE92D0001); // stmfd sp!,{r0}
mStackUse += 4;
}
#else
if (r0ct != TY_DOUBLE) {
o4(0xE92D0001); // stmfd sp!,{r0}
mStackUse += 4;
} else {
o4(0xE92D0003); // stmfd sp!,{r0,r1}
mStackUse += 8;
}
#endif
pushType();
LOG_STACK("pushR0: %d\n", mStackUse);
}
virtual void over() {
// We know it's only used for int-ptr ops (++/--)
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
Type* pTOSType = getTOSType();
TypeTag tosct = collapseType(pTOSType->tag);
assert (r0ct == TY_INT && tosct == TY_INT);
o4(0xE8BD0002); // ldmfd sp!,{r1}
o4(0xE92D0001); // stmfd sp!,{r0}
o4(0xE92D0002); // stmfd sp!,{r1}
overType();
mStackUse += 4;
}
virtual void popR0() {
Type* pTOSType = getTOSType();
TypeTag tosct = collapseType(pTOSType->tag);
#ifdef ARM_USE_VFP
if (tosct == TY_FLOAT || tosct == TY_DOUBLE) {
error("Unsupported popR0 float/double");
}
#endif
switch (tosct){
case TY_INT:
case TY_FLOAT:
o4(0xE8BD0001); // ldmfd sp!,{r0}
mStackUse -= 4;
break;
case TY_DOUBLE:
o4(0xE8BD0003); // ldmfd sp!,{r0, r1} // Restore R0
mStackUse -= 8;
break;
default:
error("Can't pop this type.");
break;
}
popType();
LOG_STACK("popR0: %d\n", mStackUse);
}
virtual void storeR0ToTOS() {
Type* pPointerType = getTOSType();
assert(pPointerType->tag == TY_POINTER);
Type* pDestType = pPointerType->pHead;
convertR0(pDestType);
o4(0xE8BD0004); // ldmfd sp!,{r2}
popType();
mStackUse -= 4;
switch (pDestType->tag) {
case TY_POINTER:
case TY_INT:
o4(0xE5820000); // str r0, [r2]
break;
case TY_FLOAT:
#ifdef ARM_USE_VFP
o4(0xEDC27A00); // fsts s15, [r2, #0]
#else
o4(0xE5820000); // str r0, [r2]
#endif
break;
case TY_SHORT:
o4(0xE1C200B0); // strh r0, [r2]
break;
case TY_CHAR:
o4(0xE5C20000); // strb r0, [r2]
break;
case TY_DOUBLE:
#ifdef ARM_USE_VFP
o4(0xED827B00); // fstd d7, [r2, #0]
#else
o4(0xE1C200F0); // strd r0, [r2]
#endif
break;
case TY_STRUCT:
{
int size = sizeOf(pDestType);
if (size > 0) {
liReg(size, 1);
callRuntime((void*) runtime_structCopy);
}
}
break;
default:
error("storeR0ToTOS: unimplemented type %d",
pDestType->tag);
break;
}
}
virtual void loadR0FromR0() {
Type* pPointerType = getR0Type();
assert(pPointerType->tag == TY_POINTER);
Type* pNewType = pPointerType->pHead;
TypeTag tag = pNewType->tag;
switch (tag) {
case TY_POINTER:
case TY_INT:
o4(0xE5900000); // ldr r0, [r0]
break;
case TY_FLOAT:
#ifdef ARM_USE_VFP
o4(0xEDD07A00); // flds s15, [r0, #0]
#else
o4(0xE5900000); // ldr r0, [r0]
#endif
break;
case TY_SHORT:
o4(0xE1D000F0); // ldrsh r0, [r0]
break;
case TY_CHAR:
o4(0xE5D00000); // ldrb r0, [r0]
break;
case TY_DOUBLE:
#ifdef ARM_USE_VFP
o4(0xED907B00); // fldd d7, [r0, #0]
#else
o4(0xE1C000D0); // ldrd r0, [r0]
#endif
break;
case TY_ARRAY:
pNewType = pNewType->pTail;
break;
case TY_STRUCT:
break;
default:
error("loadR0FromR0: unimplemented type %d", tag);
break;
}
setR0Type(pNewType);
}
virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) {
if (ea > -LOCAL && ea < LOCAL) {
// Local, fp relative
size_t immediate = 0;
bool inRange = false;
if (ea < 0) {
inRange = encode12BitImmediate(-ea, &immediate);
o4(0xE24B0000 | immediate); // sub r0, fp, #ea
} else {
inRange = encode12BitImmediate(ea, &immediate);
o4(0xE28B0000 | immediate); // add r0, fp, #ea
}
if (! inRange) {
error("Offset out of range: %08x", ea);
}
} else {
// Global, absolute.
o4(0xE59F0000); // ldr r0, .L1
o4(0xEA000000); // b .L99
o4(ea); // .L1: .word 0
// .L99:
}
setR0Type(pPointerType, et);
}
virtual int leaForward(int ea, Type* pPointerType) {
setR0Type(pPointerType);
int result = ea;
int pc = getPC();
int offset = 0;
if (ea) {
offset = (pc - ea - 8) >> 2;
if ((offset & 0xffff) != offset) {
error("function forward reference out of bounds");
}
} else {
offset = 0;
}
o4(0xE59F0000 | offset); // ldr r0, .L1
if (ea == 0) {
o4(0xEA000000); // b .L99
result = getPC();
o4(ea); // .L1: .word 0
// .L99:
}
return result;
}
virtual void convertR0Imp(Type* pType, bool isCast){
Type* pR0Type = getR0Type();
if (isPointerType(pType) && isPointerType(pR0Type)) {
Type* pA = pR0Type;
Type* pB = pType;
// Array decays to pointer
if (pA->tag == TY_ARRAY && pB->tag == TY_POINTER) {
pA = pA->pTail;
}
if (! (typeEqual(pA, pB)
|| pB->pHead->tag == TY_VOID
|| (pA->tag == TY_POINTER && pB->tag == TY_POINTER && isCast)
)) {
error("Incompatible pointer or array types");
}
} else if (bitsSame(pType, pR0Type)) {
// do nothing special
} else {
TypeTag r0Tag = collapseType(pR0Type->tag);
TypeTag destTag = collapseType(pType->tag);
if (r0Tag == TY_INT) {
if (destTag == TY_FLOAT) {
#ifdef ARM_USE_VFP
o4(0xEE070A90); // fmsr s15, r0
o4(0xEEF87AE7); // fsitos s15, s15
#else
callRuntime((void*) runtime_int_to_float);
#endif
} else {
assert(destTag == TY_DOUBLE);
#ifdef ARM_USE_VFP
o4(0xEE070A90); // fmsr s15, r0
o4(0xEEB87BE7); // fsitod d7, s15
#else
callRuntime((void*) runtime_int_to_double);
#endif
}
} else if (r0Tag == TY_FLOAT) {
if (destTag == TY_INT) {
#ifdef ARM_USE_VFP
o4(0xEEFD7AE7); // ftosizs s15, s15
o4(0xEE170A90); // fmrs r0, s15
#else
callRuntime((void*) runtime_float_to_int);
#endif
} else {
assert(destTag == TY_DOUBLE);
#ifdef ARM_USE_VFP
o4(0xEEB77AE7); // fcvtds d7, s15
#else
callRuntime((void*) runtime_float_to_double);
#endif
}
} else {
if (r0Tag == TY_DOUBLE) {
if (destTag == TY_INT) {
#ifdef ARM_USE_VFP
o4(0xEEFD7BC7); // ftosizd s15, d7
o4(0xEE170A90); // fmrs r0, s15
#else
callRuntime((void*) runtime_double_to_int);
#endif
} else {
if(destTag == TY_FLOAT) {
#ifdef ARM_USE_VFP
o4(0xEEF77BC7); // fcvtsd s15, d7
#else
callRuntime((void*) runtime_double_to_float);
#endif
} else {
incompatibleTypes(pR0Type, pType);
}
}
} else {
incompatibleTypes(pR0Type, pType);
}
}
}
setR0Type(pType);
}
virtual int beginFunctionCallArguments() {
int pc = getPC();
o4(0xE24DDF00); // Placeholder sub sp, sp, #0
return pc;
}
virtual size_t storeR0ToArg(int l, Type* pArgType) {
convertR0(pArgType);
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
#ifdef ARM_USE_VFP
switch(r0ct) {
case TY_INT:
if (l < 0 || l > 4096-4) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xE58D0000 | l); // str r0, [sp, #l]
return 4;
case TY_FLOAT:
if (l < 0 || l > 1020 || (l & 3)) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xEDCD7A00 | (l >> 2)); // fsts s15, [sp, #l]
return 4;
case TY_DOUBLE: {
// Align to 8 byte boundary
int l2 = (l + 7) & ~7;
if (l2 < 0 || l2 > 1020 || (l2 & 3)) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xED8D7B00 | (l2 >> 2)); // fstd d7, [sp, #l2]
return (l2 - l) + 8;
}
default:
assert(false);
return 0;
}
#else
switch(r0ct) {
case TY_INT:
case TY_FLOAT:
if (l < 0 || l > 4096-4) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xE58D0000 + l); // str r0, [sp, #l]
return 4;
case TY_DOUBLE: {
// Align to 8 byte boundary
int l2 = (l + 7) & ~7;
if (l2 < 0 || l2 > 4096-8) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xE58D0000 + l2); // str r0, [sp, #l]
o4(0xE58D1000 + l2 + 4); // str r1, [sp, #l+4]
return (l2 - l) + 8;
}
default:
assert(false);
return 0;
}
#endif
}
virtual void endFunctionCallArguments(Type* pDecl, int a, int l) {
int argumentStackUse = l;
// Have to calculate register arg count from actual stack size,
// in order to properly handle ... functions.
int regArgCount = l >> 2;
if (regArgCount > 4) {
regArgCount = 4;
}
if (regArgCount > 0) {
argumentStackUse -= regArgCount * 4;
o4(0xE8BD0000 | ((1 << regArgCount) - 1)); // ldmfd sp!,{}
}
mStackUse += argumentStackUse;
// Align stack.
int missalignment = mStackUse - ((mStackUse / STACK_ALIGNMENT)
* STACK_ALIGNMENT);
mStackAlignmentAdjustment = 0;
if (missalignment > 0) {
mStackAlignmentAdjustment = STACK_ALIGNMENT - missalignment;
}
l += mStackAlignmentAdjustment;
if (l < 0 || l > 0x3FC) {
error("L out of range for stack adjustment: 0x%08x", l);
}
flush();
* (int*) a = 0xE24DDF00 | (l >> 2); // sub sp, sp, #0 << 2
mStackUse += mStackAlignmentAdjustment;
LOG_STACK("endFunctionCallArguments mStackUse: %d, mStackAlignmentAdjustment %d\n",
mStackUse, mStackAlignmentAdjustment);
}
virtual int callForward(int symbol, Type* pFunc) {
setR0Type(pFunc->pHead);
// Forward calls are always short (local)
int pc = getPC();
o4(0xEB000000 | encodeAddress(symbol));
return pc;
}
virtual void callIndirect(int l, Type* pFunc) {
assert(pFunc->tag == TY_FUNC);
popType(); // Get rid of indirect fn pointer type
int argCount = l >> 2;
int poppedArgs = argCount > 4 ? 4 : argCount;
int adjustedL = l - (poppedArgs << 2) + mStackAlignmentAdjustment;
if (adjustedL < 0 || adjustedL > 4096-4) {
error("l out of range for stack offset: 0x%08x", l);
}
o4(0xE59DC000 | (0xfff & adjustedL)); // ldr r12, [sp,#adjustedL]
o4(0xE12FFF3C); // blx r12
Type* pReturnType = pFunc->pHead;
setR0Type(pReturnType);
#ifdef ARM_USE_VFP
switch(pReturnType->tag) {
case TY_FLOAT:
o4(0xEE070A90); // fmsr s15, r0
break;
case TY_DOUBLE:
o4(0xEC410B17); // fmdrr d7, r0, r1
break;
default:
break;
}
#endif
}
virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) {
int argCount = l >> 2;
// Have to calculate register arg count from actual stack size,
// in order to properly handle ... functions.
int regArgCount = l >> 2;
if (regArgCount > 4) {
regArgCount = 4;
}
int stackArgs = argCount - regArgCount;
int stackUse = stackArgs + (isIndirect ? 1 : 0)
+ (mStackAlignmentAdjustment >> 2);
if (stackUse) {
if (stackUse < 0 || stackUse > 255) {
error("L out of range for stack adjustment: 0x%08x", l);
}
o4(0xE28DDF00 | stackUse); // add sp, sp, #stackUse << 2
mStackUse -= stackUse * 4;
LOG_STACK("adjustStackAfterCall: %d\n", mStackUse);
}
}
virtual int jumpOffset() {
return 8;
}
/* output a symbol and patch all calls to it */
virtual void gsym(int t) {
int n;
int base = getBase();
int pc = getPC();
while (t) {
int data = * (int*) t;
int decodedOffset = ((BRANCH_REL_ADDRESS_MASK & data) << 2);
if (decodedOffset == 0) {
n = 0;
} else {
n = base + decodedOffset; /* next value */
}
*(int *) t = (data & ~BRANCH_REL_ADDRESS_MASK)
| encodeRelAddress(pc - t - 8);
t = n;
}
}
/* output a symbol and patch all calls to it */
virtual void resolveForward(int t) {
if (t) {
int pc = getPC();
*(int *) t = pc;
}
}
virtual int finishCompile() {
#if defined(__arm__)
const long base = long(getBase());
const long curr = long(getPC());
int err = cacheflush(base, curr, 0);
return err;
#else
return 0;
#endif
}
/**
* alignment (in bytes) for this type of data
*/
virtual size_t alignmentOf(Type* pType){
switch(pType->tag) {
case TY_CHAR:
return 1;
case TY_SHORT:
return 2;
case TY_DOUBLE:
return 8;
case TY_ARRAY:
return alignmentOf(pType->pHead);
case TY_STRUCT:
return pType->pHead->alignment & 0x7fffffff;
case TY_FUNC:
error("alignment of func not supported");
return 1;
default:
return 4;
}
}
/**
* Array element alignment (in bytes) for this type of data.
*/
virtual size_t sizeOf(Type* pType){
switch(pType->tag) {
case TY_INT:
return 4;
case TY_SHORT:
return 2;
case TY_CHAR:
return 1;
case TY_FLOAT:
return 4;
case TY_DOUBLE:
return 8;
case TY_POINTER:
return 4;
case TY_ARRAY:
return pType->length * sizeOf(pType->pHead);
case TY_STRUCT:
return pType->pHead->length;
default:
error("Unsupported type %d", pType->tag);
return 0;
}
}
private:
static const int BRANCH_REL_ADDRESS_MASK = 0x00ffffff;
/** Encode a relative address that might also be
* a label.
*/
int encodeAddress(int value) {
int base = getBase();
if (value >= base && value <= getPC() ) {
// This is a label, encode it relative to the base.
value = value - base;
}
return encodeRelAddress(value);
}
int encodeRelAddress(int value) {
return BRANCH_REL_ADDRESS_MASK & (value >> 2);
}
int calcRegArgCount(Type* pDecl) {
int reg = 0;
Type* pArgs = pDecl->pTail;
while (pArgs && reg < 4) {
Type* pArg = pArgs->pHead;
if ( pArg->tag == TY_DOUBLE) {
int evenReg = (reg + 1) & ~1;
if (evenReg >= 4) {
break;
}
reg = evenReg + 2;
} else {
reg++;
}
pArgs = pArgs->pTail;
}
return reg;
}
void setupIntPtrArgs() {
o4(0xE8BD0002); // ldmfd sp!,{r1}
mStackUse -= 4;
popType();
}
/* Pop TOS to R1 (use s14 if VFP)
* Make sure both R0 and TOS are floats. (Could be ints)
* We know that at least one of R0 and TOS is already a float
*/
void setupFloatArgs() {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = collapseType(pR0Type->tag);
TypeTag tagTOS = collapseType(pTOSType->tag);
if (tagR0 != TY_FLOAT) {
assert(tagR0 == TY_INT);
#ifdef ARM_USE_VFP
o4(0xEE070A90); // fmsr s15, r0
o4(0xEEF87AE7); // fsitos s15, s15
#else
callRuntime((void*) runtime_int_to_float);
#endif
}
if (tagTOS != TY_FLOAT) {
assert(tagTOS == TY_INT);
assert(tagR0 == TY_FLOAT);
#ifdef ARM_USE_VFP
o4(0xECBD7A01); // fldmfds sp!, {s14}
o4(0xEEB87AC7); // fsitos s14, s14
#else
o4(0xE92D0001); // stmfd sp!,{r0} // push R0
o4(0xE59D0004); // ldr r0, [sp, #4]
callRuntime((void*) runtime_int_to_float);
o4(0xE1A01000); // mov r1, r0
o4(0xE8BD0001); // ldmfd sp!,{r0} // pop R0
o4(0xE28DD004); // add sp, sp, #4 // Pop sp
#endif
} else {
// Pop TOS
#ifdef ARM_USE_VFP
o4(0xECBD7A01); // fldmfds sp!, {s14}
#else
o4(0xE8BD0002); // ldmfd sp!,{r1}
#endif
}
mStackUse -= 4;
popType();
}
/* Pop TOS into R2..R3 (use D6 if VFP)
* Make sure both R0 and TOS are doubles. Could be floats or ints.
* We know that at least one of R0 and TOS are already a double.
*/
void setupDoubleArgs() {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = collapseType(pR0Type->tag);
TypeTag tagTOS = collapseType(pTOSType->tag);
if (tagR0 != TY_DOUBLE) {
if (tagR0 == TY_INT) {
#ifdef ARM_USE_VFP
o4(0xEE070A90); // fmsr s15, r0
o4(0xEEB87BE7); // fsitod d7, s15
#else
callRuntime((void*) runtime_int_to_double);
#endif
} else {
assert(tagR0 == TY_FLOAT);
#ifdef ARM_USE_VFP
o4(0xEEB77AE7); // fcvtds d7, s15
#else
callRuntime((void*) runtime_float_to_double);
#endif
}
}
if (tagTOS != TY_DOUBLE) {
#ifdef ARM_USE_VFP
if (tagTOS == TY_INT) {
o4(0xECFD6A01); // fldmfds sp!,{s13}
o4(0xEEB86BE6); // fsitod d6, s13
} else {
assert(tagTOS == TY_FLOAT);
o4(0xECFD6A01); // fldmfds sp!,{s13}
o4(0xEEB76AE6); // fcvtds d6, s13
}
#else
o4(0xE92D0003); // stmfd sp!,{r0,r1} // push r0,r1
o4(0xE59D0008); // ldr r0, [sp, #8]
if (tagTOS == TY_INT) {
callRuntime((void*) runtime_int_to_double);
} else {
assert(tagTOS == TY_FLOAT);
callRuntime((void*) runtime_float_to_double);
}
o4(0xE1A02000); // mov r2, r0
o4(0xE1A03001); // mov r3, r1
o4(0xE8BD0003); // ldmfd sp!,{r0, r1} // Restore R0
o4(0xE28DD004); // add sp, sp, #4 // Pop sp
#endif
mStackUse -= 4;
} else {
#ifdef ARM_USE_VFP
o4(0xECBD6B02); // fldmfdd sp!, {d6}
#else
o4(0xE8BD000C); // ldmfd sp!,{r2,r3}
#endif
mStackUse -= 8;
}
popType();
}
void liReg(int t, int reg) {
assert(reg >= 0 && reg < 16);
int rN = (reg & 0xf) << 12;
size_t encodedImmediate;
if (encode12BitImmediate(t, &encodedImmediate)) {
o4(0xE3A00000 | encodedImmediate | rN); // mov rN, #0
} else if (encode12BitImmediate(-(t+1), &encodedImmediate)) {
// mvn means move constant ^ ~0
o4(0xE3E00000 | encodedImmediate | rN); // mvn rN, #0
} else {
o4(0xE51F0000 | rN); // ldr rN, .L3
o4(0xEA000000); // b .L99
o4(t); // .L3: .word 0
// .L99:
}
}
void incompatibleTypes(Type* pR0Type, Type* pType) {
error("Incompatible types old: %d new: %d", pR0Type->tag, pType->tag);
}
void callRuntime(void* fn) {
o4(0xE59FC000); // ldr r12, .L1
o4(0xEA000000); // b .L99
o4((int) fn); //.L1: .word fn
o4(0xE12FFF3C); //.L99: blx r12
}
// Integer math:
static int runtime_DIV(int b, int a) {
return a / b;
}
static int runtime_MOD(int b, int a) {
return a % b;
}
static void runtime_structCopy(void* src, size_t size, void* dest) {
memcpy(dest, src, size);
}
#ifndef ARM_USE_VFP
// Comparison to zero
static int runtime_is_non_zero_f(float a) {
return a != 0;
}
static int runtime_is_non_zero_d(double a) {
return a != 0;
}
// Comparison to zero
static int runtime_is_zero_f(float a) {
return a == 0;
}
static int runtime_is_zero_d(double a) {
return a == 0;
}
// Type conversion
static int runtime_float_to_int(float a) {
return (int) a;
}
static double runtime_float_to_double(float a) {
return (double) a;
}
static int runtime_double_to_int(double a) {
return (int) a;
}
static float runtime_double_to_float(double a) {
return (float) a;
}
static float runtime_int_to_float(int a) {
return (float) a;
}
static double runtime_int_to_double(int a) {
return (double) a;
}
// Comparisons float
static int runtime_cmp_eq_ff(float b, float a) {
return a == b;
}
static int runtime_cmp_ne_ff(float b, float a) {
return a != b;
}
static int runtime_cmp_lt_ff(float b, float a) {
return a < b;
}
static int runtime_cmp_le_ff(float b, float a) {
return a <= b;
}
static int runtime_cmp_ge_ff(float b, float a) {
return a >= b;
}
static int runtime_cmp_gt_ff(float b, float a) {
return a > b;
}
// Comparisons double
static int runtime_cmp_eq_dd(double b, double a) {
return a == b;
}
static int runtime_cmp_ne_dd(double b, double a) {
return a != b;
}
static int runtime_cmp_lt_dd(double b, double a) {
return a < b;
}
static int runtime_cmp_le_dd(double b, double a) {
return a <= b;
}
static int runtime_cmp_ge_dd(double b, double a) {
return a >= b;
}
static int runtime_cmp_gt_dd(double b, double a) {
return a > b;
}
// Math float
static float runtime_op_add_ff(float b, float a) {
return a + b;
}
static float runtime_op_sub_ff(float b, float a) {
return a - b;
}
static float runtime_op_mul_ff(float b, float a) {
return a * b;
}
static float runtime_op_div_ff(float b, float a) {
return a / b;
}
static float runtime_op_neg_f(float a) {
return -a;
}
// Math double
static double runtime_op_add_dd(double b, double a) {
return a + b;
}
static double runtime_op_sub_dd(double b, double a) {
return a - b;
}
static double runtime_op_mul_dd(double b, double a) {
return a * b;
}
static double runtime_op_div_dd(double b, double a) {
return a / b;
}
static double runtime_op_neg_d(double a) {
return -a;
}
#endif
static const int STACK_ALIGNMENT = 8;
int mStackUse;
// This variable holds the amount we adjusted the stack in the most
// recent endFunctionCallArguments call. It's examined by the
// following adjustStackAfterCall call.
int mStackAlignmentAdjustment;
};
#endif // PROVIDE_ARM_CODEGEN
#ifdef PROVIDE_X86_CODEGEN
class X86CodeGenerator : public CodeGenerator {
public:
X86CodeGenerator() {}
virtual ~X86CodeGenerator() {}
/* returns address to patch with local variable size
*/
virtual int functionEntry(Type* pDecl) {
o(0xe58955); /* push %ebp, mov %esp, %ebp */
return oad(0xec81, 0); /* sub $xxx, %esp */
}
virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) {
o(0xc3c9); /* leave, ret */
*(int *) localVariableAddress = localVariableSize; /* save local variables */
}
/* load immediate value */
virtual void li(int i) {
oad(0xb8, i); /* mov $xx, %eax */
setR0Type(mkpInt);
}
virtual void loadFloat(int address, Type* pType) {
setR0Type(pType);
switch (pType->tag) {
case TY_FLOAT:
oad(0x05D9, address); // flds
break;
case TY_DOUBLE:
oad(0x05DD, address); // fldl
break;
default:
assert(false);
break;
}
}
virtual void addStructOffsetR0(int offset, Type* pType) {
if (offset) {
oad(0x05, offset); // addl offset, %eax
}
setR0Type(pType, ET_LVALUE);
}
virtual int gjmp(int t) {
return psym(0xe9, t);
}
/* l = 0: je, l == 1: jne */
virtual int gtst(bool l, int t) {
Type* pR0Type = getR0Type();
TypeTag tagR0 = pR0Type->tag;
bool isFloatR0 = isFloatTag(tagR0);
if (isFloatR0) {
o(0xeed9); // fldz
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
} else {
o(0xc085); // test %eax, %eax
}
// Use two output statements to generate one instruction.
o(0x0f); // je/jne xxx
return psym(0x84 + l, t);
}
virtual void gcmp(int op) {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = pR0Type->tag;
TypeTag tagTOS = pTOSType->tag;
bool isFloatR0 = isFloatTag(tagR0);
bool isFloatTOS = isFloatTag(tagTOS);
if (!isFloatR0 && !isFloatTOS) {
int t = decodeOp(op);
o(0x59); /* pop %ecx */
o(0xc139); /* cmp %eax,%ecx */
li(0);
o(0x0f); /* setxx %al */
o(t + 0x90);
o(0xc0);
popType();
} else {
setupFloatOperands();
switch (op) {
case OP_EQUALS:
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
o(0xc0940f); // sete %al
o(0xc29b0f); // setnp %dl
o(0xd021); // andl %edx, %eax
break;
case OP_NOT_EQUALS:
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
o(0xc0950f); // setne %al
o(0xc29a0f); // setp %dl
o(0xd009); // orl %edx, %eax
break;
case OP_GREATER_EQUAL:
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x05c4f6); // testb $5, %ah
o(0xc0940f); // sete %al
break;
case OP_LESS:
o(0xc9d9); // fxch %st(1)
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
o(0xc0970f); // seta %al
break;
case OP_LESS_EQUAL:
o(0xc9d9); // fxch %st(1)
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
o(0xc0930f); // setea %al
break;
case OP_GREATER:
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x45c4f6); // testb $69, %ah
o(0xc0940f); // sete %al
break;
default:
error("Unknown comparison op");
}
o(0xc0b60f); // movzbl %al, %eax
}
setR0Type(mkpInt);
}
virtual void genOp(int op) {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = pR0Type->tag;
TypeTag tagTOS = pTOSType->tag;
bool isFloatR0 = isFloatTag(tagR0);
bool isFloatTOS = isFloatTag(tagTOS);
if (!isFloatR0 && !isFloatTOS) {
bool isPtrR0 = isPointerTag(tagR0);
bool isPtrTOS = isPointerTag(tagTOS);
if (isPtrR0 || isPtrTOS) {
if (isPtrR0 && isPtrTOS) {
if (op != OP_MINUS) {
error("Unsupported pointer-pointer operation %d.", op);
}
if (! typeEqual(pR0Type, pTOSType)) {
error("Incompatible pointer types for subtraction.");
}
o(0x59); /* pop %ecx */
o(decodeOp(op));
popType();
setR0Type(mkpInt);
int size = sizeOf(pR0Type->pHead);
if (size != 1) {
pushR0();
li(size);
// TODO: Optimize for power-of-two.
genOp(OP_DIV);
}
} else {
if (! (op == OP_PLUS || (op == OP_MINUS && isPtrR0))) {
error("Unsupported pointer-scalar operation %d", op);
}
Type* pPtrType = getPointerArithmeticResultType(
pR0Type, pTOSType);
o(0x59); /* pop %ecx */
int size = sizeOf(pPtrType->pHead);
if (size != 1) {
// TODO: Optimize for power-of-two.
if (isPtrR0) {
oad(0xC969, size); // imull $size, %ecx
} else {
oad(0xC069, size); // mul $size, %eax
}
}
o(decodeOp(op));
popType();
setR0Type(pPtrType);
}
} else {
o(0x59); /* pop %ecx */
o(decodeOp(op));
if (op == OP_MOD)
o(0x92); /* xchg %edx, %eax */
popType();
}
} else {
Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType;
setupFloatOperands();
// Both float. x87 R0 == left hand, x87 R1 == right hand
switch (op) {
case OP_MUL:
o(0xc9de); // fmulp
break;
case OP_DIV:
o(0xf1de); // fdivp
break;
case OP_PLUS:
o(0xc1de); // faddp
break;
case OP_MINUS:
o(0xe1de); // fsubp
break;
default:
error("Unsupported binary floating operation.");
break;
}
setR0Type(pResultType);
}
}
virtual void gUnaryCmp(int op) {
if (op != OP_LOGICAL_NOT) {
error("Unknown unary cmp %d", op);
} else {
Type* pR0Type = getR0Type();
TypeTag tag = collapseType(pR0Type->tag);
switch(tag) {
case TY_INT: {
oad(0xb9, 0); /* movl $0, %ecx */
int t = decodeOp(op);
o(0xc139); /* cmp %eax,%ecx */
li(0);
o(0x0f); /* setxx %al */
o(t + 0x90);
o(0xc0);
}
break;
case TY_FLOAT:
case TY_DOUBLE:
o(0xeed9); // fldz
o(0xe9da); // fucompp
o(0xe0df); // fnstsw %ax
o(0x9e); // sahf
o(0xc0950f); // setne %al
o(0xc29a0f); // setp %dl
o(0xd009); // orl %edx, %eax
o(0xc0b60f); // movzbl %al, %eax
o(0x01f083); // xorl $1, %eax
break;
default:
error("gUnaryCmp unsupported type");
break;
}
}
setR0Type(mkpInt);
}
virtual void genUnaryOp(int op) {
Type* pR0Type = getR0Type();
TypeTag tag = collapseType(pR0Type->tag);
switch(tag) {
case TY_INT:
oad(0xb9, 0); /* movl $0, %ecx */
o(decodeOp(op));
break;
case TY_FLOAT:
case TY_DOUBLE:
switch (op) {
case OP_MINUS:
o(0xe0d9); // fchs
break;
case OP_BIT_NOT:
error("Can't apply '~' operator to a float or double.");
break;
default:
error("Unknown unary op %d\n", op);
break;
}
break;
default:
error("genUnaryOp unsupported type");
break;
}
}
virtual void pushR0() {
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
switch(r0ct) {
case TY_INT:
o(0x50); /* push %eax */
break;
case TY_FLOAT:
o(0x50); /* push %eax */
o(0x241cd9); // fstps 0(%esp)
break;
case TY_DOUBLE:
o(0x50); /* push %eax */
o(0x50); /* push %eax */
o(0x241cdd); // fstpl 0(%esp)
break;
default:
error("pushR0 unsupported type %d", r0ct);
break;
}
pushType();
}
virtual void over() {
// We know it's only used for int-ptr ops (++/--)
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
Type* pTOSType = getTOSType();
TypeTag tosct = collapseType(pTOSType->tag);
assert (r0ct == TY_INT && tosct == TY_INT);
o(0x59); /* pop %ecx */
o(0x50); /* push %eax */
o(0x51); /* push %ecx */
overType();
}
virtual void popR0() {
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
switch(r0ct) {
case TY_INT:
o(0x58); /* popl %eax */
break;
case TY_FLOAT:
o(0x2404d9); // flds (%esp)
o(0x58); /* popl %eax */
break;
case TY_DOUBLE:
o(0x2404dd); // fldl (%esp)
o(0x58); /* popl %eax */
o(0x58); /* popl %eax */
break;
default:
error("popR0 unsupported type %d", r0ct);
break;
}
popType();
}
virtual void storeR0ToTOS() {
Type* pPointerType = getTOSType();
assert(pPointerType->tag == TY_POINTER);
Type* pTargetType = pPointerType->pHead;
convertR0(pTargetType);
o(0x59); /* pop %ecx */
popType();
switch (pTargetType->tag) {
case TY_POINTER:
case TY_INT:
o(0x0189); /* movl %eax/%al, (%ecx) */
break;
case TY_SHORT:
o(0x018966); /* movw %ax, (%ecx) */
break;
case TY_CHAR:
o(0x0188); /* movl %eax/%al, (%ecx) */
break;
case TY_FLOAT:
o(0x19d9); /* fstps (%ecx) */
break;
case TY_DOUBLE:
o(0x19dd); /* fstpl (%ecx) */
break;
case TY_STRUCT:
{
// TODO: use alignment information to use movsw/movsl instead of movsb
int size = sizeOf(pTargetType);
if (size > 0) {
o(0x9c); // pushf
o(0x57); // pushl %edi
o(0x56); // pushl %esi
o(0xcf89); // movl %ecx, %edi
o(0xc689); // movl %eax, %esi
oad(0xb9, size); // mov #size, %ecx
o(0xfc); // cld
o(0xf3); // rep
o(0xa4); // movsb
o(0x5e); // popl %esi
o(0x5f); // popl %edi
o(0x9d); // popf
}
}
break;
default:
error("storeR0ToTOS: unsupported type %d",
pTargetType->tag);
break;
}
}
virtual void loadR0FromR0() {
Type* pPointerType = getR0Type();
assert(pPointerType->tag == TY_POINTER);
Type* pNewType = pPointerType->pHead;
TypeTag tag = pNewType->tag;
switch (tag) {
case TY_POINTER:
case TY_INT:
o2(0x008b); /* mov (%eax), %eax */
break;
case TY_SHORT:
o(0xbf0f); /* movswl (%eax), %eax */
ob(0);
break;
case TY_CHAR:
o(0xbe0f); /* movsbl (%eax), %eax */
ob(0); /* add zero in code */
break;
case TY_FLOAT:
o2(0x00d9); // flds (%eax)
break;
case TY_DOUBLE:
o2(0x00dd); // fldl (%eax)
break;
case TY_ARRAY:
pNewType = pNewType->pTail;
break;
case TY_STRUCT:
break;
default:
error("loadR0FromR0: unsupported type %d", tag);
break;
}
setR0Type(pNewType);
}
virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) {
gmov(10, ea); /* leal EA, %eax */
setR0Type(pPointerType, et);
}
virtual int leaForward(int ea, Type* pPointerType) {
oad(0xb8, ea); /* mov $xx, %eax */
setR0Type(pPointerType);
return getPC() - 4;
}
virtual void convertR0Imp(Type* pType, bool isCast){
Type* pR0Type = getR0Type();
if (pR0Type == NULL) {
assert(false);
setR0Type(pType);
return;
}
if (isPointerType(pType) && isPointerType(pR0Type)) {
Type* pA = pR0Type;
Type* pB = pType;
// Array decays to pointer
if (pA->tag == TY_ARRAY && pB->tag == TY_POINTER) {
pA = pA->pTail;
}
if (! (typeEqual(pA, pB)
|| pB->pHead->tag == TY_VOID
|| (pA->tag == TY_POINTER && pB->tag == TY_POINTER && isCast)
)) {
error("Incompatible pointer or array types");
}
} else if (bitsSame(pType, pR0Type)) {
// do nothing special
} else if (isFloatType(pType) && isFloatType(pR0Type)) {
// do nothing special, both held in same register on x87.
} else {
TypeTag r0Tag = collapseType(pR0Type->tag);
TypeTag destTag = collapseType(pType->tag);
if (r0Tag == TY_INT && isFloatTag(destTag)) {
// Convert R0 from int to float
o(0x50); // push %eax
o(0x2404DB); // fildl 0(%esp)
o(0x58); // pop %eax
} else if (isFloatTag(r0Tag) && destTag == TY_INT) {
// Convert R0 from float to int. Complicated because
// need to save and restore the rounding mode.
o(0x50); // push %eax
o(0x50); // push %eax
o(0x02247cD9); // fnstcw 2(%esp)
o(0x2444b70f); // movzwl 2(%esp), %eax
o(0x02);
o(0x0cb4); // movb $12, %ah
o(0x24048966); // movw %ax, 0(%esp)
o(0x242cd9); // fldcw 0(%esp)
o(0x04245cdb); // fistpl 4(%esp)
o(0x02246cd9); // fldcw 2(%esp)
o(0x58); // pop %eax
o(0x58); // pop %eax
} else {
error("Incompatible types old: %d new: %d",
pR0Type->tag, pType->tag);
}
}
setR0Type(pType);
}
virtual int beginFunctionCallArguments() {
return oad(0xec81, 0); /* sub $xxx, %esp */
}
virtual size_t storeR0ToArg(int l, Type* pArgType) {
convertR0(pArgType);
Type* pR0Type = getR0Type();
TypeTag r0ct = collapseType(pR0Type->tag);
switch(r0ct) {
case TY_INT:
oad(0x248489, l); /* movl %eax, xxx(%esp) */
return 4;
case TY_FLOAT:
oad(0x249CD9, l); /* fstps xxx(%esp) */
return 4;
case TY_DOUBLE:
oad(0x249CDD, l); /* fstpl xxx(%esp) */
return 8;
default:
assert(false);
return 0;
}
}
virtual void endFunctionCallArguments(Type* pDecl, int a, int l) {
* (int*) a = l;
}
virtual int callForward(int symbol, Type* pFunc) {
assert(pFunc->tag == TY_FUNC);
setR0Type(pFunc->pHead);
return psym(0xe8, symbol); /* call xxx */
}
virtual void callIndirect(int l, Type* pFunc) {
assert(pFunc->tag == TY_FUNC);
popType(); // Get rid of indirect fn pointer type
setR0Type(pFunc->pHead);
oad(0x2494ff, l); /* call *xxx(%esp) */
}
virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) {
assert(pDecl->tag == TY_FUNC);
if (isIndirect) {
l += 4;
}
if (l > 0) {
oad(0xc481, l); /* add $xxx, %esp */
}
}
virtual int jumpOffset() {
return 5;
}
/* output a symbol and patch all calls to it */
virtual void gsym(int t) {
int n;
int pc = getPC();
while (t) {
n = *(int *) t; /* next value */
*(int *) t = pc - t - 4;
t = n;
}
}
/* output a symbol and patch all calls to it, using absolute address */
virtual void resolveForward(int t) {
int n;
int pc = getPC();
while (t) {
n = *(int *) t; /* next value */
*(int *) t = pc;
t = n;
}
}
virtual int finishCompile() {
size_t pagesize = 4096;
size_t base = (size_t) getBase() & ~ (pagesize - 1);
size_t top = ((size_t) getPC() + pagesize - 1) & ~ (pagesize - 1);
int err = mprotect((void*) base, top - base, PROT_READ | PROT_WRITE | PROT_EXEC);
if (err) {
error("mprotect() failed: %d", errno);
}
return err;
}
/**
* Alignment (in bytes) for this type of data
*/
virtual size_t alignmentOf(Type* pType){
switch (pType->tag) {
case TY_CHAR:
return 1;
case TY_SHORT:
return 2;
case TY_ARRAY:
return alignmentOf(pType->pHead);
case TY_STRUCT:
return pType->pHead->alignment & 0x7fffffff;
case TY_FUNC:
error("alignment of func not supported");
return 1;
default:
return 4;
}
}
/**
* Array element alignment (in bytes) for this type of data.
*/
virtual size_t sizeOf(Type* pType){
switch(pType->tag) {
case TY_INT:
return 4;
case TY_SHORT:
return 2;
case TY_CHAR:
return 1;
case TY_FLOAT:
return 4;
case TY_DOUBLE:
return 8;
case TY_POINTER:
return 4;
case TY_ARRAY:
return pType->length * sizeOf(pType->pHead);
case TY_STRUCT:
return pType->pHead->length;
default:
error("Unsupported type %d", pType->tag);
return 0;
}
}
private:
/** Output 1 to 4 bytes.
*
*/
void o(int n) {
/* cannot use unsigned, so we must do a hack */
while (n && n != -1) {
ob(n & 0xff);
n = n >> 8;
}
}
/* Output exactly 2 bytes
*/
void o2(int n) {
ob(n & 0xff);
ob(0xff & (n >> 8));
}
/* psym is used to put an instruction with a data field which is a
reference to a symbol. It is in fact the same as oad ! */
int psym(int n, int t) {
return oad(n, t);
}
/* instruction + address */
int oad(int n, int t) {
o(n);
int result = getPC();
o4(t);
return result;
}
static const int operatorHelper[];
int decodeOp(int op) {
if (op < 0 || op > OP_COUNT) {
error("Out-of-range operator: %d\n", op);
op = 0;
}
return operatorHelper[op];
}
void gmov(int l, int t) {
o(l + 0x83);
oad((t > -LOCAL && t < LOCAL) << 7 | 5, t);
}
void setupFloatOperands() {
Type* pR0Type = getR0Type();
Type* pTOSType = getTOSType();
TypeTag tagR0 = pR0Type->tag;
TypeTag tagTOS = pTOSType->tag;
bool isFloatR0 = isFloatTag(tagR0);
bool isFloatTOS = isFloatTag(tagTOS);
if (! isFloatR0) {
// Convert R0 from int to float
o(0x50); // push %eax
o(0x2404DB); // fildl 0(%esp)
o(0x58); // pop %eax
}
if (! isFloatTOS){
o(0x2404DB); // fildl 0(%esp);
o(0x58); // pop %eax
} else {
if (tagTOS == TY_FLOAT) {
o(0x2404d9); // flds (%esp)
o(0x58); // pop %eax
} else {
o(0x2404dd); // fldl (%esp)
o(0x58); // pop %eax
o(0x58); // pop %eax
}
}
popType();
}
};
#endif // PROVIDE_X86_CODEGEN
#ifdef PROVIDE_TRACE_CODEGEN
class TraceCodeGenerator : public CodeGenerator {
private:
CodeGenerator* mpBase;
public:
TraceCodeGenerator(CodeGenerator* pBase) {
mpBase = pBase;
}
virtual ~TraceCodeGenerator() {
delete mpBase;
}
virtual void init(ICodeBuf* pCodeBuf) {
mpBase->init(pCodeBuf);
}
void setErrorSink(ErrorSink* pErrorSink) {
mpBase->setErrorSink(pErrorSink);
}
/* returns address to patch with local variable size
*/
virtual int functionEntry(Type* pDecl) {
int result = mpBase->functionEntry(pDecl);
fprintf(stderr, "functionEntry(pDecl) -> %d\n", result);
return result;
}
virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) {
fprintf(stderr, "functionExit(pDecl, %d, %d)\n",
localVariableAddress, localVariableSize);
mpBase->functionExit(pDecl, localVariableAddress, localVariableSize);
}
/* load immediate value */
virtual void li(int t) {
fprintf(stderr, "li(%d)\n", t);
mpBase->li(t);
}
virtual void loadFloat(int address, Type* pType) {
fprintf(stderr, "loadFloat(%d, type=%d)\n", address, pType->tag);
mpBase->loadFloat(address, pType);
}
virtual void addStructOffsetR0(int offset, Type* pType) {
fprintf(stderr, "addStructOffsetR0(%d, type=%d)\n", offset, pType->tag);
mpBase->addStructOffsetR0(offset, pType);
}
virtual int gjmp(int t) {
int result = mpBase->gjmp(t);
fprintf(stderr, "gjmp(%d) = %d\n", t, result);
return result;
}
/* l = 0: je, l == 1: jne */
virtual int gtst(bool l, int t) {
int result = mpBase->gtst(l, t);
fprintf(stderr, "gtst(%d,%d) = %d\n", l, t, result);
return result;
}
virtual void gcmp(int op) {
fprintf(stderr, "gcmp(%d)\n", op);
mpBase->gcmp(op);
}
virtual void genOp(int op) {
fprintf(stderr, "genOp(%d)\n", op);
mpBase->genOp(op);
}
virtual void gUnaryCmp(int op) {
fprintf(stderr, "gUnaryCmp(%d)\n", op);
mpBase->gUnaryCmp(op);
}
virtual void genUnaryOp(int op) {
fprintf(stderr, "genUnaryOp(%d)\n", op);
mpBase->genUnaryOp(op);
}
virtual void pushR0() {
fprintf(stderr, "pushR0()\n");
mpBase->pushR0();
}
virtual void over() {
fprintf(stderr, "over()\n");
mpBase->over();
}
virtual void popR0() {
fprintf(stderr, "popR0()\n");
mpBase->popR0();
}
virtual void storeR0ToTOS() {
fprintf(stderr, "storeR0ToTOS()\n");
mpBase->storeR0ToTOS();
}
virtual void loadR0FromR0() {
fprintf(stderr, "loadR0FromR0()\n");
mpBase->loadR0FromR0();
}
virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) {
fprintf(stderr, "leaR0(%d, %d, %d)\n", ea,
pPointerType->pHead->tag, et);
mpBase->leaR0(ea, pPointerType, et);
}
virtual int leaForward(int ea, Type* pPointerType) {
fprintf(stderr, "leaForward(%d)\n", ea);
return mpBase->leaForward(ea, pPointerType);
}
virtual void convertR0Imp(Type* pType, bool isCast){
fprintf(stderr, "convertR0(pType tag=%d, %d)\n", pType->tag, isCast);
mpBase->convertR0Imp(pType, isCast);
}
virtual int beginFunctionCallArguments() {
int result = mpBase->beginFunctionCallArguments();
fprintf(stderr, "beginFunctionCallArguments() = %d\n", result);
return result;
}
virtual size_t storeR0ToArg(int l, Type* pArgType) {
fprintf(stderr, "storeR0ToArg(%d, pArgType=%d)\n", l,
pArgType->tag);
return mpBase->storeR0ToArg(l, pArgType);
}
virtual void endFunctionCallArguments(Type* pDecl, int a, int l) {
fprintf(stderr, "endFunctionCallArguments(%d, %d)\n", a, l);
mpBase->endFunctionCallArguments(pDecl, a, l);
}
virtual int callForward(int symbol, Type* pFunc) {
int result = mpBase->callForward(symbol, pFunc);
fprintf(stderr, "callForward(%d) = %d\n", symbol, result);
return result;
}
virtual void callIndirect(int l, Type* pFunc) {
fprintf(stderr, "callIndirect(%d returntype = %d)\n", l,
pFunc->pHead->tag);
mpBase->callIndirect(l, pFunc);
}
virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) {
fprintf(stderr, "adjustStackAfterCall(pType, %d, %d)\n", l, isIndirect);
mpBase->adjustStackAfterCall(pDecl, l, isIndirect);
}
virtual int jumpOffset() {
return mpBase->jumpOffset();
}
/* output a symbol and patch all calls to it */
virtual void gsym(int t) {
fprintf(stderr, "gsym(%d)\n", t);
mpBase->gsym(t);
}
virtual void resolveForward(int t) {
mpBase->resolveForward(t);
}
virtual int finishCompile() {
int result = mpBase->finishCompile();
fprintf(stderr, "finishCompile() = %d\n", result);
return result;
}
/**
* Alignment (in bytes) for this type of data
*/
virtual size_t alignmentOf(Type* pType){
return mpBase->alignmentOf(pType);
}
/**
* Array element alignment (in bytes) for this type of data.
*/
virtual size_t sizeOf(Type* pType){
return mpBase->sizeOf(pType);
}
virtual Type* getR0Type() {
return mpBase->getR0Type();
}
virtual ExpressionType getR0ExpressionType() {
return mpBase->getR0ExpressionType();
}
virtual void setR0ExpressionType(ExpressionType et) {
mpBase->setR0ExpressionType(et);
}
virtual size_t getExpressionStackDepth() {
return mpBase->getExpressionStackDepth();
}
virtual void forceR0RVal() {
return mpBase->forceR0RVal();
}
};
#endif // PROVIDE_TRACE_CODEGEN
class Arena {
public:
// Used to record a given allocation amount.
// Used:
// Mark mark = arena.mark();
// ... lots of arena.allocate()
// arena.free(mark);
struct Mark {
size_t chunk;
size_t offset;
};
Arena() {
mCurrentChunk = 0;
Chunk start(CHUNK_SIZE);
mData.push_back(start);
}
~Arena() {
for(size_t i = 0; i < mData.size(); i++) {
mData[i].free();
}
}
// Alloc using the standard alignment size safe for any variable
void* alloc(size_t size) {
return alloc(size, 8);
}
Mark mark(){
Mark result;
result.chunk = mCurrentChunk;
result.offset = mData[mCurrentChunk].mOffset;
return result;
}
void freeToMark(const Mark& mark) {
mCurrentChunk = mark.chunk;
mData[mCurrentChunk].mOffset = mark.offset;
}
private:
// Allocate memory aligned to a given size
// and a given power-of-two-sized alignment (e.g. 1,2,4,8,...)
// Memory is not zero filled.
void* alloc(size_t size, size_t alignment) {
while (size > mData[mCurrentChunk].remainingCapacity(alignment)) {
if (mCurrentChunk + 1 < mData.size()) {
mCurrentChunk++;
} else {
size_t allocSize = CHUNK_SIZE;
if (allocSize < size + alignment - 1) {
allocSize = size + alignment - 1;
}
Chunk chunk(allocSize);
mData.push_back(chunk);
mCurrentChunk++;
}
}
return mData[mCurrentChunk].allocate(size, alignment);
}
static const size_t CHUNK_SIZE = 128*1024;
// Note: this class does not deallocate its
// memory when it's destroyed. It depends upon
// its parent to deallocate the memory.
struct Chunk {
Chunk() {
mpData = 0;
mSize = 0;
mOffset = 0;
}
Chunk(size_t size) {
mSize = size;
mpData = (char*) malloc(size);
mOffset = 0;
}
~Chunk() {
// Doesn't deallocate memory.
}
void* allocate(size_t size, size_t alignment) {
size_t alignedOffset = aligned(mOffset, alignment);
void* result = mpData + alignedOffset;
mOffset = alignedOffset + size;
return result;
}
void free() {
if (mpData) {
::free(mpData);
mpData = 0;
}
}
size_t remainingCapacity(size_t alignment) {
return aligned(mSize, alignment) - aligned(mOffset, alignment);
}
// Assume alignment is a power of two
inline size_t aligned(size_t v, size_t alignment) {
size_t mask = alignment-1;
return (v + mask) & ~mask;
}
char* mpData;
size_t mSize;
size_t mOffset;
};
size_t mCurrentChunk;
Vector<Chunk> mData;
};
struct VariableInfo;
struct Token {
int hash;
size_t length;
char* pText;
tokenid_t id;
// Current values for the token
char* mpMacroDefinition;
VariableInfo* mpVariableInfo;
VariableInfo* mpStructInfo;
};
class TokenTable {
public:
// Don't use 0..0xff, allows characters and operators to be tokens too.
static const int TOKEN_BASE = 0x100;
TokenTable() {
mpMap = hashmapCreate(128, hashFn, equalsFn);
}
~TokenTable() {
hashmapFree(mpMap);
}
void setArena(Arena* pArena) {
mpArena = pArena;
}
// Returns a token for a given string of characters.
tokenid_t intern(const char* pText, size_t length) {
Token probe;
int hash = hashmapHash((void*) pText, length);
{
Token probe;
probe.hash = hash;
probe.length = length;
probe.pText = (char*) pText;
Token* pValue = (Token*) hashmapGet(mpMap, &probe);
if (pValue) {
return pValue->id;
}
}
Token* pToken = (Token*) mpArena->alloc(sizeof(Token));
memset(pToken, 0, sizeof(*pToken));
pToken->hash = hash;
pToken->length = length;
pToken->pText = (char*) mpArena->alloc(length + 1);
memcpy(pToken->pText, pText, length);
pToken->pText[length] = 0;
pToken->id = mTokens.size() + TOKEN_BASE;
mTokens.push_back(pToken);
hashmapPut(mpMap, pToken, pToken);
return pToken->id;
}
// Return the Token for a given tokenid.
Token& operator[](tokenid_t id) {
return *mTokens[id - TOKEN_BASE];
}
inline size_t size() {
return mTokens.size();
}
private:
static int hashFn(void* pKey) {
Token* pToken = (Token*) pKey;
return pToken->hash;
}
static bool equalsFn(void* keyA, void* keyB) {
Token* pTokenA = (Token*) keyA;
Token* pTokenB = (Token*) keyB;
// Don't need to compare hash values, they should always be equal
return pTokenA->length == pTokenB->length
&& strcmp(pTokenA->pText, pTokenB->pText) == 0;
}
Hashmap* mpMap;
Vector<Token*> mTokens;
Arena* mpArena;
};
class InputStream {
public:
virtual ~InputStream() {}
virtual int getChar() = 0;
};
class TextInputStream : public InputStream {
public:
TextInputStream(const char* text, size_t textLength)
: pText(text), mTextLength(textLength), mPosition(0) {
}
virtual int getChar() {
return mPosition < mTextLength ? pText[mPosition++] : EOF;
}
private:
const char* pText;
size_t mTextLength;
size_t mPosition;
};
class String {
public:
String() {
mpBase = 0;
mUsed = 0;
mSize = 0;
}
String(const char* item, int len, bool adopt) {
if (len < 0) {
len = strlen(item);
}
if (adopt) {
mpBase = (char*) item;
mUsed = len;
mSize = len + 1;
} else {
mpBase = 0;
mUsed = 0;
mSize = 0;
appendBytes(item, len);
}
}
String(const String& other) {
mpBase = 0;
mUsed = 0;
mSize = 0;
appendBytes(other.getUnwrapped(), other.len());
}
~String() {
if (mpBase) {
free(mpBase);
}
}
String& operator=(const String& other) {
clear();
appendBytes(other.getUnwrapped(), other.len());
return *this;
}
inline char* getUnwrapped() const {
return mpBase;
}
void clear() {
mUsed = 0;
if (mSize > 0) {
mpBase[0] = 0;
}
}
void appendCStr(const char* s) {
appendBytes(s, strlen(s));
}
void appendBytes(const char* s, int n) {
memcpy(ensure(n), s, n + 1);
}
void append(char c) {
* ensure(1) = c;
}
void append(String& other) {
appendBytes(other.getUnwrapped(), other.len());
}
char* orphan() {
char* result = mpBase;
mpBase = 0;
mUsed = 0;
mSize = 0;
return result;
}
void printf(const char* fmt,...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
void vprintf(const char* fmt, va_list ap) {
char* temp;
int numChars = vasprintf(&temp, fmt, ap);
memcpy(ensure(numChars), temp, numChars+1);
free(temp);
}
inline size_t len() const {
return mUsed;
}
private:
char* ensure(int n) {
size_t newUsed = mUsed + n;
if (newUsed > mSize) {
size_t newSize = mSize * 2 + 10;
if (newSize < newUsed) {
newSize = newUsed;
}
mpBase = (char*) realloc(mpBase, newSize + 1);
mSize = newSize;
}
mpBase[newUsed] = '\0';
char* result = mpBase + mUsed;
mUsed = newUsed;
return result;
}
char* mpBase;
size_t mUsed;
size_t mSize;
};
void internKeywords() {
// Note: order has to match TOK_ constants
static const char* keywords[] = {
"int",
"char",
"void",
"if",
"else",
"while",
"break",
"return",
"for",
"auto",
"case",
"const",
"continue",
"default",
"do",
"double",
"enum",
"extern",
"float",
"goto",
"long",
"register",
"short",
"signed",
"sizeof",
"static",
"struct",
"switch",
"typedef",
"union",
"unsigned",
"volatile",
"_Bool",
"_Complex",
"_Imaginary",
"inline",
"restrict",
// predefined tokens that can also be symbols start here:
"pragma",
"define",
"line",
0};
for(int i = 0; keywords[i]; i++) {
mTokenTable.intern(keywords[i], strlen(keywords[i]));
}
}
struct InputState {
InputStream* pStream;
int oldCh;
};
struct VariableInfo {
void* pAddress;
void* pForward; // For a forward direction, linked list of data to fix up
tokenid_t tok;
size_t level;
VariableInfo* pOldDefinition;
Type* pType;
bool isStructTag;
};
class SymbolStack {
public:
SymbolStack() {
mpArena = 0;
mpTokenTable = 0;
}
void setArena(Arena* pArena) {
mpArena = pArena;
}
void setTokenTable(TokenTable* pTokenTable) {
mpTokenTable = pTokenTable;
}
void pushLevel() {
Mark mark;
mark.mArenaMark = mpArena->mark();
mark.mSymbolHead = mStack.size();
mLevelStack.push_back(mark);
}
void popLevel() {
// Undo any shadowing that was done:
Mark mark = mLevelStack.back();
mLevelStack.pop_back();
while (mStack.size() > mark.mSymbolHead) {
VariableInfo* pV = mStack.back();
mStack.pop_back();
if (pV->isStructTag) {
(*mpTokenTable)[pV->tok].mpStructInfo = pV->pOldDefinition;
} else {
(*mpTokenTable)[pV->tok].mpVariableInfo = pV->pOldDefinition;
}
}
mpArena->freeToMark(mark.mArenaMark);
}
bool isDefinedAtCurrentLevel(tokenid_t tok) {
VariableInfo* pV = (*mpTokenTable)[tok].mpVariableInfo;
return pV && pV->level == level();
}
bool isStructTagDefinedAtCurrentLevel(tokenid_t tok) {
VariableInfo* pV = (*mpTokenTable)[tok].mpStructInfo;
return pV && pV->level == level();
}
VariableInfo* add(tokenid_t tok) {
Token& token = (*mpTokenTable)[tok];
VariableInfo* pOldV = token.mpVariableInfo;
VariableInfo* pNewV =
(VariableInfo*) mpArena->alloc(sizeof(VariableInfo));
memset(pNewV, 0, sizeof(VariableInfo));
pNewV->tok = tok;
pNewV->level = level();
pNewV->pOldDefinition = pOldV;
token.mpVariableInfo = pNewV;
mStack.push_back(pNewV);
return pNewV;
}
VariableInfo* addStructTag(tokenid_t tok) {
Token& token = (*mpTokenTable)[tok];
VariableInfo* pOldS = token.mpStructInfo;
VariableInfo* pNewS =
(VariableInfo*) mpArena->alloc(sizeof(VariableInfo));
memset(pNewS, 0, sizeof(VariableInfo));
pNewS->tok = tok;
pNewS->level = level();
pNewS->isStructTag = true;
pNewS->pOldDefinition = pOldS;
token.mpStructInfo = pNewS;
mStack.push_back(pNewS);
return pNewS;
}
VariableInfo* add(Type* pType) {
VariableInfo* pVI = add(pType->id);
pVI->pType = pType;
return pVI;
}
void forEach(bool (*fn)(VariableInfo*, void*), void* context) {
for (size_t i = 0; i < mStack.size(); i++) {
if (! fn(mStack[i], context)) {
break;
}
}
}
private:
inline size_t level() {
return mLevelStack.size();
}
struct Mark {
Arena::Mark mArenaMark;
size_t mSymbolHead;
};
Arena* mpArena;
TokenTable* mpTokenTable;
Vector<VariableInfo*> mStack;
Vector<Mark> mLevelStack;
};
int ch; // Current input character, or EOF
tokenid_t tok; // token
intptr_t tokc; // token extra info
double tokd; // floating point constant value
int tokl; // token operator level
intptr_t rsym; // return symbol
Type* pReturnType; // type of the current function's return.
intptr_t loc; // local variable index
char* glo; // global variable index
String mTokenString;
bool mbSuppressMacroExpansion;
char* dptr; // Macro state: Points to macro text during macro playback.
int dch; // Macro state: Saves old value of ch during a macro playback.
char* pGlobalBase;
ACCSymbolLookupFn mpSymbolLookupFn;
void* mpSymbolLookupContext;
// Arena for the duration of the compile
Arena mGlobalArena;
// Arena for data that's only needed when compiling a single function
Arena mLocalArena;
Arena* mpCurrentArena;
TokenTable mTokenTable;
SymbolStack mGlobals;
SymbolStack mLocals;
SymbolStack* mpCurrentSymbolStack;
// Prebuilt types, makes things slightly faster.
Type* mkpInt; // int
Type* mkpShort; // short
Type* mkpChar; // char
Type* mkpVoid; // void
Type* mkpFloat;
Type* mkpDouble;
Type* mkpIntFn;
Type* mkpIntPtr;
Type* mkpCharPtr;
Type* mkpFloatPtr;
Type* mkpDoublePtr;
Type* mkpPtrIntFn;
InputStream* file;
int mLineNumber;
bool mbBumpLine;
ICodeBuf* pCodeBuf;
CodeGenerator* pGen;
String mErrorBuf;
String mPragmas;
int mPragmaStringCount;
int mCompileResult;
static const int ALLOC_SIZE = 99999;
static const int TOK_DUMMY = 1;
static const int TOK_NUM = 2;
static const int TOK_NUM_FLOAT = 3;
static const int TOK_NUM_DOUBLE = 4;
static const int TOK_OP_ASSIGNMENT = 5;
static const int TOK_OP_ARROW = 6;
// 3..255 are character and/or operators
// Keywords start at 0x100 and increase by 1
// Order has to match string list in "internKeywords".
enum {
TOK_KEYWORD = TokenTable::TOKEN_BASE,
TOK_INT = TOK_KEYWORD,
TOK_CHAR,
TOK_VOID,
TOK_IF,
TOK_ELSE,
TOK_WHILE,
TOK_BREAK,
TOK_RETURN,
TOK_FOR,
TOK_AUTO,
TOK_CASE,
TOK_CONST,
TOK_CONTINUE,
TOK_DEFAULT,
TOK_DO,
TOK_DOUBLE,
TOK_ENUM,
TOK_EXTERN,
TOK_FLOAT,
TOK_GOTO,
TOK_LONG,
TOK_REGISTER,
TOK_SHORT,
TOK_SIGNED,
TOK_SIZEOF,
TOK_STATIC,
TOK_STRUCT,
TOK_SWITCH,
TOK_TYPEDEF,
TOK_UNION,
TOK_UNSIGNED,
TOK_VOLATILE,
TOK__BOOL,
TOK__COMPLEX,
TOK__IMAGINARY,
TOK_INLINE,
TOK_RESTRICT,
// Symbols start after keywords
TOK_SYMBOL,
TOK_PRAGMA = TOK_SYMBOL,
TOK_DEFINE,
TOK_LINE
};
static const int LOCAL = 0x200;
static const int SYM_FORWARD = 0;
static const int SYM_DEFINE = 1;
/* tokens in string heap */
static const int TAG_TOK = ' ';
static const int OP_INCREMENT = 0;
static const int OP_DECREMENT = 1;
static const int OP_MUL = 2;
static const int OP_DIV = 3;
static const int OP_MOD = 4;
static const int OP_PLUS = 5;
static const int OP_MINUS = 6;
static const int OP_SHIFT_LEFT = 7;
static const int OP_SHIFT_RIGHT = 8;
static const int OP_LESS_EQUAL = 9;
static const int OP_GREATER_EQUAL = 10;
static const int OP_LESS = 11;
static const int OP_GREATER = 12;
static const int OP_EQUALS = 13;
static const int OP_NOT_EQUALS = 14;
static const int OP_LOGICAL_AND = 15;
static const int OP_LOGICAL_OR = 16;
static const int OP_BIT_AND = 17;
static const int OP_BIT_XOR = 18;
static const int OP_BIT_OR = 19;
static const int OP_BIT_NOT = 20;
static const int OP_LOGICAL_NOT = 21;
static const int OP_COUNT = 22;
/* Operators are searched from front, the two-character operators appear
* before the single-character operators with the same first character.
* @ is used to pad out single-character operators.
*/
static const char* operatorChars;
static const char operatorLevel[];
/* Called when we detect an internal problem. Does nothing in production.
*
*/
void internalError() {
* (char*) 0 = 0;
}
void assertImpl(bool isTrue, int line) {
if (!isTrue) {
LOGD("%d: assertion failed at line %s:%d.", mLineNumber, __FILE__, line);
internalError();
}
}
bool isSymbol(tokenid_t t) {
return t >= TOK_SYMBOL &&
((size_t) (t-TOK_SYMBOL)) < mTokenTable.size();
}
bool isSymbolOrKeyword(tokenid_t t) {
return t >= TOK_KEYWORD &&
((size_t) (t-TOK_KEYWORD)) < mTokenTable.size();
}
VariableInfo* VI(tokenid_t t) {
assert(isSymbol(t));
VariableInfo* pV = mTokenTable[t].mpVariableInfo;
if (pV && pV->tok != t) {
internalError();
}
return pV;
}
inline bool isDefined(tokenid_t t) {
return t >= TOK_SYMBOL && VI(t) != 0;
}
const char* nameof(tokenid_t t) {
assert(isSymbolOrKeyword(t));
return mTokenTable[t].pText;
}
void pdef(int t) {
mTokenString.append(t);
}
void inp() {
if (dptr) {
ch = *dptr++;
if (ch == 0) {
dptr = 0;
ch = dch;
}
} else {
if (mbBumpLine) {
mLineNumber++;
mbBumpLine = false;
}
ch = file->getChar();
if (ch == '\n') {
mbBumpLine = true;
}
}
#if 0
printf("ch='%c' 0x%x\n", ch, ch);
#endif
}
int isid() {
return isalnum(ch) | (ch == '_');
}
int decodeHex(int c) {
if (isdigit(c)) {
c -= '0';
} else if (c <= 'F') {
c = c - 'A' + 10;
} else {
c =c - 'a' + 10;
}
return c;
}
/* read a character constant, advances ch to after end of constant */
int getq() {
int val = ch;
if (ch == '\\') {
inp();
if (isoctal(ch)) {
// 1 to 3 octal characters.
val = 0;
for(int i = 0; i < 3; i++) {
if (isoctal(ch)) {
val = (val << 3) + ch - '0';
inp();
}
}
return val;
} else if (ch == 'x' || ch == 'X') {
// N hex chars
inp();
if (! isxdigit(ch)) {
error("'x' character escape requires at least one digit.");
} else {
val = 0;
while (isxdigit(ch)) {
val = (val << 4) + decodeHex(ch);
inp();
}
}
} else {
int val = ch;
switch (ch) {
case 'a':
val = '\a';
break;
case 'b':
val = '\b';
break;
case 'f':
val = '\f';
break;
case 'n':
val = '\n';
break;
case 'r':
val = '\r';
break;
case 't':
val = '\t';
break;
case 'v':
val = '\v';
break;
case '\\':
val = '\\';
break;
case '\'':
val = '\'';
break;
case '"':
val = '"';
break;
case '?':
val = '?';
break;
default:
error("Undefined character escape %c", ch);
break;
}
inp();
return val;
}
} else {
inp();
}
return val;
}
static bool isoctal(int ch) {
return ch >= '0' && ch <= '7';
}
bool acceptCh(int c) {
bool result = c == ch;
if (result) {
pdef(ch);
inp();
}
return result;
}
bool acceptDigitsCh() {
bool result = false;
while (isdigit(ch)) {
result = true;
pdef(ch);
inp();
}
return result;
}
void parseFloat() {
tok = TOK_NUM_DOUBLE;
// mTokenString already has the integral part of the number.
if(mTokenString.len() == 0) {
mTokenString.append('0');
}
acceptCh('.');
acceptDigitsCh();
if (acceptCh('e') || acceptCh('E')) {
acceptCh('-') || acceptCh('+');
acceptDigitsCh();
}
if (ch == 'f' || ch == 'F') {
tok = TOK_NUM_FLOAT;
inp();
} else if (ch == 'l' || ch == 'L') {
inp();
error("Long floating point constants not supported.");
}
char* pText = mTokenString.getUnwrapped();
char* pEnd = pText + strlen(pText);
char* pEndPtr = 0;
errno = 0;
if (tok == TOK_NUM_FLOAT) {
tokd = strtof(pText, &pEndPtr);
} else {
tokd = strtod(pText, &pEndPtr);
}
if (errno || pEndPtr != pEnd) {
error("Can't parse constant: %s", pText);
}
// fprintf(stderr, "float constant: %s (%d) %g\n", pText, tok, tokd);
}
void next() {
int l, a;
while (isspace(ch) | (ch == '#')) {
if (ch == '#') {
inp();
next();
if (tok == TOK_DEFINE) {
doDefine();
} else if (tok == TOK_PRAGMA) {
doPragma();
} else if (tok == TOK_LINE) {
doLine();
} else {
error("Unsupported preprocessor directive \"%s\"",
mTokenString.getUnwrapped());
}
}
inp();
}
tokl = 0;
tok = ch;
/* encode identifiers & numbers */
if (isdigit(ch) || ch == '.') {
// Start of a numeric constant. Could be integer, float, or
// double, won't know until we look further.
mTokenString.clear();
pdef(ch);
inp();
if (tok == '.' && !isdigit(ch)) {
goto done;
}
int base = 10;
if (tok == '0') {
if (ch == 'x' || ch == 'X') {
base = 16;
tok = TOK_NUM;
tokc = 0;
inp();
while ( isxdigit(ch) ) {
tokc = (tokc << 4) + decodeHex(ch);
inp();
}
} else if (isoctal(ch)){
base = 8;
tok = TOK_NUM;
tokc = 0;
while ( isoctal(ch) ) {
tokc = (tokc << 3) + (ch - '0');
inp();
}
}
} else if (isdigit(tok)){
acceptDigitsCh();
}
if (base == 10) {
if (tok == '.' || ch == '.' || ch == 'e' || ch == 'E') {
parseFloat();
} else {
// It's an integer constant
char* pText = mTokenString.getUnwrapped();
char* pEnd = pText + strlen(pText);
char* pEndPtr = 0;
errno = 0;
tokc = strtol(pText, &pEndPtr, base);
if (errno || pEndPtr != pEnd) {
error("Can't parse constant: %s %d %d", pText, base, errno);
}
tok = TOK_NUM;
}
}
} else if (isid()) {
mTokenString.clear();
while (isid()) {
pdef(ch);
inp();
}
tok = mTokenTable.intern(mTokenString.getUnwrapped(), mTokenString.len());
if (! mbSuppressMacroExpansion) {
// Is this a macro?
char* pMacroDefinition = mTokenTable[tok].mpMacroDefinition;
if (pMacroDefinition) {
// Yes, it is a macro
dptr = pMacroDefinition;
dch = ch;
inp();
next();
}
}
} else {
inp();
if (tok == '\'') {
tok = TOK_NUM;
tokc = getq();
if (ch != '\'') {
error("Expected a ' character, got %c", ch);
} else {
inp();
}
} else if ((tok == '/') & (ch == '*')) {
inp();
while (ch && ch != EOF) {
while (ch != '*' && ch != EOF)
inp();
inp();
if (ch == '/')
ch = 0;
}
if (ch == EOF) {
error("End of file inside comment.");
}
inp();
next();
} else if ((tok == '/') & (ch == '/')) {
inp();
while (ch && (ch != '\n') && (ch != EOF)) {
inp();
}
inp();
next();
} else if ((tok == '-') & (ch == '>')) {
inp();
tok = TOK_OP_ARROW;
} else {
const char* t = operatorChars;
int opIndex = 0;
while ((l = *t++) != 0) {
a = *t++;
tokl = operatorLevel[opIndex];
tokc = opIndex;
if ((l == tok) & ((a == ch) | (a == '@'))) {
#if 0
printf("%c%c -> tokl=%d tokc=0x%x\n",
l, a, tokl, tokc);
#endif
if (a == ch) {
inp();
tok = TOK_DUMMY; /* dummy token for double tokens */
}
/* check for op=, valid for * / % + - << >> & ^ | */
if (ch == '=' &&
((tokl >= 1 && tokl <= 3)
|| (tokl >=6 && tokl <= 8)) ) {
inp();
tok = TOK_OP_ASSIGNMENT;
}
break;
}
opIndex++;
}
if (l == 0) {
tokl = 0;
tokc = 0;
}
}
}
done: ;
#if 0
{
String buf;
decodeToken(buf, tok, true);
fprintf(stderr, "%s\n", buf.getUnwrapped());
}
#endif
}
void doDefine() {
mbSuppressMacroExpansion = true;
next();
mbSuppressMacroExpansion = false;
tokenid_t name = tok;
String* pName = new String();
if (ch == '(') {
delete pName;
error("Defines with arguments not supported");
return;
}
while (isspace(ch)) {
inp();
}
String value;
bool appendToValue = true;
while (ch != '\n' && ch != EOF) {
// Check for '//' comments.
if (appendToValue && ch == '/') {
inp();
if (ch == '/') {
appendToValue = false;
} else {
value.append('/');
}
}
if (appendToValue && ch != EOF) {
value.append(ch);
}
inp();
}
char* pDefn = (char*)mGlobalArena.alloc(value.len() + 1);
memcpy(pDefn, value.getUnwrapped(), value.len());
pDefn[value.len()] = 0;
mTokenTable[name].mpMacroDefinition = pDefn;
}
void doPragma() {
// # pragma name(val)
int state = 0;
while(ch != EOF && ch != '\n' && state < 10) {
switch(state) {
case 0:
if (isspace(ch)) {
inp();
} else {
state++;
}
break;
case 1:
if (isalnum(ch)) {
mPragmas.append(ch);
inp();
} else if (ch == '(') {
mPragmas.append(0);
inp();
state++;
} else {
state = 11;
}
break;
case 2:
if (isalnum(ch)) {
mPragmas.append(ch);
inp();
} else if (ch == ')') {
mPragmas.append(0);
inp();
state = 10;
} else {
state = 11;
}
break;
}
}
if(state != 10) {
error("Unexpected pragma syntax");
}
mPragmaStringCount += 2;
}
void doLine() {
// # line number { "filename "}
next();
if (tok != TOK_NUM) {
error("Expected a line-number");
} else {
mLineNumber = tokc-1; // The end-of-line will increment it.
}
while(ch != EOF && ch != '\n') {
inp();
}
}
virtual void verror(const char* fmt, va_list ap) {
mErrorBuf.printf("%ld: ", mLineNumber);
mErrorBuf.vprintf(fmt, ap);
mErrorBuf.printf("\n");
}
void skip(intptr_t c) {
if (!accept(c)) {
error("'%c' expected", c);
}
}
bool accept(intptr_t c) {
if (tok == c) {
next();
return true;
}
return false;
}
bool acceptStringLiteral() {
if (tok == '"') {
pGen->leaR0((int) glo, mkpCharPtr, ET_RVALUE);
// This while loop merges multiple adjacent string constants.
while (tok == '"') {
while (ch != '"' && ch != EOF) {
*allocGlobalSpace(1,1) = getq();
}
if (ch != '"') {
error("Unterminated string constant.");
}
inp();
next();
}
/* Null terminate */
*glo = 0;
/* align heap */
allocGlobalSpace(1,(char*) (((intptr_t) glo + 4) & -4) - glo);
return true;
}
return false;
}
void linkGlobal(tokenid_t t, bool isFunction) {
VariableInfo* pVI = VI(t);
void* n = NULL;
if (mpSymbolLookupFn) {
n = mpSymbolLookupFn(mpSymbolLookupContext, nameof(t));
}
if (pVI->pType == NULL) {
if (isFunction) {
pVI->pType = mkpIntFn;
} else {
pVI->pType = mkpInt;
}
}
pVI->pAddress = n;
}
void unaryOrAssignment() {
unary();
if (accept('=')) {
checkLVal();
pGen->pushR0();
expr();
pGen->forceR0RVal();
pGen->storeR0ToTOS();
} else if (tok == TOK_OP_ASSIGNMENT) {
int t = tokc;
next();
checkLVal();
pGen->pushR0();
pGen->forceR0RVal();
pGen->pushR0();
expr();
pGen->forceR0RVal();
pGen->genOp(t);
pGen->storeR0ToTOS();
}
}
/* Parse and evaluate a unary expression.
*/
void unary() {
tokenid_t t;
intptr_t a;
t = 0;
if (acceptStringLiteral()) {
// Nothing else to do.
} else {
int c = tokl;
a = tokc;
double ad = tokd;
t = tok;
next();
if (t == TOK_NUM) {
pGen->li(a);
} else if (t == TOK_NUM_FLOAT) {
// Align to 4-byte boundary
glo = (char*) (((intptr_t) glo + 3) & -4);
* (float*) glo = (float) ad;
pGen->loadFloat((int) glo, mkpFloat);
glo += 4;
} else if (t == TOK_NUM_DOUBLE) {
// Align to 8-byte boundary
glo = (char*) (((intptr_t) glo + 7) & -8);
* (double*) glo = ad;
pGen->loadFloat((int) glo, mkpDouble);
glo += 8;
} else if (c == 2) {
/* -, +, !, ~ */
unary();
pGen->forceR0RVal();
if (t == '!')
pGen->gUnaryCmp(a);
else if (t == '+') {
// ignore unary plus.
} else {
pGen->genUnaryOp(a);
}
} else if (c == 11) {
// pre increment / pre decrement
unary();
doIncDec(a == OP_INCREMENT, 0);
}
else if (t == '(') {
// It's either a cast or an expression
Type* pCast = acceptCastTypeDeclaration();
if (pCast) {
skip(')');
unary();
pGen->forceR0RVal();
pGen->castR0(pCast);
} else {
commaExpr();
skip(')');
}
} else if (t == '*') {
/* This is a pointer dereference.
*/
unary();
doPointer();
} else if (t == '&') {
unary();
doAddressOf();
} else if (t == EOF ) {
error("Unexpected EOF.");
} else if (t == ';') {
error("Unexpected ';'");
} else if (!checkSymbol(t)) {
// Don't have to do anything special here, the error
// message was printed by checkSymbol() above.
} else {
if (!isDefined(t)) {
mGlobals.add(t);
// printf("Adding new global function %s\n", nameof(t));
}
VariableInfo* pVI = VI(t);
int n = (intptr_t) pVI->pAddress;
/* forward reference: try our lookup function */
if (!n) {
linkGlobal(t, tok == '(');
n = (intptr_t) pVI->pAddress;
if (!n && tok != '(') {
error("Undeclared variable %s", nameof(t));
}
}
if (tok != '(') {
/* variable or function name */
if (!n) {
linkGlobal(t, false);
n = (intptr_t) pVI->pAddress;
if (!n) {
error("Undeclared variable %s", nameof(t));
}
}
}
// load a variable
Type* pVal;
ExpressionType et;
if (pVI->pType->tag == TY_ARRAY) {
pVal = pVI->pType;
et = ET_RVALUE;
} else {
pVal = createPtrType(pVI->pType);
et = ET_LVALUE;
}
if (n) {
int tag = pVal->pHead->tag;
if (tag == TY_FUNC) {
et = ET_RVALUE;
}
pGen->leaR0(n, pVal, et);
} else {
pVI->pForward = (void*) pGen->leaForward(
(int) pVI->pForward, pVal);
}
}
}
/* Now handle postfix operators */
for(;;) {
if (tokl == 11) {
// post inc / post dec
doIncDec(tokc == OP_INCREMENT, true);
next();
} else if (accept('[')) {
// Array reference
pGen->forceR0RVal();
pGen->pushR0();
commaExpr();
pGen->forceR0RVal();
pGen->genOp(OP_PLUS);
doPointer();
skip(']');
} else if (accept('.')) {
// struct element
pGen->forceR0RVal();
Type* pStruct = pGen->getR0Type();
if (pStruct->tag == TY_STRUCT) {
doStructMember(pStruct, true);
} else {
error("expected a struct value to the left of '.'");
}
} else if (accept(TOK_OP_ARROW)) {
pGen->forceR0RVal();
Type* pPtr = pGen->getR0Type();
if (pPtr->tag == TY_POINTER && pPtr->pHead->tag == TY_STRUCT) {
pGen->loadR0FromR0();
doStructMember(pPtr->pHead, false);
} else {
error("Expected a pointer to a struct to the left of '->'");
}
} else if (accept('(')) {
/* function call */
Type* pDecl = NULL;
VariableInfo* pVI = NULL;
Type* pFn = pGen->getR0Type();
assert(pFn->tag == TY_POINTER);
assert(pFn->pHead->tag == TY_FUNC);
pDecl = pFn->pHead;
pGen->pushR0();
Type* pArgList = pDecl->pTail;
bool varArgs = pArgList == NULL;
/* push args and invert order */
a = pGen->beginFunctionCallArguments();
int l = 0;
int argCount = 0;
while (tok != ')' && tok != EOF) {
if (! varArgs && !pArgList) {
error("Unexpected argument.");
}
expr();
pGen->forceR0RVal();
Type* pTargetType;
if (pArgList) {
pTargetType = pArgList->pHead;
pArgList = pArgList->pTail;
} else {
// This is a ... function, just pass arguments in their
// natural type.
pTargetType = pGen->getR0Type();
if (pTargetType->tag == TY_FLOAT) {
pTargetType = mkpDouble;
} else if (pTargetType->tag == TY_ARRAY) {
// Pass arrays by pointer.
pTargetType = pTargetType->pTail;
}
}
if (pTargetType->tag == TY_VOID) {
error("Can't pass void value for argument %d",
argCount + 1);
} else {
l += pGen->storeR0ToArg(l, pTargetType);
}
if (accept(',')) {
// fine
} else if ( tok != ')') {
error("Expected ',' or ')'");
}
argCount += 1;
}
if (! varArgs && pArgList) {
error("Expected more argument(s). Saw %d", argCount);
}
pGen->endFunctionCallArguments(pDecl, a, l);
skip(')');
pGen->callIndirect(l, pDecl);
pGen->adjustStackAfterCall(pDecl, l, true);
} else {
break;
}
}
}
void doStructMember(Type* pStruct, bool isDot) {
Type* pStructElement = lookupStructMember(pStruct, tok);
if (pStructElement) {
next();
pGen->addStructOffsetR0(pStructElement->length, createPtrType(pStructElement->pHead));
} else {
String buf;
decodeToken(buf, tok, true);
error("Expected a struct member to the right of '%s', got %s",
isDot ? "." : "->", buf.getUnwrapped());
}
}
void doIncDec(int isInc, int isPost) {
// R0 already has the lval
checkLVal();
int lit = isInc ? 1 : -1;
pGen->pushR0();
pGen->loadR0FromR0();
int tag = pGen->getR0Type()->tag;
if (!(tag == TY_INT || tag == TY_SHORT || tag == TY_CHAR ||
tag == TY_POINTER)) {
error("++/-- illegal for this type. %d", tag);
}
if (isPost) {
pGen->over();
pGen->pushR0();
pGen->li(lit);
pGen->genOp(OP_PLUS);
pGen->storeR0ToTOS();
pGen->popR0();
} else {
pGen->pushR0();
pGen->li(lit);
pGen->genOp(OP_PLUS);
pGen->over();
pGen->storeR0ToTOS();
pGen->popR0();
}
}
void doPointer() {
pGen->forceR0RVal();
Type* pR0Type = pGen->getR0Type();
if (pR0Type->tag != TY_POINTER) {
error("Expected a pointer type.");
} else {
if (pR0Type->pHead->tag != TY_FUNC) {
pGen->setR0ExpressionType(ET_LVALUE);
}
}
}
void doAddressOf() {
Type* pR0 = pGen->getR0Type();
bool isFuncPtr = pR0->tag == TY_POINTER && pR0->pHead->tag == TY_FUNC;
if ((! isFuncPtr) && pGen->getR0ExpressionType() != ET_LVALUE) {
error("Expected an lvalue");
}
Type* pR0Type = pGen->getR0Type();
pGen->setR0ExpressionType(ET_RVALUE);
}
/* Recursive descent parser for binary operations.
*/
void binaryOp(int level) {
intptr_t t, a;
t = 0;
if (level-- == 1)
unaryOrAssignment();
else {
binaryOp(level);
a = 0;
while (level == tokl) {
t = tokc;
next();
pGen->forceR0RVal();
if (level > 8) {
a = pGen->gtst(t == OP_LOGICAL_OR, a); /* && and || output code generation */
binaryOp(level);
} else {
pGen->pushR0();
binaryOp(level);
// Check for syntax error.
if (pGen->getR0Type() == NULL) {
// We failed to parse a right-hand argument.
// Push a dummy value so we don't fail
pGen->li(0);
}
pGen->forceR0RVal();
if ((level == 4) | (level == 5)) {
pGen->gcmp(t);
} else {
pGen->genOp(t);
}
}
}
/* && and || output code generation */
if (a && level > 8) {
pGen->forceR0RVal();
a = pGen->gtst(t == OP_LOGICAL_OR, a);
pGen->li(t != OP_LOGICAL_OR);
int b = pGen->gjmp(0);
pGen->gsym(a);
pGen->li(t == OP_LOGICAL_OR);
pGen->gsym(b);
}
}
}
void commaExpr() {
for(;;) {
expr();
if (!accept(',')) {
break;
}
}
}
void expr() {
binaryOp(11);
}
int test_expr() {
commaExpr();
pGen->forceR0RVal();
return pGen->gtst(0, 0);
}
void block(intptr_t l, bool outermostFunctionBlock) {
intptr_t a, n, t;
Type* pBaseType;
if ((pBaseType = acceptPrimitiveType())) {
/* declarations */
localDeclarations(pBaseType);
} else if (tok == TOK_IF) {
next();
skip('(');
a = test_expr();
skip(')');
block(l, false);
if (tok == TOK_ELSE) {
next();
n = pGen->gjmp(0); /* jmp */
pGen->gsym(a);
block(l, false);
pGen->gsym(n); /* patch else jmp */
} else {
pGen->gsym(a); /* patch if test */
}
} else if ((tok == TOK_WHILE) | (tok == TOK_FOR)) {
t = tok;
next();
skip('(');
if (t == TOK_WHILE) {
n = pCodeBuf->getPC(); // top of loop, target of "next" iteration
a = test_expr();
} else {
if (tok != ';')
commaExpr();
skip(';');
n = pCodeBuf->getPC();
a = 0;
if (tok != ';')
a = test_expr();
skip(';');
if (tok != ')') {
t = pGen->gjmp(0);
commaExpr();
pGen->gjmp(n - pCodeBuf->getPC() - pGen->jumpOffset());
pGen->gsym(t);
n = t + 4;
}
}
skip(')');
block((intptr_t) &a, false);
pGen->gjmp(n - pCodeBuf->getPC() - pGen->jumpOffset()); /* jmp */
pGen->gsym(a);
} else if (tok == '{') {
if (! outermostFunctionBlock) {
mLocals.pushLevel();
}
next();
while (tok != '}' && tok != EOF)
block(l, false);
skip('}');
if (! outermostFunctionBlock) {
mLocals.popLevel();
}
} else {
if (accept(TOK_RETURN)) {
if (tok != ';') {
commaExpr();
pGen->forceR0RVal();
if (pReturnType->tag == TY_VOID) {
error("Must not return a value from a void function");
} else {
pGen->convertR0(pReturnType);
}
} else {
if (pReturnType->tag != TY_VOID) {
error("Must specify a value here");
}
}
rsym = pGen->gjmp(rsym); /* jmp */
} else if (accept(TOK_BREAK)) {
*(int *) l = pGen->gjmp(*(int *) l);
} else if (tok != ';')
commaExpr();
skip(';');
}
}
static bool typeEqual(Type* a, Type* b) {
if (a == b) {
return true;
}
if (a == NULL || b == NULL) {
return false;
}
TypeTag at = a->tag;
if (at != b->tag) {
return false;
}
if (at == TY_POINTER) {
return typeEqual(a->pHead, b->pHead);
} else if (at == TY_ARRAY) {
return a->length == b->length && typeEqual(a->pHead, b->pHead);
} else if (at == TY_FUNC || at == TY_PARAM) {
return typeEqual(a->pHead, b->pHead)
&& typeEqual(a->pTail, b->pTail);
} else if (at == TY_STRUCT) {
return a->pHead == b->pHead;
}
return true;
}
Type* createType(TypeTag tag, Type* pHead, Type* pTail) {
assert(tag >= TY_INT && tag <= TY_PARAM);
Type* pType = (Type*) mpCurrentArena->alloc(sizeof(Type));
memset(pType, 0, sizeof(*pType));
pType->tag = tag;
pType->pHead = pHead;
pType->pTail = pTail;
return pType;
}
Type* createPtrType(Type* pType) {
return createType(TY_POINTER, pType, NULL);
}
/**
* Try to print a type in declaration order
*/
void decodeType(String& buffer, Type* pType) {
buffer.clear();
if (pType == NULL) {
buffer.appendCStr("null");
return;
}
decodeTypeImp(buffer, pType);
}
void decodeTypeImp(String& buffer, Type* pType) {
decodeTypeImpPrefix(buffer, pType);
decodeId(buffer, pType->id);
decodeTypeImpPostfix(buffer, pType);
}
void decodeId(String& buffer, tokenid_t id) {
if (id) {
String temp;
decodeToken(temp, id, false);
buffer.append(temp);
}
}
void decodeTypeImpPrefix(String& buffer, Type* pType) {
TypeTag tag = pType->tag;
if ((tag >= TY_INT && tag <= TY_DOUBLE) || tag == TY_STRUCT) {
switch (tag) {
case TY_INT:
buffer.appendCStr("int");
break;
case TY_SHORT:
buffer.appendCStr("short");
break;
case TY_CHAR:
buffer.appendCStr("char");
break;
case TY_VOID:
buffer.appendCStr("void");
break;
case TY_FLOAT:
buffer.appendCStr("float");
break;
case TY_DOUBLE:
buffer.appendCStr("double");
break;
case TY_STRUCT:
{
bool isStruct = (pType->pHead->alignment & 0x80000000) != 0;
buffer.appendCStr(isStruct ? "struct" : "union");
if (pType->pHead && pType->pHead->structTag) {
buffer.append(' ');
decodeId(buffer, pType->pHead->structTag);
}
}
break;
default:
break;
}
buffer.append(' ');
}
switch (tag) {
case TY_INT:
break;
case TY_SHORT:
break;
case TY_CHAR:
break;
case TY_VOID:
break;
case TY_FLOAT:
break;
case TY_DOUBLE:
break;
case TY_POINTER:
decodeTypeImpPrefix(buffer, pType->pHead);
if(pType->pHead && pType->pHead->tag == TY_FUNC) {
buffer.append('(');
}
buffer.append('*');
break;
case TY_ARRAY:
decodeTypeImpPrefix(buffer, pType->pHead);
break;
case TY_STRUCT:
break;
case TY_FUNC:
decodeTypeImp(buffer, pType->pHead);
break;
case TY_PARAM:
decodeTypeImp(buffer, pType->pHead);
break;
default:
String temp;
temp.printf("Unknown tag %d", pType->tag);
buffer.append(temp);
break;
}
}
void decodeTypeImpPostfix(String& buffer, Type* pType) {
TypeTag tag = pType->tag;
switch(tag) {
case TY_POINTER:
if(pType->pHead && pType->pHead->tag == TY_FUNC) {
buffer.append(')');
}
decodeTypeImpPostfix(buffer, pType->pHead);
break;
case TY_ARRAY:
{
String temp;
temp.printf("[%d]", pType->length);
buffer.append(temp);
}
break;
case TY_STRUCT:
if (pType->pHead->length >= 0) {
buffer.appendCStr(" {");
for(Type* pArg = pType->pTail; pArg; pArg = pArg->pTail) {
decodeTypeImp(buffer, pArg->pHead);
buffer.appendCStr(";");
}
buffer.append('}');
}
break;
case TY_FUNC:
buffer.append('(');
for(Type* pArg = pType->pTail; pArg; pArg = pArg->pTail) {
decodeTypeImp(buffer, pArg);
if (pArg->pTail) {
buffer.appendCStr(", ");
}
}
buffer.append(')');
break;
default:
break;
}
}
void printType(Type* pType) {
String buffer;
decodeType(buffer, pType);
fprintf(stderr, "%s\n", buffer.getUnwrapped());
}
Type* acceptPrimitiveType() {
Type* pType;
if (tok == TOK_INT) {
pType = mkpInt;
} else if (tok == TOK_SHORT) {
pType = mkpShort;
} else if (tok == TOK_CHAR) {
pType = mkpChar;
} else if (tok == TOK_VOID) {
pType = mkpVoid;
} else if (tok == TOK_FLOAT) {
pType = mkpFloat;
} else if (tok == TOK_DOUBLE) {
pType = mkpDouble;
} else if (tok == TOK_STRUCT || tok == TOK_UNION) {
return acceptStruct();
} else {
return NULL;
}
next();
return pType;
}
Type* acceptStruct() {
assert(tok == TOK_STRUCT || tok == TOK_UNION);
bool isStruct = tok == TOK_STRUCT;
next();
tokenid_t structTag = acceptSymbol();
bool isDeclaration = accept('{');
bool fail = false;
Type* pStructType = createType(TY_STRUCT, NULL, NULL);
if (structTag) {
Token* pToken = &mTokenTable[structTag];
VariableInfo* pStructInfo = pToken->mpStructInfo;
bool needToDeclare = !pStructInfo;
if (pStructInfo) {
if (isDeclaration) {
if (mpCurrentSymbolStack->isStructTagDefinedAtCurrentLevel(structTag)) {
if (pStructInfo->pType->pHead->length == -1) {
// we're filling in a forward declaration.
needToDeclare = false;
} else {
error("A struct with the same name is already defined at this level.");
fail = true;
}
} else {
needToDeclare = true;
}
}
if (!fail) {
assert(pStructInfo->isStructTag);
pStructType->pHead = pStructInfo->pType;
pStructType->pTail = pStructType->pHead->pTail;
}
}
if (needToDeclare) {
// This is a new struct name
pToken->mpStructInfo = mpCurrentSymbolStack->addStructTag(structTag);
pStructType = createType(TY_STRUCT, NULL, NULL);
pStructType->structTag = structTag;
pStructType->pHead = pStructType;
if (! isDeclaration) {
// A forward declaration
pStructType->length = -1;
}
pToken->mpStructInfo->pType = pStructType;
}
} else {
// An anonymous struct
pStructType->pHead = pStructType;
}
if (isDeclaration) {
size_t offset = 0;
size_t structSize = 0;
size_t structAlignment = 0;
Type** pParamHolder = & pStructType->pHead->pTail;
while (tok != '}' && tok != EOF) {
Type* pPrimitiveType = expectPrimitiveType();
if (pPrimitiveType) {
while (tok != ';' && tok != EOF) {
Type* pItem = acceptDeclaration(pPrimitiveType, true, false);
if (!pItem) {
break;
}
if (lookupStructMember(pStructType, pItem->id)) {
String buf;
decodeToken(buf, pItem->id, false);
error("Duplicate struct member %s", buf.getUnwrapped());
}
Type* pStructElement = createType(TY_PARAM, pItem, NULL);
size_t alignment = pGen->alignmentOf(pItem);
if (alignment > structAlignment) {
structAlignment = alignment;
}
size_t alignmentMask = alignment - 1;
offset = (offset + alignmentMask) & ~alignmentMask;
pStructElement->length = offset;
size_t size = pGen->sizeOf(pItem);
if (isStruct) {
offset += size;
structSize = offset;
} else {
if (size >= structSize) {
structSize = size;
}
}
*pParamHolder = pStructElement;
pParamHolder = &pStructElement->pTail;
accept(',');
}
skip(';');
} else {
// Some sort of syntax error, skip token and keep trying
next();
}
}
if (!fail) {
pStructType->pHead->length = structSize;
pStructType->pHead->alignment = structAlignment | (isStruct << 31);
}
skip('}');
}
if (fail) {
pStructType = NULL;
}
return pStructType;
}
Type* lookupStructMember(Type* pStruct, tokenid_t memberId) {
for(Type* pStructElement = pStruct->pHead->pTail; pStructElement; pStructElement = pStructElement->pTail) {
if (pStructElement->pHead->id == memberId) {
return pStructElement;
}
}
return NULL;
}
Type* acceptDeclaration(Type* pType, bool nameAllowed, bool nameRequired) {
tokenid_t declName = 0;
bool reportFailure = false;
pType = acceptDecl2(pType, declName, nameAllowed,
nameRequired, reportFailure);
if (declName) {
// Clone the parent type so we can set a unique ID
Type* pOldType = pType;
pType = createType(pType->tag, pType->pHead, pType->pTail);
*pType = *pOldType;
pType->id = declName;
} else if (nameRequired) {
error("Expected a variable name");
}
#if 0
fprintf(stderr, "Parsed a declaration: ");
printType(pType);
#endif
if (reportFailure) {
return NULL;
}
return pType;
}
Type* expectDeclaration(Type* pBaseType) {
bool nameRequired = pBaseType->tag != TY_STRUCT;
Type* pType = acceptDeclaration(pBaseType, true, nameRequired);
if (! pType) {
error("Expected a declaration");
}
return pType;
}
/* Used for accepting types that appear in casts */
Type* acceptCastTypeDeclaration() {
Type* pType = acceptPrimitiveType();
if (pType) {
pType = acceptDeclaration(pType, false, false);
}
return pType;
}
Type* expectCastTypeDeclaration() {
Type* pType = acceptCastTypeDeclaration();
if (! pType) {
error("Expected a declaration");
}
return pType;
}
Type* acceptDecl2(Type* pType, tokenid_t& declName,
bool nameAllowed, bool nameRequired,
bool& reportFailure) {
while (accept('*')) {
pType = createType(TY_POINTER, pType, NULL);
}
pType = acceptDecl3(pType, declName, nameAllowed, nameRequired,
reportFailure);
return pType;
}
Type* acceptDecl3(Type* pType, tokenid_t& declName,
bool nameAllowed, bool nameRequired,
bool& reportFailure) {
// direct-dcl :
// name
// (dcl)
// direct-dcl()
// direct-dcl[]
Type* pNewHead = NULL;
if (accept('(')) {
pNewHead = acceptDecl2(pNewHead, declName, nameAllowed,
nameRequired, reportFailure);
skip(')');
} else if ((declName = acceptSymbol()) != 0) {
if (nameAllowed == false && declName) {
error("Symbol %s not allowed here", nameof(declName));
reportFailure = true;
}
} else if (nameRequired && ! declName) {
String temp;
decodeToken(temp, tok, true);
error("Expected name. Got %s", temp.getUnwrapped());
reportFailure = true;
}
for(;;) {
if (accept('(')) {
// Function declaration
Type* pTail = acceptArgs(nameAllowed);
pType = createType(TY_FUNC, pType, pTail);
skip(')');
} if (accept('[')) {
if (tok != ']') {
if (tok != TOK_NUM || tokc <= 0) {
error("Expected positive integer constant");
} else {
Type* pDecayType = createPtrType(pType);
pType = createType(TY_ARRAY, pType, pDecayType);
pType->length = tokc;
}
next();
}
skip(']');
} else {
break;
}
}
if (pNewHead) {
Type* pA = pNewHead;
while (pA->pHead) {
pA = pA->pHead;
}
pA->pHead = pType;
pType = pNewHead;
}
return pType;
}
Type* acceptArgs(bool nameAllowed) {
Type* pHead = NULL;
Type* pTail = NULL;
for(;;) {
Type* pBaseArg = acceptPrimitiveType();
if (pBaseArg) {
Type* pArg = acceptDeclaration(pBaseArg, nameAllowed, false);
if (pArg) {
Type* pParam = createType(TY_PARAM, pArg, NULL);
if (!pHead) {
pHead = pParam;
pTail = pParam;
} else {
pTail->pTail = pParam;
pTail = pParam;
}
}
}
if (! accept(',')) {
break;
}
}
return pHead;
}
Type* expectPrimitiveType() {
Type* pType = acceptPrimitiveType();
if (!pType) {
String buf;
decodeToken(buf, tok, true);
error("Expected a type, got %s", buf.getUnwrapped());
}
return pType;
}
void checkLVal() {
if (pGen->getR0ExpressionType() != ET_LVALUE) {
error("Expected an lvalue");
}
}
void addGlobalSymbol(Type* pDecl) {
tokenid_t t = pDecl->id;
VariableInfo* pVI = VI(t);
if(pVI && pVI->pAddress) {
reportDuplicate(t);
}
mGlobals.add(pDecl);
}
void reportDuplicate(tokenid_t t) {
error("Duplicate definition of %s", nameof(t));
}
void addLocalSymbol(Type* pDecl) {
tokenid_t t = pDecl->id;
if (mLocals.isDefinedAtCurrentLevel(t)) {
reportDuplicate(t);
}
mLocals.add(pDecl);
}
bool checkUndeclaredStruct(Type* pBaseType) {
if (pBaseType->tag == TY_STRUCT && pBaseType->length < 0) {
String temp;
decodeToken(temp, pBaseType->structTag, false);
error("Undeclared struct %s", temp.getUnwrapped());
return true;
}
return false;
}
void localDeclarations(Type* pBaseType) {
intptr_t a;
while (pBaseType) {
while (tok != ';' && tok != EOF) {
Type* pDecl = expectDeclaration(pBaseType);
if (!pDecl) {
break;
}
if (!pDecl->id) {
break;
}
if (checkUndeclaredStruct(pDecl)) {
break;
}
addLocalSymbol(pDecl);
if (pDecl->tag == TY_FUNC) {
if (tok == '{') {
error("Nested functions are not allowed. Did you forget a '}' ?");
break;
}
// Else it's a forward declaration of a function.
} else {
int variableAddress = 0;
size_t alignment = pGen->alignmentOf(pDecl);
assert(alignment > 0);
size_t alignmentMask = ~ (alignment - 1);
size_t sizeOf = pGen->sizeOf(pDecl);
assert(sizeOf > 0);
loc = (loc + alignment - 1) & alignmentMask;
size_t alignedSize = (sizeOf + alignment - 1) & alignmentMask;
loc = loc + alignedSize;
variableAddress = -loc;
VI(pDecl->id)->pAddress = (void*) variableAddress;
if (accept('=')) {
/* assignment */
pGen->leaR0(variableAddress, createPtrType(pDecl), ET_LVALUE);
pGen->pushR0();
expr();
pGen->forceR0RVal();
pGen->storeR0ToTOS();
}
}
if (tok == ',')
next();
}
skip(';');
pBaseType = acceptPrimitiveType();
}
}
bool checkSymbol() {
return checkSymbol(tok);
}
void decodeToken(String& buffer, tokenid_t token, bool quote) {
if (token == EOF ) {
buffer.printf("EOF");
} else if (token == TOK_NUM) {
buffer.printf("numeric constant");
} else if (token >= 0 && token < 256) {
if (token < 32) {
buffer.printf("'\\x%02x'", token);
} else {
buffer.printf("'%c'", token);
}
} else {
if (quote) {
if (token >= TOK_KEYWORD && token < TOK_SYMBOL) {
buffer.printf("keyword \"%s\"", nameof(token));
} else {
buffer.printf("symbol \"%s\"", nameof(token));
}
} else {
buffer.printf("%s", nameof(token));
}
}
}
void printToken(tokenid_t token) {
String buffer;
decodeToken(buffer, token, true);
fprintf(stderr, "%s\n", buffer.getUnwrapped());
}
bool checkSymbol(tokenid_t token) {
bool result = token >= TOK_SYMBOL;
if (!result) {
String temp;
decodeToken(temp, token, true);
error("Expected symbol. Got %s", temp.getUnwrapped());
}
return result;
}
tokenid_t acceptSymbol() {
tokenid_t result = 0;
if (tok >= TOK_SYMBOL) {
result = tok;
next();
}
return result;
}
void globalDeclarations() {
mpCurrentSymbolStack = &mGlobals;
while (tok != EOF) {
Type* pBaseType = expectPrimitiveType();
if (!pBaseType) {
break;
}
Type* pDecl = expectDeclaration(pBaseType);
if (!pDecl) {
break;
}
if (!pDecl->id) {
skip(';');
continue;
}
if (checkUndeclaredStruct(pDecl)) {
skip(';');
continue;
}
if (! isDefined(pDecl->id)) {
addGlobalSymbol(pDecl);
}
VariableInfo* name = VI(pDecl->id);
if (name && name->pAddress) {
error("Already defined global %s", nameof(pDecl->id));
}
if (pDecl->tag < TY_FUNC) {
// it's a variable declaration
for(;;) {
if (name && !name->pAddress) {
name->pAddress = (int*) allocGlobalSpace(
pGen->alignmentOf(name->pType),
pGen->sizeOf(name->pType));
}
if (accept('=')) {
if (tok == TOK_NUM) {
if (name) {
* (int*) name->pAddress = tokc;
}
next();
} else {
error("Expected an integer constant");
}
}
if (!accept(',')) {
break;
}
pDecl = expectDeclaration(pBaseType);
if (!pDecl) {
break;
}
if (! isDefined(pDecl->id)) {
addGlobalSymbol(pDecl);
}
name = VI(pDecl->id);
}
skip(';');
} else {
// Function declaration
if (accept(';')) {
// forward declaration.
} else if (tok != '{') {
error("expected '{'");
} else {
mpCurrentArena = &mLocalArena;
mpCurrentSymbolStack = &mLocals;
if (name) {
/* patch forward references */
pGen->resolveForward((int) name->pForward);
/* put function address */
name->pAddress = (void*) pCodeBuf->getPC();
}
// Calculate stack offsets for parameters
mLocals.pushLevel();
intptr_t a = 8;
int argCount = 0;
for (Type* pP = pDecl->pTail; pP; pP = pP->pTail) {
Type* pArg = pP->pHead;
if (pArg->id) {
addLocalSymbol(pArg);
}
/* read param name and compute offset */
Type* pPassingType = passingType(pArg);
size_t alignment = pGen->alignmentOf(pPassingType);
a = (a + alignment - 1) & ~ (alignment-1);
if (pArg->id) {
VI(pArg->id)->pAddress = (void*) a;
}
a = a + pGen->sizeOf(pPassingType);
argCount++;
}
rsym = loc = 0;
pReturnType = pDecl->pHead;
a = pGen->functionEntry(pDecl);
block(0, true);
pGen->gsym(rsym);
pGen->functionExit(pDecl, a, loc);
mLocals.popLevel();
mpCurrentArena = &mGlobalArena;
mpCurrentSymbolStack = &mGlobals;
}
}
}
}
Type* passingType(Type* pType) {
switch (pType->tag) {
case TY_CHAR:
case TY_SHORT:
return mkpInt;
default:
return pType;
}
}
char* allocGlobalSpace(size_t alignment, size_t bytes) {
size_t base = (((size_t) glo) + alignment - 1) & ~(alignment-1);
size_t end = base + bytes;
if ((end - (size_t) pGlobalBase) > (size_t) ALLOC_SIZE) {
error("Global space exhausted");
assert(false);
return NULL;
}
char* result = (char*) base;
glo = (char*) end;
return result;
}
void cleanup() {
if (pGlobalBase != 0) {
free(pGlobalBase);
pGlobalBase = 0;
}
if (pGen) {
delete pGen;
pGen = 0;
}
if (pCodeBuf) {
delete pCodeBuf;
pCodeBuf = 0;
}
if (file) {
delete file;
file = 0;
}
}
// One-time initialization, when class is constructed.
void init() {
mpSymbolLookupFn = 0;
mpSymbolLookupContext = 0;
}
void clear() {
tok = 0;
tokc = 0;
tokl = 0;
ch = 0;
rsym = 0;
loc = 0;
glo = 0;
dptr = 0;
dch = 0;
file = 0;
pGlobalBase = 0;
pCodeBuf = 0;
pGen = 0;
mPragmaStringCount = 0;
mCompileResult = 0;
mLineNumber = 1;
mbBumpLine = false;
mbSuppressMacroExpansion = false;
}
void setArchitecture(const char* architecture) {
delete pGen;
pGen = 0;
delete pCodeBuf;
pCodeBuf = new CodeBuf();
if (architecture != NULL) {
#ifdef PROVIDE_ARM_CODEGEN
if (! pGen && strcmp(architecture, "arm") == 0) {
pGen = new ARMCodeGenerator();
pCodeBuf = new ARMCodeBuf(pCodeBuf);
}
#endif
#ifdef PROVIDE_X86_CODEGEN
if (! pGen && strcmp(architecture, "x86") == 0) {
pGen = new X86CodeGenerator();
}
#endif
if (!pGen ) {
error("Unknown architecture %s\n", architecture);
}
}
if (pGen == NULL) {
#if defined(DEFAULT_ARM_CODEGEN)
pGen = new ARMCodeGenerator();
pCodeBuf = new ARMCodeBuf(pCodeBuf);
#elif defined(DEFAULT_X86_CODEGEN)
pGen = new X86CodeGenerator();
#endif
}
if (pGen == NULL) {
error("No code generator defined.");
} else {
pGen->setErrorSink(this);
pGen->setTypes(mkpInt);
}
}
public:
struct args {
args() {
architecture = 0;
}
const char* architecture;
};
Compiler() {
init();
clear();
}
~Compiler() {
cleanup();
}
void registerSymbolCallback(ACCSymbolLookupFn pFn, ACCvoid* pContext) {
mpSymbolLookupFn = pFn;
mpSymbolLookupContext = pContext;
}
int compile(const char* text, size_t textLength) {
int result;
mpCurrentArena = &mGlobalArena;
createPrimitiveTypes();
cleanup();
clear();
mTokenTable.setArena(&mGlobalArena);
mGlobals.setArena(&mGlobalArena);
mGlobals.setTokenTable(&mTokenTable);
mLocals.setArena(&mLocalArena);
mLocals.setTokenTable(&mTokenTable);
internKeywords();
setArchitecture(NULL);
if (!pGen) {
return -1;
}
#ifdef PROVIDE_TRACE_CODEGEN
pGen = new TraceCodeGenerator(pGen);
#endif
pGen->setErrorSink(this);
if (pCodeBuf) {
pCodeBuf->init(ALLOC_SIZE);
}
pGen->init(pCodeBuf);
file = new TextInputStream(text, textLength);
pGlobalBase = (char*) calloc(1, ALLOC_SIZE);
glo = pGlobalBase;
inp();
next();
globalDeclarations();
checkForUndefinedForwardReferences();
result = pGen->finishCompile();
if (result == 0) {
if (mErrorBuf.len()) {
result = -2;
}
}
mCompileResult = result;
return result;
}
void createPrimitiveTypes() {
mkpInt = createType(TY_INT, NULL, NULL);
mkpShort = createType(TY_SHORT, NULL, NULL);
mkpChar = createType(TY_CHAR, NULL, NULL);
mkpVoid = createType(TY_VOID, NULL, NULL);
mkpFloat = createType(TY_FLOAT, NULL, NULL);
mkpDouble = createType(TY_DOUBLE, NULL, NULL);
mkpIntFn = createType(TY_FUNC, mkpInt, NULL);
mkpIntPtr = createPtrType(mkpInt);
mkpCharPtr = createPtrType(mkpChar);
mkpFloatPtr = createPtrType(mkpFloat);
mkpDoublePtr = createPtrType(mkpDouble);
mkpPtrIntFn = createPtrType(mkpIntFn);
}
void checkForUndefinedForwardReferences() {
mGlobals.forEach(static_ufrcFn, this);
}
static bool static_ufrcFn(VariableInfo* value, void* context) {
Compiler* pCompiler = (Compiler*) context;
return pCompiler->undefinedForwardReferenceCheck(value);
}
bool undefinedForwardReferenceCheck(VariableInfo* value) {
if (!value->pAddress && value->pForward) {
error("Undefined forward reference: %s",
mTokenTable[value->tok].pText);
}
return true;
}
/* Look through the symbol table to find a symbol.
* If found, return its value.
*/
void* lookup(const char* name) {
if (mCompileResult == 0) {
tokenid_t tok = mTokenTable.intern(name, strlen(name));
VariableInfo* pVariableInfo = VI(tok);
if (pVariableInfo) {
return pVariableInfo->pAddress;
}
}
return NULL;
}
void getPragmas(ACCsizei* actualStringCount,
ACCsizei maxStringCount, ACCchar** strings) {
int stringCount = mPragmaStringCount;
if (actualStringCount) {
*actualStringCount = stringCount;
}
if (stringCount > maxStringCount) {
stringCount = maxStringCount;
}
if (strings) {
char* pPragmas = mPragmas.getUnwrapped();
while (stringCount-- > 0) {
*strings++ = pPragmas;
pPragmas += strlen(pPragmas) + 1;
}
}
}
void getProgramBinary(ACCvoid** base, ACCsizei* length) {
*base = pCodeBuf->getBase();
*length = (ACCsizei) pCodeBuf->getSize();
}
char* getErrorMessage() {
return mErrorBuf.getUnwrapped();
}
};
const char* Compiler::operatorChars =
"++--*@/@%@+@-@<<>><=>=<@>@==!=&&||&@^@|@~@!@";
const char Compiler::operatorLevel[] =
{11, 11, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4,
5, 5, /* ==, != */
9, 10, /* &&, || */
6, 7, 8, /* & ^ | */
2, 2 /* ~ ! */
};
#ifdef PROVIDE_X86_CODEGEN
const int Compiler::X86CodeGenerator::operatorHelper[] = {
0x1, // ++
0xff, // --
0xc1af0f, // *
0xf9f79991, // /
0xf9f79991, // % (With manual assist to swap results)
0xc801, // +
0xd8f7c829, // -
0xe0d391, // <<
0xf8d391, // >>
0xe, // <=
0xd, // >=
0xc, // <
0xf, // >
0x4, // ==
0x5, // !=
0x0, // &&
0x1, // ||
0xc821, // &
0xc831, // ^
0xc809, // |
0xd0f7, // ~
0x4 // !
};
#endif
struct ACCscript {
ACCscript() {
text = 0;
textLength = 0;
accError = ACC_NO_ERROR;
}
~ACCscript() {
delete text;
}
void registerSymbolCallback(ACCSymbolLookupFn pFn, ACCvoid* pContext) {
compiler.registerSymbolCallback(pFn, pContext);
}
void setError(ACCenum error) {
if (accError == ACC_NO_ERROR && error != ACC_NO_ERROR) {
accError = error;
}
}
ACCenum getError() {
ACCenum result = accError;
accError = ACC_NO_ERROR;
return result;
}
Compiler compiler;
char* text;
int textLength;
ACCenum accError;
};
extern "C"
ACCscript* accCreateScript() {
return new ACCscript();
}
extern "C"
ACCenum accGetError( ACCscript* script ) {
return script->getError();
}
extern "C"
void accDeleteScript(ACCscript* script) {
delete script;
}
extern "C"
void accRegisterSymbolCallback(ACCscript* script, ACCSymbolLookupFn pFn,
ACCvoid* pContext) {
script->registerSymbolCallback(pFn, pContext);
}
extern "C"
void accScriptSource(ACCscript* script,
ACCsizei count,
const ACCchar ** string,
const ACCint * length) {
int totalLength = 0;
for(int i = 0; i < count; i++) {
int len = -1;
const ACCchar* s = string[i];
if (length) {
len = length[i];
}
if (len < 0) {
len = strlen(s);
}
totalLength += len;
}
delete script->text;
char* text = new char[totalLength + 1];
script->text = text;
script->textLength = totalLength;
char* dest = text;
for(int i = 0; i < count; i++) {
int len = -1;
const ACCchar* s = string[i];
if (length) {
len = length[i];
}
if (len < 0) {
len = strlen(s);
}
memcpy(dest, s, len);
dest += len;
}
text[totalLength] = '\0';
#ifdef DEBUG_SAVE_INPUT_TO_FILE
LOGD("Saving input to file...");
int counter;
char path[PATH_MAX];
for (counter = 0; counter < 4096; counter++) {
sprintf(path, DEBUG_DUMP_PATTERN, counter);
if(access(path, F_OK) != 0) {
break;
}
}
if (counter < 4096) {
LOGD("Saving input to file %s", path);
FILE* fd = fopen(path, "w");
if (fd) {
fwrite(text, totalLength, 1, fd);
fclose(fd);
LOGD("Saved input to file %s", path);
} else {
LOGD("Could not save. errno: %d", errno);
}
}
#endif
}
extern "C"
void accCompileScript(ACCscript* script) {
int result = script->compiler.compile(script->text, script->textLength);
if (result) {
script->setError(ACC_INVALID_OPERATION);
}
}
extern "C"
void accGetScriptiv(ACCscript* script,
ACCenum pname,
ACCint * params) {
switch (pname) {
case ACC_INFO_LOG_LENGTH:
*params = 0;
break;
}
}
extern "C"
void accGetScriptInfoLog(ACCscript* script,
ACCsizei maxLength,
ACCsizei * length,
ACCchar * infoLog) {
char* message = script->compiler.getErrorMessage();
int messageLength = strlen(message) + 1;
if (length) {
*length = messageLength;
}
if (infoLog && maxLength > 0) {
int trimmedLength = maxLength < messageLength ?
maxLength : messageLength;
memcpy(infoLog, message, trimmedLength);
infoLog[trimmedLength] = 0;
}
}
extern "C"
void accGetScriptLabel(ACCscript* script, const ACCchar * name,
ACCvoid ** address) {
void* value = script->compiler.lookup(name);
if (value) {
*address = value;
} else {
script->setError(ACC_INVALID_VALUE);
}
}
extern "C"
void accGetPragmas(ACCscript* script, ACCsizei* actualStringCount,
ACCsizei maxStringCount, ACCchar** strings){
script->compiler.getPragmas(actualStringCount, maxStringCount, strings);
}
extern "C"
void accGetProgramBinary(ACCscript* script,
ACCvoid** base, ACCsizei* length) {
script->compiler.getProgramBinary(base, length);
}
} // namespace acc