Commit 8c6fa982cd

Andrew Kelley <andrew@ziglang.org>
2019-02-05 02:30:00
SIMD: array to vector, vector to array, wrapping int add
also vectors and arrays now use the same ConstExprVal representation See #903
1 parent 2828a96
src/all_types.hpp
@@ -252,10 +252,6 @@ struct ConstArgTuple {
     size_t end_index;
 };
 
-struct ConstVector {
-    ConstExprValue *elements;
-};
-
 enum ConstValSpecial {
     ConstValSpecialRuntime,
     ConstValSpecialStatic,
@@ -322,7 +318,6 @@ struct ConstExprValue {
         ConstPtrValue x_ptr;
         ImportTableEntry *x_import;
         ConstArgTuple x_arg_tuple;
-        ConstVector x_vector;
 
         // populated if special == ConstValSpecialRuntime
         RuntimeHintErrorUnion rh_error_union;
@@ -2239,6 +2234,8 @@ enum IrInstructionId {
     IrInstructionIdToBytes,
     IrInstructionIdFromBytes,
     IrInstructionIdCheckRuntimeScope,
+    IrInstructionIdVectorToArray,
+    IrInstructionIdArrayToVector,
 };
 
 struct IrInstruction {
@@ -3368,6 +3365,19 @@ struct IrInstructionBitReverse {
     IrInstruction *op;
 };
 
+struct IrInstructionArrayToVector {
+    IrInstruction base;
+
+    IrInstruction *array;
+};
+
+struct IrInstructionVectorToArray {
+    IrInstruction base;
+
+    IrInstruction *vector;
+    LLVMValueRef tmp_ptr;
+};
+
 static const size_t slice_ptr_index = 0;
 static const size_t slice_len_index = 1;
 
src/analyze.cpp
@@ -4457,7 +4457,15 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
     return new_entry;
 }
 
+bool is_valid_vector_elem_type(ZigType *elem_type) {
+    return elem_type->id == ZigTypeIdInt ||
+        elem_type->id == ZigTypeIdFloat ||
+        get_codegen_ptr_type(elem_type) != nullptr;
+}
+
 ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+    assert(is_valid_vector_elem_type(elem_type));
+
     TypeId type_id = {};
     type_id.id = ZigTypeIdVector;
     type_id.data.vector.len = len;
@@ -5749,6 +5757,28 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
     zig_unreachable();
 }
 
+static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
+    assert(a->data.x_array.special != ConstArraySpecialUndef);
+    assert(b->data.x_array.special != ConstArraySpecialUndef);
+    if (a->data.x_array.special == ConstArraySpecialBuf &&
+        b->data.x_array.special == ConstArraySpecialBuf)
+    {
+        return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
+    }
+    expand_undef_array(g, a);
+    expand_undef_array(g, b);
+
+    ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
+    ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
+
+    for (size_t i = 0; i < len; i += 1) {
+        if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+            return false;
+    }
+
+    return true;
+}
+
 bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
     assert(a->type->id == b->type->id);
     assert(a->special == ConstValSpecialStatic);
@@ -5803,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
         case ZigTypeIdPointer:
         case ZigTypeIdFn:
             return const_values_equal_ptr(a, b);
+        case ZigTypeIdVector:
+            assert(a->type->data.vector.len == b->type->data.vector.len);
+            return const_values_equal_array(g, a, b, a->type->data.vector.len);
         case ZigTypeIdArray: {
             assert(a->type->data.array.len == b->type->data.array.len);
-            assert(a->data.x_array.special != ConstArraySpecialUndef);
-            assert(b->data.x_array.special != ConstArraySpecialUndef);
-            if (a->data.x_array.special == ConstArraySpecialBuf &&
-                b->data.x_array.special == ConstArraySpecialBuf)
-            {
-                return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
-            }
-            expand_undef_array(g, a);
-            expand_undef_array(g, b);
-
-            size_t len = a->type->data.array.len;
-            ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
-            ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
-
-            for (size_t i = 0; i < len; i += 1) {
-                if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
-                    return false;
-            }
-
-            return true;
+            return const_values_equal_array(g, a, b, a->type->data.array.len);
         }
         case ZigTypeIdStruct:
             for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
