Commit 4010f6a11d

Jimmi Holst Christensen <jhc@liab.dk>
2019-02-05 11:00:32
Added support for vector wrapping mult and sub * I also merged the code that generates ir for add, sub, and mult
1 parent 06be65a
Changed files (2)
src
test
stage1
behavior
src/codegen.cpp
@@ -2582,6 +2582,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
 
 }
 
+typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
+
 static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
         IrInstructionBinOp *bin_op_instruction)
 {
@@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
             } else {
                 zig_unreachable();
             }
+        case IrBinOpMult:
+        case IrBinOpMultWrap:
         case IrBinOpAdd:
         case IrBinOpAddWrap:
+        case IrBinOpSub:
+        case IrBinOpSubWrap: {
+            // These are lookup table using the AddSubMul enum as the lookup.
+            // If AddSubMul ever changes, then these tables will be out of
+            // date.
+            static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
+            static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
+            static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
+            static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
+
+            bool is_vector = type_entry->id == ZigTypeIdVector;
+            bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
+            AddSubMul add_sub_mul =
+                op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
+                op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
+                AddSubMulMul;
+
+            // The code that is generated for vectors and scalars are the same,
+            // so we can just set type_entry to the vectors elem_type an avoid
+            // a lot of repeated code.
+            if (is_vector)
+                type_entry = type_entry->data.vector.elem_type;
+
             if (type_entry->id == ZigTypeIdPointer) {
                 assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
+                LLVMValueRef subscript_value;
+                if (is_vector)
+                    zig_panic("TODO: Implement vector operations on pointers.");
+
+                switch (add_sub_mul) {
+                    case AddSubMulAdd:
+                        subscript_value = op2_value;
+                        break;
+                    case AddSubMulSub:
+                        subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
+                        break;
+                    case AddSubMulMul:
+                        zig_unreachable();
+                }
+
                 // TODO runtime safety
-                return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");
+                return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
             } else if (type_entry->id == ZigTypeIdFloat) {
                 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
-                return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
+                return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
             } else if (type_entry->id == ZigTypeIdInt) {
-                bool is_wrapping = (op_id == IrBinOpAddWrap);
                 if (is_wrapping) {
-                    return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
+                    return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
                 } else if (want_runtime_safety) {
-                    return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value);
+                    if (is_vector)
+                        zig_panic("TODO: Implement runtime safety vector operations.");
+                    return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
                 } else if (type_entry->data.integral.is_signed) {
-                    return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
-                } else {
-                    return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
-                }
-            } else if (type_entry->id == ZigTypeIdVector) {
-                ZigType *elem_type = type_entry->data.vector.elem_type;
-                if (elem_type->id == ZigTypeIdFloat) {
-                    ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
-                    return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
-                } else if (elem_type->id == ZigTypeIdPointer) {
-                    zig_panic("TODO codegen for pointers in vectors");
-                } else if (elem_type->id == ZigTypeIdInt) {
-                    bool is_wrapping = (op_id == IrBinOpAddWrap);
-                    if (is_wrapping) {
-                        return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
-                    } else if (want_runtime_safety) {
-                        zig_panic("TODO runtime safety for vector integer addition");
-                    } else if (elem_type->data.integral.is_signed) {
-                        return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
-                    } else {
-                        return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
-                    }
+                    return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
                 } else {
-                    zig_unreachable();
+                    return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
                 }
             } else {
                 zig_unreachable();
             }
+        }
         case IrBinOpBinOr:
             return LLVMBuildOr(g->builder, op1_value, op2_value, "");
         case IrBinOpBinXor:
@@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
                     return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
                 }
             }
-        case IrBinOpSub:
-        case IrBinOpSubWrap:
-            if (type_entry->id == ZigTypeIdPointer) {
-                assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
-                // TODO runtime safety
-                LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
-                return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
-            } else if (type_entry->id == ZigTypeIdFloat) {
-                ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
-                return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
-            } else if (type_entry->id == ZigTypeIdInt) {
-                bool is_wrapping = (op_id == IrBinOpSubWrap);
-                if (is_wrapping) {
-                    return LLVMBuildSub(g->builder, op1_value, op2_value, "");
-                } else if (want_runtime_safety) {
-                    return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value);
-                } else if (type_entry->data.integral.is_signed) {
-                    return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
-                } else {
-                    return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
-                }
-            } else {
-                zig_unreachable();
-            }
-        case IrBinOpMult:
-        case IrBinOpMultWrap:
-            if (type_entry->id == ZigTypeIdFloat) {
-                ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
-                return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
-            } else if (type_entry->id == ZigTypeIdInt) {
-                bool is_wrapping = (op_id == IrBinOpMultWrap);
-                if (is_wrapping) {
-                    return LLVMBuildMul(g->builder, op1_value, op2_value, "");
-                } else if (want_runtime_safety) {
-                    return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value);
-                } else if (type_entry->data.integral.is_signed) {
-                    return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
-                } else {
-                    return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
-                }
-            } else {
-                zig_unreachable();
-            }
         case IrBinOpDivUnspecified:
             return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
                     op1_value, op2_value, type_entry, DivKindFloat);
test/stage1/behavior/vector.zig
@@ -1,20 +1,17 @@
 const std = @import("std");
+const mem = std.mem;
 const assertOrPanic = std.debug.assertOrPanic;
 
-test "implicit array to vector and vector to array" {
+test "vector wrap operators" {
     const S = struct {
         fn doTheTest() void {
-            var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
-            const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
-            v +%= x;
-            const result: [4]i32 = v;
-            assertOrPanic(result[0] == 11);
-            assertOrPanic(result[1] == 22);
-            assertOrPanic(result[2] == 33);
-            assertOrPanic(result[3] == 44);
+            const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
+            const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
+            assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
+            assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
+            assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
         }
     };
     S.doTheTest();
     comptime S.doTheTest();
 }
-