#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
+39 -8
View File
@@ -1350,7 +1350,25 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
base_type_name = v.name; 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, ", "); if !hash && index > 0 do strings.write_string(fi.buf, ", ");
field_count := -1; field_count := -1;
@@ -1361,7 +1379,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
strings.write_byte(fi.buf, '{'); strings.write_byte(fi.buf, '{');
defer 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; field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", "); 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, name);
strings.write_string(fi.buf, " = "); strings.write_string(fi.buf, " = ");
t := info.types[i].variant.(runtime.Type_Info_Array).elem; if info.soa_kind == .Fixed {
t_size := uintptr(t.size); t := info.types[i].variant.(runtime.Type_Info_Array).elem;
if reflect.is_any(t) { t_size := uintptr(t.size);
strings.write_string(fi.buf, "any{}"); 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 { } else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size); t := info.types[i].variant.(runtime.Type_Info_Pointer).elem;
fmt_arg(fi, any{data, t.id}, 'v'); 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"); if hash do strings.write_string(fi.buf, ",\n");
+88 -68
View File
@@ -7,7 +7,7 @@ import "core:reflect"
import "intrinsics" 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: It is designed with the intent of replacing C with the following goals:
* simplicity * simplicity
* high performance * high performance
@@ -36,7 +36,7 @@ the_basics :: proc() {
my_integer_variable: int; // A comment for documentaton 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): // also be nested (unlike in C):
/* /*
You can have any text or code here and You can have any text or code here and
@@ -63,16 +63,16 @@ the_basics :: proc() {
// Numbers // Numbers
// Numerical literals are written similar to most other programming languages. // Numerical literals are written similar to most other programming languages.
// A useful feature in Odin is that underscores are allowed for better // 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 // 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 // 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). // 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). // 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. // 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 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 // Constant declarations
// Constants are entities (symbols) which have an assigned value. // Constants are entities (symbols) which have an assigned value.
// The constants value cannot be changed. // The constants value cannot be changed.
// The constants value must be able to be evaluated at compile time: // The constants value must be able to be evaluated at compile time:
X :: "what"; // constant `X` has the untyped string value "what" X :: "what"; // constant `X` has the untyped string value "what"
@@ -234,7 +234,7 @@ control_flow :: proc() {
} }
// Switch statement // 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. // In Odin, the default case is denoted as a case without any expression.
switch arch := ODIN_ARCH; arch { switch arch := ODIN_ARCH; arch {
@@ -246,12 +246,12 @@ control_flow :: proc() {
fmt.println("Unsupported architecture"); fmt.println("Unsupported architecture");
} }
// Odins `switch` is like one in C or C++, except that Odin only runs the selected 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. // 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. // 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. // 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"); fmt.println("one_angry_dwarf was called");
return 1; return 1;
} }
@@ -261,8 +261,8 @@ control_flow :: proc() {
case one_angry_dwarf(): case one_angry_dwarf():
} }
// A switch statement without a condition is the same as `switch true`. // 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 // This can be used to write a clean and long if-else chain and have the
// ability to break if needed // ability to break if needed
switch { switch {
@@ -293,7 +293,7 @@ control_flow :: proc() {
} }
{ // Defer statement { // 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 scope it is in.
// The following will print 4 then 234: // The following will print 4 then 234:
@@ -318,11 +318,11 @@ control_flow :: proc() {
fmt.println("2"); fmt.println("2");
} }
cond := false; cond := false;
defer if cond { defer if cond {
bar(); bar();
} }
} }
// Defer statements are executed in the reverse order that they were declared: // Defer statements are executed in the reverse order that they were declared:
{ {
@@ -343,13 +343,13 @@ control_flow :: proc() {
} }
{ // When statement { // When statement
/* /*
The when statement is almost identical to the if statement but with some differences: 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. statement is evaluated at compile time.
* The statements within a branch do not create a new scope * 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 that belong to the first condition that is true
* An initial statement is not allowed in a when statement * An initial statement is not allowed in a when statement
* when statements are allowed at file scope * when statements are allowed at file scope
@@ -363,8 +363,8 @@ control_flow :: proc() {
} else { } else {
fmt.println("Unsupported architecture"); fmt.println("Unsupported architecture");
} }
// The when statement is very useful for writing platform specific code. // The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in Cs preprocessor however, in Odin, // This is akin to the #if construct in Cs preprocessor however, in Odin,
// it is type checked. // it is type checked.
} }
@@ -401,9 +401,9 @@ control_flow :: proc() {
// Fallthrough statement // Fallthrough statement
// Odins switch is like one in C or C++, except that Odin only runs the selected // 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. // 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 // Another important difference is that the case values need not be integers nor
// constants. // constants.
// fallthrough can be used to explicitly fall through into the next case block: // fallthrough can be used to explicitly fall through into the next case block:
@@ -477,8 +477,8 @@ explicit_procedure_overloading :: proc() {
struct_type :: proc() { struct_type :: proc() {
fmt.println("\n# struct type"); fmt.println("\n# struct type");
// A struct is a record type in Odin. It is a collection of fields. // A struct is a record type in Odin. It is a collection of fields.
// Struct fields are accessed by using a dot: // Struct fields are accessed by using a dot:
{ {
Vector2 :: struct { Vector2 :: struct {
x: f32, x: f32,
@@ -495,13 +495,13 @@ struct_type :: proc() {
p.x = 1335; p.x = 1335;
fmt.println(v); fmt.println(v);
// We could write p^.x, however, it is to nice abstract the ability // We could write p^.x, however, it is to nice abstract the ability
// to not explicitly dereference the pointer. This is very useful when // to not explicitly dereference the pointer. This is very useful when
// refactoring code to use a pointer rather than a value, and vice versa. // refactoring code to use a pointer rather than a value, and vice versa.
} }
{ {
// A struct literal can be denoted by providing the structs type // A struct literal can be denoted by providing the structs type
// followed by {}. A struct literal must either provide all the // followed by {}. A struct literal must either provide all the
// arguments or none: // arguments or none:
Vector3 :: struct { Vector3 :: struct {
x, y, z: f32, x, y, z: f32,
@@ -510,12 +510,12 @@ struct_type :: proc() {
v = Vector3{}; // Zero value v = Vector3{}; // Zero value
v = Vector3{1, 4, 9}; 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): // field by name (the order of the named fields does not matter):
v = Vector3{z=1, y=2}; v = Vector3{z=1, y=2};
assert(v.x == 0); assert(v.x == 0);
assert(v.y == 2); assert(v.y == 2);
assert(v.z == 1); assert(v.z == 1);
} }
{ {
// Structs can tagged with different memory layout and alignment requirements: // Structs can tagged with different memory layout and alignment requirements:
@@ -704,8 +704,8 @@ union_type :: proc() {
using_statement :: proc() { using_statement :: proc() {
fmt.println("\n# using statement"); fmt.println("\n# using statement");
// using can used to bring entities declared in a scope/namespace // using can used to bring entities declared in a scope/namespace
// into the current scope. This can be applied to import declarations, // into the current scope. This can be applied to import declarations,
// import names, struct fields, procedure fields, and struct values. // import names, struct fields, procedure fields, and struct values.
Vector3 :: struct{x, y, z: f32}; 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: // making all the fields of position appear as if they on Entity itself:
Entity :: struct { Entity :: struct {
using position: Vector3, using position: Vector3,
@@ -747,11 +747,11 @@ using_statement :: proc() {
foo :: proc(entity: ^Entity) { foo :: proc(entity: ^Entity) {
fmt.println(entity.x, entity.y, entity.z); fmt.println(entity.x, entity.y, entity.z);
} }
// Subtype polymorphism // Subtype polymorphism
// It is possible to get subtype polymorphism, similar to inheritance-like // It is possible to get subtype polymorphism, similar to inheritance-like
// functionality in C++, but without the requirement of vtables or unknown // functionality in C++, but without the requirement of vtables or unknown
// struct layout: // struct layout:
Colour :: struct {r, g, b, a: u8}; Colour :: struct {r, g, b, a: u8};
@@ -767,7 +767,7 @@ using_statement :: proc() {
foo(&frog); foo(&frog);
frog.x = 123; 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). // the ability to have multiple subtype polymorphism (but also its issues).
// Note: usingd fields can still be referred by name. // Note: usingd fields can still be referred by name.
@@ -787,19 +787,19 @@ using_statement :: proc() {
implicit_context_system :: proc() { implicit_context_system :: proc() {
fmt.println("\n# implicit context system"); fmt.println("\n# implicit context system");
// In each scope, there is an implicit value named context. This // In each scope, there is an implicit value named context. This
// context variable is local to each scope and is implicitly passed // context variable is local to each scope and is implicitly passed
// by pointer to any procedure call in that scope (if the procedure // by pointer to any procedure call in that scope (if the procedure
// has the Odin calling convention). // has the Odin calling convention).
// The main purpose of the implicit context system is for the ability // The main purpose of the implicit context system is for the ability
// to intercept third-party code and libraries and modify their // to intercept third-party code and libraries and modify their
// functionality. One such case is modifying how a library allocates // functionality. One such case is modifying how a library allocates
// something or logs something. In C, this was usually achieved with // something or logs something. In C, this was usually achieved with
// the library defining macros which could be overridden so that the // the library defining macros which could be overridden so that the
// user could define what he wanted. However, not many libraries // user could define what he wanted. However, not many libraries
// supported this in many languages by default which meant intercepting // 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 // third-party code to see what it does and to change how it does it is
// not possible. // not possible.
c := context; // copy the current scope's context 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()` // An context.allocator is assigned to the return value of `my_custom_allocator()`
assert(context.user_index == 123); 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 // default unless explicitly specified otherwise
china_grove := new(int); china_grove := new(int);
free(china_grove); free(china_grove);
@@ -828,10 +828,10 @@ implicit_context_system :: proc() {
my_custom_allocator :: mem.nil_allocator; 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. // 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. // definition in package runtime.
} }
@@ -1133,14 +1133,14 @@ map_type :: proc() {
m := make(map[string]int); m := make(map[string]int);
defer delete(m); defer delete(m);
m["Bob"] = 2; m["Bob"] = 2;
m["Ted"] = 5; m["Ted"] = 5;
fmt.println(m["Bob"]); fmt.println(m["Bob"]);
delete_key(&m, "Ted"); delete_key(&m, "Ted");
// If an element of a key does not exist, the zero value of the // 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 // element will be returned. To check to see if an element exists
// can be done in two ways: // can be done in two ways:
elem, ok := m["Bob"]; elem, ok := m["Bob"];
exists := "Bob" in m; exists := "Bob" in m;
@@ -1508,26 +1508,26 @@ when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib"
foreign_system :: proc() { foreign_system :: proc() {
fmt.println("\n#foreign system"); fmt.println("\n#foreign system");
when ODIN_OS == "windows" { when ODIN_OS == "windows" {
// It is sometimes necessarily to interface with foreign code, // It is sometimes necessarily to interface with foreign code,
// such as a C library. In Odin, this is achieved through the // such as a C library. In Odin, this is achieved through the
// foreign system. You can “import” a library into the code // foreign system. You can “import” a library into the code
// using the same semantics as a normal import declaration. // using the same semantics as a normal import declaration.
// This foreign import declaration will create a // This foreign import declaration will create a
// “foreign import name” which can then be used to associate // “foreign import name” which can then be used to associate
// entities within a foreign block. // entities within a foreign block.
foreign kernel32 { foreign kernel32 {
ExitProcess :: proc "stdcall" (exit_code: u32) --- ExitProcess :: proc "stdcall" (exit_code: u32) ---
} }
// Foreign procedure declarations have the cdecl/c calling // Foreign procedure declarations have the cdecl/c calling
// convention by default unless specified otherwise. Due to // convention by default unless specified otherwise. Due to
// foreign procedures do not have a body declared within this // foreign procedures do not have a body declared within this
// code, you need append the --- symbol to the end to distinguish // code, you need append the --- symbol to the end to distinguish
// it as a procedure literal without a body and not a procedure type. // 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: // of entities declared within a block:
@(default_calling_convention = "std") @(default_calling_convention = "std")
@@ -1718,6 +1718,26 @@ soa_struct_layout :: proc() {
v_soa[0].y = 4; v_soa[0].y = 4;
v_soa[0].z = 9; 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);
}
} }
+24 -2
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 set_procedure_abi_types(gbAllocator a, Type *type);
void check_assignment_error_suggestion(CheckerContext *c, Operand *o, 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) { Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr); expr = unparen_expr(expr);
switch (expr->kind) { switch (expr->kind) {
@@ -3228,6 +3232,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
} }
} else if (operand->mode == Addressing_MapIndex) { } else if (operand->mode == Addressing_MapIndex) {
operand->mode = Addressing_Value; 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) { } else if (sel.indirect || operand->mode != Addressing_Value || operand->mode == Addressing_SoaVariable) {
operand->mode = Addressing_Variable; operand->mode = Addressing_Variable;
} else { } 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) { switch (t->kind) {
case Type_Basic: case Type_Basic:
if (t->Basic.kind == Basic_string) { 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; 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; return false;
} }
@@ -7973,7 +7988,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
} }
i64 max_count = -1; 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) { if (is_const) {
valid = false; valid = false;
@@ -8055,6 +8070,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
valid = true; valid = true;
o->type = alloc_type_slice(t->DynamicArray.elem); o->type = alloc_type_slice(t->DynamicArray.elem);
break; 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) { if (!valid) {
+117 -2
View File
@@ -2702,6 +2702,108 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
// error(node, "'map' types are not yet implemented"); // 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) { 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); *type = alloc_type_simd_vector(count, elem);
} else { } 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 { } else {
*type = alloc_type_array(elem, count, generic_type); *type = alloc_type_array(elem, count, generic_type);
} }
} else { } else {
Type *elem = check_type(ctx, at->elem); 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: array_end:
set_base_type(named_type, *type); set_base_type(named_type, *type);
+4 -2
View File
@@ -51,8 +51,10 @@ enum EntityFlag {
EntityFlag_ImplicitReference = 1<<17, // NOTE(bill): equivalent to `const &` in C++ EntityFlag_ImplicitReference = 1<<17, // NOTE(bill): equivalent to `const &` in C++
EntityFlag_CVarArg = 1<<20, EntityFlag_SoaPtrField = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
EntityFlag_AutoCast = 1<<21,
EntityFlag_CVarArg = 1<<21,
EntityFlag_AutoCast = 1<<22,
}; };
enum EntityState { enum EntityState {
+204 -23
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) { void ir_addr_store(irProcedure *proc, irAddr const &addr, irValue *value) {
if (addr.addr == nullptr) { if (addr.addr == nullptr) {
return; 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); value = ir_emit_conv(proc, value, t->Struct.soa_elem);
irValue *index = addr.soa.index; 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))); Type *t = base_type(type_deref(ir_type(addr.addr)));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None); GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
i64 count = t->Struct.soa_count; i64 count = t->Struct.soa_count;
@@ -3697,27 +3764,53 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) {
t = base_type(t); t = base_type(t);
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None); GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
Type *elem = t->Struct.soa_elem; 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); irValue *res = ir_add_local_generated(proc, elem, true);
if (addr.soa.index->kind != irValue_Constant) { if (addr.soa.index->kind != irValue_Constant || t->Struct.soa_kind != StructSoa_Fixed) {
irValue *len = ir_const_int(count);
ir_emit_bounds_check(proc, ast_token(addr.soa.index_expr), addr.soa.index, len); ir_emit_bounds_check(proc, ast_token(addr.soa.index_expr), addr.soa.index, len);
} }
for_array(i, t->Struct.fields) { if (t->Struct.soa_kind == StructSoa_Fixed) {
Entity *field = t->Struct.fields[i]; for_array(i, t->Struct.fields) {
Type *base_type = field->type; Entity *field = t->Struct.fields[i];
GB_ASSERT(base_type->kind == Type_Array); Type *base_type = field->type;
Type *elem = base_type->Array.elem; 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 *dst = ir_emit_struct_ep(proc, res, cast(i32)i);
irValue *src_ptr = ir_emit_struct_ep(proc, addr.addr, 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); src_ptr = ir_emit_ptr_offset(proc, src_ptr, addr.soa.index);
irValue *src = ir_emit_load(proc, src_ptr); irValue *src = ir_emit_load(proc, src_ptr);
ir_emit_store(proc, dst, src); src = ir_emit_load(proc, src);
ir_emit_store(proc, dst, src);
}
} }
return ir_emit_load(proc, res); return ir_emit_load(proc, res);
@@ -3796,6 +3889,7 @@ irValue *ir_map_cap(irProcedure *proc, irValue *value) {
struct irLoopData { struct irLoopData {
irValue *idx_addr; irValue *idx_addr;
irValue *idx; 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); return ir_dynamic_array_len(proc, v);
} else if (is_type_map(t)) { } else if (is_type_map(t)) {
return ir_map_len(proc, v); return ir_map_len(proc, v);
} else if (is_type_soa_struct(t)) {
return ir_soa_struct_len(proc, v);
} }
GB_PANIC("Unreachable"); 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); return ir_dynamic_array_cap(proc, v);
} else if (is_type_map(t)) { } else if (is_type_map(t)) {
return ir_map_cap(proc, v); return ir_map_cap(proc, v);
} else if (is_type_soa_struct(t)) {
return ir_soa_struct_cap(proc, v);
} }
GB_PANIC("Unreachable"); 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); 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)));
Type *t = base_type(type_deref(ir_type(addr.addr))); GB_ASSERT(is_type_soa_struct(t));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
i64 count = t->Struct.soa_count; if (addr.soa.index->kind != irValue_Constant || t->Struct.soa_kind != StructSoa_Fixed) {
irValue *len = ir_const_int(count); irValue *len = ir_soa_struct_len(proc, addr.addr);
ir_emit_bounds_check(proc, ast_token(addr.soa.index_expr), addr.soa.index, len); 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) { if (sub_sel.index.count > 0) {
item = ir_emit_deep_field_gep(proc, item, sub_sel); 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); bool deref = is_type_pointer(t);
t = base_type(type_deref(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!!!! // SOA STRUCTURES!!!!
Type *elem = t->Struct.soa_elem;
irValue *val = ir_build_addr_ptr(proc, ie->expr); irValue *val = ir_build_addr_ptr(proc, ie->expr);
if (deref) { if (deref) {
val = ir_emit_load(proc, val); 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); 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)); GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) { 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); ir_fill_string(proc, str, elem, new_len);
return ir_addr(str); 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"); GB_PANIC("Unknown slicable type");
+5 -1
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 && if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
x->Struct.fields.count == y->Struct.fields.count && x->Struct.fields.count == y->Struct.fields.count &&
x->Struct.is_packed == y->Struct.is_packed && 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 // TODO(bill); Fix the custom alignment rule
for_array(i, x->Struct.fields) { for_array(i, x->Struct.fields) {
Entity *xf = x->Struct.fields[i]; Entity *xf = x->Struct.fields[i];