Commit 57a7ab0d33

Andrew Kelley <andrew@ziglang.org>
2019-02-12 01:12:01
comptime support for pointer arithmetic with hard coded addresses
1 parent 90b8cd4
Changed files (2)
src
test
stage1
behavior
src/ir.cpp
@@ -11682,28 +11682,34 @@ static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
 }
 
 static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
-    if (op_id == IrBinOpCmpEq) {
-        return cmp == CmpEQ;
-    } else if (op_id == IrBinOpCmpNotEq) {
-        return cmp != CmpEQ;
-    } else if (op_id == IrBinOpCmpLessThan) {
-        return cmp == CmpLT;
-    } else if (op_id == IrBinOpCmpGreaterThan) {
-        return cmp == CmpGT;
-    } else if (op_id == IrBinOpCmpLessOrEq) {
-        return cmp != CmpGT;
-    } else if (op_id == IrBinOpCmpGreaterOrEq) {
-        return cmp != CmpLT;
-    } else {
-        zig_unreachable();
+    switch (op_id) {
+        case IrBinOpCmpEq:
+            return cmp == CmpEQ;
+        case IrBinOpCmpNotEq:
+            return cmp != CmpEQ;
+        case IrBinOpCmpLessThan:
+            return cmp == CmpLT;
+        case IrBinOpCmpGreaterThan:
+            return cmp == CmpGT;
+        case IrBinOpCmpLessOrEq:
+            return cmp != CmpGT;
+        case IrBinOpCmpGreaterOrEq:
+            return cmp != CmpLT;
+        default:
+            zig_unreachable();
     }
 }
 
 static bool optional_value_is_null(ConstExprValue *val) {
     assert(val->special == ConstValSpecialStatic);
     if (get_codegen_ptr_type(val->type) != nullptr) {
-        return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
-            val->data.x_ptr.data.hard_coded_addr.addr == 0;
+        if (val->data.x_ptr.special == ConstPtrSpecialNull) {
+            return true;
+        } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
+            return val->data.x_ptr.data.hard_coded_addr.addr == 0;
+        } else {
+            return false;
+        }
     } else if (is_opt_err_set(val->type)) {
         return val->data.x_err_set == nullptr;
     } else {
@@ -11879,7 +11885,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
             break;
 
         case ZigTypeIdPointer:
-            operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len != PtrLenSingle);
+            operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC);
             break;
 
         case ZigTypeIdUnreachable:
@@ -11929,15 +11935,38 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
         if (op2_val == nullptr)
             return ira->codegen->invalid_instruction;
 
-        bool answer;
         if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) {
             Cmp cmp_result = float_cmp(op1_val, op2_val);
-            answer = resolve_cmp_op_id(op_id, cmp_result);
+            bool answer = resolve_cmp_op_id(op_id, cmp_result);
+            return ir_const_bool(ira, &bin_op_instruction->base, answer);
         } else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) {
             Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
-            answer = resolve_cmp_op_id(op_id, cmp_result);
+            bool answer = resolve_cmp_op_id(op_id, cmp_result);
+            return ir_const_bool(ira, &bin_op_instruction->base, answer);
+        } else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) {
+            if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+                    op1_val->data.x_ptr.special == ConstPtrSpecialNull) &&
+                (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+                    op2_val->data.x_ptr.special == ConstPtrSpecialNull))
+            {
+                uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ?
+                    0 : op1_val->data.x_ptr.data.hard_coded_addr.addr;
+                uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ?
+                    0 : op2_val->data.x_ptr.data.hard_coded_addr.addr;
+                Cmp cmp_result;
+                if (op1_addr > op2_addr) {
+                    cmp_result = CmpGT;
+                } else if (op1_addr < op2_addr) {
+                    cmp_result = CmpLT;
+                } else {
+                    cmp_result = CmpEQ;
+                }
+                bool answer = resolve_cmp_op_id(op_id, cmp_result);
+                return ir_const_bool(ira, &bin_op_instruction->base, answer);
+            }
         } else {
             bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val);
