commit cb8af1c6d48d5fdb26484a070b8af069c4152049 (tree)
parent a26e9fa7238e972301f761db80434fa0205d1016
Author: Andrew Kelley <andrew@ziglang.org>
Date: Tue, 5 Nov 2019 13:39:56 -0500
Merge branch 'vector-element-access'
This introduces the concept of vector index being part of a pointer
type. This avoids vectors having well-defined in-memory layout, and
allows vectors of any integer bit width to work the same way.
When a vector is indexed with a scalar, this is vector element access,
which this branch implements. When a vector is indexed with a vector,
this is gather/scatter, which is not implemented in this branch.
closes #3575
closes #3580
Diffstat:
8 files changed, 422 insertions(+), 41 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
@@ -1183,13 +1183,22 @@ struct FnTypeId {
uint32_t fn_type_id_hash(FnTypeId*);
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
+static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX;
+static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1;
+
struct ZigTypePointer {
ZigType *child_type;
ZigType *slice_parent;
+
PtrLen ptr_len;
uint32_t explicit_alignment; // 0 means use ABI alignment
+
uint32_t bit_offset_in_host;
- uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned
+ // size of host integer. 0 means no host integer; this field is aligned
+ // when vector_index != VECTOR_INDEX_NONE this is the len of the containing vector
+ uint32_t host_int_bytes;
+
+ uint32_t vector_index; // see the VECTOR_INDEX_* constants
bool is_const;
bool is_volatile;
bool allow_zero;
@@ -1732,8 +1741,11 @@ struct TypeId {
ZigType *child_type;
PtrLen ptr_len;
uint32_t alignment;
+
uint32_t bit_offset_in_host;
uint32_t host_int_bytes;
+
+ uint32_t vector_index;
bool is_const;
bool is_volatile;
bool allow_zero;
@@ -2414,6 +2426,7 @@ enum IrInstructionId {
IrInstructionIdLoadPtr,
IrInstructionIdLoadPtrGen,
IrInstructionIdStorePtr,
+ IrInstructionIdVectorStoreElem,
IrInstructionIdFieldPtr,
IrInstructionIdStructFieldPtr,
IrInstructionIdUnionFieldPtr,
@@ -2563,6 +2576,7 @@ enum IrInstructionId {
IrInstructionIdResume,
IrInstructionIdSpillBegin,
IrInstructionIdSpillEnd,
+ IrInstructionIdVectorExtractElem,
};
struct IrInstruction {
@@ -2757,6 +2771,14 @@ struct IrInstructionStorePtr {
IrInstruction *value;
};
+struct IrInstructionVectorStoreElem {
+ IrInstruction base;
+
+ IrInstruction *vector_ptr;
+ IrInstruction *index;
+ IrInstruction *value;
+};
+
struct IrInstructionFieldPtr {
IrInstruction base;
@@ -3890,6 +3912,13 @@ struct IrInstructionSpillEnd {
IrInstructionSpillBegin *begin;
};
+struct IrInstructionVectorExtractElem {
+ IrInstruction base;
+
+ IrInstruction *vector;
+ IrInstruction *index;
+};
+
enum ResultLocId {
ResultLocIdInvalid,
ResultLocIdNone,
diff --git a/src/analyze.cpp b/src/analyze.cpp
@@ -480,9 +480,10 @@ ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) {
return entry;
}
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
+ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
- uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
+ uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero,
+ uint32_t vector_index)
{
assert(ptr_len != PtrLenC || allow_zero);
assert(!type_is_invalid(child_type));
@@ -494,7 +495,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
byte_alignment = 0;
}
- if (host_int_bytes != 0) {
+ if (host_int_bytes != 0 && vector_index == VECTOR_INDEX_NONE) {
uint32_t child_type_bits = type_size_bits(g, child_type);
if (host_int_bytes * 8 == child_type_bits) {
assert(bit_offset_in_host == 0);
@@ -504,7 +505,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
TypeId type_id = {};
ZigType **parent_pointer = nullptr;
- if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) {
+ if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
+ allow_zero || vector_index != VECTOR_INDEX_NONE)
+ {
type_id.id = ZigTypeIdPointer;
type_id.data.pointer.child_type = child_type;
type_id.data.pointer.is_const = is_const;
@@ -514,6 +517,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
type_id.data.pointer.host_int_bytes = host_int_bytes;
type_id.data.pointer.ptr_len = ptr_len;
type_id.data.pointer.allow_zero = allow_zero;
+ type_id.data.pointer.vector_index = vector_index;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
@@ -540,19 +544,36 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
allow_zero_str = allow_zero ? "allowzero " : "";
}
buf_resize(&entry->name, 0);
- if (host_int_bytes == 0 && byte_alignment == 0) {
+ if (host_int_bytes == 0 && byte_alignment == 0 && vector_index == VECTOR_INDEX_NONE) {
buf_appendf(&entry->name, "%s%s%s%s%s",
star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
- } else if (host_int_bytes == 0) {
+ } else if (host_int_bytes == 0 && vector_index == VECTOR_INDEX_NONE) {
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (byte_alignment == 0) {
- buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str,
- bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+ assert(vector_index == VECTOR_INDEX_NONE);
+ buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
+ star_str,
+ bit_offset_in_host, host_int_bytes,
+ const_str, volatile_str, allow_zero_str,
+ buf_ptr(&child_type->name));
+ } else if (vector_index == VECTOR_INDEX_NONE) {
+ buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
+ star_str, byte_alignment,
+ bit_offset_in_host, host_int_bytes,
+ const_str, volatile_str, allow_zero_str,
+ buf_ptr(&child_type->name));
+ } else if (vector_index == VECTOR_INDEX_RUNTIME) {
+ buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":?) %s%s%s%s",
+ star_str, byte_alignment,
+ bit_offset_in_host, host_int_bytes,
+ const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
} else {
- buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
- bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+ buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
+ star_str, byte_alignment,
+ bit_offset_in_host, host_int_bytes, vector_index,
+ const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
}
@@ -581,6 +602,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
entry->data.pointer.host_int_bytes = host_int_bytes;
entry->data.pointer.allow_zero = allow_zero;
+ entry->data.pointer.vector_index = vector_index;
if (parent_pointer) {
*parent_pointer = entry;
@@ -590,8 +612,17 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
return entry;
}
+ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
+ bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
+ uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
+{
+ return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len,
+ byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE);
+}
+
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
- return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false);
+ return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false,
+ VECTOR_INDEX_NONE);
}
ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
@@ -6910,6 +6941,7 @@ uint32_t type_id_hash(TypeId x) {
(x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
(((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
(((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
+ (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) +
(((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881);
case ZigTypeIdArray:
return hash_ptr(x.data.array.child_type) +
@@ -6962,6 +6994,7 @@ bool type_id_eql(TypeId a, TypeId b) {
a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
a.data.pointer.alignment == b.data.pointer.alignment &&
a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
+ a.data.pointer.vector_index == b.data.pointer.vector_index &&
a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
case ZigTypeIdArray:
return a.data.array.child_type == b.data.array.child_type &&
@@ -8266,11 +8299,21 @@ static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus
if (type->data.pointer.is_const || type->data.pointer.is_volatile ||
type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle ||
- type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero)
+ type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero ||
+ type->data.pointer.vector_index != VECTOR_INDEX_NONE)
{
assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
- ZigType *peer_type = get_pointer_to_type_extra(g, elem_type, false, false,
- PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false);
+ ZigType *peer_type;
+ if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) {
+ peer_type = get_pointer_to_type_extra2(g, elem_type, false, false,
+ PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false,
+ VECTOR_INDEX_NONE);
+ } else {
+ uint32_t host_vec_len = type->data.pointer.host_int_bytes;
+ ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type);
+ peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false,
+ PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE);
+ }
type->llvm_type = get_llvm_type(g, peer_type);
type->llvm_di_type = get_llvm_di_type(g, peer_type);
assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
diff --git a/src/analyze.hpp b/src/analyze.hpp
@@ -17,10 +17,14 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node,
ZigType *new_type_table_entry(ZigTypeId id);
ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn);
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
- bool is_volatile, PtrLen ptr_len,
+ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type,
+ bool is_const, bool is_volatile, PtrLen ptr_len,
uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
bool allow_zero);
+ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type,
+ bool is_const, bool is_volatile, PtrLen ptr_len,
+ uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
+ bool allow_zero, uint32_t vector_index);
uint64_t type_size(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
diff --git a/src/codegen.cpp b/src/codegen.cpp
@@ -838,6 +838,11 @@ static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type
}
}
+static void ir_assert(bool ok, IrInstruction *source_instruction) {
+ if (ok) return;
+ src_assert(ok, source_instruction->source_node);
+}
+
static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
// TODO memoize
Scope *scope = instruction->scope;
@@ -1635,6 +1640,17 @@ static void gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type,
return;
}
+ assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME);
+ if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) {
+ LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(),
+ ptr_type->data.pointer.vector_index, false);
+ LLVMValueRef loaded_vector = gen_load(g, ptr, ptr_type, "");
+ LLVMValueRef new_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value,
+ index_val, "");
+ gen_store(g, new_vector, ptr, ptr_type);
+ return;
+ }
+
uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes;
if (host_int_bytes == 0) {
gen_store(g, value, ptr, ptr_type);
@@ -1695,11 +1711,11 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
}
if (instruction->spill != nullptr) {
ZigType *ptr_type = instruction->spill->value.type;
- src_assert(ptr_type->id == ZigTypeIdPointer, instruction->source_node);
+ ir_assert(ptr_type->id == ZigTypeIdPointer, instruction);
return get_handle_value(g, ir_llvm_value(g, instruction->spill),
ptr_type->data.pointer.child_type, instruction->spill->value.type);
}
- src_assert(instruction->value.special != ConstValSpecialRuntime, instruction->source_node);
+ ir_assert(instruction->value.special != ConstValSpecialRuntime, instruction);
assert(instruction->value.type);
render_const_val(g, &instruction->value, "");
// we might have to do some pointer casting here due to the way union
@@ -2428,8 +2444,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
return nullptr;
}
assert(g->cur_ret_ptr);
- src_assert(instruction->operand->value.special != ConstValSpecialRuntime,
- instruction->base.source_node);
+ ir_assert(instruction->operand->value.special != ConstValSpecialRuntime, &instruction->base);
LLVMValueRef value = ir_llvm_value(g, instruction->operand);
ZigType *return_type = instruction->operand->value.type;
gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
@@ -3399,7 +3414,9 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrI
return nullptr;
}
-static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) {
+static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable,
+ IrInstructionLoadPtrGen *instruction)
+{
ZigType *child_type = instruction->base.value.type;
if (!type_has_bits(child_type))
return nullptr;
@@ -3408,6 +3425,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
ZigType *ptr_type = instruction->ptr->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
+ ir_assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME, &instruction->base);
+ if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) {
+ LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(),
+ ptr_type->data.pointer.vector_index, false);
+ LLVMValueRef loaded_vector = LLVMBuildLoad(g->builder, ptr, "");
+ return LLVMBuildExtractElement(g->builder, loaded_vector, index_val, "");
+ }
+
uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes;
if (host_int_bytes == 0)
return get_handle_value(g, ptr, child_type, ptr_type);
@@ -3619,6 +3644,19 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir
return nullptr;
}
+static LLVMValueRef ir_render_vector_store_elem(CodeGen *g, IrExecutable *executable,
+ IrInstructionVectorStoreElem *instruction)
+{
+ LLVMValueRef vector_ptr = ir_llvm_value(g, instruction->vector_ptr);
+ LLVMValueRef index = ir_llvm_value(g, instruction->index);
+ LLVMValueRef value = ir_llvm_value(g, instruction->value);
+
+ LLVMValueRef loaded_vector = gen_load(g, vector_ptr, instruction->vector_ptr->value.type, "");
+ LLVMValueRef modified_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, index, "");
+ gen_store(g, modified_vector, vector_ptr, instruction->vector_ptr->value.type);
+ return nullptr;
+}
+
static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
if (instruction->base.value.special != ConstValSpecialRuntime)
return ir_llvm_value(g, &instruction->base);
@@ -3636,7 +3674,7 @@ static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
{
if (!type_has_bits(instruction->base.value.type))
return nullptr;
- src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node);
+ ir_assert(g->cur_ret_ptr != nullptr, &instruction->base);
return g->cur_ret_ptr;
}
@@ -3645,7 +3683,6 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
ZigType *array_ptr_type = instruction->array_ptr->value.type;
assert(array_ptr_type->id == ZigTypeIdPointer);
ZigType *array_type = array_ptr_type->data.pointer.child_type;
- LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index);
assert(subscript_value);
@@ -3657,6 +3694,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
if (array_type->id == ZigTypeIdArray ||
(array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
{
+ LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
if (array_type->id == ZigTypeIdPointer) {
assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);
array_type = array_type->data.pointer.child_type;
@@ -3696,12 +3734,14 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
};
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
} else if (array_type->id == ZigTypeIdPointer) {
+ LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
LLVMValueRef indices[] = {
subscript_value
};
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
} else if (array_type->id == ZigTypeIdStruct) {
+ LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
assert(array_type->data.structure.is_slice);
ZigType *ptr_type = instruction->base.value.type;
@@ -3729,6 +3769,8 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");
LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, "");
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
+ } else if (array_type->id == ZigTypeIdVector) {
+ return array_ptr_ptr;
} else {
zig_unreachable();
}
@@ -3917,9 +3959,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
if (instruction->modifier == CallModifierAsync) {
frame_result_loc = result_loc;
} else {
- src_assert(instruction->frame_result_loc != nullptr, instruction->base.source_node);
+ ir_assert(instruction->frame_result_loc != nullptr, &instruction->base);
frame_result_loc_uncasted = ir_llvm_value(g, instruction->frame_result_loc);
- src_assert(instruction->fn_entry != nullptr, instruction->base.source_node);
+ ir_assert(instruction->fn_entry != nullptr, &instruction->base);
frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted,
LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), "");
}
@@ -4271,10 +4313,10 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa
if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull)))
codegen_report_errors_and_exit(g);
- src_assert(field->gen_index != SIZE_MAX, instruction->base.source_node);
+ ir_assert(field->gen_index != SIZE_MAX, &instruction->base);
LLVMValueRef field_ptr_val = LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, "");
ZigType *res_type = instruction->base.value.type;
- src_assert(res_type->id == ZigTypeIdPointer, instruction->base.source_node);
+ ir_assert(res_type->id == ZigTypeIdPointer, &instruction->base);
if (res_type->data.pointer.host_int_bytes != 0) {
// We generate packed structs with get_llvm_type_of_n_bytes, which is
// u8 for 1 byte or [n]u8 for multiple bytes. But the pointer to the type
@@ -4684,7 +4726,7 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutable *executabl
static LLVMValueRef ir_render_splat(CodeGen *g, IrExecutable *executable, IrInstructionSplatGen *instruction) {
ZigType *result_type = instruction->base.value.type;
- src_assert(result_type->id == ZigTypeIdVector, instruction->base.source_node);
+ ir_assert(result_type->id == ZigTypeIdVector, &instruction->base);
uint32_t len = result_type->data.vector.len;
LLVMTypeRef op_llvm_type = LLVMVectorType(get_llvm_type(g, instruction->scalar->value.type), 1);
LLVMTypeRef mask_llvm_type = LLVMVectorType(LLVMInt32Type(), len);
@@ -5039,8 +5081,8 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
}
LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc);
- src_assert(result_loc != nullptr, instruction->base.source_node);
- src_assert(type_has_bits(child_type), instruction->base.source_node);
+ ir_assert(result_loc != nullptr, &instruction->base);
+ ir_assert(type_has_bits(child_type), &instruction->base);
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, "");
@@ -5973,6 +6015,14 @@ static LLVMValueRef ir_render_spill_end(CodeGen *g, IrExecutable *executable, Ir
zig_unreachable();
}
+static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutable *executable,
+ IrInstructionVectorExtractElem *instruction)
+{
+ LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
+ LLVMValueRef index = ir_llvm_value(g, instruction->index);
+ return LLVMBuildExtractElement(g->builder, vector, index, "");
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -6093,6 +6143,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction);
case IrInstructionIdStorePtr:
return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction);
+ case IrInstructionIdVectorStoreElem:
+ return ir_render_vector_store_elem(g, executable, (IrInstructionVectorStoreElem *)instruction);
case IrInstructionIdVarPtr:
return ir_render_var_ptr(g, executable, (IrInstructionVarPtr *)instruction);
case IrInstructionIdReturnPtr:
@@ -6233,6 +6285,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_shuffle_vector(g, executable, (IrInstructionShuffleVector *) instruction);
case IrInstructionIdSplatGen:
return ir_render_splat(g, executable, (IrInstructionSplatGen *) instruction);
+ case IrInstructionIdVectorExtractElem:
+ return ir_render_vector_extract_elem(g, executable, (IrInstructionVectorExtractElem *) instruction);
}
zig_unreachable();
}
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -491,6 +491,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
return IrInstructionIdStorePtr;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorStoreElem *) {
+ return IrInstructionIdVectorStoreElem;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) {
return IrInstructionIdFieldPtr;
}
@@ -1083,6 +1087,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {
return IrInstructionIdSpillEnd;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorExtractElem *) {
+ return IrInstructionIdVectorExtractElem;
+}
+
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
const char *name = nullptr;
@@ -1627,6 +1635,23 @@ static IrInstructionStorePtr *ir_build_store_ptr(IrBuilder *irb, Scope *scope, A
return instruction;
}
+static IrInstruction *ir_build_vector_store_elem(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *vector_ptr, IrInstruction *index, IrInstruction *value)
+{
+ IrInstructionVectorStoreElem *inst = ir_build_instruction<IrInstructionVectorStoreElem>(
+ &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+ inst->base.value.type = ira->codegen->builtin_types.entry_void;
+ inst->vector_ptr = vector_ptr;
+ inst->index = index;
+ inst->value = value;
+
+ ir_ref_instruction(vector_ptr, ira->new_irb.current_basic_block);
+ ir_ref_instruction(index, ira->new_irb.current_basic_block);
+ ir_ref_instruction(value, ira->new_irb.current_basic_block);
+
+ return &inst->base;
+}
+
static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
ZigVar *var, IrInstruction *align_value, IrInstruction *ptr)
{
@@ -3419,6 +3444,21 @@ static IrInstruction *ir_build_spill_end(IrBuilder *irb, Scope *scope, AstNode *
return &instruction->base;
}
+static IrInstruction *ir_build_vector_extract_elem(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *vector, IrInstruction *index)
+{
+ IrInstructionVectorExtractElem *instruction = ir_build_instruction<IrInstructionVectorExtractElem>(
+ &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = vector->value.type->data.vector.elem_type;
+ instruction->vector = vector;
+ instruction->index = index;
+
+ ir_ref_instruction(vector, ira->new_irb.current_basic_block);
+ ir_ref_instruction(index, 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;
@@ -12908,18 +12948,18 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
ResultLoc *result_loc)
{
Error err;
- ZigType *type_entry = ptr->value.type;
- if (type_is_invalid(type_entry))
+ ZigType *ptr_type = ptr->value.type;
+ if (type_is_invalid(ptr_type))
return ira->codegen->invalid_instruction;
- if (type_entry->id != ZigTypeIdPointer) {
+ if (ptr_type->id != ZigTypeIdPointer) {
ir_add_error_node(ira, source_instruction->source_node,
buf_sprintf("attempt to dereference non-pointer type '%s'",
- buf_ptr(&type_entry->name)));
+ buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}
- ZigType *child_type = type_entry->data.pointer.child_type;
+ ZigType *child_type = ptr_type->data.pointer.child_type;
// if the child type has one possible value, the deref is comptime
switch (type_has_one_possible_value(ira->codegen, child_type)) {
case OnePossibleValueInvalid:
@@ -12949,14 +12989,36 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
}
}
}
+
// if the instruction is a const ref instruction we can skip it
if (ptr->id == IrInstructionIdRef) {
IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr);
return ref_inst->value;
}
+ // If the instruction is a element pointer instruction to a vector, we emit
+ // vector element extract instruction rather than load pointer. If the
+ // pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been
+ // possible to implement this in the codegen for IrInstructionLoadPtrGen.
+ // However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error
+ // if the vector index cannot be determined right here, right now, because
+ // the type information does not contain enough information to actually
+ // perform a dereference.
+ if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
+ if (ptr->id == IrInstructionIdElemPtr) {
+ IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr;
+ IrInstruction *vector_loaded = ir_get_deref(ira, elem_ptr->array_ptr,
+ elem_ptr->array_ptr, nullptr);
+ IrInstruction *elem_index = elem_ptr->elem_index;
+ return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index);
+ }
+ ir_add_error(ira, ptr,
+ buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
IrInstruction *result_loc_inst;
- if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
+ if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
if (result_loc == nullptr) result_loc = no_result_loc();
result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr,
true, false, true);
@@ -16085,6 +16147,24 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
mark_comptime_value_escape(ira, source_instr, &value->value);
}
+ // If this is a store to a pointer with a runtime-known vector index,
+ // we have to figure out the IrInstruction which represents the index and
+ // emit a IrInstructionVectorStoreElem, or emit a compile error
+ // explaining why it is impossible for this store to work. Which is that
+ // the pointer address is of the vector; without the element index being known
+ // we cannot properly perform the insertion.
+ if (ptr->value.type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
+ if (ptr->id == IrInstructionIdElemPtr) {
+ IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr;
+ return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr,
+ elem_ptr->elem_index, value);
+ }
+ ir_add_error(ira, ptr,
+ buf_sprintf("unable to determine vector element index of type '%s'",
+ buf_ptr(&ptr->value.type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, ptr, value);
return &store_ptr->base;
@@ -17488,6 +17568,9 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
return ir_get_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val,
ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile, 0);
}
+ } else if (array_type->id == ZigTypeIdVector) {
+ // This depends on whether the element index is comptime, so it is computed later.
+ return_type = nullptr;
} else {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
@@ -17512,8 +17595,14 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
}
safety_check_on = false;
}
-
- if (return_type->data.pointer.explicit_alignment != 0) {
+ if (array_type->id == ZigTypeIdVector) {
+ ZigType *elem_type = array_type->data.vector.elem_type;
+ uint32_t host_vec_len = array_type->data.vector.len;
+ return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
+ ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
+ elem_ptr_instruction->ptr_len,
+ get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index);
+ } else if (return_type->data.pointer.explicit_alignment != 0) {
// figure out the largest alignment possible
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
@@ -17551,7 +17640,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
if (array_ptr_val->special == ConstValSpecialUndef && elem_ptr_instruction->init_array_type != nullptr) {
- if (array_type->id == ZigTypeIdArray) {
+ if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) {
array_ptr_val->data.x_array.special = ConstArraySpecialNone;
array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len);
array_ptr_val->special = ConstValSpecialStatic;
@@ -17720,7 +17809,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
zig_panic("TODO elem ptr on a slice has a null pointer");
}
return result;
- } else if (array_type->id == ZigTypeIdArray) {
+ } else if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) {
IrInstruction *result;
if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
@@ -17742,6 +17831,14 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
}
}
}
+ } else if (array_type->id == ZigTypeIdVector) {
+ // runtime known element index
+ ZigType *elem_type = array_type->data.vector.elem_type;
+ uint32_t host_vec_len = array_type->data.vector.len;
+ return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
+ ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
+ elem_ptr_instruction->ptr_len,
+ get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME);
} else {
// runtime known element index
switch (type_requires_comptime(ira->codegen, return_type)) {
@@ -26004,6 +26101,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
case IrInstructionIdFrameSizeGen:
case IrInstructionIdAwaitGen:
case IrInstructionIdSplatGen:
+ case IrInstructionIdVectorExtractElem:
+ case IrInstructionIdVectorStoreElem:
zig_unreachable();
case IrInstructionIdReturn:
@@ -26387,6 +26486,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdDeclVarSrc:
case IrInstructionIdDeclVarGen:
case IrInstructionIdStorePtr:
+ case IrInstructionIdVectorStoreElem:
case IrInstructionIdCallSrc:
case IrInstructionIdCallGen:
case IrInstructionIdReturn:
@@ -26539,6 +26639,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAllocaSrc:
case IrInstructionIdAllocaGen:
case IrInstructionIdSpillEnd:
+ case IrInstructionIdVectorExtractElem:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
@@ -78,6 +78,8 @@ const char* ir_instruction_type_str(IrInstructionId id) {
return "LoadPtrGen";
case IrInstructionIdStorePtr:
return "StorePtr";
+ case IrInstructionIdVectorStoreElem:
+ return "VectorStoreElem";
case IrInstructionIdFieldPtr:
return "FieldPtr";
case IrInstructionIdStructFieldPtr:
@@ -370,6 +372,8 @@ const char* ir_instruction_type_str(IrInstructionId id) {
return "SpillBegin";
case IrInstructionIdSpillEnd:
return "SpillEnd";
+ case IrInstructionIdVectorExtractElem:
+ return "VectorExtractElem";
}
zig_unreachable();
}
@@ -788,6 +792,15 @@ static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction)
ir_print_other_instruction(irp, instruction->value);
}
+static void ir_print_vector_store_elem(IrPrint *irp, IrInstructionVectorStoreElem *instruction) {
+ fprintf(irp->f, "vector_ptr=");
+ ir_print_var_instruction(irp, instruction->vector_ptr);
+ fprintf(irp->f, ",index=");
+ ir_print_var_instruction(irp, instruction->index);
+ fprintf(irp->f, ",value=");
+ ir_print_other_instruction(irp, instruction->value);
+}
+
static void ir_print_typeof(IrPrint *irp, IrInstructionTypeOf *instruction) {
fprintf(irp->f, "@typeOf(");
ir_print_other_instruction(irp, instruction->value);
@@ -1969,6 +1982,14 @@ static void ir_print_spill_end(IrPrint *irp, IrInstructionSpillEnd *instruction)
fprintf(irp->f, ")");
}
+static void ir_print_vector_extract_elem(IrPrint *irp, IrInstructionVectorExtractElem *instruction) {
+ fprintf(irp->f, "@vectorExtractElem(");
+ ir_print_other_instruction(irp, instruction->vector);
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->index);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool trailing) {
ir_print_prefix(irp, instruction, trailing);
switch (instruction->id) {
@@ -2037,6 +2058,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool
case IrInstructionIdStorePtr:
ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction);
break;
+ case IrInstructionIdVectorStoreElem:
+ ir_print_vector_store_elem(irp, (IrInstructionVectorStoreElem *)instruction);
+ break;
case IrInstructionIdTypeOf:
ir_print_typeof(irp, (IrInstructionTypeOf *)instruction);
break;
@@ -2466,6 +2490,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool
case IrInstructionIdSpillEnd:
ir_print_spill_end(irp, (IrInstructionSpillEnd *)instruction);
break;
+ case IrInstructionIdVectorExtractElem:
+ ir_print_vector_extract_elem(irp, (IrInstructionVectorExtractElem *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -25,6 +25,38 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
);
cases.add(
+ "store vector pointer with unknown runtime index",
+ \\export fn entry() void {
+ \\ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+ \\
+ \\ var i: u32 = 0;
+ \\ storev(&v[i], 42);
+ \\}
+ \\
+ \\fn storev(ptr: var, val: i32) void {
+ \\ ptr.* = val;
+ \\}
+ ,
+ "tmp.zig:9:8: error: unable to determine vector element index of type '*align(16:0:4:?) i32",
+ );
+
+ cases.add(
+ "load vector pointer with unknown runtime index",
+ \\export fn entry() void {
+ \\ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+ \\
+ \\ var i: u32 = 0;
+ \\ var x = loadv(&v[i]);
+ \\}
+ \\
+ \\fn loadv(ptr: var) i32 {
+ \\ return ptr.*;
+ \\}
+ ,
+ "tmp.zig:9:12: error: unable to determine vector element index of type '*align(16:0:4:?) i32",
+ );
+
+ cases.add(
"using an unknown len ptr type instead of array",
\\const resolutions = [*][*]const u8{
\\ c"[320 240 ]",
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
@@ -159,3 +159,94 @@ test "vector @splat" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "load vector elements via comptime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined };
+ expect(v[0] == 1);
+ expect(v[1] == 2);
+ expect(loadv(&v[2]) == 3);
+ }
+ fn loadv(ptr: var) i32 {
+ return ptr.*;
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "store vector elements via comptime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+
+ v[2] = 42;
+ expect(v[1] == 5);
+ v[3] = -364;
+ expect(v[2] == 42);
+ expect(-364 == v[3]);
+
+ storev(&v[0], 100);
+ expect(v[0] == 100);
+ }
+ fn storev(ptr: var, x: i32) void {
+ ptr.* = x;
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "load vector elements via runtime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined };
+ var i: u32 = 0;
+ expect(v[i] == 1);
+ i += 1;
+ expect(v[i] == 2);
+ i += 1;
+ expect(v[i] == 3);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "store vector elements via runtime index" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
+ var i: u32 = 2;
+ v[i] = 1;
+ expect(v[1] == 5);
+ expect(v[2] == 1);
+ i += 1;
+ v[i] = -364;
+ expect(-364 == v[3]);
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "initialize vector which is a struct field" {
+ const Vec4Obj = struct {
+ data: @Vector(4, f32),
+ };
+
+ const S = struct {
+ fn doTheTest() void {
+ var foo = Vec4Obj{
+ .data = [_]f32{ 1, 2, 3, 4 },
+ };
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}