| /* | |
| * 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> | |
| #include <sys/mman.h> | |
| #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) | |
| #define MAP_ANONYMOUS MAP_ANON | |
| #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) { | |
| clear(); | |
| 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; | |
| } | |
| inline size_t size() { | |
| return mUsed; | |
| } | |
| void clear() { | |
| if (mpBase) { | |
| size_t used = mUsed; | |
| for(size_t i = 0; i < used; i++) { | |
| mpBase[i].~E(); | |
| } | |
| } | |
| mUsed = 0; | |
| } | |
| 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_UNKNOWN = -1, | |
| 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 | |
| }; | |
| enum StorageClass { | |
| SC_DEFAULT, // 0 | |
| SC_AUTO, // 1 | |
| SC_REGISTER, // 2 | |
| SC_STATIC, // 3 | |
| SC_EXTERN, // 4 | |
| SC_TYPEDEF // 5 | |
| }; | |
| struct Type { | |
| TypeTag tag; | |
| StorageClass storageClass; | |
| 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) { | |
| munmap(pProgramBase, mSize); | |
| 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*) mmap(NULL, size, | |
| PROT_EXEC | PROT_READ | PROT_WRITE, | |
| MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| 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; | |
| } | |
| setR0Type(pDestType); | |
| } | |
| 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; | |
| } | |
| setR0Type(pTargetType); | |
| } | |
| 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; | |
| }; | |
| struct MacroState { | |
| tokenid_t name; // Name of the current macro we are expanding | |
| char* dptr; // point to macro text during macro playback | |
| int dch; // Saves old value of ch during a macro playback | |
| }; | |
| #define MACRO_NESTING_MAX 32 | |
| MacroState macroState[MACRO_NESTING_MAX]; | |
| int macroLevel; // -1 means not playing any macro. | |
| 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* 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; | |
| /* 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() { | |
| // Close any totally empty macros. We leave them on the stack until now | |
| // so that we know which macros are being expanded when checking if the | |
| // last token in the macro is a macro that's already being expanded. | |
| while (macroLevel >= 0 && macroState[macroLevel].dptr == NULL) { | |
| macroLevel--; | |
| } | |
| if (macroLevel >= 0) { | |
| ch = *macroState[macroLevel].dptr++; | |
| if (ch == 0) { | |
| ch = macroState[macroLevel].dch; | |
| macroState[macroLevel].dptr = NULL; // This macro's done | |
| } | |
| } 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); | |
| } | |
| bool currentlyBeingExpanded(tokenid_t id) { | |
| for (int i = 0; i <= macroLevel; i++) { | |
| if (macroState[macroLevel].name == id) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| 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 && !currentlyBeingExpanded(tok)) { | |
| // Yes, it is a macro | |
| #if 0 | |
| printf("Expanding macro %s -> %s", | |
| mTokenString.getUnwrapped(), pMacroDefinition); | |
| #endif | |
| if (macroLevel >= MACRO_NESTING_MAX-1) { | |
| error("Too many levels of macro recursion."); | |
| } else { | |
| macroLevel++; | |
| macroState[macroLevel].name = tok; | |
| macroState[macroLevel].dptr = pMacroDefinition; | |
| macroState[macroLevel].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; | |
| if (ch == '(') { | |
| 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; | |
| #if 0 | |
| { | |
| String buf; | |
| decodeToken(buf, name, true); | |
| fprintf(stderr, "define %s = \"%s\"\n", buf.getUnwrapped(), pDefn); | |
| } | |
| #endif | |
| } | |
| 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(); | |
| if (pFn->tag == TY_POINTER && 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 { | |
| error("Expected a function value to left of '('."); | |
| } | |
| } 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* breakLabel, intptr_t continueAddress, bool outermostFunctionBlock) { | |
| intptr_t a, n, t; | |
| Type* pBaseType; | |
| if ((pBaseType = acceptPrimitiveType(true))) { | |
| /* declarations */ | |
| localDeclarations(pBaseType); | |
| } else if (tok == TOK_IF) { | |
| next(); | |
| skip('('); | |
| a = test_expr(); | |
| skip(')'); | |
| block(breakLabel, continueAddress, false); | |
| if (tok == TOK_ELSE) { | |
| next(); | |
| n = pGen->gjmp(0); /* jmp */ | |
| pGen->gsym(a); | |
| block(breakLabel, continueAddress, 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(&a, n, 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(breakLabel, continueAddress, 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)) { | |
| if (breakLabel) { | |
| *breakLabel = pGen->gjmp(*breakLabel); | |
| } else { | |
| error("break statement must be within a for, do, while, or switch statement"); | |
| } | |
| } else if (accept(TOK_CONTINUE)) { | |
| if (continueAddress) { | |
| pGen->gjmp(continueAddress - pCodeBuf->getPC() - pGen->jumpOffset()); | |
| } else { | |
| error("continue statement must be within a for, do, or while statement"); | |
| } | |
| } 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_UNKNOWN && tag <= TY_PARAM); | |
| Type* pType = (Type*) mpCurrentArena->alloc(sizeof(Type)); | |
| memset(pType, 0, sizeof(*pType)); | |
| pType->storageClass = SC_DEFAULT; | |
| 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()); | |
| } | |
| void insertTypeSpecifier(Type** ppType, TypeTag tag) { | |
| if (! *ppType) { | |
| *ppType = createType(tag, NULL, NULL); | |
| } else { | |
| if ((*ppType)->tag != TY_UNKNOWN) { | |
| error("Only one type specifier allowed."); | |
| } else { | |
| (*ppType)->tag = tag; | |
| } | |
| } | |
| } | |
| void insertStorageClass(Type** ppType, StorageClass storageClass) { | |
| if (! *ppType) { | |
| *ppType = createType(TY_UNKNOWN, NULL, NULL); | |
| } | |
| if ((*ppType)->storageClass != SC_DEFAULT) { | |
| error("Only one storage class allowed."); | |
| } else { | |
| (*ppType)->storageClass = storageClass; | |
| } | |
| } | |
| Type* acceptPrimitiveType(bool allowStorageClass) { | |
| Type* pType = NULL; | |
| for (bool keepGoing = true; keepGoing;) { | |
| switch(tok) { | |
| case TOK_AUTO: | |
| insertStorageClass(&pType, SC_AUTO); | |
| break; | |
| case TOK_REGISTER: | |
| insertStorageClass(&pType, SC_REGISTER); | |
| break; | |
| case TOK_STATIC: | |
| insertStorageClass(&pType, SC_STATIC); | |
| break; | |
| case TOK_EXTERN: | |
| insertStorageClass(&pType, SC_EXTERN); | |
| break; | |
| case TOK_TYPEDEF: | |
| insertStorageClass(&pType, SC_TYPEDEF); | |
| break; | |
| case TOK_INT: | |
| insertTypeSpecifier(&pType, TY_INT); | |
| break; | |
| case TOK_SHORT: | |
| insertTypeSpecifier(&pType, TY_SHORT); | |
| break; | |
| case TOK_CHAR: | |
| insertTypeSpecifier(&pType, TY_CHAR); | |
| break; | |
| case TOK_VOID: | |
| insertTypeSpecifier(&pType, TY_VOID); | |
| break; | |
| case TOK_FLOAT: | |
| insertTypeSpecifier(&pType, TY_FLOAT); | |
| break; | |
| case TOK_DOUBLE: | |
| insertTypeSpecifier(&pType, TY_DOUBLE); | |
| break; | |
| case TOK_STRUCT: | |
| case TOK_UNION: | |
| { | |
| insertTypeSpecifier(&pType, TY_STRUCT); | |
| bool isStruct = (tok == TOK_STRUCT); | |
| next(); | |
| pType = acceptStruct(pType, isStruct); | |
| keepGoing = false; | |
| } | |
| break; | |
| default: | |
| // Is it a typedef? | |
| if (isSymbol(tok)) { | |
| VariableInfo* pV = VI(tok); | |
| if (pV && pV->pType->storageClass == SC_TYPEDEF) { | |
| if (! pType) { | |
| pType = createType(TY_UNKNOWN, NULL, NULL); | |
| } | |
| StorageClass storageClass = pType->storageClass; | |
| *pType = *pV->pType; | |
| pType->storageClass = storageClass; | |
| } else { | |
| keepGoing = false; | |
| } | |
| } else { | |
| keepGoing = false; | |
| } | |
| } | |
| if (keepGoing) { | |
| next(); | |
| } | |
| } | |
| if (pType) { | |
| if (pType->tag == TY_UNKNOWN) { | |
| pType->tag = TY_INT; | |
| } | |
| if (allowStorageClass) { | |
| switch(pType->storageClass) { | |
| case SC_AUTO: error("auto not supported."); break; | |
| case SC_REGISTER: error("register not supported."); break; | |
| case SC_STATIC: error("static not supported."); break; | |
| case SC_EXTERN: error("extern not supported."); break; | |
| default: break; | |
| } | |
| } else { | |
| if (pType->storageClass != SC_DEFAULT) { | |
| error("An explicit storage class is not allowed in this type declaration"); | |
| } | |
| } | |
| } | |
| return pType; | |
| } | |
| Type* acceptStruct(Type* pStructType, bool isStruct) { | |
| tokenid_t structTag = acceptSymbol(); | |
| bool isDeclaration = accept('{'); | |
| bool fail = false; | |
| 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); | |
| StorageClass storageClass = pStructType->storageClass; | |
| pStructType = createType(TY_STRUCT, NULL, NULL); | |
| pStructType->structTag = structTag; | |
| pStructType->pHead = pStructType; | |
| pStructType->storageClass = storageClass; | |
| 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(false); | |
| 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; | |
| StorageClass storageClass = pType->storageClass; | |
| 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; | |
| pType->storageClass = storageClass; | |
| } 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(false); | |
| 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(false); | |
| 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(bool allowStorageClass) { | |
| Type* pType = acceptPrimitiveType(allowStorageClass); | |
| 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 if (pDecl->storageClass != SC_TYPEDEF) { | |
| 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(true); | |
| } | |
| } | |
| 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 %d(0x%x)", tokc, tokc); | |
| } else if (token == TOK_NUM_FLOAT) { | |
| buffer.printf("numeric constant float %g", tokd); | |
| } else if (token == TOK_NUM_DOUBLE) { | |
| buffer.printf("numeric constant double %g", tokd); | |
| } 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(true); | |
| 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 (pDecl->storageClass == SC_TYPEDEF) { | |
| // Do not allocate storage. | |
| } else { | |
| 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, 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; | |
| macroLevel = -1; | |
| 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 | |