Merge change 7050

* changes:
  Implement pointer arithmetic.
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
index 7f726f1..78f3306 100644
--- a/libacc/acc.cpp
+++ b/libacc/acc.cpp
@@ -269,6 +269,10 @@
             }
         }
 
+        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.
@@ -545,6 +549,8 @@
             return tag == TY_FLOAT || tag == TY_DOUBLE;
         }
 
+        Type* mkpInt;
+
     private:
         Vector<Type*> mExpressionStack;
         CodeBuf* pCodeBuf;
@@ -615,18 +621,7 @@
 
         /* load immediate value */
         virtual void li(int t, Type* pType) {
-            LOG_API("li(%d);\n", t);
-            if (t >= 0 && t < 255) {
-                 o4(0xE3A00000 + t); // mov    r0, #0
-            } else if (t >= -256 && t < 0) {
-                // mvn means move constant ^ ~0
-                o4(0xE3E00001 - t); // mvn    r0, #0
-            } else {
-                  o4(0xE51F0000); //         ldr    r0, .L3
-                  o4(0xEA000000); //         b .L99
-                  o4(t);          // .L3:   .word 0
-                                  // .L99:
-            }
+            liReg(t, 0);
             setR0Type(pType);
         }
 
@@ -774,50 +769,102 @@
             LOG_API("genOp(%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}
+            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;
+                    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();
                 }
-                popType();
             } else {
                 Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType;
                 if (pResultType->tag == TY_DOUBLE) {
@@ -873,9 +920,9 @@
                 switch(tag) {
                     case TY_INT:
                         o4(0xE3A01000); // mov    r1, #0
-                        o4(0xE1510000); // cmp r1, r1
-                        o4(0x03A00000); // moveq r0,#0
-                        o4(0x13A00001); // movne r0,#1
+                        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);
@@ -1589,6 +1636,22 @@
             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
@@ -1897,12 +1960,53 @@
             bool isFloatR0 = isFloatTag(tagR0);
             bool isFloatTOS = isFloatTag(tagTOS);
             if (!isFloatR0 && !isFloatTOS) {
-                // TODO: Deal with pointer arithmetic
-                o(0x59); /* pop %ecx */
-                o(decodeOp(op));
-                if (op == OP_MOD)
-                    o(0x92); /* xchg %edx, %eax */
-                popType();
+                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();
@@ -2347,7 +2451,6 @@
             return result;
         }
 
-
         static const int operatorHelper[];
 
         int decodeOp(int op) {
@@ -4003,7 +4106,7 @@
         }
     }
 
-    bool typeEqual(Type* a, Type* b) {
+    static bool typeEqual(Type* a, Type* b) {
         if (a == b) {
             return true;
         }
@@ -4550,6 +4653,7 @@
             error("No code generator defined.");
         } else {
             pGen->setErrorSink(this);
+            pGen->setTypes(mkpInt);
         }
     }
 
@@ -4572,6 +4676,7 @@
     int compile(const char* text, size_t textLength) {
         int result;
 
+        createPrimitiveTypes();
         cleanup();
         clear();
         mTokenTable.setArena(&mGlobalArena);
@@ -4581,7 +4686,6 @@
         mLocals.setTokenTable(&mTokenTable);
 
         internKeywords();
-        createPrimitiveTypes();
         codeBuf.init(ALLOC_SIZE);
         setArchitecture(NULL);
         if (!pGen) {
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/test.py b/libacc/tests/test.py
index 02999f0..3928277 100644
--- a/libacc/tests/test.py
+++ b/libacc/tests/test.py
@@ -255,6 +255,14 @@
 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
+""")
+
 
 if __name__ == '__main__':
     if not outputCanRun():