diff options
Diffstat (limited to 'libacc')
38 files changed, 9013 insertions, 0 deletions
diff --git a/libacc/Android.mk b/libacc/Android.mk new file mode 100644 index 0000000..f77e2b3 --- /dev/null +++ b/libacc/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# Shared library for target +# ======================================================== + +LOCAL_MODULE:= libacc +LOCAL_SRC_FILES := acc.cpp + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += disassem.cpp +endif + +LOCAL_SHARED_LIBRARIES := libdl libcutils + +include $(BUILD_SHARED_LIBRARY) + +# Shared library for host +# ======================================================== + +include $(CLEAR_VARS) +LOCAL_MODULE:= libacc +LOCAL_SRC_FILES := acc.cpp + +LOCAL_CFLAGS := -O0 -g + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += disassem.cpp +endif + +LOCAL_STATIC_LIBRARIES := libcutils +LOCAL_LDLIBS := -ldl + +include $(BUILD_HOST_SHARED_LIBRARY) + +# Build children +# ======================================================== + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libacc/FEATURES b/libacc/FEATURES new file mode 100644 index 0000000..1a44415 --- /dev/null +++ b/libacc/FEATURES @@ -0,0 +1,65 @@ + +Supported C language subset (read joint example 'otccex.c' to have + an introduction to OTCC dialect): + + - Expressions: + + * binary operators, by decreasing priority order: '*' '/' '%', + '+' '-', '>>' '<<', '<' '<=' '>' '>=', '==' '!=', '&', + '^', '|', '=', '&&', '||'. + + * '&&' and '||' have the same semantics as C : left to right + evaluation and early exit. + + * Parenthesis are supported. + + * Unary operators: '&', '*' (pointer indirection), '-' + (negation), '+', '!', '~', post fixed '++' and '--'. + + * Pointer indirection ('*') only works with explicit cast to + 'char *', 'int *' or 'int (*)()' (function pointer). + + * '++', '--', and unary '&' can only be used with variable + lvalue (left value). + + * '=' can only be used with variable or '*' (pointer + indirection) lvalue. + + * Function calls are supported with standard i386 calling + convention. Function pointers are supported with explicit + cast. Functions can be used before being declared. + + - Types: only signed integer ('int') variables and functions can + be declared. Variables cannot be initialized in + declarations. Only old K&R function declarations are parsed + (implicit integer return value and no types on arguments). + + - Any function or variable from the libc can be used because OTCC + uses the libc dynamic linker to resolve undefined symbols. + + - Instructions: blocks ('{' '}') are supported as in C. 'if' and + 'else' can be used for tests. The 'while' and 'for' C constructs + are supported for loops. 'break' can be used to exit + loops. 'return' is used for the return value of a function. + + - Identifiers are parsed the same way as C. Local variables are + handled, but there is no local name space (not a problem if + different names are used for local and global variables). + + - Numbers can be entered in decimal, hexadecimal ('0x' or '0X' + prefix), or octal ('0' prefix). + + - '#define' is supported without function like arguments. No macro + recursion is tolerated. Other preprocessor directives are + ignored. + + - C Strings and C character constants are supported. Only '\n', + '\"', '\'' and '\\' escapes are recognized. + + - Both C comments ( /* */ ) and C++ comments ( // ... end-of-line ) can be used. + + - No error is displayed if an incorrect program is given. + + - Memory: the code, data, and symbol sizes are limited to 100KB + (it can be changed in the source code). + diff --git a/libacc/LICENSE b/libacc/LICENSE new file mode 100644 index 0000000..aea41e0 --- /dev/null +++ b/libacc/LICENSE @@ -0,0 +1,21 @@ + Obfuscated Tiny C Compiler + + Copyright (C) 2001-2003 Fabrice Bellard + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product and its documentation + *is* required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + diff --git a/libacc/MODULE_LICENSE_BSD_LIKE b/libacc/MODULE_LICENSE_BSD_LIKE new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libacc/MODULE_LICENSE_BSD_LIKE diff --git a/libacc/acc.cpp b/libacc/acc.cpp new file mode 100644 index 0000000..2a7d66e --- /dev/null +++ b/libacc/acc.cpp @@ -0,0 +1,5094 @@ +/* + * 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. + * + */ + +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <cutils/hashmap.h> + +#if defined(__i386__) +#include <sys/mman.h> +#endif + +#if defined(__arm__) +#include <unistd.h> +#endif + +#if defined(__arm__) +#define DEFAULT_ARM_CODEGEN +#define PROVIDE_ARM_CODEGEN +#elif defined(__i386__) +#define DEFAULT_X86_CODEGEN +#define PROVIDE_X86_CODEGEN +#elif defined(__x86_64__) +#define DEFAULT_X64_CODEGEN +#define PROVIDE_X64_CODEGEN +#endif + +#ifdef PROVIDE_ARM_CODEGEN +#include "disassem.h" +#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 ENABLE_ARM_DISASSEMBLY +// #define PROVIDE_TRACE_CODEGEN + +namespace acc { + +// Subset of STL vector. +template<class E> class Vector { + public: + Vector() { + mpBase = 0; + mUsed = 0; + mSize = 0; + } + + ~Vector() { + if (mpBase) { + for(size_t i = 0; i < mUsed; i++) { + mpBase[mUsed].~E(); + } + free(mpBase); + } + } + + inline E& operator[](size_t i) { + return mpBase[i]; + } + + inline E& front() { + return mpBase[0]; + } + + inline E& back() { + return mpBase[mUsed - 1]; + } + + void pop_back() { + mUsed -= 1; + mpBase[mUsed].~E(); + } + + void push_back(const E& item) { + * ensure(1) = item; + } + + size_t size() { + return mUsed; + } + +private: + E* ensure(int n) { + size_t newUsed = mUsed + n; + if (newUsed > mSize) { + size_t newSize = mSize * 2 + 10; + if (newSize < newUsed) { + newSize = newUsed; + } + mpBase = (E*) realloc(mpBase, sizeof(E) * newSize); + mSize = newSize; + } + E* result = mpBase + mUsed; + mUsed = newUsed; + return result; + } + + E* mpBase; + size_t mUsed; + size_t mSize; +}; + +class ErrorSink { +public: + void error(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + verror(fmt, ap); + va_end(ap); + } + + virtual ~ErrorSink() {} + virtual void verror(const char* fmt, va_list ap) = 0; +}; + +class Compiler : public ErrorSink { + typedef int tokenid_t; + enum TypeTag { + TY_INT, TY_CHAR, TY_VOID, TY_FLOAT, TY_DOUBLE, + TY_POINTER, TY_FUNC, TY_PARAM + }; + + struct Type { + TypeTag tag; + tokenid_t id; // For function arguments + Type* pHead; + Type* pTail; + }; + + class CodeBuf { + char* ind; // Output code pointer + char* pProgramBase; + ErrorSink* mErrorSink; + int mSize; + bool mOverflowed; + + void release() { + if (pProgramBase != 0) { + free(pProgramBase); + pProgramBase = 0; + } + } + + bool check(int n) { + int newSize = ind - pProgramBase + n; + bool overflow = newSize > mSize; + if (overflow && !mOverflowed) { + mOverflowed = true; + if (mErrorSink) { + mErrorSink->error("Code too large: %d bytes", newSize); + } + } + return overflow; + } + + public: + CodeBuf() { + pProgramBase = 0; + ind = 0; + mErrorSink = 0; + mSize = 0; + mOverflowed = false; + } + + ~CodeBuf() { + release(); + } + + void init(int size) { + release(); + mSize = size; + pProgramBase = (char*) calloc(1, size); + ind = pProgramBase; + } + + void setErrorSink(ErrorSink* pErrorSink) { + mErrorSink = pErrorSink; + } + + int o4(int n) { + if(check(4)) { + return 0; + } + intptr_t result = (intptr_t) ind; + * (int*) ind = n; + ind += 4; + return result; + } + + /* + * Output a byte. Handles all values, 0..ff. + */ + void ob(int n) { + if(check(1)) { + return; + } + *ind++ = n; + } + + inline void* getBase() { + return (void*) pProgramBase; + } + + intptr_t getSize() { + return ind - pProgramBase; + } + + intptr_t getPC() { + return (intptr_t) ind; + } + }; + + /** + * 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(CodeBuf* pCodeBuf) { + this->pCodeBuf = pCodeBuf; + pCodeBuf->setErrorSink(mErrorSink); + } + + virtual void setErrorSink(ErrorSink* pErrorSink) { + mErrorSink = pErrorSink; + if (pCodeBuf) { + pCodeBuf->setErrorSink(mErrorSink); + } + } + + 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, Type* pType) = 0; + + /* Load floating point value from global address. */ + virtual void loadFloat(int address, 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, Type* pResultType) = 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, Type* pResultType) = 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. + */ + virtual void pushR0() = 0; + + /* Store R0 to the address stored in TOS. + * The TOS is popped. + * pPointerType is the type of the pointer (of the input R0). + */ + virtual void storeR0ToTOS(Type* pPointerType) = 0; + + /* Load R0 from the address stored in R0. + * pPointerType is the type of the pointer (of the input R0). + */ + virtual void loadR0FromR0(Type* pPointerType) = 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. + */ + virtual void leaR0(int ea, Type* pPointerType) = 0; + + /* Store R0 to a variable. + * If ea <= LOCAL, then this is a local variable, or an + * argument, addressed relative to FP. + * else it is an absolute global address. + */ + virtual void storeR0(int ea, Type* pType) = 0; + + /* load R0 from a variable. + * If ea <= LOCAL, then this is a local variable, or an + * argument, addressed relative to FP. + * else it is an absolute global address. + * If isIncDec is true, then the stored variable's value + * should be post-incremented or post-decremented, based + * on the value of op. + */ + virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) = 0; + + /** + * Convert R0 to the given type. + */ + virtual void convertR0(Type* pType) = 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 using PC-relative addressing. t is the PC-relative + * address of the function. It has already been adjusted for the + * architectural jump offset, so just store it as-is. + */ + virtual void callRelative(int t, 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; + + /* Print a disassembly of the assembled code to out. Return + * non-zero if there is an error. + */ + virtual int disassemble(FILE* out) = 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; + + /* + * 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; + + /** + * Stack argument size of this data type. + */ + virtual size_t stackSizeOf(Type* pType) = 0; + + virtual Type* getR0Type() { + return mExpressionStack.back(); + } + + protected: + /* + * Output a byte. Handles all values, 0..ff. + */ + void ob(int n) { + pCodeBuf->ob(n); + } + + intptr_t o4(int data) { + return pCodeBuf->o4(data); + } + + intptr_t getBase() { + return (intptr_t) pCodeBuf->getBase(); + } + + intptr_t getPC() { + return pCodeBuf->getPC(); + } + + intptr_t getSize() { + return pCodeBuf->getSize(); + } + + void error(const char* fmt,...) { + va_list ap; + va_start(ap, fmt); + mErrorSink->verror(fmt, ap); + va_end(ap); + } + + void assert(bool test) { + if (!test) { + * (char*) 0 = 0; + error("code generator assertion failed."); + } + } + + void setR0Type(Type* pType) { + mExpressionStack.back() = pType; + } + + Type* getTOSType() { + return mExpressionStack[mExpressionStack.size()-2]; + } + + void pushType() { + mExpressionStack.push_back(NULL); + } + + 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_VOID, TY_FLOAT, TY_DOUBLE, TY_INT, + TY_VOID, TY_VOID}; + return collapsedTag[tag]; + } + + TypeTag collapseTypeR0() { + return collapseType(getR0Type()->tag); + } + + bool isFloatType(Type* pType) { + return isFloatTag(pType->tag); + } + + bool isFloatTag(TypeTag tag) { + return tag == TY_FLOAT || tag == TY_DOUBLE; + } + + Type* mkpInt; + + private: + Vector<Type*> mExpressionStack; + CodeBuf* pCodeBuf; + ErrorSink* mErrorSink; + }; + +#ifdef PROVIDE_ARM_CODEGEN + + class ARMCodeGenerator : public CodeGenerator { + public: + ARMCodeGenerator() {} + + virtual ~ARMCodeGenerator() {} + + /* returns address to patch with local variable size + */ + virtual int functionEntry(Type* pDecl) { + LOG_API("functionEntry(%d);\n", 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); + return 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. + } + + virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) { + LOG_API("functionExit(%d, %d, %d);\n", argCount, localVariableAddress, 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; + + // 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, Type* pType) { + liReg(t, 0); + setR0Type(pType); + } + + 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: + o4(0xE5900000); // ldr r0, [r0] + break; + case TY_DOUBLE: + o4(0xE1C000D0); // ldrd r0, [r0] + break; + default: + assert(false); + break; + } + } + + virtual int gjmp(int t) { + LOG_API("gjmp(%d);\n", t); + return o4(0xEA000000 | encodeAddress(t)); // b .L33 + } + + /* l = 0: je, l == 1: jne */ + virtual int gtst(bool l, int t) { + LOG_API("gtst(%d, %d);\n", l, t); + Type* pR0Type = getR0Type(); + TypeTag tagR0 = pR0Type->tag; + switch(tagR0) { + case TY_FLOAT: + callRuntime((void*) runtime_is_non_zero_f); + break; + case TY_DOUBLE: + callRuntime((void*) runtime_is_non_zero_d); + break; + default: + break; + } + o4(0xE3500000); // cmp r0,#0 + int branch = l ? 0x1A000000 : 0x0A000000; // bne : beq + return o4(branch | encodeAddress(t)); + } + + virtual void gcmp(int op, Type* pResultType) { + LOG_API("gcmp(%d);\n", op); + Type* pR0Type = getR0Type(); + Type* pTOSType = getTOSType(); + TypeTag tagR0 = collapseType(pR0Type->tag); + TypeTag tagTOS = collapseType(pTOSType->tag); + if (tagR0 == TY_INT && tagTOS == TY_INT) { + o4(0xE8BD0002); // ldmfd sp!,{r1} + mStackUse -= 4; + 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; + } + popType(); + } else if (tagR0 == TY_DOUBLE || tagTOS == TY_DOUBLE) { + setupDoubleArgs(); + 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; + } + } else { + setupFloatArgs(); + 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; + } + } + setR0Type(pResultType); + } + + virtual void genOp(int op) { + LOG_API("genOp(%d);\n", 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 = tagR0 == TY_POINTER; + bool isPtrTOS = tagTOS == TY_POINTER; + 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(0xE8BD0002); // ldmfd sp!,{r1} + o4(0xE0410000); // sub r0,r1,r0 + popType(); + setR0Type(mkpInt); + int size = sizeOf(pR0Type->pHead); + if (size != 1) { + pushR0(); + li(size, mkpInt); + // 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 = isPtrR0 ? pR0Type : pTOSType; + o4(0xE8BD0002); // ldmfd sp!,{r1} + 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; + } + popType(); + setR0Type(pPtrType); + } + } else { + o4(0xE8BD0002); // ldmfd sp!,{r1} + mStackUse -= 4; + 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; + } + popType(); + } + } else { + Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType; + if (pResultType->tag == TY_DOUBLE) { + setupDoubleArgs(); + switch(op) { + case OP_MUL: + callRuntime((void*) runtime_op_mul_dd); + break; + case OP_DIV: + callRuntime((void*) runtime_op_div_dd); + break; + case OP_PLUS: + callRuntime((void*) runtime_op_add_dd); + break; + case OP_MINUS: + callRuntime((void*) runtime_op_sub_dd); + break; + default: + error("Unsupported binary floating operation %d\n", op); + break; + } + } else { + setupFloatArgs(); + switch(op) { + case OP_MUL: + callRuntime((void*) runtime_op_mul_ff); + break; + case OP_DIV: + callRuntime((void*) runtime_op_div_ff); + break; + case OP_PLUS: + callRuntime((void*) runtime_op_add_ff); + break; + case OP_MINUS: + callRuntime((void*) runtime_op_sub_ff); + break; + default: + error("Unsupported binary floating operation %d\n", op); + break; + } + } + setR0Type(pResultType); + } + } + + virtual void gUnaryCmp(int op, Type* pResultType) { + LOG_API("gUnaryCmp(%d);\n", 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: + callRuntime((void*) runtime_is_zero_f); + break; + case TY_DOUBLE: + callRuntime((void*) runtime_is_zero_d); + break; + default: + error("gUnaryCmp unsupported type"); + break; + } + } + setR0Type(pResultType); + } + + virtual void genUnaryOp(int op) { + LOG_API("genOp(%d);\n", 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) { + callRuntime((void*) runtime_op_neg_f); + } else { + callRuntime((void*) runtime_op_neg_d); + } + 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() { + LOG_API("pushR0();\n"); + Type* pR0Type = getR0Type(); + TypeTag r0ct = collapseType(pR0Type->tag); + if (r0ct != TY_DOUBLE) { + o4(0xE92D0001); // stmfd sp!,{r0} + mStackUse += 4; + } else { + o4(0xE92D0003); // stmfd sp!,{r0,r1} + mStackUse += 8; + } + pushType(); + LOG_STACK("pushR0: %d\n", mStackUse); + } + + virtual void storeR0ToTOS(Type* pPointerType) { + LOG_API("storeR0ToTOS(%d);\n", isInt); + assert(pPointerType->tag == TY_POINTER); + o4(0xE8BD0004); // ldmfd sp!,{r2} + popType(); + mStackUse -= 4; + switch (pPointerType->pHead->tag) { + case TY_INT: + case TY_FLOAT: + o4(0xE5820000); // str r0, [r2] + break; + case TY_CHAR: + o4(0xE5C20000); // strb r0, [r2] + break; + case TY_DOUBLE: + o4(0xE1C200F0); // strd r0, [r2] + break; + default: + error("storeR0ToTOS: unimplemented type"); + break; + } + } + + virtual void loadR0FromR0(Type* pPointerType) { + LOG_API("loadR0FromR0(%d);\n", pPointerType); + assert(pPointerType->tag == TY_POINTER); + switch (pPointerType->pHead->tag) { + case TY_INT: + case TY_FLOAT: + o4(0xE5900000); // ldr r0, [r0] + break; + case TY_CHAR: + o4(0xE5D00000); // ldrb r0, [r0] + break; + case TY_DOUBLE: + o4(0xE1C000D0); // ldrd r0, [r0] + break; + default: + error("loadR0FromR0: unimplemented type"); + break; + } + setR0Type(pPointerType->pHead); + } + + virtual void leaR0(int ea, Type* pPointerType) { + LOG_API("leaR0(%d);\n", ea); + if (ea > -LOCAL && ea < LOCAL) { + // Local, fp relative + if (ea < -1023 || ea > 1023 || ((ea & 3) != 0)) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE24B0F00 | (0xff & ((-ea) >> 2))); // sub r0, fp, #ea + } else { + o4(0xE28B0F00 | (0xff & (ea >> 2))); // add r0, fp, #ea + } + } else { + // Global, absolute. + o4(0xE59F0000); // ldr r0, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word 0 + // .L99: + } + setR0Type(pPointerType); + } + + virtual void storeR0(int ea, Type* pType) { + LOG_API("storeR0(%d);\n", ea); + convertR0(pType); + TypeTag tag = pType->tag; + switch (tag) { + case TY_CHAR: + if (ea > -LOCAL && ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE54B0000 | (0xfff & (-ea))); // strb r0, [fp,#-ea] + } else { + o4(0xE5CB0000 | (0xfff & ea)); // strb r0, [fp,#ea] + } + } else{ + // Global, absolute + o4(0xE59F1000); // ldr r1, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word 0 + o4(0xE5C10000); // .L99: strb r0, [r1] + } + break; + case TY_POINTER: + case TY_INT: + case TY_FLOAT: + if (ea > -LOCAL && ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE50B0000 | (0xfff & (-ea))); // str r0, [fp,#-ea] + } else { + o4(0xE58B0000 | (0xfff & ea)); // str r0, [fp,#ea] + } + } else{ + // Global, absolute + o4(0xE59F1000); // ldr r1, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word 0 + o4(0xE5810000); // .L99: str r0, [r1] + } + break; + case TY_DOUBLE: + if ((ea & 0x7) != 0) { + error("double address is not aligned: %d", ea); + } + if (ea > -LOCAL && ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE50B0000 | (0xfff & (4-ea))); // str r0, [fp,#-ea] + o4(0xE50B1000 | (0xfff & (-ea))); // str r1, [fp,#-ea+4] +#if 0 + // strd doesn't seem to work. Is encoding wrong? + } else if (ea < 0) { + o4(0xE1CB000F | ((0xff & (-ea)) << 4)); // strd r0, [fp,#-ea] + } else if (ea < 256) { + o4(0xE14B000F | ((0xff & ea) << 4)); // strd r0, [fp,#ea] +#endif + } else { + o4(0xE58B0000 | (0xfff & ea)); // str r0, [fp,#ea] + o4(0xE58B1000 | (0xfff & (ea + 4))); // str r1, [fp,#ea+4] + } + } else{ + // Global, absolute + o4(0xE59F2000); // ldr r2, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word 0 + o4(0xE1C200F0); // .L99: strd r0, [r2] + } + break; + default: + error("Unable to store to type %d", tag); + break; + } + } + + virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) { + LOG_API("loadR0(%d, %d, %d, %d);\n", ea, isIncDec, op, pType); + TypeTag tag = pType->tag; + switch (tag) { + case TY_CHAR: + if (ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE55B0000 | (0xfff & (-ea))); // ldrb r0, [fp,#-ea] + } else { + o4(0xE5DB0000 | (0xfff & ea)); // ldrb r0, [fp,#ea] + } + } else { + // Global, absolute + o4(0xE59F2000); // ldr r2, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word ea + o4(0xE5D20000); // .L99: ldrb r0, [r2] + } + + if (isIncDec) { + error("inc/dec not implemented for char."); + } + break; + case TY_POINTER: + case TY_INT: + case TY_FLOAT: + if (ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE51B0000 | (0xfff & (-ea))); // ldr r0, [fp,#-ea] + } else { + o4(0xE59B0000 | (0xfff & ea)); // ldr r0, [fp,#ea] + } + } else { + // Global, absolute + o4(0xE59F2000); // ldr r2, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word ea + o4(0xE5920000); // .L99: ldr r0, [r2] + } + + if (isIncDec) { + if (tag == TY_INT) { + switch (op) { + case OP_INCREMENT: + o4(0xE2801001); // add r1, r0, #1 + break; + case OP_DECREMENT: + o4(0xE2401001); // sub r1, r0, #1 + break; + default: + error("unknown opcode: %d", op); + } + if (ea < LOCAL) { + // Local, fp relative + // Don't need range check, was already checked above + if (ea < 0) { + o4(0xE50B1000 | (0xfff & (-ea))); // str r1, [fp,#-ea] + } else { + o4(0xE58B1000 | (0xfff & ea)); // str r1, [fp,#ea] + } + } else{ + // Global, absolute + // r2 is already set up from before. + o4(0xE5821000); // str r1, [r2] + } + } + else { + error("inc/dec not implemented for float."); + } + } + break; + case TY_DOUBLE: + if ((ea & 0x7) != 0) { + error("double address is not aligned: %d", ea); + } + if (ea < LOCAL) { + // Local, fp relative + if (ea < -4095 || ea > 4095) { + error("Offset out of range: %08x", ea); + } + if (ea < 0) { + o4(0xE51B0000 | (0xfff & (4-ea))); // ldr r0, [fp,#-ea] + o4(0xE51B1000 | (0xfff & (-ea))); // ldr r1, [fp,#-ea+4] + } else { + o4(0xE59B0000 | (0xfff & ea)); // ldr r0, [fp,#ea] + o4(0xE59B1000 | (0xfff & (ea+4))); // ldr r0, [fp,#ea+4] + } + } else { + // Global, absolute + o4(0xE59F2000); // ldr r2, .L1 + o4(0xEA000000); // b .L99 + o4(ea); // .L1: .word ea + o4(0xE1C200D0); // .L99: ldrd r0, [r2] + } + break; + default: + error("Unable to load type %d", tag); + break; + } + setR0Type(pType); + } + + virtual void convertR0(Type* pType){ + Type* pR0Type = getR0Type(); + 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) { + callRuntime((void*) runtime_int_to_float); + } else { + assert(destTag == TY_DOUBLE); + callRuntime((void*) runtime_int_to_double); + } + } else if (r0Tag == TY_FLOAT) { + if (destTag == TY_INT) { + callRuntime((void*) runtime_float_to_int); + } else { + assert(destTag == TY_DOUBLE); + callRuntime((void*) runtime_float_to_double); + } + } else { + assert (r0Tag == TY_DOUBLE); + if (destTag == TY_INT) { + callRuntime((void*) runtime_double_to_int); + } else { + assert(destTag == TY_FLOAT); + callRuntime((void*) runtime_double_to_float); + } + } + } + setR0Type(pType); + } + + virtual int beginFunctionCallArguments() { + LOG_API("beginFunctionCallArguments();\n"); + return o4(0xE24DDF00); // Placeholder + } + + virtual size_t storeR0ToArg(int l, Type* pArgType) { + LOG_API("storeR0ToArg(%d);\n", l); + convertR0(pArgType); + Type* pR0Type = getR0Type(); + TypeTag r0ct = collapseType(pR0Type->tag); + 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; + } + } + + virtual void endFunctionCallArguments(Type* pDecl, int a, int l) { + LOG_API("endFunctionCallArguments(0x%08x, %d);\n", a, 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); + } + * (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) { + LOG_API("callForward(%d);\n", symbol); + setR0Type(pFunc->pHead); + // Forward calls are always short (local) + return o4(0xEB000000 | encodeAddress(symbol)); + } + + virtual void callRelative(int t, Type* pFunc) { + LOG_API("callRelative(%d);\n", t); + setR0Type(pFunc->pHead); + int abs = t + getPC() + jumpOffset(); + LOG_API("abs=%d (0x%08x)\n", abs, abs); + if (t >= - (1 << 25) && t < (1 << 25)) { + o4(0xEB000000 | encodeAddress(t)); + } else { + // Long call. + o4(0xE59FC000); // ldr r12, .L1 + o4(0xEA000000); // b .L99 + o4(t - 12); // .L1: .word 0 + o4(0xE08CC00F); // .L99: add r12,pc + o4(0xE12FFF3C); // blx r12 + } + } + + virtual void callIndirect(int l, Type* pFunc) { + LOG_API("callIndirect(%d);\n", l); + setR0Type(pFunc->pHead); + 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 + } + + virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) { + LOG_API("adjustStackAfterCall(%d, %d);\n", l, 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) { + LOG_API("gsym(0x%x)\n", t); + int n; + int base = getBase(); + int pc = getPC(); + LOG_API("pc = 0x%x\n", pc); + 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; + } + } + + 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 + } + + virtual int disassemble(FILE* out) { +#ifdef ENABLE_ARM_DISASSEMBLY + disasmOut = out; + disasm_interface_t di; + di.di_readword = disassemble_readword; + di.di_printaddr = disassemble_printaddr; + di.di_printf = disassemble_printf; + + int base = getBase(); + int pc = getPC(); + for(int i = base; i < pc; i += 4) { + fprintf(out, "%08x: %08x ", i, *(int*) i); + ::disasm(&di, i, 0); + } +#endif + return 0; + } + + /** + * alignment (in bytes) for this type of data + */ + virtual size_t alignmentOf(Type* pType){ + switch(pType->tag) { + case TY_DOUBLE: + return 8; + 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_CHAR: + return 1; + default: + return 0; + case TY_FLOAT: + return 4; + case TY_DOUBLE: + return 8; + case TY_POINTER: + return 4; + } + } + + virtual size_t stackSizeOf(Type* pType) { + switch(pType->tag) { + case TY_DOUBLE: + return 8; + default: + return 4; + } + } + + private: + static FILE* disasmOut; + + static u_int + disassemble_readword(u_int address) + { + return(*((u_int *)address)); + } + + static void + disassemble_printaddr(u_int address) + { + fprintf(disasmOut, "0x%08x", address); + } + + static void + disassemble_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(disasmOut, fmt, ap); + va_end(ap); + } + + 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; + } + + /* Pop TOS to R1 + * 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); + callRuntime((void*) runtime_int_to_float); + } + if (tagTOS != TY_FLOAT) { + assert(tagTOS == TY_INT); + assert(tagR0 == TY_FLOAT); + 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 + } else { + // Pop TOS + o4(0xE8BD0002); // ldmfd sp!,{r1} + } + mStackUse -= 4; + popType(); + } + + /* Pop TOS into R2..R3 + * 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) { + callRuntime((void*) runtime_int_to_double); + } else { + assert(tagR0 == TY_FLOAT); + callRuntime((void*) runtime_float_to_double); + } + } + if (tagTOS != TY_DOUBLE) { + 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 + mStackUse -= 4; + } else { + o4(0xE8BD000C); // ldmfd sp!,{r2,r3} + mStackUse -= 8; + } + popType(); + } + + void liReg(int t, int reg) { + assert(reg >= 0 && reg < 16); + int rN = (reg & 0xf) << 12; + if (t >= 0 && t < 255) { + o4((0xE3A00000 + t) | rN); // mov rN, #0 + } else if (t >= -256 && t < 0) { + // mvn means move constant ^ ~0 + o4((0xE3E00001 - t) | rN); // mvn rN, #0 + } else { + o4(0xE51F0000 | rN); // ldr rN, .L3 + o4(0xEA000000); // b .L99 + o4(t); // .L3: .word 0 + // .L99: + } + } + + 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; + } + + // 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; + } + + 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, Type* pType) { + oad(0xb8, i); /* mov $xx, %eax */ + setR0Type(pType); + } + + 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 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* pResultType) { + 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, NULL); + 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(pResultType); + } + + 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 = tagR0 == TY_POINTER; + bool isPtrTOS = tagTOS == TY_POINTER; + 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, mkpInt); + // 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 = isPtrR0 ? 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, Type* pResultType) { + 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, NULL); + 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(pResultType); + } + + 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 storeR0ToTOS(Type* pPointerType) { + assert(pPointerType->tag == TY_POINTER); + Type* pTargetType = pPointerType->pHead; + convertR0(pTargetType); + o(0x59); /* pop %ecx */ + popType(); + switch (pTargetType->tag) { + case TY_INT: + o(0x0189); /* movl %eax/%al, (%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; + default: + error("storeR0ToTOS: unsupported type"); + break; + } + } + + virtual void loadR0FromR0(Type* pPointerType) { + assert(pPointerType->tag == TY_POINTER); + switch (pPointerType->pHead->tag) { + case TY_INT: + o2(0x008b); /* mov (%eax), %eax */ + 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; + default: + error("loadR0FromR0: unsupported type"); + break; + } + setR0Type(pPointerType->pHead); + } + + virtual void leaR0(int ea, Type* pPointerType) { + gmov(10, ea); /* leal EA, %eax */ + setR0Type(pPointerType); + } + + virtual void storeR0(int ea, Type* pType) { + TypeTag tag = pType->tag; + convertR0(pType); + switch (tag) { + case TY_CHAR: + if (ea < -LOCAL || ea > LOCAL) { + oad(0xa2, ea); // movb %al,ea + } else { + oad(0x8588, ea); // movb %al,ea(%ebp) + } + break; + case TY_INT: + case TY_POINTER: + gmov(6, ea); /* mov %eax, EA */ + break; + case TY_FLOAT: + if (ea < -LOCAL || ea > LOCAL) { + oad(0x1dd9, ea); // fstps ea + } else { + oad(0x9dd9, ea); // fstps ea(%ebp) + } + break; + case TY_DOUBLE: + if (ea < -LOCAL || ea > LOCAL) { + oad(0x1ddd, ea); // fstpl ea + } else { + oad(0x9ddd, ea); // fstpl ea(%ebp) + } + break; + default: + error("Unable to store to type %d", tag); + break; + } + } + + virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) { + TypeTag tag = pType->tag; + switch (tag) { + case TY_CHAR: + if (ea < -LOCAL || ea > LOCAL) { + oad(0x05BE0F, ea); // movsbl ea,%eax + } else { + oad(0x85BE0F, ea); // movsbl ea(%ebp),%eax + } + if (isIncDec) { + error("inc/dec not implemented for char."); + } + break; + case TY_INT: + case TY_POINTER: + if (tag == TY_CHAR) { + } else { + gmov(8, ea); /* mov EA, %eax */ + } + if (isIncDec) { + /* Implement post-increment or post decrement. + */ + gmov(0, ea); /* 83 ADD */ + o(decodeOp(op)); + } + break; + case TY_FLOAT: + if (ea < -LOCAL || ea > LOCAL) { + oad(0x05d9, ea); // flds ea + } else { + oad(0x85d9, ea); // flds ea(%ebp) + } + if (isIncDec) { + error("inc/dec not implemented for float."); + } + break; + case TY_DOUBLE: + if (ea < -LOCAL || ea > LOCAL) { + oad(0x05dd, ea); // fldl ea + } else { + oad(0x85dd, ea); // fldl ea(%ebp) + } + if (isIncDec) { + error("inc/dec not implemented for double."); + } + break; + default: + error("Unable to load type %d", tag); + break; + } + setR0Type(pType); + } + + virtual void convertR0(Type* pType){ + Type* pR0Type = getR0Type(); + if (pR0Type == NULL) { + assert(false); + setR0Type(pType); + return; + } + 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) { + setR0Type(pFunc->pHead); + return psym(0xe8, symbol); /* call xxx */ + } + + virtual void callRelative(int t, Type* pFunc) { + setR0Type(pFunc->pHead); + psym(0xe8, t); /* call xxx */ + } + + virtual void callIndirect(int l, Type* pFunc) { + setR0Type(pFunc->pHead); + oad(0x2494ff, l); /* call *xxx(%esp) */ + } + + virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) { + if (isIndirect) { + l += 4; + } + if (l > 0) { + oad(0xc481, l); /* add $xxx, %esp */ + } + } + + virtual int jumpOffset() { + return 5; + } + + virtual int disassemble(FILE* out) { + return 0; + } + + /* 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; + } + } + + 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){ + 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_CHAR: + return 1; + default: + return 0; + case TY_FLOAT: + return 4; + case TY_DOUBLE: + return 8; + case TY_POINTER: + return 4; + } + } + + virtual size_t stackSizeOf(Type* pType) { + switch(pType->tag) { + case TY_DOUBLE: + return 8; + default: + return 4; + } + } + + 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(CodeBuf* 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, Type* pType) { + fprintf(stderr, "li(%d)\n", t); + mpBase->li(t, pType); + } + + virtual void loadFloat(int address, Type* pType) { + fprintf(stderr, "loadFloat(%d, type=%d)\n", address, pType->tag); + mpBase->loadFloat(address, 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, Type* pResultType) { + fprintf(stderr, "gcmp(%d, pResultType)\n", op); + mpBase->gcmp(op, pResultType); + } + + virtual void genOp(int op) { + fprintf(stderr, "genOp(%d)\n", op); + mpBase->genOp(op); + } + + + virtual void gUnaryCmp(int op, Type* pResultType) { + fprintf(stderr, "gUnaryCmp(%d, pResultType)\n", op); + mpBase->gUnaryCmp(op, pResultType); + } + + 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 storeR0ToTOS(Type* pPointerType) { + fprintf(stderr, "storeR0ToTOS(%d)\n", pPointerType->pHead->tag); + mpBase->storeR0ToTOS(pPointerType); + } + + virtual void loadR0FromR0(Type* pPointerType) { + fprintf(stderr, "loadR0FromR0(%d)\n", pPointerType->pHead->tag); + mpBase->loadR0FromR0(pPointerType); + } + + virtual void leaR0(int ea, Type* pPointerType) { + fprintf(stderr, "leaR0(%d)\n", ea); + mpBase->leaR0(ea, pPointerType); + } + + virtual void storeR0(int ea, Type* pType) { + fprintf(stderr, "storeR0(%d, pType=%d)\n", ea, pType->tag); + mpBase->storeR0(ea, pType); + } + + virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) { + fprintf(stderr, "loadR0(%d, %d, %d, pType)\n", ea, isIncDec, op); + mpBase->loadR0(ea, isIncDec, op, pType); + } + + virtual void convertR0(Type* pType){ + fprintf(stderr, "convertR0(pType tag=%d)\n", pType->tag); + mpBase->convertR0(pType); + } + + 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 callRelative(int t, Type* pFunc) { + fprintf(stderr, "callRelative(%d)\n", t); + mpBase->callRelative(t, pFunc); + } + + virtual void callIndirect(int l, Type* pFunc) { + fprintf(stderr, "callIndirect(%d)\n", l); + 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(); + } + + virtual int disassemble(FILE* out) { + return mpBase->disassemble(out); + } + + /* output a symbol and patch all calls to it */ + virtual void gsym(int t) { + fprintf(stderr, "gsym(%d)\n", t); + mpBase->gsym(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 size_t stackSizeOf(Type* pType) { + return mpBase->stackSizeOf(pType); + } + + + virtual Type* getR0Type() { + return mpBase->getR0Type(); + } + }; + +#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; + }; + + 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; + }; + + 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(); + (*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(); + } + + 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* add(Type* pType) { + VariableInfo* pVI = add(pType->id); + pVI->pType = pType; + return pVI; + } + + void forEach(bool (*fn)(VariableInfo*, void*), void* context) { + for (size_t i = 0; i < mStack.size(); i++) { + if (! fn(mStack[i], context)) { + break; + } + } + } + + private: + inline size_t level() { + return mLevelStack.size(); + } + + struct Mark { + Arena::Mark mArenaMark; + size_t mSymbolHead; + }; + + Arena* mpArena; + TokenTable* mpTokenTable; + Vector<VariableInfo*> mStack; + Vector<Mark> mLevelStack; + }; + + int ch; // Current input character, or EOF + tokenid_t tok; // token + intptr_t tokc; // token extra info + double tokd; // floating point constant value + int tokl; // token operator level + intptr_t rsym; // return symbol + Type* pReturnType; // type of the current function's return. + intptr_t loc; // local variable index + char* glo; // global variable index + String mTokenString; + char* dptr; // Macro state: Points to macro text during macro playback. + int dch; // Macro state: Saves old value of ch during a macro playback. + char* pGlobalBase; + ACCSymbolLookupFn mpSymbolLookupFn; + void* mpSymbolLookupContext; + + // Arena for the duration of the compile + Arena mGlobalArena; + // Arena for data that's only needed when compiling a single function + Arena mLocalArena; + + TokenTable mTokenTable; + SymbolStack mGlobals; + SymbolStack mLocals; + + // Prebuilt types, makes things slightly faster. + Type* mkpInt; // int + 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; + + CodeBuf codeBuf; + 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; + + // 3..255 are character and/or operators + + // Keywords start at 0x100 and increase by 1 + // Order has to match string list in "internKeywords". + enum { + TOK_KEYWORD = TokenTable::TOKEN_BASE, + TOK_INT = TOK_KEYWORD, + TOK_CHAR, + TOK_VOID, + TOK_IF, + TOK_ELSE, + TOK_WHILE, + TOK_BREAK, + TOK_RETURN, + TOK_FOR, + TOK_AUTO, + TOK_CASE, + TOK_CONST, + TOK_CONTINUE, + TOK_DEFAULT, + TOK_DO, + TOK_DOUBLE, + TOK_ENUM, + TOK_EXTERN, + TOK_FLOAT, + TOK_GOTO, + TOK_LONG, + TOK_REGISTER, + TOK_SHORT, + TOK_SIGNED, + TOK_SIZEOF, + TOK_STATIC, + TOK_STRUCT, + TOK_SWITCH, + TOK_TYPEDEF, + TOK_UNION, + TOK_UNSIGNED, + TOK_VOLATILE, + TOK__BOOL, + TOK__COMPLEX, + TOK__IMAGINARY, + TOK_INLINE, + TOK_RESTRICT, + + // Symbols start after keywords + + TOK_SYMBOL, + TOK_PRAGMA = TOK_SYMBOL, + TOK_DEFINE, + TOK_LINE + }; + + static const int LOCAL = 0x200; + + static const int SYM_FORWARD = 0; + static const int SYM_DEFINE = 1; + + /* tokens in string heap */ + static const int TAG_TOK = ' '; + + static const int OP_INCREMENT = 0; + static const int OP_DECREMENT = 1; + static const int OP_MUL = 2; + static const int OP_DIV = 3; + static const int OP_MOD = 4; + static const int OP_PLUS = 5; + static const int OP_MINUS = 6; + static const int OP_SHIFT_LEFT = 7; + static const int OP_SHIFT_RIGHT = 8; + static const int OP_LESS_EQUAL = 9; + static const int OP_GREATER_EQUAL = 10; + static const int OP_LESS = 11; + static const int OP_GREATER = 12; + static const int OP_EQUALS = 13; + static const int OP_NOT_EQUALS = 14; + static const int OP_LOGICAL_AND = 15; + static const int OP_LOGICAL_OR = 16; + static const int OP_BIT_AND = 17; + static const int OP_BIT_XOR = 18; + static const int OP_BIT_OR = 19; + static const int OP_BIT_NOT = 20; + static const int OP_LOGICAL_NOT = 21; + static const int OP_COUNT = 22; + + /* Operators are searched from front, the two-character operators appear + * before the single-character operators with the same first character. + * @ is used to pad out single-character operators. + */ + static const char* operatorChars; + static const char operatorLevel[]; + + /* Called when we detect an internal problem. Does nothing in production. + * + */ + void internalError() { + * (char*) 0 = 0; + } + + void assert(bool isTrue) { + if (!isTrue) { + internalError(); + } + } + + bool isSymbol(tokenid_t t) { + return t >= TOK_SYMBOL && + ((size_t) (t-TOK_SYMBOL)) < mTokenTable.size(); + } + + bool isSymbolOrKeyword(tokenid_t t) { + return t >= TOK_KEYWORD && + ((size_t) (t-TOK_KEYWORD)) < mTokenTable.size(); + } + + VariableInfo* VI(tokenid_t t) { + assert(isSymbol(t)); + VariableInfo* pV = mTokenTable[t].mpVariableInfo; + if (pV && pV->tok != t) { + internalError(); + } + return pV; + } + + inline bool isDefined(tokenid_t t) { + return t >= TOK_SYMBOL && VI(t) != 0; + } + + const char* nameof(tokenid_t t) { + assert(isSymbolOrKeyword(t)); + return mTokenTable[t].pText; + } + + void pdef(int t) { + mTokenString.append(t); + } + + void inp() { + if (dptr) { + ch = *dptr++; + if (ch == 0) { + dptr = 0; + ch = dch; + } + } else { + if (mbBumpLine) { + mLineNumber++; + mbBumpLine = false; + } + ch = file->getChar(); + if (ch == '\n') { + mbBumpLine = true; + } + } +#if 0 + printf("ch='%c' 0x%x\n", ch, ch); +#endif + } + + int isid() { + return isalnum(ch) | (ch == '_'); + } + + int decodeHex(int c) { + if (isdigit(c)) { + c -= '0'; + } else if (c <= 'F') { + c = c - 'A' + 10; + } else { + c =c - 'a' + 10; + } + return c; + } + + /* read a character constant, advances ch to after end of constant */ + int getq() { + int val = ch; + if (ch == '\\') { + inp(); + if (isoctal(ch)) { + // 1 to 3 octal characters. + val = 0; + for(int i = 0; i < 3; i++) { + if (isoctal(ch)) { + val = (val << 3) + ch - '0'; + inp(); + } + } + return val; + } else if (ch == 'x' || ch == 'X') { + // N hex chars + inp(); + if (! isxdigit(ch)) { + error("'x' character escape requires at least one digit."); + } else { + val = 0; + while (isxdigit(ch)) { + val = (val << 4) + decodeHex(ch); + inp(); + } + } + } else { + int val = ch; + switch (ch) { + case 'a': + val = '\a'; + break; + case 'b': + val = '\b'; + break; + case 'f': + val = '\f'; + break; + case 'n': + val = '\n'; + break; + case 'r': + val = '\r'; + break; + case 't': + val = '\t'; + break; + case 'v': + val = '\v'; + break; + case '\\': + val = '\\'; + break; + case '\'': + val = '\''; + break; + case '"': + val = '"'; + break; + case '?': + val = '?'; + break; + default: + error("Undefined character escape %c", ch); + break; + } + inp(); + return val; + } + } else { + inp(); + } + return val; + } + + static bool isoctal(int ch) { + return ch >= '0' && ch <= '7'; + } + + bool acceptCh(int c) { + bool result = c == ch; + if (result) { + pdef(ch); + inp(); + } + return result; + } + + bool acceptDigitsCh() { + bool result = false; + while (isdigit(ch)) { + result = true; + pdef(ch); + inp(); + } + return result; + } + + void parseFloat() { + tok = TOK_NUM_DOUBLE; + // mTokenString already has the integral part of the number. + if(mTokenString.len() == 0) { + mTokenString.append('0'); + } + acceptCh('.'); + acceptDigitsCh(); + if (acceptCh('e') || acceptCh('E')) { + acceptCh('-') || acceptCh('+'); + acceptDigitsCh(); + } + if (ch == 'f' || ch == 'F') { + tok = TOK_NUM_FLOAT; + inp(); + } else if (ch == 'l' || ch == 'L') { + inp(); + error("Long floating point constants not supported."); + } + char* pText = mTokenString.getUnwrapped(); + char* pEnd = pText + strlen(pText); + char* pEndPtr = 0; + errno = 0; + if (tok == TOK_NUM_FLOAT) { + tokd = strtof(pText, &pEndPtr); + } else { + tokd = strtod(pText, &pEndPtr); + } + if (errno || pEndPtr != pEnd) { + error("Can't parse constant: %s", pText); + } + // fprintf(stderr, "float constant: %s (%d) %g\n", pText, tok, tokd); + } + + void next() { + int l, a; + + while (isspace(ch) | (ch == '#')) { + if (ch == '#') { + inp(); + next(); + if (tok == TOK_DEFINE) { + doDefine(); + } else if (tok == TOK_PRAGMA) { + doPragma(); + } else if (tok == TOK_LINE) { + doLine(); + } else { + error("Unsupported preprocessor directive \"%s\"", + mTokenString.getUnwrapped()); + } + } + inp(); + } + tokl = 0; + tok = ch; + /* encode identifiers & numbers */ + if (isdigit(ch) || ch == '.') { + // Start of a numeric constant. Could be integer, float, or + // double, won't know until we look further. + mTokenString.clear(); + pdef(ch); + inp(); + 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()); + // Is this a macro? + char* pMacroDefinition = mTokenTable[tok].mpMacroDefinition; + if (pMacroDefinition) { + // Yes, it is a macro + dptr = pMacroDefinition; + dch = ch; + inp(); + next(); + } + } else { + inp(); + if (tok == '\'') { + tok = TOK_NUM; + tokc = getq(); + if (ch != '\'') { + error("Expected a ' character, got %c", ch); + } else { + inp(); + } + } else if ((tok == '/') & (ch == '*')) { + inp(); + while (ch && ch != EOF) { + while (ch != '*' && ch != EOF) + inp(); + inp(); + if (ch == '/') + ch = 0; + } + if (ch == EOF) { + error("End of file inside comment."); + } + inp(); + next(); + } else if ((tok == '/') & (ch == '/')) { + inp(); + while (ch && (ch != '\n') && (ch != EOF)) { + inp(); + } + inp(); + next(); + } else { + 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 */ + } + break; + } + opIndex++; + } + if (l == 0) { + tokl = 0; + tokc = 0; + } + } + } +#if 0 + { + String buf; + decodeToken(buf, tok, true); + fprintf(stderr, "%s\n", buf.getUnwrapped()); + } +#endif + } + + void doDefine() { + next(); + tokenid_t name = tok; + String* pName = new String(); + while (isspace(ch)) { + inp(); + } + if (ch == '(') { + delete pName; + error("Defines with arguments not supported"); + return; + } + while (isspace(ch)) { + inp(); + } + String value; + while (ch != '\n' && ch != EOF) { + value.append(ch); + inp(); + } + char* pDefn = (char*)mGlobalArena.alloc(value.len() + 1); + memcpy(pDefn, value.getUnwrapped(), value.len()); + pDefn[value.len()] = 0; + mTokenTable[name].mpMacroDefinition = pDefn; + } + + void doPragma() { + // # pragma name(val) + int state = 0; + while(ch != EOF && ch != '\n' && state < 10) { + switch(state) { + case 0: + if (isspace(ch)) { + inp(); + } else { + state++; + } + break; + case 1: + if (isalnum(ch)) { + mPragmas.append(ch); + inp(); + } else if (ch == '(') { + mPragmas.append(0); + inp(); + state++; + } else { + state = 11; + } + break; + case 2: + if (isalnum(ch)) { + mPragmas.append(ch); + inp(); + } else if (ch == ')') { + mPragmas.append(0); + inp(); + state = 10; + } else { + state = 11; + } + break; + } + } + if(state != 10) { + error("Unexpected pragma syntax"); + } + mPragmaStringCount += 2; + } + + void doLine() { + // # line number { "filename "} + next(); + if (tok != TOK_NUM) { + error("Expected a line-number"); + } else { + mLineNumber = tokc-1; // The end-of-line will increment it. + } + while(ch != EOF && ch != '\n') { + inp(); + } + } + + virtual void verror(const char* fmt, va_list ap) { + mErrorBuf.printf("%ld: ", mLineNumber); + mErrorBuf.vprintf(fmt, ap); + mErrorBuf.printf("\n"); + } + + void skip(intptr_t c) { + if (tok != c) { + error("'%c' expected", c); + } + next(); + } + + bool accept(intptr_t c) { + if (tok == c) { + next(); + return true; + } + return false; + } + + bool acceptStringLiteral() { + if (tok == '"') { + pGen->li((int) glo, mkpCharPtr); + // 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; + } + + /* Parse and evaluate a unary expression. + * allowAssignment is true if '=' parsing wanted (quick hack) + */ + void unary(bool allowAssignment) { + tokenid_t t; + intptr_t n, a; + t = 0; + n = 1; /* type of expression 0 = forward, 1 = value, other = lvalue */ + 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, mkpInt); + } 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(false); + if (t == '!') + pGen->gUnaryCmp(a, mkpInt); + else if (t == '+') { + // ignore unary plus. + } else { + pGen->genUnaryOp(a); + } + } else if (t == '(') { + // It's either a cast or an expression + Type* pCast = acceptCastTypeDeclaration(mLocalArena); + if (pCast) { + skip(')'); + unary(false); + pGen->convertR0(pCast); + } else { + expr(); + skip(')'); + } + } else if (t == '*') { + /* This is a pointer dereference. + */ + unary(false); + Type* pR0Type = pGen->getR0Type(); + if (pR0Type->tag != TY_POINTER) { + error("Expected a pointer type."); + } else { + if (pR0Type->pHead->tag == TY_FUNC) { + t = 0; + } + if (accept('=')) { + pGen->pushR0(); + expr(); + pGen->storeR0ToTOS(pR0Type); + } else if (t) { + pGen->loadR0FromR0(pR0Type); + } + } + // Else we fall through to the function call below, with + // t == 0 to trigger an indirect function call. Hack! + } else if (t == '&') { + VariableInfo* pVI = VI(tok); + pGen->leaR0((int) pVI->pAddress, + createPtrType(pVI->pType, mLocalArena)); + next(); + } 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); + 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\n", nameof(t)); + } + } + if ((tok == '=') & allowAssignment) { + /* assignment */ + next(); + expr(); + pGen->storeR0(n, pVI->pType); + } else if (tok != '(') { + /* variable */ + if (!n) { + linkGlobal(t, false); + n = (intptr_t) pVI->pAddress; + if (!n) { + error("Undeclared variable %s\n", nameof(t)); + } + } + pGen->loadR0(n, tokl == 11, tokc, pVI->pType); + if (tokl == 11) { + next(); + } + } + } + } + + /* function call */ + if (accept('(')) { + Type* pDecl = NULL; + VariableInfo* pVI = NULL; + if (n == 1) { // Indirect function call, push address of fn. + pDecl = pGen->getR0Type(); + pGen->pushR0(); + } else { + pVI = VI(t); + pDecl = pVI->pType; + } + 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(); + Type* pTargetType; + if (pArgList) { + pTargetType = pArgList->pHead; + pArgList = pArgList->pTail; + } else { + pTargetType = pGen->getR0Type(); + if (pTargetType->tag == TY_FLOAT) { + pTargetType = mkpDouble; + } + } + 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(')'); + if (!n) { + /* forward reference */ + pVI->pForward = (void*) pGen->callForward((int) pVI->pForward, + pVI->pType); + } else if (n == 1) { + pGen->callIndirect(l, mkpPtrIntFn->pHead); + } else { + pGen->callRelative(n - codeBuf.getPC() - pGen->jumpOffset(), + VI(t)->pType); + } + pGen->adjustStackAfterCall(pDecl, l, n == 1); + } + } + + /* Recursive descent parser for binary operations. + */ + void binaryOp(int level) { + intptr_t t, a; + t = 0; + if (level-- == 1) + unary(true); + else { + binaryOp(level); + a = 0; + while (level == tokl) { + t = tokc; + next(); + + 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, mkpInt); + } + if ((level == 4) | (level == 5)) { + pGen->gcmp(t, mkpInt); + } else { + pGen->genOp(t); + } + } + } + /* && and || output code generation */ + if (a && level > 8) { + a = pGen->gtst(t == OP_LOGICAL_OR, a); + pGen->li(t != OP_LOGICAL_OR, mkpInt); + pGen->gjmp(5); /* jmp $ + 5 (sizeof li, FIXME for ARM) */ + pGen->gsym(a); + pGen->li(t == OP_LOGICAL_OR, mkpInt); + } + } + } + + void expr() { + binaryOp(11); + } + + int test_expr() { + expr(); + return pGen->gtst(0, 0); + } + + void block(intptr_t l, bool outermostFunctionBlock) { + intptr_t a, n, t; + + Type* pBaseType; + if ((pBaseType = acceptPrimitiveType(mLocalArena))) { + /* declarations */ + localDeclarations(pBaseType); + } else if (tok == TOK_IF) { + next(); + skip('('); + a = test_expr(); + skip(')'); + block(l, false); + if (tok == TOK_ELSE) { + next(); + n = pGen->gjmp(0); /* jmp */ + pGen->gsym(a); + block(l, false); + pGen->gsym(n); /* patch else jmp */ + } else { + pGen->gsym(a); /* patch if test */ + } + } else if ((tok == TOK_WHILE) | (tok == TOK_FOR)) { + t = tok; + next(); + skip('('); + if (t == TOK_WHILE) { + n = codeBuf.getPC(); // top of loop, target of "next" iteration + a = test_expr(); + } else { + if (tok != ';') + expr(); + skip(';'); + n = codeBuf.getPC(); + a = 0; + if (tok != ';') + a = test_expr(); + skip(';'); + if (tok != ')') { + t = pGen->gjmp(0); + expr(); + pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset()); + pGen->gsym(t); + n = t + 4; + } + } + skip(')'); + block((intptr_t) &a, false); + pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset()); /* jmp */ + pGen->gsym(a); + } else if (tok == '{') { + if (! outermostFunctionBlock) { + mLocals.pushLevel(); + } + next(); + while (tok != '}' && tok != EOF) + block(l, false); + skip('}'); + if (! outermostFunctionBlock) { + mLocals.popLevel(); + } + } else { + if (accept(TOK_RETURN)) { + if (tok != ';') { + expr(); + if (pReturnType->tag == TY_VOID) { + error("Must not return a value from a void function"); + } else { + pGen->convertR0(pReturnType); + } + } else { + if (pReturnType->tag != TY_VOID) { + error("Must specify a value here"); + } + } + rsym = pGen->gjmp(rsym); /* jmp */ + } else if (accept(TOK_BREAK)) { + *(int *) l = pGen->gjmp(*(int *) l); + } else if (tok != ';') + expr(); + 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_FUNC || at == TY_PARAM) { + return typeEqual(a->pHead, b->pHead) + && typeEqual(a->pTail, b->pTail); + } + return true; + } + + Type* createType(TypeTag tag, Type* pHead, Type* pTail, Arena& arena) { + assert(tag >= TY_INT && tag <= TY_PARAM); + Type* pType = (Type*) arena.alloc(sizeof(Type)); + memset(pType, 0, sizeof(*pType)); + pType->tag = tag; + pType->pHead = pHead; + pType->pTail = pTail; + return pType; + } + + Type* createPtrType(Type* pType, Arena& arena) { + return createType(TY_POINTER, pType, NULL, arena); + } + + /** + * 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); + + String temp; + if (pType->id != 0) { + decodeToken(temp, pType->id, false); + buffer.append(temp); + } + + decodeTypeImpPostfix(buffer, pType); + } + + void decodeTypeImpPrefix(String& buffer, Type* pType) { + TypeTag tag = pType->tag; + + if (tag >= TY_INT && tag <= TY_DOUBLE) { + switch (tag) { + case TY_INT: + buffer.appendCStr("int"); + 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; + default: + break; + } + buffer.append(' '); + } + + switch (tag) { + case TY_INT: + 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_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_FUNC: + buffer.append('('); + for(Type* pArg = pType->pTail; pArg; pArg = pArg->pTail) { + decodeTypeImp(buffer, pArg); + if (pArg->pTail) { + buffer.appendCStr(", "); + } + } + buffer.append(')'); + break; + default: + break; + } + } + + void printType(Type* pType) { + String buffer; + decodeType(buffer, pType); + fprintf(stderr, "%s\n", buffer.getUnwrapped()); + } + + Type* acceptPrimitiveType(Arena& arena) { + Type* pType; + if (tok == TOK_INT) { + pType = mkpInt; + } else if (tok == TOK_CHAR) { + pType = mkpChar; + } else if (tok == TOK_VOID) { + pType = mkpVoid; + } else if (tok == TOK_FLOAT) { + pType = mkpFloat; + } else if (tok == TOK_DOUBLE) { + pType = mkpDouble; + } else { + return NULL; + } + next(); + return pType; + } + + Type* acceptDeclaration(Type* pType, bool nameAllowed, bool nameRequired, + Arena& arena) { + tokenid_t declName = 0; + bool reportFailure = false; + pType = acceptDecl2(pType, declName, nameAllowed, + nameRequired, arena, reportFailure); + if (declName) { + // Clone the parent type so we can set a unique ID + pType = createType(pType->tag, pType->pHead, + pType->pTail, arena); + + pType->id = declName; + } + // fprintf(stderr, "Parsed a declaration: "); + // printType(pType); + if (reportFailure) { + return NULL; + } + return pType; + } + + Type* expectDeclaration(Type* pBaseType, Arena& arena) { + Type* pType = acceptDeclaration(pBaseType, true, true, arena); + if (! pType) { + error("Expected a declaration"); + } + return pType; + } + + /* Used for accepting types that appear in casts */ + Type* acceptCastTypeDeclaration(Arena& arena) { + Type* pType = acceptPrimitiveType(arena); + if (pType) { + pType = acceptDeclaration(pType, false, false, arena); + } + return pType; + } + + Type* expectCastTypeDeclaration(Arena& arena) { + Type* pType = acceptCastTypeDeclaration(arena); + if (! pType) { + error("Expected a declaration"); + } + return pType; + } + + Type* acceptDecl2(Type* pType, tokenid_t& declName, + bool nameAllowed, bool nameRequired, Arena& arena, + bool& reportFailure) { + int ptrCounter = 0; + while (accept('*')) { + ptrCounter++; + } + pType = acceptDecl3(pType, declName, nameAllowed, nameRequired, arena, + reportFailure); + while (ptrCounter-- > 0) { + pType = createType(TY_POINTER, pType, NULL, arena); + } + return pType; + } + + Type* acceptDecl3(Type* pType, tokenid_t& declName, + bool nameAllowed, bool nameRequired, Arena& arena, + bool& reportFailure) { + // direct-dcl : + // name + // (dcl) + // direct-dcl() + // direct-dcl[] + Type* pNewHead = NULL; + if (accept('(')) { + pNewHead = acceptDecl2(pNewHead, declName, nameAllowed, + nameRequired, arena, 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; + } + while (accept('(')) { + // Function declaration + Type* pTail = acceptArgs(nameAllowed, arena); + pType = createType(TY_FUNC, pType, pTail, arena); + skip(')'); + } + + if (pNewHead) { + Type* pA = pNewHead; + while (pA->pHead) { + pA = pA->pHead; + } + pA->pHead = pType; + pType = pNewHead; + } + return pType; + } + + Type* acceptArgs(bool nameAllowed, Arena& arena) { + Type* pHead = NULL; + Type* pTail = NULL; + for(;;) { + Type* pBaseArg = acceptPrimitiveType(arena); + if (pBaseArg) { + Type* pArg = acceptDeclaration(pBaseArg, nameAllowed, false, + arena); + if (pArg) { + Type* pParam = createType(TY_PARAM, pArg, NULL, arena); + if (!pHead) { + pHead = pParam; + pTail = pParam; + } else { + pTail->pTail = pParam; + pTail = pParam; + } + } + } + if (! accept(',')) { + break; + } + } + return pHead; + } + + Type* expectPrimitiveType(Arena& arena) { + Type* pType = acceptPrimitiveType(arena); + if (!pType) { + String buf; + decodeToken(buf, tok, true); + error("Expected a type, got %s", buf.getUnwrapped()); + } + return pType; + } + + 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); + } + + void localDeclarations(Type* pBaseType) { + intptr_t a; + + while (pBaseType) { + while (tok != ';' && tok != EOF) { + Type* pDecl = expectDeclaration(pBaseType, mLocalArena); + if (!pDecl) { + break; + } + int variableAddress = 0; + addLocalSymbol(pDecl); + size_t alignment = pGen->alignmentOf(pDecl); + loc = (loc + alignment - 1) & ~ (alignment-1); + loc = loc + pGen->sizeOf(pDecl); + variableAddress = -loc; + VI(pDecl->id)->pAddress = (void*) variableAddress; + if (accept('=')) { + /* assignment */ + expr(); + pGen->storeR0(variableAddress, pDecl); + } + if (tok == ',') + next(); + } + skip(';'); + pBaseType = acceptPrimitiveType(mLocalArena); + } + } + + bool checkSymbol() { + return checkSymbol(tok); + } + + void decodeToken(String& buffer, tokenid_t token, bool quote) { + if (token == EOF ) { + buffer.printf("EOF"); + } else if (token == TOK_NUM) { + buffer.printf("numeric constant"); + } else if (token >= 0 && token < 256) { + if (token < 32) { + buffer.printf("'\\x%02x'", token); + } else { + buffer.printf("'%c'", token); + } + } else { + if (quote) { + if (token >= TOK_KEYWORD && token < TOK_SYMBOL) { + buffer.printf("keyword \"%s\"", nameof(token)); + } else { + buffer.printf("symbol \"%s\"", nameof(token)); + } + } else { + buffer.printf("%s", nameof(token)); + } + } + } + + 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() { + while (tok != EOF) { + Type* pBaseType = expectPrimitiveType(mGlobalArena); + if (!pBaseType) { + break; + } + Type* pDecl = expectDeclaration(pBaseType, mGlobalArena); + if (!pDecl) { + break; + } + if (! isDefined(pDecl->id)) { + addGlobalSymbol(pDecl); + } + VariableInfo* name = VI(pDecl->id); + if (name && name->pAddress) { + error("Already defined global %s", nameof(pDecl->id)); + } + if (pDecl->tag < TY_FUNC) { + // it's a variable declaration + for(;;) { + if (name && !name->pAddress) { + name->pAddress = (int*) allocGlobalSpace( + pGen->alignmentOf(name->pType), + pGen->sizeOf(name->pType)); + } + if (accept('=')) { + if (tok == TOK_NUM) { + if (name) { + * (int*) name->pAddress = tokc; + } + next(); + } else { + error("Expected an integer constant"); + } + } + if (!accept(',')) { + break; + } + pDecl = expectDeclaration(pBaseType, mGlobalArena); + 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 { + if (name) { + /* patch forward references (XXX: does not work for function + pointers) */ + pGen->gsym((int) name->pForward); + /* put function address */ + name->pAddress = (void*) codeBuf.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; + addLocalSymbol(pArg); + /* read param name and compute offset */ + size_t alignment = pGen->alignmentOf(pArg); + a = (a + alignment - 1) & ~ (alignment-1); + VI(pArg->id)->pAddress = (void*) a; + a = a + pGen->stackSizeOf(pArg); + argCount++; + } + rsym = loc = 0; + pReturnType = pDecl->pHead; + a = pGen->functionEntry(pDecl); + block(0, true); + pGen->gsym(rsym); + pGen->functionExit(pDecl, a, loc); + mLocals.popLevel(); + } + } + } + } + + 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"); + 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 (file) { + delete file; + file = 0; + } + } + + // One-time initialization, when class is constructed. + void init() { + mpSymbolLookupFn = 0; + mpSymbolLookupContext = 0; + } + + void clear() { + tok = 0; + tokc = 0; + tokl = 0; + ch = 0; + rsym = 0; + loc = 0; + glo = 0; + dptr = 0; + dch = 0; + file = 0; + pGlobalBase = 0; + pGen = 0; + mPragmaStringCount = 0; + mCompileResult = 0; + mLineNumber = 1; + mbBumpLine = false; + } + + void setArchitecture(const char* architecture) { + delete pGen; + pGen = 0; + + if (architecture != NULL) { +#ifdef PROVIDE_ARM_CODEGEN + if (! pGen && strcmp(architecture, "arm") == 0) { + pGen = new ARMCodeGenerator(); + } +#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(); +#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; + + createPrimitiveTypes(); + cleanup(); + clear(); + mTokenTable.setArena(&mGlobalArena); + mGlobals.setArena(&mGlobalArena); + mGlobals.setTokenTable(&mTokenTable); + mLocals.setArena(&mLocalArena); + mLocals.setTokenTable(&mTokenTable); + + internKeywords(); + codeBuf.init(ALLOC_SIZE); + setArchitecture(NULL); + if (!pGen) { + return -1; + } +#ifdef PROVIDE_TRACE_CODEGEN + pGen = new TraceCodeGenerator(pGen); +#endif + pGen->setErrorSink(this); + pGen->init(&codeBuf); + 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, mGlobalArena); + mkpChar = createType(TY_CHAR, NULL, NULL, mGlobalArena); + mkpVoid = createType(TY_VOID, NULL, NULL, mGlobalArena); + mkpFloat = createType(TY_FLOAT, NULL, NULL, mGlobalArena); + mkpDouble = createType(TY_DOUBLE, NULL, NULL, mGlobalArena); + mkpIntFn = createType(TY_FUNC, mkpInt, NULL, mGlobalArena); + mkpIntPtr = createPtrType(mkpInt, mGlobalArena); + mkpCharPtr = createPtrType(mkpChar, mGlobalArena); + mkpFloatPtr = createPtrType(mkpFloat, mGlobalArena); + mkpDoublePtr = createPtrType(mkpDouble, mGlobalArena); + mkpPtrIntFn = createPtrType(mkpIntFn, mGlobalArena); + } + + 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; + } + + int dump(FILE* out) { + fwrite(codeBuf.getBase(), 1, codeBuf.getSize(), out); + return 0; + } + + int disassemble(FILE* out) { + return pGen->disassemble(out); + } + + /* 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; + } + } + } + + 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_ARM_CODEGEN +FILE* Compiler::ARMCodeGenerator::disasmOut; +#endif + +#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'; +} + +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 accDisassemble(ACCscript* script) { + script->compiler.disassemble(stderr); +} + + +} // namespace acc + diff --git a/libacc/armreg.h b/libacc/armreg.h new file mode 100644 index 0000000..fde81ba --- /dev/null +++ b/libacc/armreg.h @@ -0,0 +1,300 @@ +/* $NetBSD: armreg.h,v 1.28 2003/10/31 16:30:15 scw Exp $ */ + +/*- + * Copyright (c) 1998, 2001 Ben Harris + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/armreg.h,v 1.3 2005/11/21 19:06:25 cognet Exp $ + */ + +#ifndef MACHINE_ARMREG_H +#define MACHINE_ARMREG_H +#define INSN_SIZE 4 +#define INSN_COND_MASK 0xf0000000 /* Condition mask */ +#define PSR_MODE 0x0000001f /* mode mask */ +#define PSR_USR26_MODE 0x00000000 +#define PSR_FIQ26_MODE 0x00000001 +#define PSR_IRQ26_MODE 0x00000002 +#define PSR_SVC26_MODE 0x00000003 +#define PSR_USR32_MODE 0x00000010 +#define PSR_FIQ32_MODE 0x00000011 +#define PSR_IRQ32_MODE 0x00000012 +#define PSR_SVC32_MODE 0x00000013 +#define PSR_ABT32_MODE 0x00000017 +#define PSR_UND32_MODE 0x0000001b +#define PSR_SYS32_MODE 0x0000001f +#define PSR_32_MODE 0x00000010 +#define PSR_FLAGS 0xf0000000 /* flags */ + +#define PSR_C_bit (1 << 29) /* carry */ + +/* The high-order byte is always the implementor */ +#define CPU_ID_IMPLEMENTOR_MASK 0xff000000 +#define CPU_ID_ARM_LTD 0x41000000 /* 'A' */ +#define CPU_ID_DEC 0x44000000 /* 'D' */ +#define CPU_ID_INTEL 0x69000000 /* 'i' */ +#define CPU_ID_TI 0x54000000 /* 'T' */ + +/* How to decide what format the CPUID is in. */ +#define CPU_ID_ISOLD(x) (((x) & 0x0000f000) == 0x00000000) +#define CPU_ID_IS7(x) (((x) & 0x0000f000) == 0x00007000) +#define CPU_ID_ISNEW(x) (!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x)) + +/* On ARM3 and ARM6, this byte holds the foundry ID. */ +#define CPU_ID_FOUNDRY_MASK 0x00ff0000 +#define CPU_ID_FOUNDRY_VLSI 0x00560000 + +/* On ARM7 it holds the architecture and variant (sub-model) */ +#define CPU_ID_7ARCH_MASK 0x00800000 +#define CPU_ID_7ARCH_V3 0x00000000 +#define CPU_ID_7ARCH_V4T 0x00800000 +#define CPU_ID_7VARIANT_MASK 0x007f0000 + +/* On more recent ARMs, it does the same, but in a different format */ +#define CPU_ID_ARCH_MASK 0x000f0000 +#define CPU_ID_ARCH_V3 0x00000000 +#define CPU_ID_ARCH_V4 0x00010000 +#define CPU_ID_ARCH_V4T 0x00020000 +#define CPU_ID_ARCH_V5 0x00030000 +#define CPU_ID_ARCH_V5T 0x00040000 +#define CPU_ID_ARCH_V5TE 0x00050000 +#define CPU_ID_VARIANT_MASK 0x00f00000 + +/* Next three nybbles are part number */ +#define CPU_ID_PARTNO_MASK 0x0000fff0 + +/* Intel XScale has sub fields in part number */ +#define CPU_ID_XSCALE_COREGEN_MASK 0x0000e000 /* core generation */ +#define CPU_ID_XSCALE_COREREV_MASK 0x00001c00 /* core revision */ +#define CPU_ID_XSCALE_PRODUCT_MASK 0x000003f0 /* product number */ + +/* And finally, the revision number. */ +#define CPU_ID_REVISION_MASK 0x0000000f + +/* Individual CPUs are probably best IDed by everything but the revision. */ +#define CPU_ID_CPU_MASK 0xfffffff0 + +/* Fake CPU IDs for ARMs without CP15 */ +#define CPU_ID_ARM2 0x41560200 +#define CPU_ID_ARM250 0x41560250 + +/* Pre-ARM7 CPUs -- [15:12] == 0 */ +#define CPU_ID_ARM3 0x41560300 +#define CPU_ID_ARM600 0x41560600 +#define CPU_ID_ARM610 0x41560610 +#define CPU_ID_ARM620 0x41560620 + +/* ARM7 CPUs -- [15:12] == 7 */ +#define CPU_ID_ARM700 0x41007000 /* XXX This is a guess. */ +#define CPU_ID_ARM710 0x41007100 +#define CPU_ID_ARM7500 0x41027100 /* XXX This is a guess. */ +#define CPU_ID_ARM710A 0x41047100 /* inc ARM7100 */ +#define CPU_ID_ARM7500FE 0x41077100 +#define CPU_ID_ARM710T 0x41807100 +#define CPU_ID_ARM720T 0x41807200 +#define CPU_ID_ARM740T8K 0x41807400 /* XXX no MMU, 8KB cache */ +#define CPU_ID_ARM740T4K 0x41817400 /* XXX no MMU, 4KB cache */ + +/* Post-ARM7 CPUs */ +#define CPU_ID_ARM810 0x41018100 +#define CPU_ID_ARM920T 0x41129200 +#define CPU_ID_ARM920T_ALT 0x41009200 +#define CPU_ID_ARM922T 0x41029220 +#define CPU_ID_ARM940T 0x41029400 /* XXX no MMU */ +#define CPU_ID_ARM946ES 0x41049460 /* XXX no MMU */ +#define CPU_ID_ARM966ES 0x41049660 /* XXX no MMU */ +#define CPU_ID_ARM966ESR1 0x41059660 /* XXX no MMU */ +#define CPU_ID_ARM1020E 0x4115a200 /* (AKA arm10 rev 1) */ +#define CPU_ID_ARM1022ES 0x4105a220 +#define CPU_ID_SA110 0x4401a100 +#define CPU_ID_SA1100 0x4401a110 +#define CPU_ID_TI925T 0x54029250 +#define CPU_ID_SA1110 0x6901b110 +#define CPU_ID_IXP1200 0x6901c120 +#define CPU_ID_80200 0x69052000 +#define CPU_ID_PXA250 0x69052100 /* sans core revision */ +#define CPU_ID_PXA210 0x69052120 +#define CPU_ID_PXA250A 0x69052100 /* 1st version Core */ +#define CPU_ID_PXA210A 0x69052120 /* 1st version Core */ +#define CPU_ID_PXA250B 0x69052900 /* 3rd version Core */ +#define CPU_ID_PXA210B 0x69052920 /* 3rd version Core */ +#define CPU_ID_PXA250C 0x69052d00 /* 4th version Core */ +#define CPU_ID_PXA210C 0x69052d20 /* 4th version Core */ +#define CPU_ID_80321_400 0x69052420 +#define CPU_ID_80321_600 0x69052430 +#define CPU_ID_80321_400_B0 0x69052c20 +#define CPU_ID_80321_600_B0 0x69052c30 +#define CPU_ID_IXP425_533 0x690541c0 +#define CPU_ID_IXP425_400 0x690541d0 +#define CPU_ID_IXP425_266 0x690541f0 + +/* ARM3-specific coprocessor 15 registers */ +#define ARM3_CP15_FLUSH 1 +#define ARM3_CP15_CONTROL 2 +#define ARM3_CP15_CACHEABLE 3 +#define ARM3_CP15_UPDATEABLE 4 +#define ARM3_CP15_DISRUPTIVE 5 + +/* ARM3 Control register bits */ +#define ARM3_CTL_CACHE_ON 0x00000001 +#define ARM3_CTL_SHARED 0x00000002 +#define ARM3_CTL_MONITOR 0x00000004 + +/* + * Post-ARM3 CP15 registers: + * + * 1 Control register + * + * 2 Translation Table Base + * + * 3 Domain Access Control + * + * 4 Reserved + * + * 5 Fault Status + * + * 6 Fault Address + * + * 7 Cache/write-buffer Control + * + * 8 TLB Control + * + * 9 Cache Lockdown + * + * 10 TLB Lockdown + * + * 11 Reserved + * + * 12 Reserved + * + * 13 Process ID (for FCSE) + * + * 14 Reserved + * + * 15 Implementation Dependent + */ + +/* Some of the definitions below need cleaning up for V3/V4 architectures */ + +/* CPU control register (CP15 register 1) */ +#define CPU_CONTROL_MMU_ENABLE 0x00000001 /* M: MMU/Protection unit enable */ +#define CPU_CONTROL_AFLT_ENABLE 0x00000002 /* A: Alignment fault enable */ +#define CPU_CONTROL_DC_ENABLE 0x00000004 /* C: IDC/DC enable */ +#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */ +#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */ +#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */ +#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */ +#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */ +#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */ +#define CPU_CONTROL_ROM_ENABLE 0x00000200 /* R: ROM protection bit */ +#define CPU_CONTROL_CPCLK 0x00000400 /* F: Implementation defined */ +#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */ +#define CPU_CONTROL_IC_ENABLE 0x00001000 /* I: IC enable */ +#define CPU_CONTROL_VECRELOC 0x00002000 /* V: Vector relocation */ +#define CPU_CONTROL_ROUNDROBIN 0x00004000 /* RR: Predictable replacement */ +#define CPU_CONTROL_V4COMPAT 0x00008000 /* L4: ARMv4 compat LDR R15 etc */ + +#define CPU_CONTROL_IDC_ENABLE CPU_CONTROL_DC_ENABLE + +/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */ +#define XSCALE_AUXCTL_K 0x00000001 /* dis. write buffer coalescing */ +#define XSCALE_AUXCTL_P 0x00000002 /* ECC protect page table access */ +#define XSCALE_AUXCTL_MD_WB_RA 0x00000000 /* mini-D$ wb, read-allocate */ +#define XSCALE_AUXCTL_MD_WB_RWA 0x00000010 /* mini-D$ wb, read/write-allocate */ +#define XSCALE_AUXCTL_MD_WT 0x00000020 /* mini-D$ wt, read-allocate */ +#define XSCALE_AUXCTL_MD_MASK 0x00000030 + +/* Cache type register definitions */ +#define CPU_CT_ISIZE(x) ((x) & 0xfff) /* I$ info */ +#define CPU_CT_DSIZE(x) (((x) >> 12) & 0xfff) /* D$ info */ +#define CPU_CT_S (1U << 24) /* split cache */ +#define CPU_CT_CTYPE(x) (((x) >> 25) & 0xf) /* cache type */ + +#define CPU_CT_CTYPE_WT 0 /* write-through */ +#define CPU_CT_CTYPE_WB1 1 /* write-back, clean w/ read */ +#define CPU_CT_CTYPE_WB2 2 /* w/b, clean w/ cp15,7 */ +#define CPU_CT_CTYPE_WB6 6 /* w/b, cp15,7, lockdown fmt A */ +#define CPU_CT_CTYPE_WB7 7 /* w/b, cp15,7, lockdown fmt B */ + +#define CPU_CT_xSIZE_LEN(x) ((x) & 0x3) /* line size */ +#define CPU_CT_xSIZE_M (1U << 2) /* multiplier */ +#define CPU_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x7) /* associativity */ +#define CPU_CT_xSIZE_SIZE(x) (((x) >> 6) & 0x7) /* size */ + +/* Fault status register definitions */ + +#define FAULT_TYPE_MASK 0x0f +#define FAULT_USER 0x10 + +#define FAULT_WRTBUF_0 0x00 /* Vector Exception */ +#define FAULT_WRTBUF_1 0x02 /* Terminal Exception */ +#define FAULT_BUSERR_0 0x04 /* External Abort on Linefetch -- Section */ +#define FAULT_BUSERR_1 0x06 /* External Abort on Linefetch -- Page */ +#define FAULT_BUSERR_2 0x08 /* External Abort on Non-linefetch -- Section */ +#define FAULT_BUSERR_3 0x0a /* External Abort on Non-linefetch -- Page */ +#define FAULT_BUSTRNL1 0x0c /* External abort on Translation -- Level 1 */ +#define FAULT_BUSTRNL2 0x0e /* External abort on Translation -- Level 2 */ +#define FAULT_ALIGN_0 0x01 /* Alignment */ +#define FAULT_ALIGN_1 0x03 /* Alignment */ +#define FAULT_TRANS_S 0x05 /* Translation -- Section */ +#define FAULT_TRANS_P 0x07 /* Translation -- Page */ +#define FAULT_DOMAIN_S 0x09 /* Domain -- Section */ +#define FAULT_DOMAIN_P 0x0b /* Domain -- Page */ +#define FAULT_PERM_S 0x0d /* Permission -- Section */ +#define FAULT_PERM_P 0x0f /* Permission -- Page */ + +#define FAULT_IMPRECISE 0x400 /* Imprecise exception (XSCALE) */ + +/* + * Address of the vector page, low and high versions. + */ +#define ARM_VECTORS_LOW 0x00000000U +#define ARM_VECTORS_HIGH 0xffff0000U + +/* + * ARM Instructions + * + * 3 3 2 2 2 + * 1 0 9 8 7 0 + * +-------+-------------------------------------------------------+ + * | cond | instruction dependant | + * |c c c c| | + * +-------+-------------------------------------------------------+ + */ + +#define INSN_SIZE 4 /* Always 4 bytes */ +#define INSN_COND_MASK 0xf0000000 /* Condition mask */ +#define INSN_COND_AL 0xe0000000 /* Always condition */ + +#endif /* !MACHINE_ARMREG_H */ diff --git a/libacc/disassem.cpp b/libacc/disassem.cpp new file mode 100644 index 0000000..ac35342 --- /dev/null +++ b/libacc/disassem.cpp @@ -0,0 +1,711 @@ +/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */ + +/*- + * Copyright (c) 1996 Mark Brinicombe. + * Copyright (c) 1996 Brini. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * RiscBSD kernel project + * + * db_disasm.c + * + * Kernel disassembler + * + * Created : 10/02/96 + * + * Structured after the sparc/sparc/db_disasm.c by David S. Miller & + * Paul Kranenburg + * + * This code is not complete. Not all instructions are disassembled. + */ + +#include <sys/cdefs.h> +//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $"); +#include <sys/param.h> +#include <stdio.h> +#include <stdarg.h> + +#include "disassem.h" +#include "armreg.h" +//#include <ddb/ddb.h> + +/* + * General instruction format + * + * insn[cc][mod] [operands] + * + * Those fields with an uppercase format code indicate that the field + * follows directly after the instruction before the separator i.e. + * they modify the instruction rather than just being an operand to + * the instruction. The only exception is the writeback flag which + * follows a operand. + * + * + * 2 - print Operand 2 of a data processing instruction + * d - destination register (bits 12-15) + * n - n register (bits 16-19) + * s - s register (bits 8-11) + * o - indirect register rn (bits 16-19) (used by swap) + * m - m register (bits 0-3) + * a - address operand of ldr/str instruction + * e - address operand of ldrh/strh instruction + * l - register list for ldm/stm instruction + * f - 1st fp operand (register) (bits 12-14) + * g - 2nd fp operand (register) (bits 16-18) + * h - 3rd fp operand (register/immediate) (bits 0-4) + * b - branch address + * t - thumb branch address (bits 24, 0-23) + * k - breakpoint comment (bits 0-3, 8-19) + * X - block transfer type + * Y - block transfer type (r13 base) + * c - comment field bits(0-23) + * p - saved or current status register + * F - PSR transfer fields + * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN + * L - co-processor transfer size + * S - set status flag + * P - fp precision + * Q - fp precision (for ldf/stf) + * R - fp rounding + * v - co-processor data transfer registers + addressing mode + * W - writeback flag + * x - instruction in hex + * # - co-processor number + * y - co-processor data processing registers + * z - co-processor register transfer registers + */ + +struct arm32_insn { + u_int mask; + u_int pattern; + const char* name; + const char* format; +}; + +static const struct arm32_insn arm32_i[] = { + { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */ + { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */ + { 0x0f000000, 0x0f000000, "swi", "c" }, + { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */ + { 0x0f000000, 0x0a000000, "b", "b" }, + { 0x0f000000, 0x0b000000, "bl", "b" }, + { 0x0fe000f0, 0x00000090, "mul", "Snms" }, + { 0x0fe000f0, 0x00200090, "mla", "Snmsd" }, + { 0x0fe000f0, 0x00800090, "umull", "Sdnms" }, + { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, + { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, + { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, + { 0x0d700000, 0x04200000, "strt", "daW" }, + { 0x0d700000, 0x04300000, "ldrt", "daW" }, + { 0x0d700000, 0x04600000, "strbt", "daW" }, + { 0x0d700000, 0x04700000, "ldrbt", "daW" }, + { 0x0c500000, 0x04000000, "str", "daW" }, + { 0x0c500000, 0x04100000, "ldr", "daW" }, + { 0x0c500000, 0x04400000, "strb", "daW" }, + { 0x0c500000, 0x04500000, "ldrb", "daW" }, + { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */ + { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */ + { 0x0e100000, 0x08000000, "stm", "XnWl" }, + { 0x0e100000, 0x08100000, "ldm", "XnWl" }, + { 0x0e1000f0, 0x00100090, "ldrb", "deW" }, + { 0x0e1000f0, 0x00000090, "strb", "deW" }, + { 0x0e1000f0, 0x001000d0, "ldrsb", "deW" }, + { 0x0e1000f0, 0x001000b0, "ldrh", "deW" }, + { 0x0e1000f0, 0x000000b0, "strh", "deW" }, + { 0x0e1000f0, 0x001000f0, "ldrsh", "deW" }, + { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */ + { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */ + { 0x0ff00ff0, 0x01000090, "swp", "dmo" }, + { 0x0ff00ff0, 0x01400090, "swpb", "dmo" }, + { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */ + { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */ + { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */ + { 0x0ffffff0, 0x012fff10, "bx", "m" }, + { 0x0fff0ff0, 0x016f0f10, "clz", "dm" }, + { 0x0ffffff0, 0x012fff30, "blx", "m" }, + { 0xfff000f0, 0xe1200070, "bkpt", "k" }, + { 0x0de00000, 0x00000000, "and", "Sdn2" }, + { 0x0de00000, 0x00200000, "eor", "Sdn2" }, + { 0x0de00000, 0x00400000, "sub", "Sdn2" }, + { 0x0de00000, 0x00600000, "rsb", "Sdn2" }, + { 0x0de00000, 0x00800000, "add", "Sdn2" }, + { 0x0de00000, 0x00a00000, "adc", "Sdn2" }, + { 0x0de00000, 0x00c00000, "sbc", "Sdn2" }, + { 0x0de00000, 0x00e00000, "rsc", "Sdn2" }, + { 0x0df00000, 0x01100000, "tst", "Dn2" }, + { 0x0df00000, 0x01300000, "teq", "Dn2" }, + { 0x0df00000, 0x01500000, "cmp", "Dn2" }, + { 0x0df00000, 0x01700000, "cmn", "Dn2" }, + { 0x0de00000, 0x01800000, "orr", "Sdn2" }, + { 0x0de00000, 0x01a00000, "mov", "Sd2" }, + { 0x0de00000, 0x01c00000, "bic", "Sdn2" }, + { 0x0de00000, 0x01e00000, "mvn", "Sd2" }, + { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" }, + { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" }, + { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" }, + { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" }, + { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" }, + { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" }, + { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" }, + { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" }, + { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" }, + { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" }, + { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" }, + { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" }, + { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" }, + { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" }, + { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" }, + { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" }, + { 0x0ff08f10, 0x0e208100, "abs", "PRfh" }, + { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" }, + { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" }, + { 0x0ff08f10, 0x0e508100, "log", "PRfh" }, + { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" }, + { 0x0ff08f10, 0x0e708100, "exp", "PRfh" }, + { 0x0ff08f10, 0x0e808100, "sin", "PRfh" }, + { 0x0ff08f10, 0x0e908100, "cos", "PRfh" }, + { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" }, + { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" }, + { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" }, + { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" }, + { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" }, + { 0x0e100f00, 0x0c000100, "stf", "QLv" }, + { 0x0e100f00, 0x0c100100, "ldf", "QLv" }, + { 0x0ff00f10, 0x0e000110, "flt", "PRgd" }, + { 0x0ff00f10, 0x0e100110, "fix", "PRdh" }, + { 0x0ff00f10, 0x0e200110, "wfs", "d" }, + { 0x0ff00f10, 0x0e300110, "rfs", "d" }, + { 0x0ff00f10, 0x0e400110, "wfc", "d" }, + { 0x0ff00f10, 0x0e500110, "rfc", "d" }, + { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" }, + { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" }, + { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" }, + { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" }, + { 0xff100010, 0xfe000010, "mcr2", "#z" }, + { 0x0f100010, 0x0e000010, "mcr", "#z" }, + { 0xff100010, 0xfe100010, "mrc2", "#z" }, + { 0x0f100010, 0x0e100010, "mrc", "#z" }, + { 0xff000010, 0xfe000000, "cdp2", "#y" }, + { 0x0f000010, 0x0e000000, "cdp", "#y" }, + { 0xfe100090, 0xfc100000, "ldc2", "L#v" }, + { 0x0e100090, 0x0c100000, "ldc", "L#v" }, + { 0xfe100090, 0xfc000000, "stc2", "L#v" }, + { 0x0e100090, 0x0c000000, "stc", "L#v" }, + { 0xf550f000, 0xf550f000, "pld", "ne" }, + { 0x0ff00ff0, 0x01000050, "qaad", "dmn" }, + { 0x0ff00ff0, 0x01400050, "qdaad", "dmn" }, + { 0x0ff00ff0, 0x01600050, "qdsub", "dmn" }, + { 0x0ff00ff0, 0x01200050, "dsub", "dmn" }, + { 0x0ff000f0, 0x01000080, "smlabb", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000a0, "smlatb", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000c0, "smlabt", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x010000e0, "smlatt", "nmsd" }, // d & n inverted!! + { 0x0ff000f0, 0x01400080, "smlalbb","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000a0, "smlaltb","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000c0, "smlalbt","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x014000e0, "smlaltt","ndms" }, // d & n inverted!! + { 0x0ff000f0, 0x01200080, "smlawb", "nmsd" }, // d & n inverted!! + { 0x0ff0f0f0, 0x012000a0, "smulwb","nms" }, // d & n inverted!! + { 0x0ff000f0, 0x012000c0, "smlawt", "nmsd" }, // d & n inverted!! + { 0x0ff0f0f0, 0x012000e0, "smulwt","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x01600080, "smulbb","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000a0, "smultb","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000c0, "smulbt","nms" }, // d & n inverted!! + { 0x0ff0f0f0, 0x016000e0, "smultt","nms" }, // d & n inverted!! + { 0x00000000, 0x00000000, NULL, NULL } +}; + +static char const arm32_insn_conditions[][4] = { + "eq", "ne", "cs", "cc", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "", "nv" +}; + +static char const insn_block_transfers[][4] = { + "da", "ia", "db", "ib" +}; + +static char const insn_stack_block_transfers[][4] = { + "ed", "ea", "fd", "fa" +}; + +static char const op_shifts[][4] = { + "lsl", "lsr", "asr", "ror" +}; + +static char const insn_fpa_rounding[][2] = { + "", "p", "m", "z" +}; + +static char const insn_fpa_precision[][2] = { + "s", "d", "e", "p" +}; + +static char const insn_fpaconstants[][8] = { + "0.0", "1.0", "2.0", "3.0", + "4.0", "5.0", "0.5", "10.0" +}; + +#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] +#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] +#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3] +#define op2_shift(x) op_shifts[(x >> 5) & 3] +#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] +#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] +#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1] +#define insn_fpaimm(x) insn_fpaconstants[x & 0x07] + +/* Local prototypes */ +static void disasm_register_shift(const disasm_interface_t *di, u_int insn); +static void disasm_print_reglist(const disasm_interface_t *di, u_int insn); +static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, + u_int loc); +static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, + u_int loc); +static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, + u_int loc); +static u_int disassemble_readword(u_int address); +static void disassemble_printaddr(u_int address); + +u_int +disasm(const disasm_interface_t *di, u_int loc, int altfmt) +{ + const struct arm32_insn *i_ptr = &arm32_i[0]; + + u_int insn; + int matchp; + int branch; + const char* f_ptr; + int fmt; + + fmt = 0; + matchp = 0; + insn = di->di_readword(loc); + +/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ + + while (i_ptr->name) { + if ((insn & i_ptr->mask) == i_ptr->pattern) { + matchp = 1; + break; + } + i_ptr++; + } + + if (!matchp) { + di->di_printf("und%s\t%08x\n", insn_condition(insn), insn); + return(loc + INSN_SIZE); + } + + /* If instruction forces condition code, don't print it. */ + if ((i_ptr->mask & 0xf0000000) == 0xf0000000) + di->di_printf("%s", i_ptr->name); + else + di->di_printf("%s%s", i_ptr->name, insn_condition(insn)); + + f_ptr = i_ptr->format; + + /* Insert tab if there are no instruction modifiers */ + + if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') { + ++fmt; + di->di_printf("\t"); + } + + while (*f_ptr) { + switch (*f_ptr) { + /* 2 - print Operand 2 of a data processing instruction */ + case '2': + if (insn & 0x02000000) { + int rotate= ((insn >> 7) & 0x1e); + + di->di_printf("#0x%08x", + (insn & 0xff) << (32 - rotate) | + (insn & 0xff) >> rotate); + } else { + disasm_register_shift(di, insn); + } + break; + /* d - destination register (bits 12-15) */ + case 'd': + di->di_printf("r%d", ((insn >> 12) & 0x0f)); + break; + /* D - insert 'p' if Rd is R15 */ + case 'D': + if (((insn >> 12) & 0x0f) == 15) + di->di_printf("p"); + break; + /* n - n register (bits 16-19) */ + case 'n': + di->di_printf("r%d", ((insn >> 16) & 0x0f)); + break; + /* s - s register (bits 8-11) */ + case 's': + di->di_printf("r%d", ((insn >> 8) & 0x0f)); + break; + /* o - indirect register rn (bits 16-19) (used by swap) */ + case 'o': + di->di_printf("[r%d]", ((insn >> 16) & 0x0f)); + break; + /* m - m register (bits 0-4) */ + case 'm': + di->di_printf("r%d", ((insn >> 0) & 0x0f)); + break; + /* a - address operand of ldr/str instruction */ + case 'a': + disasm_insn_ldrstr(di, insn, loc); + break; + /* e - address operand of ldrh/strh instruction */ + case 'e': + disasm_insn_ldrhstrh(di, insn, loc); + break; + /* l - register list for ldm/stm instruction */ + case 'l': + disasm_print_reglist(di, insn); + break; + /* f - 1st fp operand (register) (bits 12-14) */ + case 'f': + di->di_printf("f%d", (insn >> 12) & 7); + break; + /* g - 2nd fp operand (register) (bits 16-18) */ + case 'g': + di->di_printf("f%d", (insn >> 16) & 7); + break; + /* h - 3rd fp operand (register/immediate) (bits 0-4) */ + case 'h': + if (insn & (1 << 3)) + di->di_printf("#%s", insn_fpaimm(insn)); + else + di->di_printf("f%d", insn & 7); + break; + /* b - branch address */ + case 'b': + branch = ((insn << 2) & 0x03ffffff); + if (branch & 0x02000000) + branch |= 0xfc000000; + di->di_printaddr(loc + 8 + branch); + break; + /* t - blx address */ + case 't': + branch = ((insn << 2) & 0x03ffffff) | + (insn >> 23 & 0x00000002); + if (branch & 0x02000000) + branch |= 0xfc000000; + di->di_printaddr(loc + 8 + branch); + break; + /* X - block transfer type */ + case 'X': + di->di_printf("%s", insn_blktrans(insn)); + break; + /* Y - block transfer type (r13 base) */ + case 'Y': + di->di_printf("%s", insn_stkblktrans(insn)); + break; + /* c - comment field bits(0-23) */ + case 'c': + di->di_printf("0x%08x", (insn & 0x00ffffff)); + break; + /* k - breakpoint comment (bits 0-3, 8-19) */ + case 'k': + di->di_printf("0x%04x", + (insn & 0x000fff00) >> 4 | (insn & 0x0000000f)); + break; + /* p - saved or current status register */ + case 'p': + if (insn & 0x00400000) + di->di_printf("spsr"); + else + di->di_printf("cpsr"); + break; + /* F - PSR transfer fields */ + case 'F': + di->di_printf("_"); + if (insn & (1 << 16)) + di->di_printf("c"); + if (insn & (1 << 17)) + di->di_printf("x"); + if (insn & (1 << 18)) + di->di_printf("s"); + if (insn & (1 << 19)) + di->di_printf("f"); + break; + /* B - byte transfer flag */ + case 'B': + if (insn & 0x00400000) + di->di_printf("b"); + break; + /* L - co-processor transfer size */ + case 'L': + if (insn & (1 << 22)) + di->di_printf("l"); + break; + /* S - set status flag */ + case 'S': + if (insn & 0x00100000) + di->di_printf("s"); + break; + /* P - fp precision */ + case 'P': + di->di_printf("%s", insn_fpaprec(insn)); + break; + /* Q - fp precision (for ldf/stf) */ + case 'Q': + break; + /* R - fp rounding */ + case 'R': + di->di_printf("%s", insn_fparnd(insn)); + break; + /* W - writeback flag */ + case 'W': + if (insn & (1 << 21)) + di->di_printf("!"); + break; + /* # - co-processor number */ + case '#': + di->di_printf("p%d", (insn >> 8) & 0x0f); + break; + /* v - co-processor data transfer registers+addressing mode */ + case 'v': + disasm_insn_ldcstc(di, insn, loc); + break; + /* x - instruction in hex */ + case 'x': + di->di_printf("0x%08x", insn); + break; + /* y - co-processor data processing registers */ + case 'y': + di->di_printf("%d, ", (insn >> 20) & 0x0f); + + di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f, + (insn >> 16) & 0x0f, insn & 0x0f); + + di->di_printf(", %d", (insn >> 5) & 0x07); + break; + /* z - co-processor register transfer registers */ + case 'z': + di->di_printf("%d, ", (insn >> 21) & 0x07); + di->di_printf("r%d, c%d, c%d, %d", + (insn >> 12) & 0x0f, (insn >> 16) & 0x0f, + insn & 0x0f, (insn >> 5) & 0x07); + +/* if (((insn >> 5) & 0x07) != 0) + di->di_printf(", %d", (insn >> 5) & 0x07);*/ + break; + default: + di->di_printf("[%c - unknown]", *f_ptr); + break; + } + if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z') + ++f_ptr; + else if (*(++f_ptr)) { + ++fmt; + if (fmt == 1) + di->di_printf("\t"); + else + di->di_printf(", "); + } + }; + + di->di_printf("\n"); + + return(loc + INSN_SIZE); +} + + +static void +disasm_register_shift(const disasm_interface_t *di, u_int insn) +{ + di->di_printf("r%d", (insn & 0x0f)); + if ((insn & 0x00000ff0) == 0) + ; + else if ((insn & 0x00000ff0) == 0x00000060) + di->di_printf(", rrx"); + else { + if (insn & 0x10) + di->di_printf(", %s r%d", op2_shift(insn), + (insn >> 8) & 0x0f); + else + di->di_printf(", %s #%d", op2_shift(insn), + (insn >> 7) & 0x1f); + } +} + + +static void +disasm_print_reglist(const disasm_interface_t *di, u_int insn) +{ + int loop; + int start; + int comma; + + di->di_printf("{"); + start = -1; + comma = 0; + + for (loop = 0; loop < 17; ++loop) { + if (start != -1) { + if (loop == 16 || !(insn & (1 << loop))) { + if (comma) + di->di_printf(", "); + else + comma = 1; + if (start == loop - 1) + di->di_printf("r%d", start); + else + di->di_printf("r%d-r%d", start, loop - 1); + start = -1; + } + } else { + if (insn & (1 << loop)) + start = loop; + } + } + di->di_printf("}"); + + if (insn & (1 << 22)) + di->di_printf("^"); +} + +static void +disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc) +{ + int offset; + + offset = insn & 0xfff; + if ((insn & 0x032f0000) == 0x010f0000) { + /* rA = pc, immediate index */ + if (insn & 0x00800000) + loc += offset; + else + loc -= offset; + di->di_printaddr(loc + 8); + } else { + di->di_printf("[r%d", (insn >> 16) & 0x0f); + if ((insn & 0x03000fff) != 0x01000000) { + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + if (!(insn & 0x00800000)) + di->di_printf("-"); + if (insn & (1 << 25)) + disasm_register_shift(di, insn); + else + di->di_printf("#0x%03x", offset); + } + if (insn & (1 << 24)) + di->di_printf("]"); + } +} + +static void +disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) +{ + int offset; + + offset = ((insn & 0xf00) >> 4) | (insn & 0xf); + if ((insn & 0x004f0000) == 0x004f0000) { + /* rA = pc, immediate index */ + if (insn & 0x00800000) + loc += offset; + else + loc -= offset; + di->di_printaddr(loc + 8); + } else { + di->di_printf("[r%d", (insn >> 16) & 0x0f); + if ((insn & 0x01400f0f) != 0x01400000) { + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + if (!(insn & 0x00800000)) + di->di_printf("-"); + if (insn & (1 << 22)) + di->di_printf("#0x%02x", offset); + else + di->di_printf("r%d", (insn & 0x0f)); + } + if (insn & (1 << 24)) + di->di_printf("]"); + } +} + +static void +disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) +{ + if (((insn >> 8) & 0xf) == 1) + di->di_printf("f%d, ", (insn >> 12) & 0x07); + else + di->di_printf("c%d, ", (insn >> 12) & 0x0f); + + di->di_printf("[r%d", (insn >> 16) & 0x0f); + + di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); + + if (!(insn & (1 << 23))) + di->di_printf("-"); + + di->di_printf("#0x%03x", (insn & 0xff) << 2); + + if (insn & (1 << 24)) + di->di_printf("]"); + + if (insn & (1 << 21)) + di->di_printf("!"); +} + +static u_int +disassemble_readword(u_int address) +{ + return(*((u_int *)address)); +} + +static void +disassemble_printaddr(u_int address) +{ + printf("0x%08x", address); +} + +static void +disassemble_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static const disasm_interface_t disassemble_di = { + disassemble_readword, disassemble_printaddr, disassemble_printf +}; + +void +disassemble(u_int address) +{ + + (void)disasm(&disassemble_di, address, 0); +} + +/* End of disassem.c */ diff --git a/libacc/disassem.h b/libacc/disassem.h new file mode 100644 index 0000000..02747cd --- /dev/null +++ b/libacc/disassem.h @@ -0,0 +1,65 @@ +/* $NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $ */ + +/*- + * Copyright (c) 1997 Mark Brinicombe. + * Copyright (c) 1997 Causality Limited. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Brinicombe. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Define the interface structure required by the disassembler. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/disassem.h,v 1.2 2005/01/05 21:58:48 imp Exp $ + */ + +#ifndef ANDROID_MACHINE_DISASSEM_H +#define ANDROID_MACHINE_DISASSEM_H + +#include <sys/types.h> + +#if __cplusplus +extern "C" { +#endif + +typedef struct { + u_int (*di_readword)(u_int); + void (*di_printaddr)(u_int); + void (*di_printf)(const char *, ...); +} disasm_interface_t; + +/* Prototypes for callable functions */ + +u_int disasm(const disasm_interface_t *, u_int, int); +void disassemble(u_int); + +#if __cplusplus +} +#endif + +#endif /* !ANDROID_MACHINE_DISASSEM_H */ diff --git a/libacc/tests/.gitignore b/libacc/tests/.gitignore new file mode 100644 index 0000000..a26b298 --- /dev/null +++ b/libacc/tests/.gitignore @@ -0,0 +1,2 @@ +test-acc +*.out diff --git a/libacc/tests/Android.mk b/libacc/tests/Android.mk new file mode 100644 index 0000000..77e48be --- /dev/null +++ b/libacc/tests/Android.mk @@ -0,0 +1,65 @@ +LOCAL_PATH:= $(call my-dir) + +# Executable for host +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE:= acc + +LOCAL_SRC_FILES:= \ + main.cpp + +LOCAL_SHARED_LIBRARIES := \ + libacc + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_HOST_EXECUTABLE) + +# Executable for target +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE:= acc + +LOCAL_SRC_FILES:= \ + main.cpp + +LOCAL_SHARED_LIBRARIES := \ + libacc + +LOCAL_CFLAGS := -O0 -g + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + +# Runtime tests for host +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE:= accRuntimeTest + +LOCAL_SRC_FILES:= \ + runtimeTest.cpp + +LOCAL_SHARED_LIBRARIES := \ + libacc + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_HOST_EXECUTABLE) + +# Runtime tests for target +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE:= accRuntimeTest + +LOCAL_SRC_FILES:= \ + runtimeTest.cpp + +LOCAL_SHARED_LIBRARIES := \ + libacc + +LOCAL_CFLAGS := -O0 -g + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libacc/tests/data/bellard.otccex.c b/libacc/tests/data/bellard.otccex.c new file mode 100644 index 0000000..e8f0989 --- /dev/null +++ b/libacc/tests/data/bellard.otccex.c @@ -0,0 +1,126 @@ +/* #!/usr/local/bin/otcc */ +/* + * Sample OTCC C example. You can uncomment the first line and install + * otcc in /usr/local/bin to make otcc scripts ! + */ + +/* Any preprocessor directive except #define are ignored. We put this + include so that a standard C compiler can compile this code too. */ +#include <stdio.h> + +/* defines are handled, but macro arguments cannot be given. No + recursive defines are tolerated */ +#define DEFAULT_BASE 10 + +/* + * Only old style K&R prototypes are parsed. Only int arguments are + * allowed (implicit types). + * + * By benchmarking the execution time of this function (for example + * for fib(35)), you'll notice that OTCC is quite fast because it + * generates native i386 machine code. + */ +fib(n) +{ + if (n <= 2) + return 1; + else + return fib(n-1) + fib(n-2); +} + +/* Identifiers are parsed the same way as C: begins with letter or + '_', and then letters, '_' or digits */ +fact(n) +{ + /* local variables can be declared. Only 'int' type is supported */ + int i, r; + r = 1; + /* 'while' and 'for' loops are supported */ + for(i=2;i<=n;i++) + r = r * i; + return r; +} + +/* Well, we could use printf, but it would be too easy */ +print_num(n, b) +{ + int tab, p, c; + /* Numbers can be entered in decimal, hexadecimal ('0x' prefix) and + octal ('0' prefix) */ + /* more complex programs use malloc */ + tab = malloc(0x100); + p = tab; + while (1) { + c = n % b; + /* Character constants can be used */ + if (c >= 10) + c = c + 'a' - 10; + else + c = c + '0'; + *(char *)p = c; + p++; + n = n / b; + /* 'break' is supported */ + if (n == 0) + break; + } + while (p != tab) { + p--; + printf("%c", *(char *)p); + } + free(tab); +} + +/* 'main' takes standard 'argc' and 'argv' parameters */ +main(argc, argv) +{ + /* no local name space is supported, but local variables ARE + supported. As long as you do not use a globally defined + variable name as local variable (which is a bad habbit), you + won't have any problem */ + int s, n, f, base; + + /* && and || operator have the same semantics as C (left to right + evaluation and early exit) */ + if (argc != 2 && argc != 3) { + /* '*' operator is supported with explicit casting to 'int *', + 'char *' or 'int (*)()' (function pointer). Of course, 'int' + are supposed to be used as pointers too. */ + s = *(int *)argv; + help(s); + return 1; + } + /* Any libc function can be used because OTCC uses dynamic linking */ + n = atoi(*(int *)(argv + 4)); + base = DEFAULT_BASE; + if (argc >= 3) { + base = atoi(*(int *)(argv + 8)); + if (base < 2 || base > 36) { + /* external variables can be used too (here: 'stderr') */ + fprintf(stderr, "Invalid base\n"); + return 1; + } + } + printf("fib(%d) = ", n); + print_num(fib(n), base); + printf("\n"); + + printf("fact(%d) = ", n); + if (n > 12) { + printf("Overflow"); + } else { + /* why not using a function pointer ? */ + f = &fact; + print_num((*(int (*)())f)(n), base); + } + printf("\n"); + return 0; +} + +/* functions can be used before being defined */ +help(name) +{ + printf("usage: %s n [base]\n", name); + printf("Compute fib(n) and fact(n) and output the result in base 'base'\n"); +} + diff --git a/libacc/tests/data/casts.c b/libacc/tests/data/casts.c new file mode 100644 index 0000000..d3a49b4 --- /dev/null +++ b/libacc/tests/data/casts.c @@ -0,0 +1,85 @@ +void test1() { + int a = 3; + int* pb = &a; + int c = *pb; + printf("Reading from a pointer: %d %d\n", a, c); + *pb = 4; + printf("Writing to a pointer: %d\n", a); + printf("Testing casts: %d %g %g %d\n", 3, (float) 3, 4.5, (int) 4.5); +} + +void test2() { + int x = 4; + int px = &x; + // int z = * px; // An error, expected a pointer type + int y = * (int*) px; + printf("Testing reading (int*): %d\n", y); +} + +void test3() { + int px = (int) malloc(120); + * (int*) px = 8; + * (int*) (px + 4) = 9; + printf("Testing writing (int*): %d %d\n", * (int*) px, * (int*) (px + 4)); + free((void*) px); +} + +void test4() { + int x = 0x12345678; + int px = &x; + int a = * (char*) px; + int b = * (char*) (px + 1); + int c = * (char*) (px + 2); + int d = * (char*) (px + 3); + printf("Testing reading (char*): 0x%02x 0x%02x 0x%02x 0x%02x\n", a, b, c, d); +} + +void test5() { + int x = 0xFFFFFFFF; + int px = &x; + * (char*) px = 0x21; + * (char*) (px + 1) = 0x43; + * (char*) (px + 2) = 0x65; + * (char*) (px + 3) = 0x87; + printf("Testing writing (char*): 0x%08x\n", x); +} + +int f(int b) { + printf("f(%d)\n", b); + return 7 * b; +} + +void test6() { + int fp = &f; + int x = (*(int(*)()) fp)(10); + printf("Function pointer result: %d\n", x); +} + +void test7() { + int px = (int) malloc(120); + * (float*) px = 8.8f; + * (float*) (px + 4) = 9.9f; + printf("Testing read/write (float*): %g %g\n", * (float*) px, * (float*) (px + 4)); + free((void*) px); +} + +void test8() { + int px = (int) malloc(120); + * (double*) px = 8.8; + * (double*) (px + 8) = 9.9; + printf("Testing read/write (double*): %g %g\n", * (double*) px, * (double*) (px + 8)); + free((void*) px); +} + + +int main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + return 0; +} diff --git a/libacc/tests/data/char.c b/libacc/tests/data/char.c new file mode 100644 index 0000000..8c63ba2 --- /dev/null +++ b/libacc/tests/data/char.c @@ -0,0 +1,13 @@ +char ga; +char gb; + +int main() { + char a = 'c'; + char b = a * 3; + printf("a = %d, b = %d\n", a, b); + ga = 'd'; + gb = ga * 3; + printf("ga = %d, gb = %d\n", ga, gb); + return 0; +} + diff --git a/libacc/tests/data/constants.c b/libacc/tests/data/constants.c new file mode 100644 index 0000000..230109a --- /dev/null +++ b/libacc/tests/data/constants.c @@ -0,0 +1,30 @@ +#define FOO 0x10 + +int main() { + printf("0 = %d\n", 0); + printf("010 = %d\n", 010); + printf("0x10 = %d\n", FOO); + printf("'\\a' = %d\n", '\a'); + printf("'\\b' = %d\n", '\b'); + printf("'\\f' = %d\n", '\f'); + printf("'\\n' = %d\n", '\n'); + printf("'\\r' = %d\n", '\r'); + printf("'\\t' = %d\n", '\t'); + printf("'\\v' = %d\n", '\v'); + // Undefined + // printf("'\\z' = %d\n", '\z'); + printf("'\\\\' = %d\n", '\\'); + printf("'\\'' = %d\n", '\''); + printf("'\\\"' = %d\n", '\"'); + printf("'\\?' = %d\n", '\?'); + printf("'\\0' = %d\n", '\0'); + printf("'\\1' = %d\n", '\1'); + printf("'\\12' = %d\n", '\12'); + printf("'\\123' = %d\n", '\123'); + printf("'\\x0' = %d\n", '\x0'); + printf("'\\x1' = %d\n", '\x1'); + printf("'\\x12' = %d\n", '\x12'); + printf("'\\x123' = %d\n", '\x123'); + printf("'\\x1f' = %d\n", '\x1f'); + printf("'\\x1F' = %d\n", '\x1F'); +} diff --git a/libacc/tests/data/double.c b/libacc/tests/data/double.c new file mode 100644 index 0000000..5bc20a3 --- /dev/null +++ b/libacc/tests/data/double.c @@ -0,0 +1,7 @@ +double atof(char *nptr); + +int main() { + printf("Value = %g\n", atof("10.42")); + return 0; +} + diff --git a/libacc/tests/data/error.c b/libacc/tests/data/error.c new file mode 100644 index 0000000..2e08dcc --- /dev/null +++ b/libacc/tests/data/error.c @@ -0,0 +1,2 @@ +void foo; + diff --git a/libacc/tests/data/expr-ansi.c b/libacc/tests/data/expr-ansi.c new file mode 100644 index 0000000..d463659 --- /dev/null +++ b/libacc/tests/data/expr-ansi.c @@ -0,0 +1,60 @@ +/* Test operators */ + +void testInc() { int a, b; a = 3; b = a++; printf("3++ = %d %d\n", b, a); } +void testDec() { int a, b; a = 3; b = a--; printf("3-- = %d %d\n", b, a); } +void testTimes(){ printf("%d * %d = %d\n", 10, 4, 10 * 4); } +void testDiv(){ printf("%d / %d = %d\n", 11, 4, 11 / 4); } +void testMod(){ printf("%d %% %d = %d\n", 11, 4, 11 % 4); } +void testPlus(){ printf("%d + %d = %d\n", 10, 4, 10 + 4); } +void testMinus(){ printf("%d - %d = %d\n", 10, 4, 10 - 4); } +void testShiftLeft(){ printf("%d << %d = %d\n", 10, 4, 10 << 4); } +void testShiftRight(){ printf("%d >> %d = %d\n", 100, 4, 100 >> 4); } +void testLess(){ printf("%d < %d = %d\n", 10, 4, 10 < 4); } +void testLesEqual(){ printf("%d <= %d = %d\n", 10, 4, 10 <= 4); } +void testGreater(){ printf("%d > %d = %d\n", 10, 4, 10 > 4); } +void testGreaterEqual(){ printf("%d >= %d = %d\n", 10, 4, 10 >= 4); } +void testEqualTo(){ printf("%d == %d = %d\n", 10, 4, 10 == 4); } +void testNotEqualTo(){ printf("%d != %d = %d\n", 10, 4, 10 != 4); } +void testBitAnd(){ printf("%d & %d = %d\n", 10, 7, 10 & 7); } +void testBitXor(){ printf("%d ^ %d = %d\n", 10, 7, 10 ^ 7); } +void testBitOr(){ printf("%d | %d = %d\n", 10, 4, 10 | 4); } +void testAssignment(){ int a, b; a = 3; b = a; printf("b == %d\n", b); } +void testLogicalAnd(){ printf("%d && %d = %d\n", 10, 4, 10 && 4); } +void testLogicalOr(){ printf("%d || %d = %d\n", 10, 4, 10 || 4); } +void testAddressOf(){ int a; printf("&a is %d\n", &a); } +void testPointerIndirection(){ int a, b; a = &b; b = 17; printf("*%d = %d =?= %d\n", a, * (int*) a, b); } +void testNegation(){ printf("-%d = %d\n", 10, -10); } +void testUnaryPlus(){ printf("+%d = %d\n", 10, +10); } +void testUnaryNot(){ printf("!%d = %d\n", 10, !10); } +void testBitNot(){ printf("~%d = %d\n", 10, ~10); } + +int main(int a, char** b) { + testInc(); + testDec(); + testTimes(); + testDiv(); + testMod(); + testPlus(); + testMinus(); + testShiftLeft(); + testShiftRight(); + testLess(); + testLesEqual(); + testGreater(); + testGreaterEqual(); + testEqualTo(); + testNotEqualTo(); + testBitAnd(); + testBinXor(); + testBitOr(); + testAssignment(); + testLogicalAnd(); + testLogicalOr(); + testAddressOf(); + testPointerIndirection(); + testNegation(); + testUnaryPlus(); + testUnaryNot(); + testBitNot(); + return 0; +} diff --git a/libacc/tests/data/expr.c b/libacc/tests/data/expr.c new file mode 100644 index 0000000..4f2d2e7 --- /dev/null +++ b/libacc/tests/data/expr.c @@ -0,0 +1,60 @@ +/* Test operators */ + +testInc() { int a, b; a = 3; b = a++; printf("3++ = %d %d\n", b, a); } +testDec() { int a, b; a = 3; b = a--; printf("3-- = %d %d\n", b, a); } +testTimes(){ printf("%d * %d = %d\n", 10, 4, 10 * 4); } +testDiv(){ printf("%d / %d = %d\n", 11, 4, 11 / 4); } +testMod(){ printf("%d %% %d = %d\n", 11, 4, 11 % 4); } +testPlus(){ printf("%d + %d = %d\n", 10, 4, 10 + 4); } +testMinus(){ printf("%d - %d = %d\n", 10, 4, 10 - 4); } +testShiftLeft(){ printf("%d << %d = %d\n", 10, 4, 10 << 4); } +testShiftRight(){ printf("%d >> %d = %d\n", 100, 4, 100 >> 4); } +testLess(){ printf("%d < %d = %d\n", 10, 4, 10 < 4); } +testLesEqual(){ printf("%d <= %d = %d\n", 10, 4, 10 <= 4); } +testGreater(){ printf("%d > %d = %d\n", 10, 4, 10 > 4); } +testGreaterEqual(){ printf("%d >= %d = %d\n", 10, 4, 10 >= 4); } +testEqualTo(){ printf("%d == %d = %d\n", 10, 4, 10 == 4); } +testNotEqualTo(){ printf("%d != %d = %d\n", 10, 4, 10 != 4); } +testBitAnd(){ printf("%d & %d = %d\n", 10, 7, 10 & 7); } +testBitXor(){ printf("%d ^ %d = %d\n", 10, 7, 10 ^ 7); } +testBitOr(){ printf("%d | %d = %d\n", 10, 4, 10 | 4); } +testAssignment(){ int a, b; a = 3; b = a; printf("b == %d\n", b); } +testLogicalAnd(){ printf("%d && %d = %d\n", 10, 4, 10 && 4); } +testLogicalOr(){ printf("%d || %d = %d\n", 10, 4, 10 || 4); } +testAddressOf(){ int a; printf("&a is %d\n", &a); } +testPointerIndirection(){ int a, b; a = &b; b = 17; printf("*%d = %d =?= %d\n", a, * (int*) a, b); } +testNegation(){ printf("-%d = %d\n", 10, -10); } +testUnaryPlus(){ printf("+%d = %d\n", 10, +10); } +testUnaryNot(){ printf("!%d = %d\n", 10, !10); } +testBitNot(){ printf("~%d = %d\n", 10, ~10); } + +main(a,b) { + testInc(); + testDec(); + testTimes(); + testDiv(); + testMod(); + testPlus(); + testMinus(); + testShiftLeft(); + testShiftRight(); + testLess(); + testLesEqual(); + testGreater(); + testGreaterEqual(); + testEqualTo(); + testNotEqualTo(); + testBitAnd(); + testBinXor(); + testBitOr(); + testAssignment(); + testLogicalAnd(); + testLogicalOr(); + testAddressOf(); + testPointerIndirection(); + testNegation(); + testUnaryPlus(); + testUnaryNot(); + testBitNot(); + return 0; +}
\ No newline at end of file diff --git a/libacc/tests/data/expr2.c b/libacc/tests/data/expr2.c new file mode 100644 index 0000000..04b6a38 --- /dev/null +++ b/libacc/tests/data/expr2.c @@ -0,0 +1,6 @@ +/* Test operators */ + +main() { + int a; + a = a++; +} diff --git a/libacc/tests/data/float.c b/libacc/tests/data/float.c new file mode 100644 index 0000000..f48b3d1 --- /dev/null +++ b/libacc/tests/data/float.c @@ -0,0 +1,57 @@ +int ftoi(float f) { + return f; +} + +int dtoi(double d) { + return d; +} + +float itof(int i) { + return i; +} + +double itod(int i) { + return i; +} + +float f0, f1; +double d0, d1; + +void testParseConsts() { + printf("Constants: %g %g %g %g %g %g %g %g %g\n", 0e1, 0E1, 0.f, .01f, + .01e0f, 1.0e-1, 1.0e1, 1.0e+1, + .1f); +} +void testVars(float arg0, float arg1, double arg2, double arg3) { + float local0, local1; + double local2, local3; + f0 = arg0; + f1 = arg1; + d0 = arg2; + d1 = arg3; + local0 = arg0; + local1 = arg1; + local2 = arg2; + local3 = arg3; + printf("globals: %g %g %g %g\n", f0, f1, d0, d1); + printf("args: %g %g %g %g\n", arg0, arg1, arg2, arg3); + printf("locals: %g %g %g %g\n", local0, local1, local2, local3); + + + printf("cast rval: %g %g\n", * (float*) & f1, * (double*) & d1); + + * (float*) & f0 = 1.1f; + * (double*) & d0 = 3.3; + printf("cast lval: %g %g %g %g\n", f0, f1, d0, d1); +} + +int main() { + testParseConsts(); + printf("int: %d float: %g double: %g\n", 1, 2.2f, 3.3); + printf(" ftoi(1.4f)=%d\n", ftoi(1.4f)); + printf(" dtoi(2.4)=%d\n", dtoi(2.4)); + printf(" itof(3)=%g\n", itof(3)); + printf(" itod(4)=%g\n", itod(4)); + testVars(1.0f, 2.0f, 3.0, 4.0); + return 0; +} diff --git a/libacc/tests/data/floatdouble.c b/libacc/tests/data/floatdouble.c new file mode 100644 index 0000000..264c641 --- /dev/null +++ b/libacc/tests/data/floatdouble.c @@ -0,0 +1,9 @@ +int main() +{ + // Test coercing values when storing. + float a = 0.002; + double b = 0.1f; + int c = 10.002; + printf("%g %g %d\n", a, b, c); + return 0; +} diff --git a/libacc/tests/data/flops.c b/libacc/tests/data/flops.c new file mode 100644 index 0000000..40b1b28 --- /dev/null +++ b/libacc/tests/data/flops.c @@ -0,0 +1,158 @@ +// Test floating point operations. + +void unaryOps() { + // Unary ops + printf("-%g = %g\n", 1.1, -1.1); + printf("!%g = %d\n", 1.2, !1.2); + printf("!%g = %d\n", 0.0, !0.0); +} + +void binaryOps() { + printf("double op double:\n"); + printf("%g + %g = %g\n", 1.0, 2.0, 1.0 + 2.0); + printf("%g - %g = %g\n", 1.0, 2.0, 1.0 - 2.0); + printf("%g * %g = %g\n", 1.0, 2.0, 1.0 * 2.0); + printf("%g / %g = %g\n", 1.0, 2.0, 1.0 / 2.0); + + printf("float op float:\n"); + printf("%g + %g = %g\n", 1.0f, 2.0f, 1.0f + 2.0f); + printf("%g - %g = %g\n", 1.0f, 2.0f, 1.0f - 2.0f); + printf("%g * %g = %g\n", 1.0f, 2.0f, 1.0f * 2.0f); + printf("%g / %g = %g\n", 1.0f, 2.0f, 1.0f / 2.0f); + + printf("double op float:\n"); + printf("%g + %g = %g\n", 1.0, 2.0f, 1.0 + 2.0f); + printf("%g - %g = %g\n", 1.0, 2.0f, 1.0 - 2.0f); + printf("%g * %g = %g\n", 1.0, 2.0f, 1.0 * 2.0f); + printf("%g / %g = %g\n", 1.0, 2.0f, 1.0 / 2.0f); + + printf("double op int:\n"); + printf("%g + %d = %g\n", 1.0, 2, 1.0 + 2); + printf("%g - %d = %g\n", 1.0, 2, 1.0 - 2); + printf("%g * %d = %g\n", 1.0, 2, 1.0 * 2); + printf("%g / %d = %g\n", 1.0, 2, 1.0 / 2); + + printf("int op double:\n"); + printf("%d + %g = %g\n", 1, 2.0, 1 + 2.0); + printf("%d - %g = %g\n", 1, 2.0, 1 - 2.0); + printf("%d * %g = %g\n", 1, 2.0, 1 * 2.0); + printf("%d / %g = %g\n", 1, 2.0, 1 / 2.0); +} + +void comparisonTestdd(double a, double b) { + printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", + a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); +} + +void comparisonOpsdd() { + printf("double op double:\n"); + comparisonTestdd(1.0, 2.0); + comparisonTestdd(1.0, 1.0); + comparisonTestdd(2.0, 1.0); +} + + +void comparisonTestdf(double a, float b) { + printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", + a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); +} + +void comparisonOpsdf() { + printf("double op float:\n"); + comparisonTestdf(1.0, 2.0f); + comparisonTestdf(1.0, 1.0f); + comparisonTestdf(2.0, 1.0f); +} + +void comparisonTestff(float a, float b) { + printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", + a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); +} + +void comparisonOpsff() { + printf("float op float:\n"); + comparisonTestff(1.0f, 2.0f); + comparisonTestff(1.0f, 1.0f); + comparisonTestff(2.0f, 1.0f); +} + +void comparisonTestid(int a, double b) { + printf("%d op %g: < %d <= %d == %d >= %d > %d != %d\n", + a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); +} + +void comparisonOpsid() { + printf("int op double:\n"); + comparisonTestid(1, 2.0); + comparisonTestid(1, 1.0); + comparisonTestid(2, 1.0); +} +void comparisonTestdi(double a, int b) { + printf("%g op %d: < %d <= %d == %d >= %d > %d != %d\n", + a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); +} + +void comparisonOpsdi() { + printf("double op int:\n"); + comparisonTestdi(1.0f, 2); + comparisonTestdi(1.0f, 1); + comparisonTestdi(2.0f, 1); +} + +void comparisonOps() { + comparisonOpsdd(); + comparisonOpsdf(); + comparisonOpsff(); + comparisonOpsid(); + comparisonOpsdi(); +} + +int branch(double d) { + if (d) { + return 1; + } + return 0; +} + +void testBranching() { + printf("branching: %d %d %d\n", branch(-1.0), branch(0.0), branch(1.0)); +} + +void testpassi(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { + printf("testpassi: %d %d %d %d %d %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g, h, i, j, k, l); +} + +void testpassf(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j, float k, float l) { + printf("testpassf: %g %g %g %g %g %g %g %g %g %g %g %g\n", a, b, c, d, e, f, g, h, i, j, k, l); +} + +void testpassd(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k, double l) { + printf("testpassd: %g %g %g %g %g %g %g %g %g %g %g %g\n", a, b, c, d, e, f, g, h, i, j, k, l); +} + +void testpassidf(int i, double d, float f) { + printf("testpassidf: %d %g %g\n", i, d, f); +} + +void testParameterPassing() { + float x; + testpassi(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + testpassf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + testpassd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + testpassi(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); + testpassf(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); + testpassd(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); + testpassi(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); + testpassf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); + testpassd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); + testpassidf(1, 2.0, 3.0f); +} + +int main() { + unaryOps(); + binaryOps(); + comparisonOps(); + testBranching(); + testParameterPassing(); + return 0; +} diff --git a/libacc/tests/data/hello.c b/libacc/tests/data/hello.c new file mode 100644 index 0000000..585ce6c --- /dev/null +++ b/libacc/tests/data/hello.c @@ -0,0 +1,3 @@ +main(a,b) { + printf("Hello, world\n"); +} diff --git a/libacc/tests/data/locals.c b/libacc/tests/data/locals.c new file mode 100644 index 0000000..f1ef363 --- /dev/null +++ b/libacc/tests/data/locals.c @@ -0,0 +1,71 @@ +int a; + +int f() { + int a; + // Undefined variable b + // printf("f 0: a = %d b = %d\n", a, b); + printf("f 0: a = %d\n", a); + a = 2; + printf("f 1: a = %d\n", a); +} + +int g(int a) { + printf("g 0: a = %d\n", a); + a = 3; + printf("g 1: a = %d\n", a); +} + +int h(int a) { + // int a; // gcc 4.3 says error: 'a' redeclared as different kind of symbol + + printf("h 0: a = %d\n", a); + a = 4; + printf("h 1: a = %d\n", a); +} + +// Already defined global +// int h() {} +int globCheck() { + fprintf(stdout, "globCheck()\n"); +} + +int fwdCheck() { + b(); + // Undefined forward reference + // c(); +} + +int b() { + printf("b()\n"); +} + +int nested() { + int a; + printf("nested 0: a = %d\n", a); + a = 50; + printf("nested 1: a = %d\n", a); + { + int a; + printf("nested 2: a = %d\n", a); + a = 51; + printf("nested 3: a = %d\n", a); + } + printf("nested 4: a = %d\n", a); +} + +int main() { + globCheck(); + fwdCheck(); + printf("main 0: a = %d\n", a); + a = 5; + printf("main 1: a = %d\n", a); + f(); + printf("main 2: a = %d\n", a); + g(77); + printf("main 3: a = %d\n", a); + h(30); + printf("main 4: a = %d\n", a); + nested(); + printf("main 5: a = %d\n", a); + return 0; +} diff --git a/libacc/tests/data/missing-main.c b/libacc/tests/data/missing-main.c new file mode 100644 index 0000000..e73eec4 --- /dev/null +++ b/libacc/tests/data/missing-main.c @@ -0,0 +1,4 @@ +/* No main. */ + +a() { +}
\ No newline at end of file diff --git a/libacc/tests/data/otcc-ansi.c b/libacc/tests/data/otcc-ansi.c new file mode 100644 index 0000000..72580e9 --- /dev/null +++ b/libacc/tests/data/otcc-ansi.c @@ -0,0 +1,466 @@ +// #include <stdio.h> +int d, z, C, h, P, K, ac, q, G, v, Q, R, D, L, W, M; + +void E(int e) { + *(char*) D++ = e; +} + +void o() { + if (L) { + h = *(char*) L++; + if (h == 2) { + L = 0; + h = W; + } + } else + h = fgetc(Q); +} + +int X() { + return isalnum(h) | h == 95; +} + +void Y() { + if (h == 92) { + o(); + if (h == 110) + h = 10; + } +} + +void ad() { + int e, j, m; + while (isspace(h) | h == 35) { + if (h == 35) { + o(); + ad(); + if (d == 536) { + ad(); + E(32); + *(int*) d = 1; + *(int*) (d + 4) = D; + } + while (h != 10) { + E(h); + o(); + } + E(h); + E(2); + } + o(); + } + C = 0; + d = h; + if (X()) { + E(32); + M = D; + while (X()) { + E(h); + o(); + } + if (isdigit(d)) { + z = strtol(M, 0, 0); + d = 2; + } else { + *(char*) D = 32; + d = strstr(R, M - 1) - R; + *(char*) D = 0; + d = d * 8 + 256; + if (d > 536) { + d = P + d; + if (*(int*) d == 1) { + L = *(int*) (d + 4); + W = h; + o(); + ad(); + } + } + } + } else { + o(); + if (d == 39) { + d = 2; + Y(); + z = h; + o(); + o(); + } else if (d == 47 & h == 42) { + o(); + while (h) { + while (h != 42) + o(); + o(); + if (h == 47) + h = 0; + } + o(); + ad(); + } else { + e + = "++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<<Z/03e>>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; + while (j = *(char*) e++) { + m = *(char*) e++; + z = 0; + while ((C = *(char*) e++ - 98) < 0) + z = z * 64 + C + 64; + if (j == d & (m == h | m == 64)) { + if (m == h) { + o(); + d = 1; + } + break; + } + } + } + } +} + +void ae(int g) { + while( g&&g!=-1) { + *(char*) q++=g; + g=g>>8; + } +} + +void A(int e) { + int g; + while( e) { + g=*(int*) e; + *(int*) e=q-e-4; + e=g; + } +} + +int s(int g, int e) { + ae(g); + *(int*) q = e; + e = q; + q = q + 4; + return e; +} + +void H(int e) { + s(184,e); +} + +int B(int e) { + return s(233,e); +} + +int S(int j, int e) { + ae(1032325); + return s(132 + j, e); +} + +void Z(int e) { + ae( 49465); + H(0); + ae( 15); + ae( e+144); + ae( 192); +} + +void N(int j, int e) { + ae(j + 131); + s((e > -512 && e < 512) << 7 | 5, e); +} + +void T (int j) { + int g,e,m,aa; + g=1; + if( d == 34) { + H(v); + while( h!=34) { + Y (); + *(char*) v++=h; + o (); + } + *(char*) v=0; + v=v +4&-4; + o (); + ad(); + } + else { + aa=C; + m= z; + e=d; + ad(); + if( e == 2) { + H(m); + } + else if( aa == 2) { + T(0); + s(185,0); + if( e == 33)Z(m); + else ae( m); + } + else if( e == 40) { + w (); + ad(); + } + else if( e == 42) { + ad(); + e=d; + ad(); + ad(); + if( d == 42) { + ad(); + ad(); + ad(); + ad(); + e=0; + } + ad(); + T(0); + if( d == 61) { + ad(); + ae( 80); + w (); + ae( 89); + ae( 392+(e == 256)); + } + else if( e) { + if( e == 256)ae( 139); + else ae( 48655); + q++; + } + } + else if( e == 38) { + N(10,*(int*) d); + ad(); + } + else { + g=*(int*) e; + if(!g)g=dlsym(0,M); + if( d == 61&j) { + ad(); + w (); + N(6,g); + } + else if( d!= 40) { + N(8,g); + if( C == 11) { + N(0,g); + ae( z); + ad(); + } + } + } + } + if( d == 40) { + if( g == 1)ae( 80); + m= s(60545,0); + ad(); + j=0; + while( d!= 41) { + w (); + s(2393225,j); + if( d == 44)ad(); + j=j +4; + } + *(int*) m= j; + ad(); + if(!g) { + e=e +4; + *(int*) e=s(232,*(int*) e); + } + else if( g == 1) { + s(2397439,j); + j=j +4; + } + else { + s(232,g-q-5); + } + if( j)s(50305,j); + } +} + +void O (int j) { + int e,g,m; + if( j--== 1)T(1); + else { + O (j); + m= 0; + while( j == C) { + g=d; + e=z; + ad(); + if( j>8) { + m= S(e,m); + O (j); + } + else { + ae( 80); + O (j); + ae( 89); + if( j == 4|j == 5) { + Z(e); + } + else { + ae( e); + if( g == 37)ae( 146); + } + } + } + if( m&&j>8) { + m= S(e,m); + H(e^1); + B(5); + A(m); + H(e); + } + } +} + +void w() { + O(11); +} + +int U() { + w(); + return S(0, 0); +} + +void I (int j) { + int m,g,e; + if( d == 288) { + ad(); + ad(); + m= U (); + ad(); + I (j); + if( d == 312) { + ad(); + g=B(0); + A(m); + I (j); + A(g); + } + else { + A(m); + } + } + else if( d == 352|d == 504) { + e=d; + ad(); + ad(); + if( e == 352) { + g=q; + m= U (); + } + else { + if( d!= 59)w (); + ad(); + g=q; + m= 0; + if( d!= 59)m= U (); + ad(); + if( d!= 41) { + e=B(0); + w (); + B(g-q-5); + A(e); + g=e +4; + } + } + ad(); + I(&m); + B(g-q-5); + A(m); + } + else if( d == 123) { + ad(); + ab(1); + while( d!= 125)I (j); + ad(); + } + else { + if( d == 448) { + ad(); + if( d!= 59)w (); + K=B(K); + } + else if( d == 400) { + ad(); + *(int*) j=B(*(int*) j); + } + else if( d!= 59)w (); + ad(); + } +} + +void ab (int j) { + int m; + while( d == 256|d!=-1&!j) { + if( d == 256) { + ad(); + while( d!= 59) { + if( j) { + G=G +4; + *(int*) d=-G; + } + else { + *(int*) d=v; + v=v +4; + } + ad(); + if( d == 44)ad() ; + } + ad(); + } + else { + A(*(int*)(d +4)); + *(int*) d=q; + ad(); + ad(); + m= 8; + while( d!= 41) { + *(int*) d=m; + m= m +4; + ad(); + if( d == 44)ad(); + } + ad(); + K=G=0; + ae( 15042901); + m= s(60545,0); + I(0); + A(K); + ae( 50121); + *(int*) m= G; + } + } +} + +int run(int g, int e) { + return (*(int(*)()) *(int*) (P + 592))(g, e); +} + +int main(int g, int e) { + int result; + Q = stdin; + if (g-- > 1) { + e = e + 4; + Q = fopen(*(int*) e, "r"); + if (!Q) { + fprintf(stderr, "otcc-ansi.c: could not open file %s\n", *(int*) e); + return -2; + } + } + D = strcpy(R = calloc(1, 99999), " int if else while break return for define main ") + 48; + v = calloc(1, 99999); + q = ac = calloc(1, 99999); + P = calloc(1, 99999); + o(); + ad(); + ab(0); + if (mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7)) { + printf("Mprotect failed. %d\n", errno); + return -1; + } + fprintf(stderr, "otcc-ansi.c: About to execute compiled code:\n"); + result = run(g, e); + fprintf(stderr, "atcc-ansi.c: result: %d\n", result); + return result; +} + diff --git a/libacc/tests/data/otcc-noinclude.c b/libacc/tests/data/otcc-noinclude.c new file mode 100644 index 0000000..530f9e2 --- /dev/null +++ b/libacc/tests/data/otcc-noinclude.c @@ -0,0 +1,446 @@ +// #include <stdio.h> +#define k *(int*) +#define a if( +#define c ad() +#define i else +#define p while( +#define x *(char*) +#define b == +#define V =calloc(1,99999) +#define f () +#define J return +#define l ae( +#define n e) +#define u d!= +#define F int +#define y (j) +#define r m= +#define t +4 +F d,z,C,h,P,K,ac,q,G,v,Q,R,D,L,W,M; +E(n{ +x D++=e; +} +o f{ +a L){ +h=x L++; +a h b 2){ +L=0; +h=W; +} +} +i h=fgetc(Q); +} +X f{ +J isalnum(h)|h b 95; +} +Y f{ +a h b 92){ +o f; +a h b 110)h=10; +} +} +c{ +F e,j,m; +p isspace(h)|h b 35){ +a h b 35){ +o f; +c; +a d b 536){ +c; +E(32); +k d=1; +k(d t)=D; +} +p h!=10){ +E(h); +o f; +} +E(h); +E(2); +} +o f; +} +C=0; +d=h; +a X f){ +E(32); +M=D; +p X f){ +E(h); +o f; +} +a isdigit(d)){ +z=strtol(M,0,0); +d=2; +} +i{ +x D=32; +d=strstr(R,M-1)-R; +x D=0; +d=d*8+256; +a d>536){ +d=P+d; +a k d b 1){ +L=k(d t); +W=h; +o f; +c; +} +} +} +} +i{ +o f; +a d b 39){ +d=2; +Y f; +z=h; +o f; +o f; +} +i a d b 47&h b 42){ +o f; +p h){ +p h!=42)o f; +o f; +a h b 47)h=0; +} +o f; +c; +} +i{ +e="++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<<Z/03e>>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; +p j=x e++){ +r x e++; +z=0; +p(C=x e++-98)<0)z=z*64+C+64; +a j b d&(m b h|m b 64)){ +a m b h){ +o f; +d=1; +} +break; +} +} +} +} +} +l g){ +p g&&g!=-1){ +x q++=g; +g=g>>8; +} +} +A(n{ +F g; +p n{ +g=k e; +k e=q-e-4; +e=g; +} +} +s(g,n{ +l g); +k q=e; +e=q; +q=q t; +J e; +} +H(n{ +s(184,n; +} +B(n{ +J s(233,n; +} +S(j,n{ +l 1032325); +J s(132+j,n; +} +Z(n{ +l 49465); +H(0); +l 15); +l e+144); +l 192); +} +N(j,n{ +l j+131); +s((e<512)<<7|5,n; +} +T y{ +F g,e,m,aa; +g=1; +a d b 34){ +H(v); +p h!=34){ +Y f; +x v++=h; +o f; +} +x v=0; +v=v t&-4; +o f; +c; +} +i{ +aa=C; +r z; +e=d; +c; +a e b 2){ +H(m); +} +i a aa b 2){ +T(0); +s(185,0); +a e b 33)Z(m); +i l m); +} +i a e b 40){ +w f; +c; +} +i a e b 42){ +c; +e=d; +c; +c; +a d b 42){ +c; +c; +c; +c; +e=0; +} +c; +T(0); +a d b 61){ +c; +l 80); +w f; +l 89); +l 392+(e b 256)); +} +i a n{ +a e b 256)l 139); +i l 48655); +q++; +} +} +i a e b 38){ +N(10,k d); +c; +} +i{ +g=k e; +a!g)g=dlsym(0,M); +a d b 61&j){ +c; +w f; +N(6,g); +} +i a u 40){ +N(8,g); +a C b 11){ +N(0,g); +l z); +c; +} +} +} +} +a d b 40){ +a g b 1)l 80); +r s(60545,0); +c; +j=0; +p u 41){ +w f; +s(2393225,j); +a d b 44)c; +j=j t; +} +k r j; +c; +a!g){ +e=e t; +k e=s(232,k n; +} +i a g b 1){ +s(2397439,j); +j=j t; +} +i{ +s(232,g-q-5); +} +a j)s(50305,j); +} +} +O y{ +F e,g,m; +a j--b 1)T(1); +i{ +O y; +r 0; +p j b C){ +g=d; +e=z; +c; +a j>8){ +r S(e,m); +O y; +} +i{ +l 80); +O y; +l 89); +a j b 4|j b 5){ +Z(n; +} +i{ +l n; +a g b 37)l 146); +} +} +} +a m&&j>8){ +r S(e,m); +H(e^1); +B(5); +A(m); +H(n; +} +} +} +w f{ +O(11); +} +U f{ +w f; +J S(0,0); +} +I y{ +F m,g,e; +a d b 288){ +c; +c; +r U f; +c; +I y; +a d b 312){ +c; +g=B(0); +A(m); +I y; +A(g); +} +i{ +A(m); +} +} +i a d b 352|d b 504){ +e=d; +c; +c; +a e b 352){ +g=q; +r U f; +} +i{ +a u 59)w f; +c; +g=q; +r 0; +a u 59)r U f; +c; +a u 41){ +e=B(0); +w f; +B(g-q-5); +A(n; +g=e t; +} +} +c; +I(&m); +B(g-q-5); +A(m); +} +i a d b 123){ +c; +ab(1); +p u 125)I y; +c; +} +i{ +a d b 448){ +c; +a u 59)w f; +K=B(K); +} +i a d b 400){ +c; +k j=B(k j); +} +i a u 59)w f; +c; +} +} +ab y{ +F m; +p d b 256|u-1&!j){ +a d b 256){ +c; +p u 59){ +a j){ +G=G t; +k d=-G; +} +i{ +k d=v; +v=v t; +} +c; +a d b 44)c; +} +c; +} +i{ +A(k(d t)); +k d=q; +c; +c; +r 8; +p u 41){ +k d=m; +r m t; +c; +a d b 44)c; +} +c; +K=G=0; +l 15042901); +r s(60545,0); +I(0); +A(K); +l 50121); +k r G; +} +} +} +main(g,n{ +Q=stdin; +a g-->1){ +e=e t; +Q=fopen(k e,"r"); +} +D=strcpy(R V," int if else while break return for define main ")+48; +v V; +q=ac V; +P V; +o f; +c; +ab(0); +J(*(int(*)f)k(P+592))(g,n; +} + diff --git a/libacc/tests/data/otcc.c b/libacc/tests/data/otcc.c new file mode 100644 index 0000000..433ae2e --- /dev/null +++ b/libacc/tests/data/otcc.c @@ -0,0 +1,448 @@ +#include <stdio.h> +#define k *(int*) +#define a if( +#define c ad() +#define i else +#define p while( +#define x *(char*) +#define b == +#define V =calloc(1,99999) +#define f () +#define J return +#define l ae( +#define n e) +#define u d!= +#define F int +#define y (j) +#define r m= +#define t +4 +F d,z,C,h,P,K,ac,q,G,v,Q,R,D,L,W,M; +E(n{ +x D++=e; +} +o f{ +a L){ +h=x L++; +a h b 2){ +L=0; +h=W; +} +} +i h=fgetc(Q); +} +X f{ +J isalnum(h)|h b 95; +} +Y f{ +a h b 92){ +o f; +a h b 110)h=10; +} +} +c{ +F e,j,m; +p isspace(h)|h b 35){ +a h b 35){ +o f; +c; +a d b 536){ +c; +E(32); +k d=1; +k(d t)=D; +} +p h!=10){ +E(h); +o f; +} +E(h); +E(2); +} +o f; +} +C=0; +d=h; +a X f){ +E(32); +M=D; +p X f){ +E(h); +o f; +} +a isdigit(d)){ +z=strtol(M,0,0); +d=2; +} +i{ +x D=32; +d=strstr(R,M-1)-R; +x D=0; +d=d*8+256; +a d>536){ +d=P+d; +a k d b 1){ +L=k(d t); +W=h; +o f; +c; +} +} +} +} +i{ +o f; +a d b 39){ +d=2; +Y f; +z=h; +o f; +o f; +} +i a d b 47&h b 42){ +o f; +p h){ +p h!=42)o f; +o f; +a h b 47)h=0; +} +o f; +c; +} +i{ +e="++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<<Z/03e>>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; +p j=x e++){ +r x e++; +z=0; +p(C=x e++-98)<0)z=z*64+C+64; +a j b d&(m b h|m b 64)){ +a m b h){ +o f; +d=1; +} +break; +} +} +} +} +} +l g){ +p g&&g!=-1){ +x q++=g; +g=g>>8; +} +} +A(n{ +F g; +p n{ +g=k e; +k e=q-e-4; +e=g; +} +} +s(g,n{ +l g); +k q=e; +e=q; +q=q t; +J e; +} +H(n{ +s(184,n; +} +B(n{ +J s(233,n; +} +S(j,n{ +l 1032325); +J s(132+j,n; +} +Z(n{ +l 49465); +H(0); +l 15); +l e+144); +l 192); +} +N(j,n{ +l j+131); +s((e<512)<<7|5,n; +} +T y{ +F g,e,m,aa; +g=1; +a d b 34){ +H(v); +p h!=34){ +Y f; +x v++=h; +o f; +} +x v=0; +v=v t&-4; +o f; +c; +} +i{ +aa=C; +r z; +e=d; +c; +a e b 2){ +H(m); +} +i a aa b 2){ +T(0); +s(185,0); +a e b 33)Z(m); +i l m); +} +i a e b 40){ +w f; +c; +} +i a e b 42){ +c; +e=d; +c; +c; +a d b 42){ +c; +c; +c; +c; +e=0; +} +c; +T(0); +a d b 61){ +c; +l 80); +w f; +l 89); +l 392+(e b 256)); +} +i a n{ +a e b 256)l 139); +i l 48655); +q++; +} +} +i a e b 38){ +N(10,k d); +c; +} +i{ +g=k e; +a!g)g=dlsym(0,M); +a d b 61&j){ +c; +w f; +N(6,g); +} +i a u 40){ +N(8,g); +a C b 11){ +N(0,g); +l z); +c; +} +} +} +} +a d b 40){ +a g b 1)l 80); +r s(60545,0); +c; +j=0; +p u 41){ +w f; +s(2393225,j); +a d b 44)c; +j=j t; +} +k r j; +c; +a!g){ +e=e t; +k e=s(232,k n; +} +i a g b 1){ +s(2397439,j); +j=j t; +} +i{ +s(232,g-q-5); +} +a j)s(50305,j); +} +} +O y{ +F e,g,m; +a j--b 1)T(1); +i{ +O y; +r 0; +p j b C){ +g=d; +e=z; +c; +a j>8){ +r S(e,m); +O y; +} +i{ +l 80); +O y; +l 89); +a j b 4|j b 5){ +Z(n; +} +i{ +l n; +a g b 37)l 146); +} +} +} +a m&&j>8){ +r S(e,m); +H(e^1); +B(5); +A(m); +H(n; +} +} +} +w f{ +O(11); +} +U f{ +w f; +J S(0,0); +} +I y{ +F m,g,e; +a d b 288){ +c; +c; +r U f; +c; +I y; +a d b 312){ +c; +g=B(0); +A(m); +I y; +A(g); +} +i{ +A(m); +} +} +i a d b 352|d b 504){ +e=d; +c; +c; +a e b 352){ +g=q; +r U f; +} +i{ +a u 59)w f; +c; +g=q; +r 0; +a u 59)r U f; +c; +a u 41){ +e=B(0); +w f; +B(g-q-5); +A(n; +g=e t; +} +} +c; +I(&m); +B(g-q-5); +A(m); +} +i a d b 123){ +c; +ab(1); +p u 125)I y; +c; +} +i{ +a d b 448){ +c; +a u 59)w f; +K=B(K); +} +i a d b 400){ +c; +k j=B(k j); +} +i a u 59)w f; +c; +} +} +ab y{ +F m; +p d b 256|u-1&!j){ +a d b 256){ +c; +p u 59){ +a j){ +G=G t; +k d=-G; +} +i{ +k d=v; +v=v t; +} +c; +a d b 44)c; +} +c; +} +i{ +A(k(d t)); +k d=q; +c; +c; +r 8; +p u 41){ +k d=m; +r m t; +c; +a d b 44)c; +} +c; +K=G=0; +l 15042901); +r s(60545,0); +I(0); +A(K); +l 50121); +k r G; +} +} +} +main(g,n{ +Q=stdin; +a g-->1){ +e=e t; +Q=fopen(k e,"r"); +} +D=strcpy(R V," int if else while break return for define main ")+48; +v V; +q=ac V; +P V; +o f; +c; +ab(0); +mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7); +fprintf(stderr, "otcc.c: about to execute compiled code.\n"); +J(*(int(*)f)k(P+592))(g,n; +} + diff --git a/libacc/tests/data/pointers.c b/libacc/tests/data/pointers.c new file mode 100644 index 0000000..461ebeb --- /dev/null +++ b/libacc/tests/data/pointers.c @@ -0,0 +1,15 @@ +int main() { + int* pa = (int*) malloc(100); + int* pb = pa + 1; + int* pc = (int*) 0; + *pa = 1; + *pb = 2; + printf("Pointer difference: %d %d\n", pb - pa, ((int) pb) - ((int) pa)); + int c = * (pa + 1); + printf("Pointer addition: %d\n", c); + printf("Pointer comparison to zero: %d %d %d\n", pa == 0, pb == 0, pc == 0); + printf("Pointer comparison: %d %d %d %d %d\n", pa < pb, pa == pb, pa > pb, ! pb, ! pc); + free(pa); + return 0; +} + diff --git a/libacc/tests/data/returnval-ansi.c b/libacc/tests/data/returnval-ansi.c new file mode 100644 index 0000000..6b53fd5 --- /dev/null +++ b/libacc/tests/data/returnval-ansi.c @@ -0,0 +1,8 @@ + +int main(int argc, char** argv) { + return f(); +} + +int f() { + return 42; +} diff --git a/libacc/tests/data/returnval.c b/libacc/tests/data/returnval.c new file mode 100644 index 0000000..1cf5bae --- /dev/null +++ b/libacc/tests/data/returnval.c @@ -0,0 +1,4 @@ +main() { + return 42; +} + diff --git a/libacc/tests/data/rollo3.c b/libacc/tests/data/rollo3.c new file mode 100644 index 0000000..b21c12f --- /dev/null +++ b/libacc/tests/data/rollo3.c @@ -0,0 +1,9 @@ + +float fabsf(float); + +int main(void* con, int ft, int launchID) +{ + float f = fabsf(-10.0f); + return f; +} + diff --git a/libacc/tests/data/simplest.c b/libacc/tests/data/simplest.c new file mode 100644 index 0000000..bae895a --- /dev/null +++ b/libacc/tests/data/simplest.c @@ -0,0 +1 @@ +main() {} diff --git a/libacc/tests/data/testStringConcat.c b/libacc/tests/data/testStringConcat.c new file mode 100644 index 0000000..bf06ae1 --- /dev/null +++ b/libacc/tests/data/testStringConcat.c @@ -0,0 +1,4 @@ +int main() { + return printf("Hello" "," " world\n"); +} + diff --git a/libacc/tests/main.cpp b/libacc/tests/main.cpp new file mode 100644 index 0000000..5e9e816 --- /dev/null +++ b/libacc/tests/main.cpp @@ -0,0 +1,148 @@ +/* + * 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. + * + */ + +#include <ctype.h> +#include <dlfcn.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__arm__) +#include <unistd.h> +#endif + +#include <acc/acc.h> + + +typedef int (*MainPtr)(int, char**); +// This is a separate function so it can easily be set by breakpoint in gdb. +int run(MainPtr mainFunc, int argc, char** argv) { + return mainFunc(argc, argv); +} + +// Private API for development: + +extern "C" +void accDisassemble(ACCscript* script); + +ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) { + return (ACCvoid*) dlsym(RTLD_DEFAULT, name); +} + +int main(int argc, char** argv) { + const char* inFile = NULL; + bool printListing; + bool runResults = false; + FILE* in = stdin; + int i; + for (i = 1; i < argc; i++) { + char* arg = argv[i]; + if (arg[0] == '-') { + switch (arg[1]) { + case 'S': + printListing = true; + break; + case 'R': + runResults = true; + break; + default: + fprintf(stderr, "Unrecognized flag %s\n", arg); + return 3; + } + } else if (inFile == NULL) { + inFile = arg; + } else { + break; + } + } + + if (! inFile) { + fprintf(stderr, "input file required\n"); + return 2; + } + + if (inFile) { + in = fopen(inFile, "r"); + if (!in) { + fprintf(stderr, "Could not open input file %s\n", inFile); + return 1; + } + } + + fseek(in, 0, SEEK_END); + size_t fileSize = (size_t) ftell(in); + rewind(in); + ACCchar* text = new ACCchar[fileSize + 1]; + size_t bytesRead = fread(text, 1, fileSize, in); + if (bytesRead != fileSize) { + fprintf(stderr, "Could not read all of file %s\n", inFile); + } + + text[fileSize] = '\0'; + + ACCscript* script = accCreateScript(); + + const ACCchar* scriptSource[] = {text}; + accScriptSource(script, 1, scriptSource, NULL); + delete[] text; + + accRegisterSymbolCallback(script, symbolLookup, NULL); + + accCompileScript(script); + int result = accGetError(script); + MainPtr mainPointer = 0; + if (result != 0) { + char buf[1024]; + accGetScriptInfoLog(script, sizeof(buf), NULL, buf); + fprintf(stderr, "%s", buf); + goto exit; + } + + { + ACCsizei numPragmaStrings; + accGetPragmas(script, &numPragmaStrings, 0, NULL); + if (numPragmaStrings) { + char** strings = new char*[numPragmaStrings]; + accGetPragmas(script, NULL, numPragmaStrings, strings); + for(ACCsizei i = 0; i < numPragmaStrings; i += 2) { + fprintf(stderr, "#pragma %s(%s)\n", strings[i], strings[i+1]); + } + delete[] strings; + } + } + + if (printListing) { + accDisassemble(script); + } + + if (runResults) { + accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer); + + result = accGetError(script); + if (result != ACC_NO_ERROR) { + fprintf(stderr, "Could not find main: %d\n", result); + } else { + fprintf(stderr, "Executing compiled code:\n"); + int codeArgc = argc - i + 1; + char** codeArgv = argv + i - 1; + codeArgv[0] = (char*) (inFile ? inFile : "stdin"); + result = run(mainPointer, codeArgc, codeArgv); + fprintf(stderr, "result: %d\n", result); + } + } + +exit: + + accDeleteScript(script); + + return result; +} diff --git a/libacc/tests/runtimeTest.cpp b/libacc/tests/runtimeTest.cpp new file mode 100644 index 0000000..d59e1d1 --- /dev/null +++ b/libacc/tests/runtimeTest.cpp @@ -0,0 +1,110 @@ +/* + * RuntimeTest for ACC compiler. + * + */ + +#include <ctype.h> +#include <dlfcn.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__arm__) +#include <unistd.h> +#endif + +#include <acc/acc.h> + + +typedef void (*ScriptPtr)(); + +// This is a separate function so it can easily be set by breakpoint in gdb. +void run(ScriptPtr scriptFn) { + scriptFn(); +} + +// Private API for development: + +extern "C" +void accDisassemble(ACCscript* script); + +void op_int(int a) { + printf("op_int(%d)\n", a); +} + +void op_float12(float a, float b, float c, float d, + float e, float f, float g, float h, + float i, float j, float k, float l) { + printf("op_float12(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g)\n", + a, b, c, d, e, f, g, h, i, j, k, l); +} + +const char* text = "void op_int(int a);\n" + "void op_float12(float a, float b, float c, float d,\n" + " float e, float f, float g, float h,\n" + " float i, float j, float k, float l);\n" + "void script() {\n" + " op_int(123);\n" + " op_float12(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0);\n" + "}\n"; + +ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) { + if (strcmp("op_int", name) == 0) { + return (ACCvoid*) op_int; + } + if (strcmp("op_float12", name) == 0) { + return (ACCvoid*) op_float12; + } + return (ACCvoid*) dlsym(RTLD_DEFAULT, name); +} + +int main(int argc, char** argv) { + ACCscript* script = accCreateScript(); + + accRegisterSymbolCallback(script, symbolLookup, NULL); + + const ACCchar* scriptSource[] = {text}; + accScriptSource(script, 1, scriptSource, NULL); + + accCompileScript(script); + int result = accGetError(script); + ScriptPtr scriptPointer = 0; + if (result != 0) { + char buf[1024]; + accGetScriptInfoLog(script, sizeof(buf), NULL, buf); + fprintf(stderr, "%s", buf); + goto exit; + } + + { + ACCsizei numPragmaStrings; + accGetPragmas(script, &numPragmaStrings, 0, NULL); + if (numPragmaStrings) { + char** strings = new char*[numPragmaStrings]; + accGetPragmas(script, NULL, numPragmaStrings, strings); + for(ACCsizei i = 0; i < numPragmaStrings; i += 2) { + fprintf(stderr, "#pragma %s(%s)\n", strings[i], strings[i+1]); + } + delete[] strings; + } + } + + accGetScriptLabel(script, "script", (ACCvoid**) & scriptPointer); + + result = accGetError(script); + if (result != ACC_NO_ERROR) { + fprintf(stderr, "Could not find script: %d\n", result); + } else { + fprintf(stderr, "Executing script:\n"); + run(scriptPointer); + } + + +exit: + + accDeleteScript(script); + + return result; +} diff --git a/libacc/tests/test b/libacc/tests/test new file mode 100755 index 0000000..ef10500 --- /dev/null +++ b/libacc/tests/test @@ -0,0 +1,6 @@ +#!/bin/bash + +SCRIPT_DIR=`dirname $BASH_SOURCE` +cd $SCRIPT_DIR +python test.py + diff --git a/libacc/tests/test.py b/libacc/tests/test.py new file mode 100644 index 0000000..909886d --- /dev/null +++ b/libacc/tests/test.py @@ -0,0 +1,295 @@ +# +# Test the acc compiler + +import unittest +import subprocess +import os +import sets + +gArmInitialized = False + +def compile(args): + proc = subprocess.Popen(["acc"] + args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + result = proc.communicate() + return result + +def runCmd(args): + proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = proc.communicate() + return result[0].strip() + +def which(item): + return runCmd(["which", item]) + +def fileType(item): + return runCmd(["file", item]) + +def outputCanRun(): + ft = fileType(which("acc")) + return ft.find("ELF 32-bit LSB executable, Intel 80386") >= 0 + +def adb(args): + return runCmd(["adb"] + args) + +def setupArm(): + global gArmInitialized + if gArmInitialized: + return + print "Setting up arm" + adb(["remount"]) + adb(["shell", "rm", "/system/bin/acc"]) + adb(["shell", "mkdir", "/system/bin/accdata"]) + adb(["shell", "mkdir", "/system/bin/accdata/data"]) + # Clear out old data TODO: handle recursion + adb(["shell", "rm", "/system/bin/accdata/data/*"]) + # Copy over data + for root, dirs, files in os.walk("data"): + for d in dirs: + adb(["shell", "mkdir", os.path.join(root, d)]) + for f in files: + adb(["push", os.path.join(root, f), os.path.join("/system/bin/accdata", root, f)]) + # Copy over compiler + adb(["sync"]) + gArmInitialized = True + +def compileArm(args): + setupArm() + proc = subprocess.Popen(["adb", "shell", "/system/bin/acc"] + args, stdout=subprocess.PIPE) + result = proc.communicate() + return result[0].replace("\r","") + +def compare(a, b): + if a != b: + firstDiff = firstDifference(a, b) + print "Strings differ at character %d. Common: %s. Difference '%s' != '%s'" % ( + firstDiff, a[0:firstDiff], safeAccess(a, firstDiff), safeAccess(b, firstDiff)) + +def safeAccess(s, i): + if 0 <= i < len(s): + return s[i] + else: + return '?' + +def firstDifference(a, b): + commonLen = min(len(a), len(b)) + for i in xrange(0, commonLen): + if a[i] != b[i]: + return i + return commonLen + +# a1 and a2 are the expected stdout and stderr. +# b1 and b2 are the actual stdout and stderr. +# Compare the two, sets. Allow any individual line +# to appear in either stdout or stderr. This is because +# the way we obtain output on the ARM combines both +# streams into one sequence. + +def compareOuput(a1,a2,b1,b2): + while True: + totalLen = len(a1) + len(a2) + len(b1) + len(b2) + a1, b1 = matchCommon(a1, b1) + a1, b2 = matchCommon(a1, b2) + a2, b1 = matchCommon(a2, b1) + a2, b2 = matchCommon(a2, b2) + newTotalLen = len(a1) + len(a2) + len(b1) + len(b2) + if newTotalLen == 0: + return True + if newTotalLen == totalLen: + print "Failed at %d %d %d %d" % (len(a1), len(a2), len(b1), len(b2)) + print "a1", a1 + print "a2", a2 + print "b1", b1 + print "b2", b2 + return False + +def matchCommon(a, b): + """Remove common items from the beginning of a and b, + return just the tails that are different.""" + while len(a) > 0 and len(b) > 0 and a[0] == b[0]: + a = a[1:] + b = b[1:] + return a, b + +def rewritePaths(args): + return [rewritePath(x) for x in args] + +def rewritePath(p): + """Take a path that's correct on the x86 and convert to a path + that's correct on ARM.""" + if p.startswith("data/"): + p = "/system/bin/accdata/" + p + return p + +class TestACC(unittest.TestCase): + + def checkResult(self, out, err, stdErrResult, stdOutResult=""): + a1 = out.splitlines() + a2 = err.splitlines() + b2 = stdErrResult.splitlines() + b1 = stdOutResult.splitlines() + self.assertEqual(True, compareOuput(a1,a2,b1,b2)) + + def compileCheck(self, args, stdErrResult, stdOutResult="", + targets=['arm', 'x86']): + targetSet = sets.ImmutableSet(targets) + if 'x86' in targetSet: + out, err = compile(args) + self.checkResult(out, err, stdErrResult, stdOutResult) + if 'arm' in targetSet: + out = compileArm(rewritePaths(args)) + self.checkResult(out, "", stdErrResult, stdOutResult) + + def compileCheckArm(self, args, result): + self.assertEqual(compileArm(args), result) + + def testCompileReturnVal(self): + self.compileCheck(["data/returnval-ansi.c"], "") + + def testCompileOTCCANSII(self): + self.compileCheck(["data/otcc-ansi.c"], "", "", ['x86']) + + def testRunReturnVal(self): + self.compileCheck(["-R", "data/returnval-ansi.c"], + "Executing compiled code:\nresult: 42\n") + + def testStringLiteralConcatenation(self): + self.compileCheck(["-R", "data/testStringConcat.c"], + "Executing compiled code:\nresult: 13\n", "Hello, world\n") + + def testRunOTCCANSI(self): + self.compileCheck(["-R", "data/otcc-ansi.c", "data/returnval.c"], + "Executing compiled code:\notcc-ansi.c: About to execute compiled code:\natcc-ansi.c: result: 42\nresult: 42\n", "", + ['x86']) + + def testRunOTCCANSI2(self): + self.compileCheck(["-R", "data/otcc-ansi.c", "data/otcc.c", "data/returnval.c"], + "Executing compiled code:\notcc-ansi.c: About to execute compiled code:\notcc.c: about to execute compiled code.\natcc-ansi.c: result: 42\nresult: 42\n", "",['x86']) + + def testRunConstants(self): + self.compileCheck(["-R", "data/constants.c"], + "Executing compiled code:\nresult: 12\n", + "0 = 0\n010 = 8\n0x10 = 16\n'\\a' = 7\n'\\b' = 8\n'\\f' = 12\n'\\n' = 10\n'\\r' = 13\n'\\t' = 9\n'\\v' = 11\n'\\\\' = 92\n'\\'' = 39\n" + + "'\\\"' = 34\n'\\?' = 63\n'\\0' = 0\n'\\1' = 1\n'\\12' = 10\n'\\123' = 83\n'\\x0' = 0\n'\\x1' = 1\n'\\x12' = 18\n'\\x123' = 291\n'\\x1f' = 31\n'\\x1F' = 31\n") + + def testRunFloat(self): + self.compileCheck(["-R", "data/float.c"], + "Executing compiled code:\nresult: 0\n", + """Constants: 0 0 0 0.01 0.01 0.1 10 10 0.1 +int: 1 float: 2.2 double: 3.3 + ftoi(1.4f)=1 + dtoi(2.4)=2 + itof(3)=3 + itod(4)=4 +globals: 1 2 3 4 +args: 1 2 3 4 +locals: 1 2 3 4 +cast rval: 2 4 +cast lval: 1.1 2 3.3 4 +""") + + def testRunFlops(self): + self.compileCheck(["-R", "data/flops.c"], + """Executing compiled code: +result: 0""", +"""-1.1 = -1.1 +!1.2 = 0 +!0 = 1 +double op double: +1 + 2 = 3 +1 - 2 = -1 +1 * 2 = 2 +1 / 2 = 0.5 +float op float: +1 + 2 = 3 +1 - 2 = -1 +1 * 2 = 2 +1 / 2 = 0.5 +double op float: +1 + 2 = 3 +1 - 2 = -1 +1 * 2 = 2 +1 / 2 = 0.5 +double op int: +1 + 2 = 3 +1 - 2 = -1 +1 * 2 = 2 +1 / 2 = 0.5 +int op double: +1 + 2 = 3 +1 - 2 = -1 +1 * 2 = 2 +1 / 2 = 0.5 +double op double: +1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 +1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 +2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 +double op float: +1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 +1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 +2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 +float op float: +1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 +1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 +2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 +int op double: +1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 +1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 +2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 +double op int: +1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 +1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 +2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 +branching: 1 0 1 +testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 +testpassidf: 1 2 3 +""") + def testCasts(self): + self.compileCheck(["-R", "data/casts.c"], + """Executing compiled code: +result: 0""", """Reading from a pointer: 3 3 +Writing to a pointer: 4 +Testing casts: 3 3 4.5 4 +Testing reading (int*): 4 +Testing writing (int*): 8 9 +Testing reading (char*): 0x78 0x56 0x34 0x12 +Testing writing (char*): 0x87654321 +f(10) +Function pointer result: 70 +Testing read/write (float*): 8.8 9.9 +Testing read/write (double*): 8.8 9.9 +""") + + def testChar(self): + self.compileCheck(["-R", "data/char.c"], """Executing compiled code: +result: 0""", """a = 99, b = 41 +ga = 100, gb = 44""") + + def testPointerArithmetic(self): + self.compileCheck(["-R", "data/pointers.c"], """Executing compiled code: +result: 0""", """Pointer difference: 1 4 +Pointer addition: 2 +Pointer comparison to zero: 0 0 1 +Pointer comparison: 1 0 0 0 1 +""") + def testRollo3(self): + self.compileCheck(["-R", "data/rollo3.c"], """Executing compiled code: +result: 10""", """""") + + def testFloatDouble(self): + self.compileCheck(["-R", "data/floatdouble.c"], """Executing compiled code: +result: 0""", """0.002 0.1 10""") + + +if __name__ == '__main__': + if not outputCanRun(): + print "Many tests are expected to fail, because acc is not a 32-bit x86 Linux executable." + unittest.main() + |
