mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 01:34:35 +00:00
fixes swizzling for array elements in soa arrays/slices
This commit is contained in:
@@ -2973,10 +2973,28 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
|
||||
return;
|
||||
}
|
||||
|
||||
if (o->mode == Addressing_SoaVariable) {
|
||||
Type *soa_for_in_type = nullptr;
|
||||
if (node->kind == Ast_UnaryExpr) {
|
||||
ast_node(ue, UnaryExpr, node);
|
||||
if (ast_node_expect(ue->expr, Ast_IndexExpr)) {
|
||||
ast_node(ie, IndexExpr, ue->expr);
|
||||
Entity *e = entity_of_node(ue->expr);
|
||||
if (e != nullptr && e->kind == Entity_Variable &&
|
||||
(e->flags & EntityFlag_SoaPtrField) != 0 &&
|
||||
e->Variable.for_loop_parent_type != nullptr) {
|
||||
Type *soa_type = type_deref(e->Variable.for_loop_parent_type);
|
||||
if (is_type_soa_struct(soa_type)) {
|
||||
soa_for_in_type = soa_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (soa_for_in_type != nullptr) {
|
||||
// &v in for-in loop over #soa container
|
||||
o->type = alloc_type_soa_pointer(soa_for_in_type);
|
||||
} else if (o->mode == Addressing_SoaVariable) {
|
||||
ast_node(ue, UnaryExpr, node);
|
||||
Ast *index_expr = unparen_expr(ue->expr);
|
||||
if (ast_node_expect(index_expr, Ast_IndexExpr)) {
|
||||
ast_node(ie, IndexExpr, index_expr);
|
||||
Type *soa_type = type_deref(type_of_expr(ie->expr));
|
||||
GB_ASSERT(is_type_soa_struct(soa_type));
|
||||
o->type = alloc_type_soa_pointer(soa_type);
|
||||
|
||||
@@ -40,6 +40,7 @@ enum lbAddrKind {
|
||||
|
||||
lbAddr_Swizzle,
|
||||
lbAddr_SwizzleLarge,
|
||||
lbAddr_SwizzleSoa,
|
||||
|
||||
lbAddr_BitField,
|
||||
};
|
||||
@@ -73,6 +74,12 @@ struct lbAddr {
|
||||
Type *type;
|
||||
Slice<i32> indices;
|
||||
} swizzle_large;
|
||||
struct {
|
||||
lbValue index;
|
||||
Ast *index_expr;
|
||||
Type *type;
|
||||
Slice<i32> indices; // soa element array is max len 4, but swizzle may repeat components
|
||||
} swizzle_soa;
|
||||
struct {
|
||||
Type *type;
|
||||
i64 bit_offset;
|
||||
@@ -440,11 +447,15 @@ gb_internal lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index
|
||||
gb_internal lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index);
|
||||
gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
|
||||
gb_internal lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
|
||||
gb_internal void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue len);
|
||||
|
||||
gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column);
|
||||
gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column);
|
||||
gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column);
|
||||
|
||||
gb_internal lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e);
|
||||
gb_internal lbValue lb_soa_variable_make_pointer(lbProcedure *p, lbAddr const &addr);
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
|
||||
gb_internal lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
|
||||
@@ -575,6 +586,7 @@ gb_internal void lb_emit_init_context(lbProcedure *p, lbAddr addr);
|
||||
gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident);
|
||||
|
||||
gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t);
|
||||
gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index);
|
||||
gb_internal LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
|
||||
|
||||
gb_internal LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
|
||||
|
||||
@@ -1393,7 +1393,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
for (i64 i = 0; i < elem_count; i++) {
|
||||
bool found = false;
|
||||
|
||||
for (isize j = 0; j < elem_count; j++) {
|
||||
for (isize j = 0; j < cl->elems.count; j++) {
|
||||
Ast *elem = cl->elems[j];
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (is_ast_range(fv->field)) {
|
||||
@@ -1453,10 +1453,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
GB_ASSERT(array_type->kind == Type_Array);
|
||||
Type *field_type = array_type->Array.elem;
|
||||
|
||||
// the element constant carries llvm padding members; remap the Odin field index
|
||||
unsigned src_index = cast(unsigned)lb_convert_struct_index(m, base_type(elem_type), cast(i32)i);
|
||||
for (isize j = 0; j < elem_count; j++) {
|
||||
LLVMValueRef v = aos_values[j];
|
||||
if (v != nullptr) {
|
||||
values[j] = llvm_const_extract_value(m, v, cast(unsigned)i);
|
||||
values[j] = llvm_const_extract_value(m, v, src_index);
|
||||
} else {
|
||||
values[j] = LLVMConstNull(lb_type(m, field_type));
|
||||
}
|
||||
@@ -1498,10 +1500,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
GB_ASSERT(array_type->kind == Type_Array);
|
||||
Type *field_type = array_type->Array.elem;
|
||||
|
||||
// the element constant carries llvm padding members; remap the Odin field index
|
||||
unsigned src_index = cast(unsigned)lb_convert_struct_index(m, base_type(elem_type), cast(i32)i);
|
||||
for (isize j = 0; j < elem_count; j++) {
|
||||
LLVMValueRef v = aos_values[j];
|
||||
if (v != nullptr) {
|
||||
values[j] = llvm_const_extract_value(m, v, cast(unsigned)i);
|
||||
values[j] = llvm_const_extract_value(m, v, src_index);
|
||||
} else {
|
||||
values[j] = LLVMConstNull(lb_type(m, field_type));
|
||||
}
|
||||
|
||||
@@ -4134,6 +4134,13 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
|
||||
return lb_addr_load(p, res);
|
||||
|
||||
} else if (is_type_soa_pointer(tv.type)) {
|
||||
Entity *e = entity_of_node(ue_expr);
|
||||
if (e != nullptr && (e->flags & EntityFlag_SoaPtrField) != 0) {
|
||||
// &v in a for-in loop over an #soa container;
|
||||
// the loop already holds the element's lbAddr_SoaVariable;
|
||||
// no bounds check needed, the index is in range by construction
|
||||
return lb_soa_variable_make_pointer(p, lb_get_soa_variable_addr(p, e));
|
||||
}
|
||||
ast_node(ie, IndexExpr, ue_expr);
|
||||
lbValue addr = lb_build_addr_ptr(p, ie->expr);
|
||||
|
||||
@@ -4143,10 +4150,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
|
||||
GB_ASSERT(is_type_pointer(addr.type));
|
||||
|
||||
lbValue index = lb_build_expr(p, ie->index);
|
||||
|
||||
if (!build_context.no_bounds_check) {
|
||||
// TODO(bill): soa bounds checking
|
||||
}
|
||||
lb_emit_soa_index_bounds_check(p, addr, index, ie->index);
|
||||
|
||||
return lb_make_soa_pointer(p, tv.type, addr, index);
|
||||
} else if (ue_expr->kind == Ast_CompoundLit) {
|
||||
@@ -4787,7 +4791,8 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
|
||||
is_soa = true;
|
||||
// NOTE(bill): using SOA value (probably from for-in statement)
|
||||
lbAddr parent_addr = lb_get_soa_variable_addr(p, parent);
|
||||
v = lb_addr_get_ptr(p, parent_addr);
|
||||
// the element has no single address, so make a soa pointer for lb_emit_deep_field_gep
|
||||
v = lb_address_from_load_or_generate_local(p, lb_soa_variable_make_pointer(p, parent_addr));
|
||||
} else if (pv != nullptr) {
|
||||
v = *pv;
|
||||
} else {
|
||||
@@ -4856,21 +4861,23 @@ gb_internal lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce,
|
||||
if (index_count == 0) {
|
||||
return addr;
|
||||
}
|
||||
Type *type = base_type(lb_addr_type(addr));
|
||||
GB_ASSERT(type->kind == Type_Array);
|
||||
i64 count = type->Array.count;
|
||||
if (count <= 4 && index_count <= 4) {
|
||||
u8 indices[4] = {};
|
||||
u8 index_count = 0;
|
||||
for (i32 i = 1; i < ce->args.count; i++) {
|
||||
TypeAndValue tv = type_and_value_of_expr(ce->args[i]);
|
||||
GB_ASSERT(is_type_integer(tv.type));
|
||||
GB_ASSERT(tv.value.kind == ExactValue_Integer);
|
||||
if (addr.kind != lbAddr_SoaVariable) {
|
||||
Type *type = base_type(lb_addr_type(addr));
|
||||
GB_ASSERT(type->kind == Type_Array);
|
||||
i64 count = type->Array.count;
|
||||
if (count <= 4 && index_count <= 4) {
|
||||
u8 indices[4] = {};
|
||||
u8 index_count = 0;
|
||||
for (i32 i = 1; i < ce->args.count; i++) {
|
||||
TypeAndValue tv = type_and_value_of_expr(ce->args[i]);
|
||||
GB_ASSERT(is_type_integer(tv.type));
|
||||
GB_ASSERT(tv.value.kind == ExactValue_Integer);
|
||||
|
||||
i64 src_index = big_int_to_i64(&tv.value.value_integer);
|
||||
indices[index_count++] = cast(u8)src_index;
|
||||
i64 src_index = big_int_to_i64(&tv.value.value_integer);
|
||||
indices[index_count++] = cast(u8)src_index;
|
||||
}
|
||||
return lb_addr_swizzle(lb_addr_get_ptr(p, addr), tv.type, index_count, indices);
|
||||
}
|
||||
return lb_addr_swizzle(lb_addr_get_ptr(p, addr), tv.type, index_count, indices);
|
||||
}
|
||||
auto indices = slice_make<i32>(permanent_allocator(), ce->args.count-1);
|
||||
isize index_index = 0;
|
||||
@@ -4882,6 +4889,9 @@ gb_internal lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce,
|
||||
i64 src_index = big_int_to_i64(&tv.value.value_integer);
|
||||
indices[index_index++] = cast(i32)src_index;
|
||||
}
|
||||
if (addr.kind == lbAddr_SoaVariable) {
|
||||
return lb_addr_swizzle_soa(addr.addr, addr.soa.index, addr.soa.index_expr, tv.type, indices);
|
||||
}
|
||||
return lb_addr_swizzle_large(lb_addr_get_ptr(p, addr), tv.type, indices);
|
||||
}
|
||||
|
||||
@@ -6514,7 +6524,31 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
if (is_type_pointer(tav.type)) {
|
||||
a = lb_build_expr(p, se->expr);
|
||||
} else {
|
||||
lbAddr addr = lb_build_addr(p, se->expr);
|
||||
lbAddr addr;
|
||||
if (is_type_soa_pointer(tav.type)) {
|
||||
// auto-deref p.xy, where p is soa pointer;
|
||||
// base p doesn't lower to an lbAddr_SoaVariable on its own
|
||||
// (it is a local holding the soa pointer), so build the element
|
||||
// addr here the same way an explicit p^ does
|
||||
lbValue value = lb_build_expr(p, se->expr);
|
||||
lbValue ptr = lb_emit_struct_ev(p, value, 0);
|
||||
lbValue idx = lb_emit_struct_ev(p, value, 1);
|
||||
addr = lb_addr_soa_variable(ptr, idx, nullptr);
|
||||
} else {
|
||||
addr = lb_build_addr(p, se->expr);
|
||||
}
|
||||
if (addr.kind == lbAddr_SoaVariable) {
|
||||
// soa[i].xy
|
||||
Type *type = type_deref(expr->tav.type);
|
||||
GB_ASSERT_MSG(is_type_array(type), "%s", type_to_string(type));
|
||||
|
||||
auto soa_indices = slice_make<i32>(permanent_allocator(), swizzle_count);
|
||||
for (u8 i = 0; i < swizzle_count; i++) {
|
||||
soa_indices[i] = cast(i32)swizzle_indices[i];
|
||||
}
|
||||
return lb_addr_swizzle_soa(addr.addr, addr.soa.index, addr.soa.index_expr,
|
||||
type, soa_indices);
|
||||
}
|
||||
a = lb_addr_get_ptr(p, addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -616,6 +616,38 @@ gb_internal lbAddr lb_addr_soa_variable(lbValue addr, lbValue index, Ast *index_
|
||||
return v;
|
||||
}
|
||||
|
||||
// pointer to the index element of the field_index component
|
||||
gb_internal lbValue lb_soa_field_elem_ptr(lbProcedure *p, lbValue soa_ptr, i32 field_index, lbValue index) {
|
||||
Type *t = base_type(type_deref(soa_ptr.type));
|
||||
GB_ASSERT_MSG(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None, "%s", type_to_string(t));
|
||||
|
||||
lbValue field = lb_emit_struct_ep(p, soa_ptr, field_index);
|
||||
if (t->Struct.soa_kind == StructSoa_Fixed) {
|
||||
return lb_emit_array_ep(p, field, index);
|
||||
}
|
||||
return lb_emit_ptr_offset(p, lb_emit_load(p, field), index);
|
||||
}
|
||||
|
||||
// bounds check for an #soa element index
|
||||
gb_internal void lb_emit_soa_index_bounds_check(lbProcedure *p, lbValue soa_ptr, lbValue index, Ast *index_expr) {
|
||||
if (index_expr == nullptr) {
|
||||
return;
|
||||
}
|
||||
Type *t = base_type(type_deref(soa_ptr.type));
|
||||
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
|
||||
if (lb_is_const(index) && t->Struct.soa_kind == StructSoa_Fixed) {
|
||||
return;
|
||||
}
|
||||
|
||||
lbValue len = {};
|
||||
if (t->Struct.soa_kind == StructSoa_Fixed) {
|
||||
len = lb_const_int(p->module, t_int, t->Struct.soa_count);
|
||||
} else {
|
||||
len = lb_soa_struct_len(p, soa_ptr);
|
||||
}
|
||||
lb_emit_bounds_check(p, ast_token(index_expr), index, len);
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_addr_swizzle(lbValue addr, Type *array_type, u8 swizzle_count, u8 swizzle_indices[4]) {
|
||||
GB_ASSERT(is_type_array(array_type) || is_type_simd_vector(array_type));
|
||||
GB_ASSERT(1 < swizzle_count && swizzle_count <= 4);
|
||||
@@ -634,6 +666,16 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
|
||||
return v;
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_addr_swizzle_soa(lbValue addr, lbValue index, Ast *index_expr, Type *type, Slice<i32> const &swizzle_indices) {
|
||||
GB_ASSERT(swizzle_indices.count > 0);
|
||||
lbAddr v = {lbAddr_SwizzleSoa, addr};
|
||||
v.swizzle_soa.index = index;
|
||||
v.swizzle_soa.index_expr = index_expr;
|
||||
v.swizzle_soa.type = type;
|
||||
v.swizzle_soa.indices = swizzle_indices;
|
||||
return v;
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 bit_offset, i64 bit_size) {
|
||||
GB_ASSERT(is_type_pointer(addr.type));
|
||||
Type *mt = type_deref(addr.type);
|
||||
@@ -662,6 +704,13 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
|
||||
return addr.swizzle.type;
|
||||
case lbAddr_SwizzleLarge:
|
||||
return addr.swizzle_large.type;
|
||||
case lbAddr_SwizzleSoa:
|
||||
return addr.swizzle_soa.type;
|
||||
case lbAddr_SoaVariable:
|
||||
// deliberately the container type (#soa[N]T), not the element type the addr denotes;
|
||||
// lb_soa_variable_make_pointer and lb_build_assign_stmt depend on this,
|
||||
// if this gets changed to Struct.soa_elem, these must be fixed with it.
|
||||
return type_deref(addr.addr.type);
|
||||
case lbAddr_Context:
|
||||
if (addr.ctx.sel.index.count > 0) {
|
||||
Type *t = t_context;
|
||||
@@ -686,6 +735,17 @@ gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue cons
|
||||
return lb_addr_load(p, v);
|
||||
}
|
||||
|
||||
// the soa pointer denoting an lbAddr_SoaVariable element is the only pointer the
|
||||
// soa element can have; the generic lb_addr_get_ptr deliberately panics for this kind
|
||||
gb_internal lbValue lb_soa_variable_make_pointer(lbProcedure *p, lbAddr const &addr) {
|
||||
GB_ASSERT(addr.kind == lbAddr_SoaVariable);
|
||||
// lb_addr_type on an SoaVariable returns the container type
|
||||
// (see the SoaVariable case in lb_addr_type),
|
||||
// which is what the soa pointer is parameterized by
|
||||
Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(addr));
|
||||
return lb_make_soa_pointer(p, soa_ptr_type, addr.addr, addr.soa.index);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
|
||||
if (addr.addr.value == nullptr) {
|
||||
GB_PANIC("Illegal addr -> nullptr");
|
||||
@@ -697,12 +757,10 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
|
||||
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
|
||||
|
||||
case lbAddr_SoaVariable:
|
||||
{
|
||||
Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(addr));
|
||||
return lb_address_from_load_or_generate_local(p, lb_make_soa_pointer(p, soa_ptr_type, addr.addr, addr.soa.index));
|
||||
// TODO(bill): FIX THIS HACK
|
||||
// return lb_address_from_load(p, lb_addr_load(p, addr));
|
||||
}
|
||||
// use lb_addr_load/lb_addr_store or lb_soa_field_elem_ptr for a single component;
|
||||
// callers that need the soa pointer get it via lb_soa_variable_make_pointer
|
||||
GB_PANIC("lbAddr_SoaVariable should be handled elsewhere");
|
||||
break;
|
||||
|
||||
case lbAddr_Context:
|
||||
GB_PANIC("lbAddr_Context should be handled elsewhere");
|
||||
@@ -715,6 +773,10 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
|
||||
case lbAddr_SwizzleLarge:
|
||||
GB_PANIC("lbAddr_SwizzleLarge should be handled elsewhere");
|
||||
break;
|
||||
|
||||
case lbAddr_SwizzleSoa:
|
||||
GB_PANIC("lbAddr_SwizzleSoa should be handled elsewhere");
|
||||
break;
|
||||
}
|
||||
|
||||
return addr.addr;
|
||||
@@ -1264,6 +1326,28 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (addr.kind == lbAddr_SwizzleSoa) {
|
||||
GB_ASSERT(value.value != nullptr);
|
||||
value = lb_emit_conv(p, value, lb_addr_type(addr));
|
||||
|
||||
lb_emit_soa_index_bounds_check(p, addr.addr, addr.swizzle_soa.index, addr.swizzle_soa.index_expr);
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
isize n = addr.swizzle_soa.indices.count;
|
||||
lbValue src = lb_address_from_load_or_generate_local(p, value);
|
||||
auto src_loads = slice_make<lbValue>(temporary_allocator(), n);
|
||||
auto dst_ptrs = slice_make<lbValue>(temporary_allocator(), n);
|
||||
for (isize i = 0; i < n; i++) {
|
||||
src_loads[i] = lb_emit_load(p, lb_emit_array_epi(p, src, i));
|
||||
}
|
||||
for (isize i = 0; i < n; i++) {
|
||||
dst_ptrs[i] = lb_soa_field_elem_ptr(p, addr.addr, addr.swizzle_soa.indices[i], addr.swizzle_soa.index);
|
||||
}
|
||||
for (isize i = 0; i < n; i++) {
|
||||
lb_emit_store(p, dst_ptrs[i], src_loads[i]);
|
||||
}
|
||||
return;
|
||||
} else if (addr.kind == lbAddr_SwizzleLarge) {
|
||||
GB_ASSERT(value.value != nullptr);
|
||||
value = lb_emit_conv(p, value, lb_addr_type(addr));
|
||||
@@ -1314,7 +1398,9 @@ gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
|
||||
Type *a = type_deref(ptr.type, true);
|
||||
if (LLVMIsNull(value.value)) {
|
||||
LLVMTypeRef src_t = llvm_addr_type(p->module, ptr);
|
||||
// used to be llvm_addr_type: for a multi-pointer typed ptr the latter is `ptr`,
|
||||
// and ConstNull of it would store 8 bytes over an element of any size
|
||||
LLVMTypeRef src_t = lb_type(p->module, a);
|
||||
if (is_type_proc(a)) {
|
||||
LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
|
||||
LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
|
||||
@@ -1640,6 +1726,17 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
}
|
||||
}
|
||||
return lb_addr_load(p, res);
|
||||
} else if (addr.kind == lbAddr_SwizzleSoa) {
|
||||
lb_emit_soa_index_bounds_check(p, addr.addr, addr.swizzle_soa.index, addr.swizzle_soa.index_expr);
|
||||
|
||||
// gather one component per field, no vector path like for lbAddr_Swizzle;
|
||||
lbAddr res = lb_add_local_generated(p, addr.swizzle_soa.type, false);
|
||||
for (isize i = 0; i < addr.swizzle_soa.indices.count; i++) {
|
||||
lbValue src = lb_soa_field_elem_ptr(p, addr.addr, addr.swizzle_soa.indices[i], addr.swizzle_soa.index);
|
||||
lbValue dst = lb_emit_array_epi(p, res.addr, i);
|
||||
lb_emit_store(p, dst, lb_emit_load(p, src));
|
||||
}
|
||||
return lb_addr_load(p, res);
|
||||
} else if (addr.kind == lbAddr_SwizzleLarge) {
|
||||
Type *array_type = base_type(addr.swizzle_large.type);
|
||||
GB_ASSERT(array_type->kind == Type_Array);
|
||||
|
||||
@@ -1348,6 +1348,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
|
||||
switch (addr.kind) {
|
||||
case lbAddr_Swizzle:
|
||||
case lbAddr_SwizzleLarge:
|
||||
case lbAddr_SwizzleSoa:
|
||||
// NOTE(laytan): apply the swizzle.
|
||||
array = lb_address_from_load(p, lb_addr_load(p, addr));
|
||||
break;
|
||||
@@ -3015,6 +3016,50 @@ gb_internal void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr
|
||||
lb_emit_store(p, lhs_ptrs[i], ops[i]);
|
||||
}
|
||||
return;
|
||||
} else if (lhs.kind == lbAddr_SwizzleSoa) {
|
||||
// the three cases largely mirror each other,
|
||||
// perhaps unify them?
|
||||
GB_ASSERT(is_type_array(lhs_type));
|
||||
|
||||
// the index list may repeat components (and so be longer than 4), but the
|
||||
// element array itself has at most 4, so distinct destinations do fit in [4];
|
||||
// each dest is done on the first occurrence
|
||||
bool indices_handled[4] = {};
|
||||
i32 indices[4] = {};
|
||||
i32 index_count = 0;
|
||||
for (i32 index : lhs.swizzle_soa.indices) {
|
||||
GB_ASSERT(index < 4);
|
||||
if (indices_handled[index]) {
|
||||
continue;
|
||||
}
|
||||
indices_handled[index] = true;
|
||||
indices[index_count++] = index;
|
||||
}
|
||||
|
||||
lb_emit_soa_index_bounds_check(p, lhs.addr, lhs.swizzle_soa.index, lhs.swizzle_soa.index_expr);
|
||||
|
||||
lbValue lhs_ptrs[4] = {};
|
||||
lbValue x_loads[4] = {};
|
||||
lbValue y_loads[4] = {};
|
||||
lbValue ops[4] = {};
|
||||
|
||||
for (i32 i = 0; i < index_count; i++) {
|
||||
lhs_ptrs[i] = lb_soa_field_elem_ptr(p, lhs.addr, indices[i], lhs.swizzle_soa.index);
|
||||
}
|
||||
for (i32 i = 0; i < index_count; i++) {
|
||||
x_loads[i] = lb_emit_load(p, lhs_ptrs[i]);
|
||||
}
|
||||
for (i32 i = 0; i < index_count; i++) {
|
||||
y_loads[i].value = LLVMBuildExtractValue(p->builder, rhs.value, i, "");
|
||||
y_loads[i].type = elem_type;
|
||||
}
|
||||
for (i32 i = 0; i < index_count; i++) {
|
||||
ops[i] = lb_emit_arith(p, op, x_loads[i], y_loads[i], elem_type);
|
||||
}
|
||||
for (i32 i = 0; i < index_count; i++) {
|
||||
lb_emit_store(p, lhs_ptrs[i], ops[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#+feature dynamic-literals
|
||||
#+feature using-stmt
|
||||
package test_core_runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
@@ -229,6 +230,226 @@ test_soa_make_len :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, array[1], [2]int{3, 4})
|
||||
}
|
||||
|
||||
// storing an all-zero constant into a slice/dynamic #soa element
|
||||
@(test)
|
||||
test_soa_zero_elem_store :: proc(t: ^testing.T) {
|
||||
|
||||
V :: struct {a: u8, b: u16, c: u32, d: u64, e: u128}
|
||||
one := V{1, 2, 3, 4, 5}
|
||||
|
||||
array := make(#soa[dynamic]V, 0, 8)
|
||||
defer delete(array)
|
||||
for _ in 0 ..< 8 {
|
||||
append(&array, one)
|
||||
}
|
||||
|
||||
array[2] = {}
|
||||
array[5] = V{0, 0, 0, 0, 0}
|
||||
|
||||
s: #soa[]V = array[:]
|
||||
s[7] = V{}
|
||||
|
||||
for i in 0 ..< 8 {
|
||||
expected := one if i != 2 && i != 5 && i != 7 else V{}
|
||||
testing.expect_value(t, array[i], expected)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
V_Padded :: struct {a: i32, b: f64, c: f32}
|
||||
soa_padded_global := #soa[3]V_Padded{
|
||||
{a = 1, b = 1.0, c = 1.0},
|
||||
{a = 2, b = 2.0, c = 1.0},
|
||||
{a = 3, b = 3.0, c = 1.0},
|
||||
}
|
||||
|
||||
// fixed #soa compound literals with a padded element struct
|
||||
@(test)
|
||||
test_soa_fixed_compound_literal :: proc(t: ^testing.T) {
|
||||
for i in 0 ..< 3 {
|
||||
testing.expect_value(t, soa_padded_global[i], V_Padded{i32(i + 1), f64(i + 1), 1.0})
|
||||
}
|
||||
|
||||
local := #soa[3]V_Padded{
|
||||
{a = 1, b = 1.0, c = 1.0},
|
||||
{a = 2, b = 2.0, c = 1.0},
|
||||
{a = 3, b = 3.0, c = 1.0},
|
||||
}
|
||||
for i in 0 ..< 3 {
|
||||
testing.expect_value(t, local[i], V_Padded{i32(i + 1), f64(i + 1), 1.0})
|
||||
}
|
||||
|
||||
sparse := #soa[4]V_Padded{
|
||||
0 ..= 1 = {a = 7, b = 7.0, c = 7.0},
|
||||
3 = {a = 9, b = 9.0, c = 9.0},
|
||||
}
|
||||
testing.expect_value(t, sparse[0], V_Padded{7, 7.0, 7.0})
|
||||
testing.expect_value(t, sparse[1], V_Padded{7, 7.0, 7.0})
|
||||
testing.expect_value(t, sparse[2], V_Padded{})
|
||||
testing.expect_value(t, sparse[3], V_Padded{9, 9.0, 9.0})
|
||||
}
|
||||
|
||||
// swizzling an element of an #soa container with an array element type
|
||||
@(test)
|
||||
test_soa_array_elem_swizzle :: proc(t: ^testing.T) {
|
||||
|
||||
ref := [4]u16{1, 2, 3, 4} // reference
|
||||
|
||||
fixed: #soa[3][4]u16
|
||||
fixed.x[0], fixed.y[0], fixed.z[0], fixed.w[0] = 90, 91, 92, 93
|
||||
fixed.x[1], fixed.y[1], fixed.z[1], fixed.w[1] = 1, 2, 3, 4
|
||||
|
||||
testing.expect_value(t, fixed[1].x, ref.x)
|
||||
testing.expect_value(t, fixed[1].xy, ref.xy)
|
||||
testing.expect_value(t, fixed[1].xyz, ref.xyz)
|
||||
testing.expect_value(t, fixed[1].xyzw, ref.xyzw)
|
||||
testing.expect_value(t, fixed[1].yx, ref.yx) // permuted
|
||||
testing.expect_value(t, fixed[1].xx, ref.xx) // repeated
|
||||
testing.expect_value(t, fixed[1].wzyx, ref.wzyx)
|
||||
|
||||
// swizzle may repeat components and count can go > the array len
|
||||
testing.expect_value(t, swizzle(fixed[1], 0, 1), swizzle(ref, 0, 1))
|
||||
testing.expect_value(t, swizzle(fixed[1], 3, 0, 1), swizzle(ref, 3, 0, 1))
|
||||
testing.expect_value(t, swizzle(fixed[1], 0, 1, 0, 1, 0), swizzle(ref, 0, 1, 0, 1, 0))
|
||||
testing.expect_value(t, swizzle(fixed[1], 3, 3, 3, 3, 3, 3), swizzle(ref, 3, 3, 3, 3, 3, 3))
|
||||
|
||||
// runtime element index
|
||||
i := 1
|
||||
testing.expect_value(t, fixed[i].xy, ref.xy)
|
||||
|
||||
// scatter writes
|
||||
fixed[1].xy = [2]u16{10, 11}
|
||||
testing.expect_value(t, fixed.x[1], 10)
|
||||
testing.expect_value(t, fixed.y[1], 11)
|
||||
testing.expect_value(t, fixed.z[1], 3)
|
||||
// scatter writes permuted
|
||||
fixed[1].yx = [2]u16{20, 21}
|
||||
testing.expect_value(t, fixed.y[1], 20)
|
||||
testing.expect_value(t, fixed.x[1], 21)
|
||||
testing.expect_value(t, fixed.x[0], 90)
|
||||
testing.expect_value(t, fixed.w[0], 93)
|
||||
|
||||
// dynamic and slice kinds
|
||||
dyn := make(#soa[dynamic][4]u16, 2)
|
||||
defer delete(dyn)
|
||||
dyn[0] = [4]u16{1, 2, 3, 4}
|
||||
dyn[1] = [4]u16{5, 6, 7, 8}
|
||||
|
||||
testing.expect_value(t, dyn[0].xy, ref.xy)
|
||||
testing.expect_value(t, dyn[0].zx, ref.zx)
|
||||
dyn[1].xy = [2]u16{40, 41}
|
||||
testing.expect_value(t, dyn.x[1], 40)
|
||||
testing.expect_value(t, dyn.y[1], 41)
|
||||
testing.expect_value(t, dyn.x[0], 1)
|
||||
|
||||
s := dyn[:]
|
||||
testing.expect_value(t, s[0].zw, ref.zw)
|
||||
s[0].zw = [2]u16{50, 51}
|
||||
testing.expect_value(t, dyn.z[0], 50)
|
||||
testing.expect_value(t, dyn.w[0], 51)
|
||||
|
||||
// through #soa pointer
|
||||
p := &dyn[0]
|
||||
testing.expect_value(t, p^.x, u16(1))
|
||||
testing.expect_value(t, p^.xy, [2]u16{1, 2})
|
||||
|
||||
// auto-deref through the #soa pointer
|
||||
testing.expect_value(t, p.x, u16(1))
|
||||
testing.expect_value(t, p.xy, [2]u16{1, 2})
|
||||
testing.expect_value(t, p.yx, [2]u16{2, 1})
|
||||
p.zw = [2]u16{60, 61}
|
||||
testing.expect_value(t, dyn.z[0], 60)
|
||||
testing.expect_value(t, dyn.w[0], 61)
|
||||
p.xy += [2]u16{1, 1}
|
||||
testing.expect_value(t, dyn.x[0], 2)
|
||||
testing.expect_value(t, dyn.y[0], 3)
|
||||
|
||||
// read-modify-write
|
||||
fixed[1].xy += [2]u16{9, 10}
|
||||
testing.expect_value(t, fixed.x[1], 30)
|
||||
testing.expect_value(t, fixed.y[1], 30)
|
||||
fixed[1].xx += [2]u16{5, 100}
|
||||
testing.expect_value(t, fixed.x[1], 35)
|
||||
|
||||
// range over an element swizzle
|
||||
sum: u16
|
||||
for c in fixed[1].wz {
|
||||
sum += c
|
||||
}
|
||||
testing.expect_value(t, sum, u16(7))
|
||||
|
||||
// shuffle
|
||||
fixed[1].xy = [2]u16{9, 10}
|
||||
fixed[1].xy = fixed[1].yx
|
||||
testing.expect_value(t, fixed[1].xy, [2]u16{10, 9})
|
||||
}
|
||||
|
||||
// "using" on an #soa for-in looping variable
|
||||
@(test)
|
||||
test_soa_for_in_using :: proc(t: ^testing.T) {
|
||||
S :: struct {
|
||||
a: int,
|
||||
b: int,
|
||||
c: int,
|
||||
}
|
||||
s: #soa[2]S = {{a = 1, b = 2, c = 3}, {a = 4, b = 5, c = 6}}
|
||||
|
||||
sum := 0
|
||||
for v in s {
|
||||
using v
|
||||
sum += a + c
|
||||
}
|
||||
testing.expect_value(t, sum, 14)
|
||||
|
||||
for &v in s {
|
||||
using v
|
||||
b += 10
|
||||
}
|
||||
testing.expect_value(t, s.b[0], 12)
|
||||
testing.expect_value(t, s.b[1], 15)
|
||||
}
|
||||
|
||||
// &v in for-in over soa container
|
||||
@(test)
|
||||
test_soa_for_in_addr :: proc(t: ^testing.T) {
|
||||
S :: struct {
|
||||
a: int,
|
||||
b: int,
|
||||
}
|
||||
s: #soa[2]S = {{a = 1, b = 2}, {a = 3, b = 4}}
|
||||
|
||||
for &v, i in s {
|
||||
p := &v
|
||||
testing.expect_value(t, p.a, s.a[i])
|
||||
p.b += 10 * (i + 1)
|
||||
}
|
||||
testing.expect_value(t, s.b[0], 12)
|
||||
testing.expect_value(t, s.b[1], 24)
|
||||
|
||||
// &v is the same type as &s[i]
|
||||
q := &s[0]
|
||||
for &v in s {
|
||||
q = &v
|
||||
}
|
||||
|
||||
// still valid
|
||||
testing.expect_value(t, q.a, 3)
|
||||
q.a = 30
|
||||
testing.expect_value(t, s.a[1], 30)
|
||||
|
||||
// array element type
|
||||
arr: #soa[2][4]u16
|
||||
arr[1] = [4]u16{1, 2, 3, 4}
|
||||
for &v, i in arr {
|
||||
pv := &v
|
||||
if i == 1 {
|
||||
testing.expect_value(t, pv.x, u16(1))
|
||||
pv.y = 20
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, arr.y[1], 20)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_soa_array_allocator_resize :: proc(t: ^testing.T) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user