#soa[]Type (Experimental)

This commit is contained in:
gingerBill
2019-11-21 00:07:21 +00:00
parent e01d8a04a9
commit 2c5a84bb78
7 changed files with 481 additions and 106 deletions

View File

@@ -1350,7 +1350,25 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
base_type_name = v.name;
}
for index in 0..<uintptr(info.soa_len) {
actual_field_count := len(info.names);
n := uintptr(info.soa_len);
fields_are_ptrs := false;
if info.soa_kind == .Slice {
actual_field_count = len(info.names)-1; // len
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
} else if info.soa_kind == .Dynamic {
actual_field_count = len(info.names)-3; // len, cap, allocator
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
}
for index in 0..<n {
if !hash && index > 0 do strings.write_string(fi.buf, ", ");
field_count := -1;
@@ -1361,7 +1379,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
for name, i in info.names {
for i in 0..<actual_field_count {
name := info.names[i];
field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
@@ -1370,13 +1389,25 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
t := info.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
if info.soa_kind == .Fixed {
t := info.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
} else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
t := info.types[i].variant.(runtime.Type_Info_Pointer).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^;
data := rawptr(uintptr(field_ptr) + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
}
if hash do strings.write_string(fi.buf, ",\n");

View File

@@ -7,7 +7,7 @@ import "core:reflect"
import "intrinsics"
/*
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
It is designed with the intent of replacing C with the following goals:
* simplicity
* high performance
@@ -36,7 +36,7 @@ the_basics :: proc() {
my_integer_variable: int; // A comment for documentaton
// Multi-line comments begin with /* and end with */. Multi-line comments can
// Multi-line comments begin with /* and end with */. Multi-line comments can
// also be nested (unlike in C):
/*
You can have any text or code here and
@@ -63,16 +63,16 @@ the_basics :: proc() {
// Numbers
// Numerical literals are written similar to most other programming languages.
// A useful feature in Odin is that underscores are allowed for better
// readability: 1_000_000_000 (one billion). A number that contains a dot is a
// floating point literal: 1.0e9 (one billion). If a number literal is suffixed
// Numerical literals are written similar to most other programming languages.
// A useful feature in Odin is that underscores are allowed for better
// readability: 1_000_000_000 (one billion). A number that contains a dot is a
// floating point literal: 1.0e9 (one billion). If a number literal is suffixed
// with i, is an imaginary number literal: 2i (2 multiply the square root of -1).
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
// literals 0x. A leading zero does not produce an octal constant (unlike C).
// In Odin, if a number constant is possible to be represented by a type without
// In Odin, if a number constant is possible to be represented by a type without
// precision loss, it will automatically convert to that type.
x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
@@ -105,8 +105,8 @@ the_basics :: proc() {
*/
// Constant declarations
// Constants are entities (symbols) which have an assigned value.
// The constants value cannot be changed.
// Constants are entities (symbols) which have an assigned value.
// The constants value cannot be changed.
// The constants value must be able to be evaluated at compile time:
X :: "what"; // constant `X` has the untyped string value "what"
@@ -234,7 +234,7 @@ control_flow :: proc() {
}
// Switch statement
// A switch statement is another way to write a sequence of if-else statements.
// A switch statement is another way to write a sequence of if-else statements.
// In Odin, the default case is denoted as a case without any expression.
switch arch := ODIN_ARCH; arch {
@@ -246,12 +246,12 @@ control_flow :: proc() {
fmt.println("Unsupported architecture");
}
// Odins `switch` is like one in C or C++, except that Odin only runs the selected case.
// This means that a `break` statement is not needed at the end of each case.
// Odins `switch` is like one in C or C++, except that Odin only runs the selected case.
// This means that a `break` statement is not needed at the end of each case.
// Another important difference is that the case values need not be integers nor constants.
// To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used.
one_angry_dwarf :: proc() -> int {
one_angry_dwarf :: proc() -> int {
fmt.println("one_angry_dwarf was called");
return 1;
}
@@ -261,8 +261,8 @@ control_flow :: proc() {
case one_angry_dwarf():
}
// A switch statement without a condition is the same as `switch true`.
// This can be used to write a clean and long if-else chain and have the
// A switch statement without a condition is the same as `switch true`.
// This can be used to write a clean and long if-else chain and have the
// ability to break if needed
switch {
@@ -293,7 +293,7 @@ control_flow :: proc() {
}
{ // Defer statement
// A defer statement defers the execution of a statement until the end of
// A defer statement defers the execution of a statement until the end of
// the scope it is in.
// The following will print 4 then 234:
@@ -318,11 +318,11 @@ control_flow :: proc() {
fmt.println("2");
}
cond := false;
cond := false;
defer if cond {
bar();
}
}
}
// Defer statements are executed in the reverse order that they were declared:
{
@@ -343,13 +343,13 @@ control_flow :: proc() {
}
{ // When statement
/*
/*
The when statement is almost identical to the if statement but with some differences:
* Each condition must be a constant expression as a when
* Each condition must be a constant expression as a when
statement is evaluated at compile time.
* The statements within a branch do not create a new scope
* The compiler checks the semantics and code only for statements
* The compiler checks the semantics and code only for statements
that belong to the first condition that is true
* An initial statement is not allowed in a when statement
* when statements are allowed at file scope
@@ -363,8 +363,8 @@ control_flow :: proc() {
} else {
fmt.println("Unsupported architecture");
}
// The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in Cs preprocessor however, in Odin,
// The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in Cs preprocessor however, in Odin,
// it is type checked.
}
@@ -401,9 +401,9 @@ control_flow :: proc() {
// Fallthrough statement
// Odins switch is like one in C or C++, except that Odin only runs the selected
// case. This means that a break statement is not needed at the end of each case.
// Another important difference is that the case values need not be integers nor
// Odins switch is like one in C or C++, except that Odin only runs the selected
// case. This means that a break statement is not needed at the end of each case.
// Another important difference is that the case values need not be integers nor
// constants.
// fallthrough can be used to explicitly fall through into the next case block:
@@ -477,8 +477,8 @@ explicit_procedure_overloading :: proc() {
struct_type :: proc() {
fmt.println("\n# struct type");
// A struct is a record type in Odin. It is a collection of fields.
// Struct fields are accessed by using a dot:
// A struct is a record type in Odin. It is a collection of fields.
// Struct fields are accessed by using a dot:
{
Vector2 :: struct {
x: f32,
@@ -495,13 +495,13 @@ struct_type :: proc() {
p.x = 1335;
fmt.println(v);
// We could write p^.x, however, it is to nice abstract the ability
// to not explicitly dereference the pointer. This is very useful when
// We could write p^.x, however, it is to nice abstract the ability
// to not explicitly dereference the pointer. This is very useful when
// refactoring code to use a pointer rather than a value, and vice versa.
}
{
// A struct literal can be denoted by providing the structs type
// followed by {}. A struct literal must either provide all the
// A struct literal can be denoted by providing the structs type
// followed by {}. A struct literal must either provide all the
// arguments or none:
Vector3 :: struct {
x, y, z: f32,
@@ -510,12 +510,12 @@ struct_type :: proc() {
v = Vector3{}; // Zero value
v = Vector3{1, 4, 9};
// You can list just a subset of the fields if you specify the
// You can list just a subset of the fields if you specify the
// field by name (the order of the named fields does not matter):
v = Vector3{z=1, y=2};
assert(v.x == 0);
assert(v.y == 2);
assert(v.z == 1);
assert(v.z == 1);
}
{
// Structs can tagged with different memory layout and alignment requirements:
@@ -704,8 +704,8 @@ union_type :: proc() {
using_statement :: proc() {
fmt.println("\n# using statement");
// using can used to bring entities declared in a scope/namespace
// into the current scope. This can be applied to import declarations,
// using can used to bring entities declared in a scope/namespace
// into the current scope. This can be applied to import declarations,
// import names, struct fields, procedure fields, and struct values.
Vector3 :: struct{x, y, z: f32};
@@ -738,7 +738,7 @@ using_statement :: proc() {
}
}
{
// We can also apply the using statement to the struct fields directly,
// We can also apply the using statement to the struct fields directly,
// making all the fields of position appear as if they on Entity itself:
Entity :: struct {
using position: Vector3,
@@ -747,11 +747,11 @@ using_statement :: proc() {
foo :: proc(entity: ^Entity) {
fmt.println(entity.x, entity.y, entity.z);
}
// Subtype polymorphism
// It is possible to get subtype polymorphism, similar to inheritance-like
// functionality in C++, but without the requirement of vtables or unknown
// It is possible to get subtype polymorphism, similar to inheritance-like
// functionality in C++, but without the requirement of vtables or unknown
// struct layout:
Colour :: struct {r, g, b, a: u8};
@@ -767,7 +767,7 @@ using_statement :: proc() {
foo(&frog);
frog.x = 123;
// Note: using can be applied to arbitrarily many things, which allows
// Note: using can be applied to arbitrarily many things, which allows
// the ability to have multiple subtype polymorphism (but also its issues).
// Note: usingd fields can still be referred by name.
@@ -787,19 +787,19 @@ using_statement :: proc() {
implicit_context_system :: proc() {
fmt.println("\n# implicit context system");
// In each scope, there is an implicit value named context. This
// context variable is local to each scope and is implicitly passed
// by pointer to any procedure call in that scope (if the procedure
// In each scope, there is an implicit value named context. This
// context variable is local to each scope and is implicitly passed
// by pointer to any procedure call in that scope (if the procedure
// has the Odin calling convention).
// The main purpose of the implicit context system is for the ability
// to intercept third-party code and libraries and modify their
// The main purpose of the implicit context system is for the ability
// to intercept third-party code and libraries and modify their
// functionality. One such case is modifying how a library allocates
// something or logs something. In C, this was usually achieved with
// the library defining macros which could be overridden so that the
// user could define what he wanted. However, not many libraries
// supported this in many languages by default which meant intercepting
// third-party code to see what it does and to change how it does it is
// something or logs something. In C, this was usually achieved with
// the library defining macros which could be overridden so that the
// user could define what he wanted. However, not many libraries
// supported this in many languages by default which meant intercepting
// third-party code to see what it does and to change how it does it is
// not possible.
c := context; // copy the current scope's context
@@ -820,7 +820,7 @@ implicit_context_system :: proc() {
// An context.allocator is assigned to the return value of `my_custom_allocator()`
assert(context.user_index == 123);
// The memory management procedure use the `context.allocator` by
// The memory management procedure use the `context.allocator` by
// default unless explicitly specified otherwise
china_grove := new(int);
free(china_grove);
@@ -828,10 +828,10 @@ implicit_context_system :: proc() {
my_custom_allocator :: mem.nil_allocator;
// By default, the context value has default values for its parameters which is
// By default, the context value has default values for its parameters which is
// decided in the package runtime. What the defaults are are compiler specific.
// To see what the implicit context value contains, please see the following
// To see what the implicit context value contains, please see the following
// definition in package runtime.
}
@@ -1133,14 +1133,14 @@ map_type :: proc() {
m := make(map[string]int);
defer delete(m);
m["Bob"] = 2;
m["Bob"] = 2;
m["Ted"] = 5;
fmt.println(m["Bob"]);
delete_key(&m, "Ted");
// If an element of a key does not exist, the zero value of the
// element will be returned. To check to see if an element exists
// If an element of a key does not exist, the zero value of the
// element will be returned. To check to see if an element exists
// can be done in two ways:
elem, ok := m["Bob"];
exists := "Bob" in m;
@@ -1508,26 +1508,26 @@ when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib"
foreign_system :: proc() {
fmt.println("\n#foreign system");
when ODIN_OS == "windows" {
// It is sometimes necessarily to interface with foreign code,
// such as a C library. In Odin, this is achieved through the
// foreign system. You can “import” a library into the code
// It is sometimes necessarily to interface with foreign code,
// such as a C library. In Odin, this is achieved through the
// foreign system. You can “import” a library into the code
// using the same semantics as a normal import declaration.
// This foreign import declaration will create a
// “foreign import name” which can then be used to associate
// This foreign import declaration will create a
// “foreign import name” which can then be used to associate
// entities within a foreign block.
foreign kernel32 {
ExitProcess :: proc "stdcall" (exit_code: u32) ---
}
// Foreign procedure declarations have the cdecl/c calling
// convention by default unless specified otherwise. Due to
// foreign procedures do not have a body declared within this
// code, you need append the --- symbol to the end to distinguish
// Foreign procedure declarations have the cdecl/c calling
// convention by default unless specified otherwise. Due to
// foreign procedures do not have a body declared within this
// code, you need append the --- symbol to the end to distinguish
// it as a procedure literal without a body and not a procedure type.
// The attributes system can be used to change specific properties
// The attributes system can be used to change specific properties
// of entities declared within a block:
@(default_calling_convention = "std")
@@ -1718,6 +1718,26 @@ soa_struct_layout :: proc() {
v_soa[0].y = 4;
v_soa[0].z = 9;
}
{
// SOA Slices
Vector3 :: struct {x, y, z: f32};
N :: 3;
v: #soa[N]Vector3;
v[0].x = 1;
v[0].y = 4;
v[0].z = 9;
s: #soa[]Vector3;
s = v[:];
assert(len(s) == N);
fmt.println(s);
fmt.println(s[0].x);
a := s[1:2];
assert(len(a) == 1);
fmt.println(a);
}
}

View File

@@ -92,6 +92,10 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type
void set_procedure_abi_types(gbAllocator a, Type *type);
void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -3228,6 +3232,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
} else if (operand->mode == Addressing_MapIndex) {
operand->mode = Addressing_Value;
} else if (entity->flags & EntityFlag_SoaPtrField) {
operand->mode = Addressing_SoaVariable;
} else if (sel.indirect || operand->mode != Addressing_Value || operand->mode == Addressing_SoaVariable) {
operand->mode = Addressing_Variable;
} else {
@@ -6739,7 +6745,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
}
}
bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count) {
bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count, Type *original_type) {
switch (t->kind) {
case Type_Basic:
if (t->Basic.kind == Basic_string) {
@@ -6796,6 +6802,15 @@ bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count)
return false;
}
if (is_type_pointer(original_type) && indirection) {
Type *ptr = base_type(original_type);
if (ptr->kind == Type_Pointer && o->mode == Addressing_SoaVariable) {
o->type = ptr->Pointer.elem;
o->mode = Addressing_Value;
return true;
}
}
return false;
}
@@ -7973,7 +7988,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
i64 max_count = -1;
bool valid = check_set_index_data(o, t, is_ptr, &max_count);
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
if (is_const) {
valid = false;
@@ -8055,6 +8070,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
valid = true;
o->type = alloc_type_slice(t->DynamicArray.elem);
break;
case Type_Struct:
if (is_type_soa_struct(t)) {
valid = true;
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
break;
}
if (!valid) {

View File

@@ -2702,6 +2702,108 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
// error(node, "'map' types are not yet implemented");
}
Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem) {
Type *bt_elem = base_type(elem);
if (!is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
GB_ASSERT(elem_expr != nullptr);
gbString str = type_to_string(elem);
error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
gb_string_free(str);
return alloc_type_slice(elem);
}
Type *soa_struct = nullptr;
Scope *scope = nullptr;
if (is_type_array(elem)) {
Type *old_array = base_type(elem);
isize field_count = old_array->Array.count;
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+1);
soa_struct->Struct.tags = array_make<String>(heap_allocator(), field_count+1);
soa_struct->Struct.node = array_typ_expr;
soa_struct->Struct.soa_kind = StructSoa_Slice;
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
scope = create_scope(ctx->scope, ctx->allocator);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
str_lit("x"),
str_lit("y"),
str_lit("z"),
str_lit("w")
};
for (i64 i = 0; i < field_count; i++) {
Type *array_type = alloc_type_pointer(old_array->Array.elem);
Token token = {};
token.string = params_xyzw[i];
Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
new_field->flags |= EntityFlag_SoaPtrField;
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx->checker, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
}
Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
soa_struct->Struct.fields[field_count] = len_field;
add_entity(ctx->checker, scope, nullptr, len_field);
add_entity_use(ctx, nullptr, len_field);
} else {
GB_ASSERT(is_type_struct(elem));
Type *old_struct = base_type(elem);
isize field_count = old_struct->Struct.fields.count;
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+1);
soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_struct->Struct.tags.count+1);
soa_struct->Struct.node = array_typ_expr;
soa_struct->Struct.soa_kind = StructSoa_Slice;
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
Entity *old_field = old_struct->Struct.fields[i];
if (old_field->kind == Entity_Variable) {
Type *array_type = alloc_type_pointer(old_field->type);
Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index);
new_field->flags |= EntityFlag_SoaPtrField;
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx->checker, scope, nullptr, new_field);
} else {
soa_struct->Struct.fields[i] = old_field;
}
soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
}
Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count);
soa_struct->Struct.fields[field_count] = len_field;
add_entity(ctx->checker, scope, nullptr, len_field);
add_entity_use(ctx, nullptr, len_field);
}
Token token = {};
token.string = str_lit("Base_Type");
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(ctx->checker, scope, nullptr, base_type_entity);
add_type_info_type(ctx, soa_struct);
return soa_struct;
}
bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
@@ -2983,14 +3085,27 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
*type = alloc_type_simd_vector(count, elem);
} else {
GB_PANIC("Unhandled array type tag %.*s", LIT(name));
error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
*type = alloc_type_array(elem, count, generic_type);
}
} else {
*type = alloc_type_array(elem, count, generic_type);
}
} else {
Type *elem = check_type(ctx, at->elem);
*type = alloc_type_slice(elem);
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name;
if (name == "soa") {
*type = make_soa_struct_slice(ctx, e, at->elem, elem);
} else {
error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
*type = alloc_type_slice(elem);
}
} else {
*type = alloc_type_slice(elem);
}
}
array_end:
set_base_type(named_type, *type);

View File

@@ -51,8 +51,10 @@ enum EntityFlag {
EntityFlag_ImplicitReference = 1<<17, // NOTE(bill): equivalent to `const &` in C++
EntityFlag_CVarArg = 1<<20,
EntityFlag_AutoCast = 1<<21,
EntityFlag_SoaPtrField = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
EntityFlag_CVarArg = 1<<21,
EntityFlag_AutoCast = 1<<22,
};
enum EntityState {

View File

@@ -3447,6 +3447,73 @@ irValue *ir_insert_dynamic_map_key_and_value(irProcedure *proc, irValue *addr, T
irValue *ir_soa_struct_len(irProcedure *proc, irValue *value) {
Type *t = base_type(ir_type(value));
bool is_ptr = false;
if (is_type_pointer(t)) {
is_ptr = true;
t = base_type(type_deref(t));
}
if (t->Struct.soa_kind == StructSoa_Fixed) {
return ir_const_int(t->Struct.soa_count);
}
GB_ASSERT(t->Struct.soa_kind == StructSoa_Slice ||
t->Struct.soa_kind == StructSoa_Dynamic);
isize n = 0;
Type *elem = base_type(t->Struct.soa_elem);
if (elem->kind == Type_Struct) {
n = elem->Struct.fields.count;
} else if (elem->kind == Type_Array) {
n = elem->Array.count;
} else {
GB_PANIC("Unreachable");
}
if (is_ptr) {
irValue *v = ir_emit_struct_ep(proc, value, cast(i32)n);
return ir_emit_load(proc, v);
}
return ir_emit_struct_ev(proc, value, cast(i32)n);
}
irValue *ir_soa_struct_cap(irProcedure *proc, irValue *value) {
Type *t = base_type(ir_type(value));
bool is_ptr = false;
if (is_type_pointer(t)) {
is_ptr = true;
t = base_type(type_deref(t));
}
if (t->Struct.soa_kind == StructSoa_Fixed) {
return ir_const_int(t->Struct.soa_count);
}
GB_ASSERT(t->Struct.soa_kind == StructSoa_Dynamic);
isize n = 0;
Type *elem = base_type(t->Struct.soa_elem);
if (elem->kind == Type_Struct) {
n = elem->Struct.fields.count+1;
} else if (elem->kind == Type_Array) {
n = elem->Array.count+1;
} else {
GB_PANIC("Unreachable");
}
if (is_ptr) {
irValue *v = ir_emit_struct_ep(proc, value, cast(i32)n);
return ir_emit_load(proc, v);
}
return ir_emit_struct_ev(proc, value, cast(i32)n);
}
void ir_addr_store(irProcedure *proc, irAddr const &addr, irValue *value) {
if (addr.addr == nullptr) {
return;
@@ -3568,7 +3635,7 @@ void ir_addr_store(irProcedure *proc, irAddr const &addr, irValue *value) {
value = ir_emit_conv(proc, value, t->Struct.soa_elem);
irValue *index = addr.soa.index;
if (index->kind != irValue_Constant) {
if (index->kind != irValue_Constant || t->Struct.soa_kind != StructSoa_Fixed) {
Type *t = base_type(type_deref(ir_type(addr.addr)));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
i64 count = t->Struct.soa_count;
@@ -3697,27 +3764,53 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
Type *elem = t->Struct.soa_elem;
i32 count = cast(i32)t->Struct.soa_count;
irValue *len = nullptr;
if (t->Struct.soa_kind == StructSoa_Fixed) {
len = ir_const_int(t->Struct.soa_count);
} else {
irValue *v = ir_emit_load(proc, addr.addr);
len = ir_soa_struct_len(proc, v);
}
irValue *res = ir_add_local_generated(proc, elem, true);
if (addr.soa.index->kind != irValue_Constant) {
irValue *len = ir_const_int(count);
if (addr.soa.index->kind != irValue_Constant || t->Struct.soa_kind != StructSoa_Fixed) {
ir_emit_bounds_check(proc, ast_token(addr.soa.index_expr), addr.soa.index, len);
}
for_array(i, t->Struct.fields) {
Entity *field = t->Struct.fields[i];
Type *base_type = field->type;
GB_ASSERT(base_type->kind == Type_Array);
Type *elem = base_type->Array.elem;
if (t->Struct.soa_kind == StructSoa_Fixed) {
for_array(i, t->Struct.fields) {
Entity *field = t->Struct.fields[i];
Type *base_type = field->type;
GB_ASSERT(base_type->kind == Type_Array);
irValue *dst = ir_emit_struct_ep(proc, res, cast(i32)i);
irValue *src_ptr = ir_emit_struct_ep(proc, addr.addr, cast(i32)i);
src_ptr = ir_emit_array_ep(proc, src_ptr, addr.soa.index);
irValue *src = ir_emit_load(proc, src_ptr);
ir_emit_store(proc, dst, src);
}
} else {
isize field_count = t->Struct.fields.count;
if (t->Struct.soa_kind == StructSoa_Slice) {
field_count -= 1;
} else if (t->Struct.soa_kind == StructSoa_Dynamic) {
field_count -= 3;
}
for (isize i = 0; i < field_count; i++) {
Entity *field = t->Struct.fields[i];
Type *base_type = field->type;
GB_ASSERT(base_type->kind == Type_Pointer);
Type *elem = base_type->Pointer.elem;
irValue *dst = ir_emit_struct_ep(proc, res, cast(i32)i);
irValue *src_ptr = ir_emit_struct_ep(proc, addr.addr, cast(i32)i);
src_ptr = ir_emit_array_ep(proc, src_ptr, addr.soa.index);
irValue *src = ir_emit_load(proc, src_ptr);
ir_emit_store(proc, dst, src);
irValue *dst = ir_emit_struct_ep(proc, res, cast(i32)i);
irValue *src_ptr = ir_emit_struct_ep(proc, addr.addr, cast(i32)i);
src_ptr = ir_emit_ptr_offset(proc, src_ptr, addr.soa.index);
irValue *src = ir_emit_load(proc, src_ptr);
src = ir_emit_load(proc, src);
ir_emit_store(proc, dst, src);
}
}
return ir_emit_load(proc, res);
@@ -3796,6 +3889,7 @@ irValue *ir_map_cap(irProcedure *proc, irValue *value) {
struct irLoopData {
irValue *idx_addr;
irValue *idx;
@@ -6297,6 +6391,8 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
return ir_dynamic_array_len(proc, v);
} else if (is_type_map(t)) {
return ir_map_len(proc, v);
} else if (is_type_soa_struct(t)) {
return ir_soa_struct_len(proc, v);
}
GB_PANIC("Unreachable");
@@ -6321,6 +6417,8 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
return ir_dynamic_array_cap(proc, v);
} else if (is_type_map(t)) {
return ir_map_cap(proc, v);
} else if (is_type_soa_struct(t)) {
return ir_soa_struct_cap(proc, v);
}
GB_PANIC("Unreachable");
@@ -7557,15 +7655,21 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
irValue *arr = ir_emit_struct_ep(proc, addr.addr, first_index);
if (addr.soa.index->kind != irValue_Constant) {
Type *t = base_type(type_deref(ir_type(addr.addr)));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
i64 count = t->Struct.soa_count;
irValue *len = ir_const_int(count);
Type *t = base_type(type_deref(ir_type(addr.addr)));
GB_ASSERT(is_type_soa_struct(t));
if (addr.soa.index->kind != irValue_Constant || t->Struct.soa_kind != StructSoa_Fixed) {
irValue *len = ir_soa_struct_len(proc, addr.addr);
ir_emit_bounds_check(proc, ast_token(addr.soa.index_expr), addr.soa.index, len);
}
irValue *item = ir_emit_array_ep(proc, arr, index);
irValue *item = nullptr;
if (t->Struct.soa_kind == StructSoa_Fixed) {
item = ir_emit_array_ep(proc, arr, index);
} else {
item = ir_emit_load(proc, ir_emit_ptr_offset(proc, arr, index));
}
if (sub_sel.index.count > 0) {
item = ir_emit_deep_field_gep(proc, item, sub_sel);
}
@@ -7626,10 +7730,8 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
bool deref = is_type_pointer(t);
t = base_type(type_deref(t));
if (t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None) {
if (is_type_soa_struct(t)) {
// SOA STRUCTURES!!!!
Type *elem = t->Struct.soa_elem;
irValue *val = ir_build_addr_ptr(proc, ie->expr);
if (deref) {
val = ir_emit_load(proc, val);
@@ -7639,6 +7741,36 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
return ir_addr_soa_variable(val, index, ie->index);
}
if (ie->expr->tav.mode == Addressing_SoaVariable) {
// SOA Structures for slices/dynamic arrays
GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
irValue *field = ir_build_expr(proc, ie->expr);
irValue *index = ir_build_expr(proc, ie->index);
if (!build_context.no_bounds_check) {
// TODO HACK(bill): Clean up this hack to get the length for bounds checking
GB_ASSERT(field->kind == irValue_Instr);
irInstr *instr = &field->Instr;
GB_ASSERT(instr->kind == irInstr_Load);
irValue *a = instr->Load.address;
GB_ASSERT(a->kind == irValue_Instr);
irInstr *b = &a->Instr;
GB_ASSERT(b->kind == irInstr_StructElementPtr);
irValue *base_struct = b->StructElementPtr.address;
GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
irValue *len = ir_soa_struct_len(proc, base_struct);
ir_emit_bounds_check(proc, ast_token(ie->index), index, len);
}
irValue *val = ir_emit_ptr_offset(proc, field, index);
return ir_addr(val);
}
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
@@ -7838,6 +7970,55 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
ir_fill_string(proc, str, elem, new_len);
return ir_addr(str);
}
case Type_Struct:
if (is_type_soa_struct(type)) {
irValue *len = ir_soa_struct_len(proc, addr);
if (high == nullptr) high = len;
if (!no_indices) {
ir_emit_slice_bounds_check(proc, se->open, low, high, len, se->low != nullptr);
}
irValue *dst = ir_add_local_generated(proc, type_of_expr(expr), true);
if (type->Struct.soa_kind == StructSoa_Fixed) {
i32 field_count = cast(i32)type->Struct.fields.count;
for (i32 i = 0; i < field_count; i++) {
irValue *field_dst = ir_emit_struct_ep(proc, dst, i);
irValue *field_src = ir_emit_struct_ep(proc, addr, i);
field_src = ir_emit_array_ep(proc, field_src, low);
ir_emit_store(proc, field_dst, field_src);
}
irValue *len_dst = ir_emit_struct_ep(proc, dst, field_count);
irValue *new_len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
ir_emit_store(proc, len_dst, new_len);
} else if (type->Struct.soa_kind == StructSoa_Slice) {
if (no_indices) {
ir_emit_store(proc, dst, base);
} else {
i32 field_count = cast(i32)type->Struct.fields.count - 1;
for (i32 i = 0; i < field_count; i++) {
irValue *field_dst = ir_emit_struct_ep(proc, dst, i);
irValue *field_src = ir_emit_struct_ev(proc, base, i);
field_src = ir_emit_ptr_offset(proc, field_src, low);
ir_emit_store(proc, field_dst, field_src);
}
irValue *len_dst = ir_emit_struct_ep(proc, dst, field_count);
irValue *new_len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
ir_emit_store(proc, len_dst, new_len);
}
} else {
GB_PANIC("TODO #soa[dynamic]T");
}
return ir_addr(dst);
}
break;
}
GB_PANIC("Unknown slicable type");

View File

@@ -811,6 +811,7 @@ Type *alloc_type_simd_vector(i64 count, Type *elem) {
////////////////////////////////////////////////////////////////
@@ -1757,7 +1758,10 @@ bool are_types_identical(Type *x, Type *y) {
if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
x->Struct.fields.count == y->Struct.fields.count &&
x->Struct.is_packed == y->Struct.is_packed &&
x->Struct.custom_align == y->Struct.custom_align) {
x->Struct.custom_align == y->Struct.custom_align &&
x->Struct.soa_kind == y->Struct.soa_kind &&
x->Struct.soa_count == y->Struct.soa_count &&
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
// TODO(bill); Fix the custom alignment rule
for_array(i, x->Struct.fields) {
Entity *xf = x->Struct.fields[i];