diff --git a/src/all_types.hpp b/src/all_types.hpp index 3fc6772b31..c4c9e13cfb 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/analyze.cpp b/src/analyze.cpp index 99378eb7a8..ff961a7044 100644 --- a/src/analyze.cpp +++ b/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; } diff --git a/src/analyze.hpp b/src/analyze.hpp index da5265a594..f558fa44b0 100644 --- a/src/analyze.hpp +++ b/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); diff --git a/src/codegen.cpp b/src/codegen.cpp index b73fda59d1..de2222afb7 100644 --- a/src/codegen.cpp +++ b/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(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(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(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); diff --git a/src/ir.cpp b/src/ir.cpp index 3d3c501df3..3cbbdc8103 100644 --- a/src/ir.cpp +++ b/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 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(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(&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(&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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a1fd450b65..e19aa6dda8 100644 --- a/src/ir_print.cpp +++ b/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"); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index e545a4c418..1fa00b34fd 100644 --- a/test/stage1/behavior.zig +++ b/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"); diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig new file mode 100644 index 0000000000..53c5d01381 --- /dev/null +++ b/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(); +} +