Browse Source

Add `#soa` pointer type to aid with refactoring to `#soa` data types

a: #soa[16]Foo
p := &a[6]
#assert(type_of(p) == #soa^#soa[16]Foo)
p^.x = 123
p.x = 123
gingerBill 3 years ago
parent
commit
5e3cf45df3

+ 3 - 0
core/encoding/json/marshal.odin

@@ -147,6 +147,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
 	case runtime.Type_Info_Multi_Pointer:
 		return .Unsupported_Type
 
+	case runtime.Type_Info_Soa_Pointer:
+		return .Unsupported_Type
+
 	case runtime.Type_Info_Procedure:
 		return .Unsupported_Type
 

+ 13 - 0
core/fmt/fmt.odin

@@ -1031,6 +1031,15 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
 	}
 }
 
+fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
+	io.write_string(fi.writer, "#soa{0x", &fi.n)
+	_fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
+	io.write_string(fi.writer, ", ", &fi.n)
+	_fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
+	io.write_string(fi.writer, "}", &fi.n)
+}
+
+
 enum_value_to_string :: proc(val: any) -> (string, bool) {
 	v := val
 	v.id = runtime.typeid_base(v.id)
@@ -1867,6 +1876,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 			fmt_pointer(fi, ptr, verb)
 		}
 
+	case runtime.Type_Info_Soa_Pointer:
+		ptr := (^runtime.Raw_Soa_Pointer)(v.data)^
+		fmt_soa_pointer(fi, ptr, verb)
+
 	case runtime.Type_Info_Multi_Pointer:
 		ptr := (^rawptr)(v.data)^
 		if ptr == nil {

+ 1 - 0
core/mem/raw.odin

@@ -8,6 +8,7 @@ Raw_Cstring       :: runtime.Raw_Cstring
 Raw_Slice         :: runtime.Raw_Slice
 Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
 Raw_Map           :: runtime.Raw_Map
+Raw_Soa_Pointer   :: runtime.Raw_Soa_Pointer
 
 Raw_Complex64     :: struct {real, imag: f32}
 Raw_Complex128    :: struct {real, imag: f64}

+ 1 - 0
core/odin/ast/ast.odin

@@ -700,6 +700,7 @@ Proc_Type :: struct {
 
 Pointer_Type :: struct {
 	using node: Expr,
+	tag:     ^Expr,
 	pointer: tokenizer.Pos,
 	elem:    ^Expr,
 }

+ 1 - 0
core/odin/ast/clone.odin

@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
 		r.results = auto_cast clone(r.results)
 	case ^Pointer_Type:
 		r.elem = clone(r.elem)
+		r.tag  = clone(r.tag)
 	case ^Multi_Pointer_Type:
 		r.elem = clone(r.elem)
 	case ^Array_Type:

+ 2 - 0
core/odin/doc-format/doc_format.odin

@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
 	Relative_Slice     = 21,
 	Multi_Pointer      = 22,
 	Matrix             = 23,
+	Soa_Pointer        = 24,
 }
 
 Type_Elems_Cap :: 4
@@ -245,6 +246,7 @@ Type :: struct {
 	// .Relative_Slice     - 2 types:   0=slice type, 1=base integer
 	// .Multi_Pointer      - 1 type:    0=element
 	// .Matrix             - 1 type:    0=element
+	// .Soa_Pointer        - 1 type:    0=element
 	types: Array(Type_Index),
 
 	// Used by:

+ 15 - 1
core/odin/parser/parser.odin

@@ -2235,7 +2235,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			return parse_call_expr(p, bd)
 
 
-		case "soa", "simd":
+		case "soa":
 			bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
 			bd.tok  = tok
 			bd.name = name.text
@@ -2244,6 +2244,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			#partial switch t in type.derived_expr {
 			case ^ast.Array_Type:         t.tag = bd
 			case ^ast.Dynamic_Array_Type: t.tag = bd
+			case ^ast.Pointer_Type:       t.tag = bd
+			case:
+				error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
+			}
+			return original_type
+
+		case "simd":
+			bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
+			bd.tok  = tok
+			bd.name = name.text
+			original_type := parse_type(p)
+			type := ast.unparen_expr(original_type)
+			#partial switch t in type.derived_expr {
+			case ^ast.Array_Type:         t.tag = bd
 			case:
 				error(p, original_type.pos, "expected an array type after #%s", name.text)
 			}