@@ -5853,20 +5867,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
         case ZigTypeIdArgTuple:
             return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
                    a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
-        case ZigTypeIdVector: {
-            assert(a->type->data.vector.len == b->type->data.vector.len);
-
-            size_t len = a->type->data.vector.len;
-            ConstExprValue *a_elems = a->data.x_vector.elements;
-            ConstExprValue *b_elems = b->data.x_vector.elements;
-
-            for (size_t i = 0; i < len; i += 1) {
-                if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
-                    return false;
-            }
-
-            return true;
-        }
         case ZigTypeIdBoundFn:
         case ZigTypeIdInvalid:
         case ZigTypeIdUnreachable:
@@ -5985,6 +5985,40 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const
     }
 }
 
+static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
+    switch (const_val->data.x_array.special) {
+        case ConstArraySpecialUndef:
+            buf_append_str(buf, "undefined");
+            return;
+        case ConstArraySpecialBuf: {
+            Buf *array_buf = const_val->data.x_array.data.s_buf;
+            buf_append_char(buf, '"');
+            for (size_t i = 0; i < buf_len(array_buf); i += 1) {
+                uint8_t c = buf_ptr(array_buf)[i];
+                if (c == '"') {
+                    buf_append_str(buf, "\\\"");
+                } else {
+                    buf_append_char(buf, c);
+                }
+            }
+            buf_append_char(buf, '"');
+            return;
+        }
+        case ConstArraySpecialNone: {
+            buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
+            for (uint64_t i = 0; i < len; i += 1) {
+                if (i != 0)
+                    buf_appendf(buf, ",");
+                ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
+                render_const_value(g, buf, child_value);
+            }
+            buf_appendf(buf, "}");
+            return;
+        }
+    }
+    zig_unreachable();
+}
+
 void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
     switch (const_val->special) {
         case ConstValSpecialRuntime:
@@ -6065,51 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
             }
         case ZigTypeIdPointer:
             return render_const_val_ptr(g, buf, const_val, type_entry);
+        case ZigTypeIdVector:
+            return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
         case ZigTypeIdArray:
-            switch (const_val->data.x_array.special) {
-                case ConstArraySpecialUndef:
-                    buf_append_str(buf, "undefined");
-                    return;
-                case ConstArraySpecialBuf: {
-                    Buf *array_buf = const_val->data.x_array.data.s_buf;
-                    buf_append_char(buf, '"');
-                    for (size_t i = 0; i < buf_len(array_buf); i += 1) {
-                        uint8_t c = buf_ptr(array_buf)[i];
-                        if (c == '"') {
-                            buf_append_str(buf, "\\\"");
-                        } else {
-                            buf_append_char(buf, c);
-                        }
-                    }
-                    buf_append_char(buf, '"');
-                    return;
-                }
-                case ConstArraySpecialNone: {
-                    buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
-                    uint64_t len = type_entry->data.array.len;
-                    for (uint64_t i = 0; i < len; i += 1) {
-                        if (i != 0)
-                            buf_appendf(buf, ",");
-                        ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
-                        render_const_value(g, buf, child_value);
-                    }
-                    buf_appendf(buf, "}");
-                    return;
-                }
-            }
-            zig_unreachable();
-        case ZigTypeIdVector: {
-            buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
-            uint64_t len = type_entry->data.vector.len;
-            for (uint32_t i = 0; i < len; i += 1) {
-                if (i != 0)
-                    buf_appendf(buf, ",");
-                ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
-                render_const_value(g, buf, child_value);
-            }
-            buf_appendf(buf, "}");
-            return;
-        }
+            return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
         case ZigTypeIdNull:
             {
                 buf_appendf(buf, "null");
@@ -6379,7 +6372,17 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
 
 // Canonicalize the array value as ConstArraySpecialNone
 void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
-    assert(const_val->type->id == ZigTypeIdArray);
+    size_t elem_count;
+    ZigType *elem_type;
+    if (const_val->type->id == ZigTypeIdArray) {
+        elem_count = const_val->type->data.array.len;
+        elem_type = const_val->type->data.array.child_type;
+    } else if (const_val->type->id == ZigTypeIdVector) {
+        elem_count = const_val->type->data.vector.len;
+        elem_type = const_val->type->data.vector.elem_type;
+    } else {
+        zig_unreachable();
+    }
     if (const_val->special == ConstValSpecialUndef) {
         const_val->special = ConstValSpecialStatic;
         const_val->data.x_array.special = ConstArraySpecialUndef;
@@ -6389,18 +6392,14 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
             return;
         case ConstArraySpecialUndef: {
             const_val->data.x_array.special = ConstArraySpecialNone;
-            size_t elem_count = const_val->type->data.array.len;
             const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
             for (size_t i = 0; i < elem_count; i += 1) {
                 ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
-                element_val->type = const_val->type->data.array.child_type;
+                element_val->type = elem_type;
                 init_const_undefined(g, element_val);
-                ConstParent *parent = get_const_val_parent(g, element_val);
-                if (parent != nullptr) {
-                    parent->id = ConstParentIdArray;
-                    parent->data.p_array.array_val = const_val;
-                    parent->data.p_array.elem_index = i;
-                }
+                element_val->parent.id = ConstParentIdArray;
+                element_val->parent.data.p_array.array_val = const_val;
+                element_val->parent.data.p_array.elem_index = i;
             }
             return;
         }
@@ -6411,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
             g->string_literals_table.maybe_remove(buf);
 
             const_val->data.x_array.special = ConstArraySpecialNone;
-            size_t elem_count = const_val->type->data.array.len;
             assert(elem_count == buf_len(buf));
             const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
             for (size_t i = 0; i < elem_count; i += 1) {
@@ -6419,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
                 this_char->special = ConstValSpecialStatic;
                 this_char->type = g->builtin_types.entry_u8;
                 bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
+                this_char->parent.id = ConstParentIdArray;
+                this_char->parent.data.p_array.array_val = const_val;
+                this_char->parent.data.p_array.elem_index = i;
             }
             return;
         }
@@ -6426,6 +6427,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
     zig_unreachable();
 }
 