+            bool answer;
             if (op_id == IrBinOpCmpEq) {
                 answer = are_equal;
             } else if (op_id == IrBinOpCmpNotEq) {
@@ -11945,9 +11974,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
             } else {
                 zig_unreachable();
             }
+            return ir_const_bool(ira, &bin_op_instruction->base, answer);
         }
-
-        return ir_const_bool(ira, &bin_op_instruction->base, answer);
     }
 
     // some comparisons with unsigned numbers can be evaluated
@@ -12363,6 +12391,8 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
 }
 
 static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
+    Error err;
+
     IrInstruction *op1 = instruction->op1->child;
     if (type_is_invalid(op1->value.type))
         return ira->codegen->invalid_instruction;
@@ -12376,9 +12406,42 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
     // look for pointer math
     if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) {
         IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize);
-        if (casted_op2 == ira->codegen->invalid_instruction)
+        if (type_is_invalid(casted_op2->value.type))
             return ira->codegen->invalid_instruction;
 
+        if (op1->value.special == ConstValSpecialUndef || casted_op2->value.special == ConstValSpecialUndef) {
+            IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
+            result->value.special = ConstValSpecialUndef;
+            return result;
+        }
+        if (casted_op2->value.special == ConstValSpecialStatic && op1->value.special == ConstValSpecialStatic &&
+            (op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+            op1->value.data.x_ptr.special == ConstPtrSpecialNull))
+        {
+            uint64_t start_addr = (op1->value.data.x_ptr.special == ConstPtrSpecialNull) ?
+                0 : op1->value.data.x_ptr.data.hard_coded_addr.addr;
+            uint64_t elem_offset;
+            if (!ir_resolve_usize(ira, casted_op2, &elem_offset))
+                return ira->codegen->invalid_instruction;
+            ZigType *elem_type = op1->value.type->data.pointer.child_type;
+            if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
+                return ira->codegen->invalid_instruction;
+            uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset;
+            uint64_t new_addr;
+            if (op_id == IrBinOpAdd) {
+                new_addr = start_addr + byte_offset;
+            } else if (op_id == IrBinOpSub) {
+                new_addr = start_addr - byte_offset;
+            } else {
+                zig_unreachable();
+            }
+            IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
+            result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
+            result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
+            result->value.data.x_ptr.data.hard_coded_addr.addr = new_addr;
+            return result;
+        }
+
         IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
                 instruction->base.source_node, op_id, op1, casted_op2, true);
         result->value.type = op1->value.type;
test/stage1/behavior/pointers.zig
@@ -58,21 +58,27 @@ test "implicit cast single item pointer to C pointer and back" {
 }
 
 test "C pointer comparison and arithmetic" {
-    var one: usize = 1;
-    var ptr1: [*c]u8 = 0;
-    var ptr2 = ptr1 + 10;
-    expect(ptr1 == 0);
-    expect(ptr1 >= 0);
-    expect(ptr1 <= 0);
-    expect(ptr1 < 1);
-    expect(ptr1 < one);
-    expect(1 > ptr1);
-    expect(one > ptr1);
-    expect(ptr1 < ptr2);
-    expect(ptr2 > ptr1);
-    expect(ptr2 >= 10);
-    expect(ptr2 == 10);
-    expect(ptr2 <= 10);
-    ptr2 -= 10;
-    expect(ptr1 == ptr2);
+    const S = struct {
+        fn doTheTest() void {
+            var one: usize = 1;
+            var ptr1: [*c]u32 = 0;
+            var ptr2 = ptr1 + 10;
+            expect(ptr1 == 0);
+            expect(ptr1 >= 0);
+            expect(ptr1 <= 0);
+            expect(ptr1 < 1);
+            expect(ptr1 < one);
+            expect(1 > ptr1);
+            expect(one > ptr1);
+            expect(ptr1 < ptr2);
+            expect(ptr2 > ptr1);
+            expect(ptr2 >= 40);
+            expect(ptr2 == 40);
+            expect(ptr2 <= 40);
+            ptr2 -= 10;
+            expect(ptr1 == ptr2);
+        }
+    };
+    S.doTheTest();
+    comptime S.doTheTest();
 }