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
master
Andrew Kelley 2019-11-05 13:39:56 -05:00
commit cb8af1c6d4
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
8 changed files with 422 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,38 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:4:20: note: referenced here",
);
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{

View File

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