+// Deprecated. Reference the parent field directly.
 ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
     return &value->parent;
 }
src/analyze.hpp
@@ -74,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
 bool is_ref(ZigType *type_entry);
 bool is_array_ref(ZigType *type_entry);
 bool is_container_ref(ZigType *type_entry);
+bool is_valid_vector_elem_type(ZigType *elem_type);
 void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
 void scan_import(CodeGen *g, ImportTableEntry *import);
 void preview_use_decl(CodeGen *g, AstNode *node);
src/codegen.cpp
@@ -1921,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
 }
 
 static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
-    assert(alignment > 0);
     LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
-    LLVMSetAlignment(result, alignment);
+    LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment);
     return result;
 }
 
@@ -3246,6 +3245,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
     return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
 }
 
+static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
+    switch (const_val->data.x_array.special) {
+        case ConstArraySpecialUndef:
+            return true;
+        case ConstArraySpecialBuf:
+            return false;
+        case ConstArraySpecialNone:
+            for (size_t i = 0; i < len; i += 1) {
+                if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
+                    return false;
+            }
+            return true;
+    }
+    zig_unreachable();
+}
+
 static bool value_is_all_undef(ConstExprValue *const_val) {
     switch (const_val->special) {
         case ConstValSpecialRuntime:
@@ -3260,19 +3275,9 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
                 }
                 return true;
             } else if (const_val->type->id == ZigTypeIdArray) {
-                switch (const_val->data.x_array.special) {
-                    case ConstArraySpecialUndef:
-                        return true;
-                    case ConstArraySpecialBuf:
-                        return false;
-                    case ConstArraySpecialNone:
-                        for (size_t i = 0; i < const_val->type->data.array.len; i += 1) {
-                            if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
-                                return false;
-                        }
-                        return true;
-                }
-                zig_unreachable();
+                return value_is_all_undef_array(const_val, const_val->type->data.array.len);
+            } else if (const_val->type->id == ZigTypeIdVector) {
+                return value_is_all_undef_array(const_val, const_val->type->data.vector.len);
             } else {
                 return false;
             }
@@ -5194,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable,
     return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
 }
 
