SIMD: array to vector, vector to array, wrapping int add

also vectors and arrays now use the same ConstExprVal representation

See #903
master
Andrew Kelley 2019-02-04 20:30:00 -05:00
parent 2828a9695f
commit 8c6fa982cd
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
8 changed files with 403 additions and 224 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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");
}

View File

@ -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");

View File

@ -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();
}