+ 5 - 0
core/reflect/reflect.odin

@@ -34,6 +34,7 @@ Type_Info_Simd_Vector      :: runtime.Type_Info_Simd_Vector
 Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
 Type_Info_Relative_Slice   :: runtime.Type_Info_Relative_Slice
 Type_Info_Matrix           :: runtime.Type_Info_Matrix
+Type_Info_Soa_Pointer      :: runtime.Type_Info_Soa_Pointer
 
 Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
 
@@ -68,6 +69,7 @@ Type_Kind :: enum {
 	Relative_Pointer,
 	Relative_Slice,
 	Matrix,
+	Soa_Pointer,
 }
 
 
@@ -102,6 +104,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 		case Type_Info_Relative_Pointer: return .Relative_Pointer
 		case Type_Info_Relative_Slice:   return .Relative_Slice
 		case Type_Info_Matrix:           return .Matrix
+		case Type_Info_Soa_Pointer:      return .Soa_Pointer
 		}
 
 	}
@@ -194,6 +197,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
 		}
 	case Type_Info_Pointer:          return v.elem.id
 	case Type_Info_Multi_Pointer:    return v.elem.id
+	case Type_Info_Soa_Pointer:      return v.elem.id
 	case Type_Info_Array:            return v.elem.id
 	case Type_Info_Enumerated_Array: return v.elem.id
 	case Type_Info_Slice:            return v.elem.id
@@ -1419,6 +1423,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		Type_Info_Enum,
 		Type_Info_Simd_Vector,
 		Type_Info_Relative_Pointer,
+		Type_Info_Soa_Pointer,
 		Type_Info_Matrix:
 		return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
 		

+ 13 - 0
core/reflect/types.odin

@@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 		y := b.variant.(Type_Info_Multi_Pointer) or_return
 		return are_types_identical(x.elem, y.elem)
 
+	case Type_Info_Soa_Pointer:
+		y := b.variant.(Type_Info_Soa_Pointer) or_return
+		return are_types_identical(x.elem, y.elem)
+
+
 	case Type_Info_Procedure:
 		y := b.variant.(Type_Info_Procedure) or_return
 		switch {
@@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
 	_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
 	return ok
 }
+is_soa_pointer :: proc(info: ^Type_Info) -> bool {
+	if info == nil { return false }
+	_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
+	return ok
+}
 is_pointer_internally :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	#partial switch v in info.variant {
@@ -437,6 +447,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
 	case Type_Info_Multi_Pointer:
 		io.write_string(w, "[^]", &n) or_return
 		write_type(w, info.elem, &n) or_return