+static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable,
+        IrInstructionVectorToArray *instruction)
+{
+    ZigType *array_type = instruction->base.value.type;
+    assert(array_type->id == ZigTypeIdArray);
+    assert(handle_is_ptr(array_type));
+    assert(instruction->tmp_ptr);
+    LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
+    LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
+            LLVMPointerType(instruction->vector->value.type->type_ref, 0), "");
+    gen_store_untyped(g, vector, casted_ptr, 0, false);
+    return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable,
+        IrInstructionArrayToVector *instruction)
+{
+    ZigType *vector_type = instruction->base.value.type;
+    assert(vector_type->id == ZigTypeIdVector);
+    assert(!handle_is_ptr(vector_type));
+    LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array);
+    LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr,
+            LLVMPointerType(vector_type->type_ref, 0), "");
+    return gen_load_untyped(g, casted_ptr, 0, false, "");
+}
+
 static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
     AstNode *source_node = instruction->source_node;
     Scope *scope = instruction->scope;
@@ -5439,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction);
         case IrInstructionIdBitReverse:
             return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction);
+        case IrInstructionIdArrayToVector:
+            return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
+        case IrInstructionIdVectorToArray:
+            return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
     }
     zig_unreachable();
 }
@@ -6016,14 +6051,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
                         return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
                     }
                 }
+                zig_unreachable();
             }
         case ZigTypeIdVector: {
             uint32_t len = type_entry->data.vector.len;
-            LLVMValueRef *values = allocate<LLVMValueRef>(len);
-            for (uint32_t i = 0; i < len; i += 1) {
-                values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+            switch (const_val->data.x_array.special) {
+                case ConstArraySpecialUndef:
+                    return LLVMGetUndef(type_entry->type_ref);
+                case ConstArraySpecialNone: {
+                    LLVMValueRef *values = allocate<LLVMValueRef>(len);
+                    for (uint64_t i = 0; i < len; i += 1) {
+                        ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
+                        values[i] = gen_const_val(g, elem_value, "");
+                    }
+                    return LLVMConstVector(values, len);
+                }
+                case ConstArraySpecialBuf: {
+                    Buf *buf = const_val->data.x_array.data.s_buf;
+                    assert(buf_len(buf) == len);
+                    LLVMValueRef *values = allocate<LLVMValueRef>(len);
+                    for (uint64_t i = 0; i < len; i += 1) {
+                        values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false);
+                    }
+                    return LLVMConstVector(values, len);
+                }
             }
-            return LLVMConstVector(values, len);
+            zig_unreachable();
         }
         case ZigTypeIdUnion:
             {
@@ -6467,6 +6520,7 @@ static void do_code_gen(CodeGen *g) {
             IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
             LLVMValueRef *slot;
             ZigType *slot_type = instruction->value.type;
+            uint32_t alignment_bytes = 0;
             if (instruction->id == IrInstructionIdCast) {
                 IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
                 slot = &cast_instruction->tmp_ptr;
@@ -6502,10 +6556,14 @@ static void do_code_gen(CodeGen *g) {
             } else if (instruction->id == IrInstructionIdCmpxchgGen) {
                 IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
                 slot = &cmpxchg_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdVectorToArray) {
+                IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
+                alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
+                slot = &vector_to_array_instruction->tmp_ptr;
             } else {
                 zig_unreachable();
             }
-            *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type));
+            *slot = build_alloca(g, slot_type, "", alignment_bytes);
         }
 
         ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
src/ir.cpp
@@ -168,6 +168,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
 static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
 static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
 static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
+static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
 
 static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
     assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -899,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop
     return IrInstructionIdCheckRuntimeScope;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
+    return IrInstructionIdVectorToArray;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
+    return IrInstructionIdArrayToVector;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -2821,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope,
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
+        IrInstruction *vector, ZigType *result_type)
+{
+    IrInstructionVectorToArray *instruction = ir_build_instruction<IrInstructionVectorToArray>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value.type = result_type;
+    instruction->vector = vector;
+
+    ir_ref_instruction(vector, ira->new_irb.current_basic_block);
+
+    ir_add_alloca(ira, &instruction->base, result_type);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
+        IrInstruction *array, ZigType *result_type)
+{
+    IrInstructionArrayToVector *instruction = ir_build_instruction<IrInstructionArrayToVector>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value.type = result_type;
+    instruction->array = array;
+
+    ir_ref_instruction(array, ira->new_irb.current_basic_block);
+
+    return &instruction->base;
+}
+
 static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
     results[ReturnKindUnconditional] = 0;
     results[ReturnKindError] = 0;
@@ -8270,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
 
     bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
     bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
+    assert(const_val_is_int || const_val_is_float);
 
     if (other_type->id == ZigTypeIdFloat) {
         if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
@@ -10714,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
     }
 }
 
+static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
+    IrInstruction *array, ZigType *vector_type)
+{
+    if (instr_is_comptime(array)) {
+        // arrays and vectors have the same ConstExprValue representation
+        IrInstruction *result = ir_const(ira, source_instr, vector_type);
+        copy_const_val(&result->value, &array->value, false);
+        result->value.type = vector_type;
+        return result;
+    }
+    return ir_build_array_to_vector(ira, source_instr, array, vector_type);
+}
+
+static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
+    IrInstruction *vector, ZigType *array_type)
+{
+    if (instr_is_comptime(vector)) {
+        // arrays and vectors have the same ConstExprValue representation
+        IrInstruction *result = ir_const(ira, source_instr, array_type);
+        copy_const_val(&result->value, &vector->value, false);
+        result->value.type = array_type;
+        return result;
+    }
+    return ir_build_vector_to_array(ira, source_instr, vector, array_type);
+}
+
 static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
     ZigType *wanted_type, IrInstruction *value)
 {
@@ -11102,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         }
     }
 
+    // cast from @Vector(N, T) to [N]T
+    if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
+        wanted_type->data.array.len == actual_type->data.vector.len &&
+        types_match_const_cast_only(ira, wanted_type->data.array.child_type,
+            actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+    {
+        return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type);
+    }
+
+    // cast from [N]T to @Vector(N, T)
+    if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
+        actual_type->data.array.len == wanted_type->data.vector.len &&
+        types_match_const_cast_only(ira, actual_type->data.array.child_type,
+            wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+    {
+        return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
+    }
 
     // cast from undefined to anything
     if (actual_type->id == ZigTypeIdUndefined) {
@@ -11780,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
     return result;
 }
 
-static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
-        IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
+static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
+        ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
 {
     bool is_int;
     bool is_float;
@@ -11803,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
     if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
         op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
     {
-        return ErrorDivByZero;
+        return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
     }
     if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
-        return ErrorNegativeDenominator;
+        return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
     }
 
     switch (op_id) {
@@ -11852,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
                 BigInt orig_bigint;
                 bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
                 if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
-                    return ErrorShiftedOutOneBits;
+                    return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
                 }
                 break;
             }
@@ -11920,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
                 BigInt remainder;
                 bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
                 if (bigint_cmp_zero(&remainder) != CmpEQ) {
-                    return ErrorExactDivRemainder;
+                    return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
                 }
             } else {
                 float_div_trunc(out_val, op1_val, op2_val);
                 ConstExprValue remainder;
                 float_rem(&remainder, op1_val, op2_val);
                 if (float_cmp_zero(&remainder) != CmpEQ) {
-                    return ErrorExactDivRemainder;
+                    return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
                 }
             }
             break;
@@ -11951,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
         if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
                 type_entry->data.integral.is_signed))
         {
-            return ErrorOverflow;
+            return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
         }
     }
 
     out_val->type = type_entry;
     out_val->special = ConstValSpecialStatic;
-    return 0;
+    return nullptr;
+}
+
+// This works on operands that have already been checked to be comptime known.
+static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
+        ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
+{
+    IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
+    ConstExprValue *out_val = &result_instruction->value;
+    if (type_entry->id == ZigTypeIdVector) {
+        expand_undef_array(ira->codegen, op1_val);
+        expand_undef_array(ira->codegen, op2_val);
+        out_val->special = ConstValSpecialUndef;
+        expand_undef_array(ira->codegen, out_val);
+        size_t len = type_entry->data.vector.len;
+        ZigType *scalar_type = type_entry->data.vector.elem_type;
+        for (size_t i = 0; i < len; i += 1) {
+            ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
+            ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
+            ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
+            assert(scalar_op1_val->type == scalar_type);
+            assert(scalar_op2_val->type == scalar_type);
+            assert(scalar_out_val->type == scalar_type);
+            ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
+                    scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
+            if (msg != nullptr) {
+                add_error_note(ira->codegen, msg, source_instr->source_node,
+                    buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
+                return ira->codegen->invalid_instruction;
+            }
+        }
+        out_val->type = type_entry;
+        out_val->special = ConstValSpecialStatic;
+    } else {
+        if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
+            return ira->codegen->invalid_instruction;
+        }
+    }
+    return ir_implicit_cast(ira, result_instruction, type_entry);
 }
 
 static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -12029,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
         if (op2_val == nullptr)
             return ira->codegen->invalid_instruction;
 