+	case Type_Info_Soa_Pointer:
+		io.write_string(w, "#soa ^", &n) or_return
+		write_type(w, info.elem, &n) or_return
 	case Type_Info_Procedure:
 		io.write_string(w, "proc", &n) or_return
 		if info.params == nil {

+ 10 - 0
core/runtime/core.odin

@@ -176,6 +176,9 @@ Type_Info_Matrix :: struct {
 	column_count: int,
 	// Total element count = column_count * elem_stride
 }
+Type_Info_Soa_Pointer :: struct {
+	elem: ^Type_Info,
+}
 
 Type_Info_Flag :: enum u8 {
 	Comparable     = 0,
@@ -217,6 +220,7 @@ Type_Info :: struct {
 		Type_Info_Relative_Pointer,
 		Type_Info_Relative_Slice,
 		Type_Info_Matrix,
+		Type_Info_Soa_Pointer,
 	},
 }
 
@@ -403,6 +407,12 @@ Raw_Cstring :: struct {
 	data: [^]byte,
 }
 
+Raw_Soa_Pointer :: struct {
+	data:  rawptr,
+	index: int,
+}
+
+
 
 /*
 	// Defined internally by the compiler

+ 3 - 0
core/runtime/print.odin

@@ -228,6 +228,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 	case Type_Info_Multi_Pointer:
 		print_string("[^]")
 		print_type(info.elem)
+	case Type_Info_Soa_Pointer:
+		print_string("#soa ^")
+		print_type(info.elem)
 	case Type_Info_Procedure:
 		print_string("proc")
 		if info.params == nil {

+ 1 - 1
src/check_decl.cpp

@@ -320,7 +320,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 		} else if (is_type_any(e->type)) {
 			error(init_expr, "'distinct' cannot be applied to 'any'");
 			is_distinct = false;
-		} else if (is_type_simd_vector(e->type)) {
+		} else if (is_type_simd_vector(e->type) || is_type_soa_pointer(e->type)) {
 			gbString str = type_to_string(e->type);
 			error(init_expr, "'distinct' cannot be applied to '%s'", str);
 			gb_string_free(str);

+ 17 - 5
src/check_expr.cpp

@@ -2052,7 +2052,7 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
 		return false;
 	}
 
-	return o->mode != Addressing_Variable;
+	return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
 }
 
 void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
@@ -2069,9 +2069,6 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 					error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
 				} else {
 					switch (o->mode) {
-					case Addressing_SoaVariable:
-						error(op, "Cannot take the pointer address of '%s' as it is an indirect index of an SOA struct", str);
-						break;
 					case Addressing_Constant:
 						error(op, "Cannot take the pointer address of '%s' which is a constant", str);
 						break;
@@ -2099,7 +2096,19 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 			return;
 		}
 
-		o->type = alloc_type_pointer(o->type);
+		if (o->mode == Addressing_SoaVariable) {
+			ast_node(ue, UnaryExpr, node);
+			if (ast_node_expect(ue->expr, Ast_IndexExpr)) {
+				ast_node(ie, IndexExpr, ue->expr);
+				Type *soa_type = type_of_expr(ie->expr);
+				GB_ASSERT(is_type_soa_struct(soa_type));
+				o->type = alloc_type_soa_pointer(soa_type);
+			} else {
+				o->type = alloc_type_pointer(o->type);
+			}
+		} else {
+			o->type = alloc_type_pointer(o->type);
+		}
 
 		switch (o->mode) {
 		case Addressing_OptionalOk:
@@ -9378,6 +9387,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			if (t->kind == Type_Pointer && !is_type_empty_union(t->Pointer.elem)) {
 				o->mode = Addressing_Variable;
 				o->type = t->Pointer.elem;
+ 			} else if (t->kind == Type_SoaPointer) {
+				o->mode = Addressing_SoaVariable;
+				o->type = type_deref(t);
  			} else if (t->kind == Type_RelativePointer) {
  				if (o->mode != Addressing_Variable) {
  					gbString str = expr_to_string(o->expr);

+ 24 - 4
src/check_type.cpp

@@ -2693,9 +2693,12 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 	case_ast_node(ue, UnaryExpr, e);
 		switch (ue->op.kind) {
 		case Token_Pointer:
-			*type = alloc_type_pointer(check_type(ctx, ue->expr));
-			set_base_type(named_type, *type);
-			return true;
+			{
+				Type *elem = check_type(ctx, ue->expr);
+				*type = alloc_type_pointer(elem);
+				set_base_type(named_type, *type);
+				return true;
+			}
 		}
 	case_end;
 
@@ -2721,7 +2724,24 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 			elem = o.type;
 		}
 
-		*type = alloc_type_pointer(elem);
+		if (pt->tag != nullptr) {
+			GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
+			String name = pt->tag->BasicDirective.name.string;
+			if (name == "soa") {
+				// TODO(bill): generic #soa pointers
+				if (is_type_soa_struct(elem)) {
+					*type = alloc_type_soa_pointer(elem);
+				} else {
+					error(pt->tag, "#soa pointers require an #soa record type as the element");
+					*type = alloc_type_pointer(elem);
+				}
+			} else {
+				error(pt->tag, "Invalid tag applied to pointer, got #%.*s", LIT(name));
+				*type = alloc_type_pointer(elem);
+			}
+		} else {
+			*type = alloc_type_pointer(elem);
+		}
 		set_base_type(named_type, *type);
 		return true;
 	case_end;

+ 11 - 0
src/checker.cpp

@@ -1947,6 +1947,11 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		add_type_info_type_internal(c, bt->Matrix.elem);
 		break;
 
+	case Type_SoaPointer:
+		add_type_info_type_internal(c, bt->SoaPointer.elem);
+		break;
+
+
 	default:
 		GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind);
 		break;
@@ -2164,6 +2169,10 @@ void add_min_dep_type_info(Checker *c, Type *t) {
 		add_min_dep_type_info(c, bt->Matrix.elem);
 		break;
 
+	case Type_SoaPointer:
+		add_min_dep_type_info(c, bt->SoaPointer.elem);
+		break;
+
 	default:
 		GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
 		break;
@@ -2756,6 +2765,7 @@ void init_core_type_info(Checker *c) {
 	t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
 	t_type_info_relative_slice   = find_core_type(c, str_lit("Type_Info_Relative_Slice"));
 	t_type_info_matrix           = find_core_type(c, str_lit("Type_Info_Matrix"));
+	t_type_info_soa_pointer      = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
 
 	t_type_info_named_ptr            = alloc_type_pointer(t_type_info_named);
 	t_type_info_integer_ptr          = alloc_type_pointer(t_type_info_integer);
@@ -2784,6 +2794,7 @@ void init_core_type_info(Checker *c) {
 	t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
 	t_type_info_relative_slice_ptr   = alloc_type_pointer(t_type_info_relative_slice);
 	t_type_info_matrix_ptr           = alloc_type_pointer(t_type_info_matrix);
+	t_type_info_soa_pointer_ptr      = alloc_type_pointer(t_type_info_soa_pointer);
 }
 
 void init_mem_allocator(Checker *c) {

+ 1 - 0
src/docs_format.cpp

@@ -83,6 +83,7 @@ enum OdinDocTypeKind : u32 {
 	OdinDocType_RelativeSlice    = 21,
 	OdinDocType_MultiPointer     = 22,
 	OdinDocType_Matrix           = 23,
+	OdinDocType_SoaPointer       = 24,
 };
 
 enum OdinDocTypeFlag_Basic : u32 {

+ 4 - 0
src/docs_writer.cpp

@@ -532,6 +532,10 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 		doc_type.kind = OdinDocType_MultiPointer;
 		doc_type.types = odin_doc_type_as_slice(w, type->MultiPointer.elem);
 		break;
+	case Type_SoaPointer:
+		doc_type.kind = OdinDocType_SoaPointer;
+		doc_type.types = odin_doc_type_as_slice(w, type->SoaPointer.elem);
+		break;
 	case Type_Array:
 		doc_type.kind = OdinDocType_Array;
 		doc_type.elem_count_len = 1;

+ 29 - 4
src/llvm_backend_expr.cpp

@@ -2803,7 +2803,15 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 	return {};
 }
 
-
+lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
+	lbAddr v = lb_add_local_generated(p, type, false);
+	lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
+	lbValue idx = lb_emit_struct_ep(p, v.addr, 1);
+	lb_emit_store(p, ptr, addr);
+	lb_emit_store(p, idx, index);
+
+	return lb_addr_load(p, v);
+}
 
 lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 	ast_node(ue, UnaryExpr, expr);
@@ -2842,7 +2850,17 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 		lb_emit_store(p, gep1, ok);
 		return lb_addr_load(p, res);
 
-	} if (ue_expr->kind == Ast_CompoundLit) {
+	} else if (is_type_soa_pointer(tv.type)) {
+		ast_node(ie, IndexExpr, ue_expr);
+		lbValue addr = lb_build_addr_ptr(p, ie->expr);
+		lbValue index = lb_build_expr(p, ie->index);
+
+		if (!build_context.no_bounds_check) {
+			// TODO(bill): soa bounds checking
+		}
+
+		return lb_make_soa_pointer(p, tv.type, addr, index);
+	} else if (ue_expr->kind == Ast_CompoundLit) {
 		lbValue v = lb_build_expr(p, ue->expr);
 
 		Type *type = v.type;
@@ -3604,6 +3622,7 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 					// NOTE(bill): just patch the index in place
 					sel.index[0] = addr.swizzle.indices[sel.index[0]];
 				}
+
 				lbValue a = lb_addr_get_ptr(p, addr);
 				a = lb_emit_deep_field_gep(p, a, sel);
 				return lb_addr(a);
@@ -4093,10 +4112,16 @@ lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 	case_end;
 
 	case_ast_node(de, DerefExpr, expr);
-		if (is_type_relative_pointer(type_of_expr(de->expr))) {
+		Type *t = type_of_expr(de->expr);
+		if (is_type_relative_pointer(t)) {
 			lbAddr addr = lb_build_addr(p, de->expr);
 			addr.relative.deref = true;
-			return addr;\
+			return addr;
+		} else if (is_type_soa_pointer(t)) {
+			lbValue value = lb_build_expr(p, de->expr);
+			lbValue ptr = lb_emit_struct_ev(p, value, 0);
+			lbValue idx = lb_emit_struct_ev(p, value, 1);
+			return lb_addr_soa_variable(ptr, idx, nullptr);
 		}
 		lbValue addr = lb_build_expr(p, de->expr);
 		return lb_addr(addr);

+ 15 - 0
src/llvm_backend_general.cpp

@@ -916,7 +916,13 @@ lbValue lb_emit_load(lbProcedure *p, lbValue value) {
 		Type *t = vt->MultiPointer.elem;
 		LLVMValueRef v = LLVMBuildLoad2(p->builder, llvm_addr_type(value), value.value, "");
 		return lbValue{v, t};
+	} else if (is_type_soa_pointer(value.type)) {
+		lbValue ptr = lb_emit_struct_ev(p, value, 0);
+		lbValue idx = lb_emit_struct_ev(p, value, 1);
+		lbAddr addr = lb_addr_soa_variable(ptr, idx, nullptr);
+		return lb_addr_load(p, addr);
 	}
+
 	GB_ASSERT(is_type_pointer(value.type));
 	Type *t = type_deref(value.type);
 	LLVMValueRef v = LLVMBuildLoad2(p->builder, llvm_addr_type(value), value.value, "");
@@ -2055,6 +2061,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			m->internal_type_level += 1;
 			return t;
 		}
+
+	case Type_SoaPointer:
+		{
+			unsigned field_count = 2;
+			LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
+			fields[0] = LLVMPointerType(lb_type(m, type->Pointer.elem), 0);
+			fields[1] = LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size);
+			return LLVMStructTypeInContext(ctx, fields, field_count, false);
+		}
 	
 	}
 

+ 15 - 0
src/llvm_backend_type.cpp

@@ -57,6 +57,7 @@ lbValue lb_typeid(lbModule *m, Type *type) {
 	case Type_SimdVector:      kind = Typeid_Simd_Vector;      break;
 	case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
 	case Type_RelativeSlice:   kind = Typeid_Relative_Slice;   break;
+	case Type_SoaPointer:      kind = Typeid_SoaPointer;       break;
 	}
 
 	if (is_type_cstring(type)) {
@@ -445,6 +446,20 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 			lb_emit_store(p, tag, res);
 			break;
 		}
+		case Type_SoaPointer: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
+			lbValue gep = lb_get_type_info_ptr(m, t->SoaPointer.elem);
+
+			LLVMValueRef vals[1] = {
+				gep.value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
 		case Type_Array: {
 			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
 			i64 ez = type_size_of(t->Array.elem);

+ 34 - 1
src/llvm_backend_utility.cpp

@@ -1007,6 +1007,11 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 		case 0: result_type = t->RelativeSlice.base_integer; break;
 		case 1: result_type = t->RelativeSlice.base_integer; break;
 		}
+	} else if (is_type_soa_pointer(t)) {
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
+		case 1: result_type = t_int; break;
+		}
 	} else {
 		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(s.type), index);
 	}
@@ -1137,6 +1142,13 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 		result_type = t->Array.elem;
 		break;
 
+	case Type_SoaPointer:
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
+		case 1: result_type = t_int; break;
+		}
+		break;
+
 	default:
 		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s.type), index);
 		break;
@@ -1164,7 +1176,28 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
 		}
 		type = core_type(type);
 
-		if (is_type_quaternion(type)) {
+		if (type->kind == Type_SoaPointer) {
+			lbValue addr = lb_emit_struct_ep(p, e, 0);
+			lbValue index = lb_emit_struct_ep(p, e, 1);
+			addr = lb_emit_load(p, addr);
+			index = lb_emit_load(p, index);
+
+			i32 first_index = sel.index[0];
+			Selection sub_sel = sel;
+			sub_sel.index.data += 1;
+			sub_sel.index.count -= 1;
+
+			lbValue arr = lb_emit_struct_ep(p, addr, first_index);
+
+			Type *t = base_type(type_deref(addr.type));
+			GB_ASSERT(is_type_soa_struct(t));
+
+			if (t->Struct.soa_kind == StructSoa_Fixed) {
+				e = lb_emit_array_ep(p, arr, index);
+			} else {
+				e = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index);
+			}
+		} else if (is_type_quaternion(type)) {
 			e = lb_emit_struct_ep(p, e, index);
 		} else if (is_type_raw_union(type)) {
 			type = get_struct_field_type(type, index);

+ 4 - 2
src/parser.cpp

@@ -356,6 +356,7 @@ Ast *clone_ast(Ast *node) {
 		break;
 	case Ast_PointerType:
 		n->PointerType.type = clone_ast(n->PointerType.type);
+		n->PointerType.tag  = clone_ast(n->PointerType.tag);
 		break;
 	case Ast_MultiPointerType:
 		n->MultiPointerType.type = clone_ast(n->MultiPointerType.type);
@@ -2167,10 +2168,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			Ast *original_type = parse_type(f);
 			Ast *type = unparen_expr(original_type);
 			switch (type->kind) {
-			case Ast_ArrayType:        type->ArrayType.tag = tag;        break;
+			case Ast_ArrayType:        type->ArrayType.tag        = tag; break;
 			case Ast_DynamicArrayType: type->DynamicArrayType.tag = tag; break;
+			case Ast_PointerType:      type->PointerType.tag      = tag; break;
 			default:
-				syntax_error(type, "Expected an array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
+				syntax_error(type, "Expected an array or pointer type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
 				break;
 			}
 			return original_type;

+ 2 - 1
src/parser.hpp

@@ -650,7 +650,8 @@ AST_KIND(_TypeBegin, "", bool) \
 	}) \
 	AST_KIND(PointerType, "pointer type", struct { \
 		Token token; \
-		Ast *type; \
+		Ast *type;   \
+		Ast *tag;    \
 	}) \
 	AST_KIND(RelativeType, "relative type", struct { \
 		Ast *tag; \

+ 51 - 5
src/types.cpp

@@ -278,7 +278,8 @@ struct TypeProc {
 		Type *generic_row_count;                          \
 		Type *generic_column_count;                       \
 		i64   stride_in_bytes;                            \
-	})
+	})                                                        \
+	TYPE_KIND(SoaPointer, struct { Type *elem; })
 
 
 enum TypeKind {
@@ -350,6 +351,7 @@ enum Typeid_Kind : u8 {
 	Typeid_Relative_Pointer,
 	Typeid_Relative_Slice,
 	Typeid_Matrix,
+	Typeid_SoaPointer,
 };
 
 // IMPORTANT NOTE(bill): This must match the same as the in core.odin
@@ -644,6 +646,7 @@ gb_global Type *t_type_info_simd_vector          = nullptr;
 gb_global Type *t_type_info_relative_pointer     = nullptr;
 gb_global Type *t_type_info_relative_slice       = nullptr;
 gb_global Type *t_type_info_matrix               = nullptr;
+gb_global Type *t_type_info_soa_pointer          = nullptr;
 
 gb_global Type *t_type_info_named_ptr            = nullptr;
 gb_global Type *t_type_info_integer_ptr          = nullptr;
@@ -672,6 +675,7 @@ gb_global Type *t_type_info_simd_vector_ptr      = nullptr;
 gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
 gb_global Type *t_type_info_relative_slice_ptr   = nullptr;
 gb_global Type *t_type_info_matrix_ptr           = nullptr;
+gb_global Type *t_type_info_soa_pointer_ptr      = nullptr;
 
 gb_global Type *t_allocator                      = nullptr;
 gb_global Type *t_allocator_ptr                  = nullptr;
@@ -735,6 +739,7 @@ Type *   bit_set_to_int(Type *t);
 bool are_types_identical(Type *x, Type *y);
 
 bool is_type_pointer(Type *t);
+bool is_type_soa_pointer(Type *t);
 bool is_type_proc(Type *t);
 bool is_type_slice(Type *t);
 bool is_type_integer(Type *t);
@@ -917,6 +922,13 @@ Type *alloc_type_multi_pointer(Type *elem) {
 	return t;
 }
 
+Type *alloc_type_soa_pointer(Type *elem) {
+	Type *t = alloc_type(Type_SoaPointer);
+	t->SoaPointer.elem = elem;
+	return t;
+}
+
+
 Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = nullptr) {
 	if (generic_count != nullptr) {
 		Type *t = alloc_type(Type_Array);
@@ -1109,11 +1121,17 @@ Type *type_deref(Type *t) {
 		if (bt == nullptr) {
 			return nullptr;
 		}
-		if (bt->kind == Type_Pointer) {
+		switch (bt->kind) {
+		case Type_Pointer:
 			return bt->Pointer.elem;
-		}
-		if (bt->kind == Type_RelativePointer) {
+		case Type_RelativePointer:
 			return type_deref(bt->RelativePointer.pointer_type);
+		case Type_SoaPointer:
+			{
+				Type *elem = base_type(bt->SoaPointer.elem);
+				GB_ASSERT(elem->kind == Type_Struct && elem->Struct.soa_kind != StructSoa_None);
+				return elem->Struct.soa_elem;
+			}
 		}
 	}
 	return t;
@@ -1327,6 +1345,10 @@ bool is_type_pointer(Type *t) {
 	}
 	return t->kind == Type_Pointer;
 }
+bool is_type_soa_pointer(Type *t) {
+	t = base_type(t);
+	return t->kind == Type_SoaPointer;
+}
 bool is_type_multi_pointer(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_MultiPointer;
@@ -1804,7 +1826,7 @@ bool is_type_dereferenceable(Type *t) {
 	if (is_type_rawptr(t)) {
 		return false;
 	}
-	return is_type_pointer(t);
+	return is_type_pointer(t) || is_type_soa_pointer(t);
 }
 
 
@@ -2079,6 +2101,9 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
 	case Type_Pointer:
 		return is_type_polymorphic(t->Pointer.elem, or_specialized);
 
+	case Type_SoaPointer:
+		return is_type_polymorphic(t->SoaPointer.elem, or_specialized);
+
 	case Type_EnumeratedArray:
 		if (is_type_polymorphic(t->EnumeratedArray.index, or_specialized)) {
 			return true;
@@ -2196,6 +2221,7 @@ bool type_has_nil(Type *t) {
 	case Type_Slice:
 	case Type_Proc:
 	case Type_Pointer:
+	case Type_SoaPointer:
 	case Type_MultiPointer:
 	case Type_DynamicArray:
 	case Type_Map:
@@ -2262,6 +2288,8 @@ bool is_type_comparable(Type *t) {
 		return true;
 	case Type_Pointer:
 		return true;
+	case Type_SoaPointer:
+		return true;
 	case Type_MultiPointer:
 		return true;
 	case Type_Enum:
@@ -2335,6 +2363,7 @@ bool is_type_simple_compare(Type *t) {
 
 	case Type_Pointer:
 	case Type_MultiPointer:
+	case Type_SoaPointer:
 	case Type_Proc:
 	case Type_BitSet:
 		return true;
@@ -2558,6 +2587,12 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
 		}
 		break;
 
+	case Type_SoaPointer:
+		if (y->kind == Type_SoaPointer) {
+			return are_types_identical(x->SoaPointer.elem, y->SoaPointer.elem);
+		}
+		break;
+
 	case Type_Named:
 		if (y->kind == Type_Named) {
 			return x->Named.type_name == y->Named.type_name;
@@ -3475,6 +3510,9 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
 		return type_align_of_internal(t->RelativePointer.base_integer, path);
 	case Type_RelativeSlice:
 		return type_align_of_internal(t->RelativeSlice.base_integer, path);
+
+	case Type_SoaPointer:
+		return build_context.word_size;
 	}
 
 	// return gb_clamp(next_pow2(type_size_of(t)), 1, build_context.max_align);
@@ -3580,6 +3618,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
 	case Type_MultiPointer:
 		return build_context.word_size;
 
+	case Type_SoaPointer:
+		return build_context.word_size*2;
+
 	case Type_Array: {
 		i64 count, align, size, alignment;
 		count = t->Array.count;
@@ -4017,6 +4058,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
 		str = write_type_to_string(str, type->Pointer.elem);
 		break;
 
+	case Type_SoaPointer:
+		str = gb_string_appendc(str, "#soa ^");
+		str = write_type_to_string(str, type->SoaPointer.elem);
+		break;
+
 	case Type_MultiPointer:
 		str = gb_string_appendc(str, "[^]");
 		str = write_type_to_string(str, type->Pointer.elem);