-        IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type);
-
-        int err;
-        if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) {
-            if (err == ErrorOverflow) {
-                ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
-                return ira->codegen->invalid_instruction;
-            } else if (err == ErrorShiftedOutOneBits) {
-                ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits"));
-                return ira->codegen->invalid_instruction;
-            } else {
-                zig_unreachable();
-            }
-            return ira->codegen->invalid_instruction;
-        }
-
-        ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false);
-        return result_instruction;
+        return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
     } else if (op1->value.type->id == ZigTypeIdComptimeInt) {
         ir_add_error(ira, &bin_op_instruction->base,
                 buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
@@ -12292,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
         if (op2_val == nullptr)
             return ira->codegen->invalid_instruction;
 
-        IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type);
-
-        int err;
-        if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) {
-            if (err == ErrorDivByZero) {
-                ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
-                return ira->codegen->invalid_instruction;
-            } else if (err == ErrorOverflow) {
-                ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow"));
-                return ira->codegen->invalid_instruction;
-            } else if (err == ErrorExactDivRemainder) {
-                ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
-                return ira->codegen->invalid_instruction;
-            } else if (err == ErrorNegativeDenominator) {
-                ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator"));
-                return ira->codegen->invalid_instruction;
-            } else {
-                zig_unreachable();
-            }
-            return ira->codegen->invalid_instruction;
-        }
-
-        ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false);
-        return result_instruction;
+        return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
     }
 
     IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
@@ -18745,10 +18824,7 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr
     if (type_is_invalid(elem_type))
         return ira->codegen->invalid_instruction;
 
-    if (elem_type->id != ZigTypeIdInt &&
-        elem_type->id != ZigTypeIdFloat &&
-        get_codegen_ptr_type(elem_type) == nullptr)
-    {
+    if (!is_valid_vector_elem_type(elem_type)) {
         ir_add_error(ira, instruction->elem_type,
             buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
                 buf_ptr(&elem_type->name)));
@@ -20345,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
     return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
 }
 
+static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
+    size_t buf_i = 0;
+    // TODO optimize the buf case
+    expand_undef_array(codegen, val);
+    for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
+        ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
+        buf_write_value_bytes(codegen, &buf[buf_i], elem);
+        buf_i += type_size(codegen, elem->type);
+    }
+}
+
 static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
     if (val->special == ConstValSpecialUndef)
         val->special = ConstValSpecialStatic;
@@ -20390,26 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
                 zig_unreachable();
             }
         case ZigTypeIdArray:
-            {
-                size_t buf_i = 0;
-                // TODO optimize the buf case
-                expand_undef_array(codegen, val);
-                for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
-                    ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
-                    buf_write_value_bytes(codegen, &buf[buf_i], elem);
-                    buf_i += type_size(codegen, elem->type);
-                }
-            }
-            return;
-        case ZigTypeIdVector: {
-            size_t buf_i = 0;
-            for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
-                ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
-                buf_write_value_bytes(codegen, &buf[buf_i], elem);
-                buf_i += type_size(codegen, elem->type);
-            }
-            return;
-        }
+            return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
+        case ZigTypeIdVector:
+            return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
         case ZigTypeIdStruct:
             zig_panic("TODO buf_write_value_bytes struct type");
         case ZigTypeIdOptional:
@@ -20426,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
     zig_unreachable();
 }
 
+static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
+        ConstExprValue *val, ZigType *elem_type, size_t len)
+{
+    Error err;
+    uint64_t elem_size = type_size(codegen, elem_type);
+
+    switch (val->data.x_array.special) {
+        case ConstArraySpecialNone:
+            val->data.x_array.data.s_none.elements = create_const_vals(len);
+            for (size_t i = 0; i < len; i++) {
+                ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
+                elem->special = ConstValSpecialStatic;
+                elem->type = elem_type;
+                if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+                    return err;
+            }
+            return ErrorNone;
+        case ConstArraySpecialUndef:
+            zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
+        case ConstArraySpecialBuf:
+            zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
+    }
+    zig_unreachable();
+}
+
 static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
     Error err;
     assert(val->special == ConstValSpecialStatic);
@@ -20464,42 +20559,12 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
                 val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
                 return ErrorNone;
             }
-        case ZigTypeIdArray: {
-            uint64_t elem_size = type_size(codegen, val->type->data.array.child_type);
-            size_t len = val->type->data.array.len;
-
-            switch (val->data.x_array.special) {
-                case ConstArraySpecialNone:
-                    val->data.x_array.data.s_none.elements = create_const_vals(len);
-                    for (size_t i = 0; i < len; i++) {
-                        ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
-                        elem->special = ConstValSpecialStatic;
-                        elem->type = val->type->data.array.child_type;
-                        if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
-                            return err;
-                    }
-                    return ErrorNone;
-                case ConstArraySpecialUndef:
-                    zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
-                case ConstArraySpecialBuf:
-                    zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
-            }
-            zig_unreachable();
-        }
-        case ZigTypeIdVector: {
-            uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
-            uint32_t len = val->type->data.vector.len;
-
-            val->data.x_vector.elements = create_const_vals(len);
-            for (uint32_t i = 0; i < len; i += 1) {
-                ConstExprValue *elem = &val->data.x_vector.elements[i];
-                elem->special = ConstValSpecialStatic;
-                elem->type = val->type->data.vector.elem_type;
-                if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
-                    return err;
-            }
-            return ErrorNone;
-        }
+        case ZigTypeIdArray:
+            return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
+                    val->type->data.array.len);
+        case ZigTypeIdVector:
+            return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
+                    val->type->data.vector.len);
         case ZigTypeIdEnum:
             switch (val->type->data.enumeration.layout) {
                 case ContainerLayoutAuto:
@@ -21634,6 +21699,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
         case IrInstructionIdDeclVarGen:
         case IrInstructionIdPtrCastGen:
         case IrInstructionIdCmpxchgGen:
+        case IrInstructionIdArrayToVector:
+        case IrInstructionIdVectorToArray:
             zig_unreachable();
 
         case IrInstructionIdReturn:
@@ -22129,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdFromBytes:
         case IrInstructionIdToBytes:
         case IrInstructionIdEnumToInt:
+        case IrInstructionIdVectorToArray:
+        case IrInstructionIdArrayToVector:
             return false;
 
         case IrInstructionIdAsm:
src/ir_print.cpp
@@ -972,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime
     fprintf(irp->f, ")");
 }
 
+static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) {
+    fprintf(irp->f, "ArrayToVector(");
+    ir_print_other_instruction(irp, instruction->array);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) {
+    fprintf(irp->f, "VectorToArray(");
+    ir_print_other_instruction(irp, instruction->vector);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
     fprintf(irp->f, "inttoerr ");
     ir_print_other_instruction(irp, instruction->target);
@@ -1825,6 +1837,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdDeclVarGen:
             ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction);
             break;
+        case IrInstructionIdArrayToVector:
+            ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction);
+            break;
+        case IrInstructionIdVectorToArray:
+            ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/stage1/behavior/vector.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+const assertOrPanic = std.debug.assertOrPanic;
+
+test "implicit array to vector and vector to array" {
+    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);
+        }
+    };
+    S.doTheTest();
+    comptime S.doTheTest();
+}
+
test/stage1/behavior.zig
@@ -74,6 +74,7 @@ comptime {
     _ = @import("behavior/underscore.zig");
     _ = @import("behavior/union.zig");
     _ = @import("behavior/var_args.zig");
+    _ = @import("behavior/vector.zig");
     _ = @import("behavior/void.zig");
     _ = @import("behavior/while.zig");
     _ = @import("behavior/widening.zig");