From 5562364a98f01a0c0221a70345656d45de0d2009 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 19 Mar 2017 16:59:11 +0000 Subject: [PATCH] Add branch labels for loops; using list --- build.bat | 7 +- code/demo.odin | 284 +++++++++---- core/fmt.odin | 6 +- core/strconv.odin | 4 +- src/array.c | 10 +- src/check_decl.c | 7 +- src/check_expr.c | 202 +++------ src/check_stmt.c | 136 ++++-- src/checker.c | 26 +- src/entity.c | 16 +- src/exact_value.c | 36 +- src/gb/gb.h | 4 + src/ir.c | 252 +++++++---- src/parser.c | 150 ++++--- src/ssa.c | 1030 +++++++++++++++++++++++++++------------------ src/ssa_op.c | 277 ++++++++++++ src/tokenizer.c | 36 +- 17 files changed, 1637 insertions(+), 846 deletions(-) create mode 100644 src/ssa_op.c diff --git a/build.bat b/build.bat index f023a3eef..51dfc84b4 100644 --- a/build.bat +++ b/build.bat @@ -44,10 +44,9 @@ del *.ilk > NUL 2> NUL cl %compiler_settings% "src\main.c" ^ /link %linker_settings% -OUT:%exe_name% ^ - && odin build code/metagen.odin ^ - && call "code\metagen.exe" "src\ast_nodes.metagen" - rem && odin build code/markdown.odin ^ - rem && call "code\markdown.exe" "misc\example.md" + && odin run code/demo.odin + rem && odin build code/metagen.odin ^ + rem && call "code\metagen.exe" "src\ast_nodes.metagen" rem && odin run code/Jaze/src/main.odin del *.obj > NUL 2> NUL diff --git a/code/demo.odin b/code/demo.odin index 7153c12f5..cb17672cc 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -10,40 +10,18 @@ #import win32 "sys/windows.odin"; main :: proc() { - fmt.println("Here"); -when false { /* - Version 0.1.1 - Added: - * Dynamic Arrays `[dynamic]Type` - * Dynamic Maps `map[Key]Value` - * Dynamic array and map literals - * Custom struct alignemnt `struct #align 8 { bar: i8 }` - * Allow `_` in numbers - * Variadic `append` - * fmt.sprint* - * Entities prefixes with an underscore do not get exported on imports - * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps - * enum types have an implict `names` field, a []string of all the names in that enum - * immutable variables are "completely immutable" - rules need a full explanation - * `slice_to_bytes` - convert any slice to a slice of bytes - * `union_cast` allows for optional ok check - * Record type field `names` (struct/raw_union/enum) - * ?: ternary operator - * Unions with variants and common fields - * New built-in procedures - - `delete` to delete map entries `delete(m, key)` - - `clear` to clear dynamic maps and arrays `clear(map_or_array)` - - `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)` - * Unexported entities and fields using an underscore prefix + * Unexported entities and fields using an underscore prefix + - See `sync.odin` and explain Removed: * Maybe/option types * Remove `type` keyword and other "reserved" keywords - * `compile_assert` and `assert`return the value of the condition for semantic reasons + * ..< and ... removed and replace with .. (half-closed range) Changed: + * `compile_assert` and `assert`return the value of the condition for semantic reasons * thread_local -> #thread_local * #include -> #load * Files only get checked if they are actually used @@ -51,20 +29,7 @@ when false { * Version numbering now starts from 0.1.0 and uses the convention: - major.minor.patch * Core library additions to Windows specific stuff - - Fixes: - * Many fmt.* fixes - * Overloading bug due to comparison of named types - * Overloading bug due to `#import .` collision - * disallow a `cast` from pointers of unions - * Minor bugs in generated IR code for slices - - To come very Soon™: - * Linux and OS X builds (unofficial ones do exist already) -*/ - { - - } + */ { Fruit :: enum { @@ -75,6 +40,147 @@ when false { fmt.println(Fruit.names); } + { + A :: struct {x, y: f32}; + B :: struct #align 16 {x, y: f32}; + fmt.println("align_of(A) =", align_of(A)); + fmt.println("align_of(B) =", align_of(B)); + } + + { + // Removal of ..< and ... + for i in 0..16 { + } + // Is similar to + for _i := 0; _i < 16; _i++ { immutable i := _i; + } + } + + { + #label thing + for i in 0..10 { + for j := i+1; j < 10; j++ { + if j == 2 { + fmt.println(i, j); + break thing; + } + } + } + return; + } + + { + cond := true; + x: int; + if cond { + x = 3; + } else { + x = 4; + } + + + // Ternary operator + y := cond ? 3 : 4; + + FOO :: true ? 123 : 432; // Constant ternary operation + fmt.println("Ternary values:", y, FOO); + } + + { + // Slices now store a capacity + buf: [256]byte; + s: []byte; + s = buf[..0]; // == buf[0..0]; + fmt.println("count =", s.count); + fmt.println("capacity =", s.capacity); + append(s, 1, 2, 3); + fmt.println(s); + + s = buf[1..2..3]; + fmt.println("count =", s.count); + fmt.println("capacity =", s.capacity); + fmt.println(s); + + clear(s); // Sets count to zero + s.count = 0; // Equivalent + } + + { + Foo :: struct { + x, y, z: f32, + ok: bool, + flags: u32, + } + foo_array: [256]Foo; + foo_as_bytes: []byte = slice_to_bytes(foo_array[..]); + // Useful for things like + // os.write(handle, foo_as_bytes); + + foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo)); + // Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone? + // And if so what would the syntax be? + // slice_transmute([]Foo, foo_as_bytes); + } + + { + Vec3 :: [vector 3]f32; + + x := Vec3{1, 2, 3}; + y := Vec3{4, 5, 6}; + fmt.println(x < y); + fmt.println(x + y); + fmt.println(x - y); + fmt.println(x * y); + fmt.println(x / y); + + for i in x { + fmt.println(i); + } + + compile_assert(size_of([vector 7]bool) == size_of([7]bool)); + compile_assert(size_of([vector 7]i32) == size_of([7]i32)); + // align_of([vector 7]i32) != align_of([7]i32) // this may be the case + } + + { + // fmt.* changes + // bprint* returns `int` (bytes written) + // sprint* returns `string` (bytes written as a string) + + data: [256]byte; + str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!'); + fmt.println(str); + + buf := data[..0]; + count := fmt.bprintf(^buf, "Hellope %d %s %c", 123, "others", '!'); + fmt.println(cast(string)buf[..count]); + + // NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet + } + + { + x: [dynamic]f64; + reserve(x, 16); + defer free(x); // `free` is overloaded for numerous types + // Number literals can have underscores in them for readability + append(x, 2_000_000.500_000, 3, 5, 7); // variadic append + + for p, i in x { + if i > 0 { fmt.print(", "); } + fmt.print(p); + } + fmt.println(); + } + + { + // Dynamic array "literals" + x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; + defer free(x); + fmt.println(x); // fmt.print* supports printing of dynamic types + clear(x); + fmt.println(x); + } + { m: map[f32]int; reserve(m, 16); @@ -109,49 +215,79 @@ when false { c := m["c"]; _, ok := m["c"]; assert(ok && c == 7654); + fmt.println(m); + + delete(m, "c"); // deletes entry with key "c" + _, found := m["c"]; + assert(!found); fmt.println(m); + clear(m); + fmt.println(m); + + // NOTE: Fixed size maps are planned but we have not yet implemented + // them as we have had no need for them as of yet } { - x: [dynamic]f64; - reserve(x, 16); - defer free(x); - append(x, 2_000_000.500_000, 3, 5, 7); + Vector3 :: struct{x, y, z: f32}; + Quaternion :: struct{x, y, z, w: f32}; - for p, i in x { - if i > 0 { fmt.print(", "); } - fmt.print(p); - } - fmt.println(); - } + Entity :: union { + // Common Fields + id: u64, + name: string, + using position: Vector3, + orientation: Quaternion, + flags: u32, - { - x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; - defer free(x); - fmt.println(x); - } - - - { - Vec3 :: [vector 3]f32; - - x := Vec3{1, 2, 3}; - y := Vec3{4, 5, 6}; - fmt.println(x < y); - fmt.println(x + y); - fmt.println(x - y); - fmt.println(x * y); - fmt.println(x / y); - - for i in x { - fmt.println(i); + // Variants + Frog{ + ribbit_volume: f32, + jump_height: f32, + }, + Door{ + openness: f32, + }, + Map{ + width, height: f32, + place_positions: []Vector3, + place_names: []string, + }, } - compile_assert(size_of([vector 7]bool) == size_of([7]bool)); - compile_assert(size_of([vector 7]i32) == size_of([7]i32)); - // align_of([vector 7]i32) != align_of([7]i32) // this may be the case + entity: Entity; + // implicit conversion from variant to base type + entity = Entity.Frog{ + id = 1337, + ribbit_volume = 0.5, + jump_height = 2.1, + /*other data */ + }; + + entity.name = "Frank"; + entity.position = Vector3{1, 4, 9}; + + using Entity; + match e in entity { + case Frog: + fmt.println("Ribbit"); + case Door: + fmt.println("Creak"); + case Map: + fmt.println("Rustle"); + default: + fmt.println("Just a normal entity"); + } + + if frog, ok := union_cast(Frog)entity; ok { + fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position); + } + + // Panics if not the correct type + frog: Frog; + frog = union_cast(Frog)entity; + frog, _ = union_cast(Frog)entity; // ignore error and force cast } } -} diff --git a/core/fmt.odin b/core/fmt.odin index 56fde40dc..ceae65e82 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -295,15 +295,15 @@ bprintln :: proc(buf: ^[]byte, args: ..any) -> int { sprint :: proc(buf: []byte, args: ..any) -> string { count := bprint(^buf, ..args); - return cast(string)buf; + return cast(string)buf[..count]; } sprintln :: proc(buf: []byte, args: ..any) -> string { count := bprintln(^buf, ..args); - return cast(string)buf; + return cast(string)buf[..count]; } sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { count := bprintf(^buf, fmt, ..args); - return cast(string)buf; + return cast(string)buf[..count]; } diff --git a/core/strconv.odin b/core/strconv.odin index e21104819..5710e76de 100644 --- a/core/strconv.odin +++ b/core/strconv.odin @@ -30,9 +30,7 @@ append_uint :: proc(buf: []byte, u: u64, base: int) -> string { append_int :: proc(buf: []byte, i: i64, base: int) -> string { return append_bits(buf, cast(u64)i, base, true, 8*size_of(int), digits, 0); } -itoa :: proc(buf: []byte, i: int) -> string { - return append_int(buf, cast(i64)i, 10); -} +itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, cast(i64)i, 10); } append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size); diff --git a/src/array.c b/src/array.c index d169a99ef..bb9e789db 100644 --- a/src/array.c +++ b/src/array.c @@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) { x->count = capacity; } - { - // TODO(bill): Resize rather than copy and delete - void *new_data = gb_alloc(x->allocator, element_size*capacity); - gb_memmove(new_data, x->e, element_size*x->count); - gb_free(x->allocator, x->e); - x->capacity = capacity; - x->e = new_data; - } + x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity); + x->capacity = capacity; } diff --git a/src/check_decl.c b/src/check_decl.c index a89460123..12ce52142 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -12,6 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex gbString expr_str = expr_to_string(operand->expr); // TODO(bill): is this a good enough error message? + // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use error_node(operand->expr, "Cannot assign builtin procedure `%s` in %.*s", expr_str, @@ -276,12 +277,6 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) { error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body"); } - // TODO(bill): Is this the best option? What about passing to external shit?! - // if (proc_type->Proc.calling_convention != ProcCC_Odin) { - // error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention"); - // proc_type->Proc.calling_convention = ProcCC_Odin; - // } - d->scope = c->context.scope; GB_ASSERT(pd->body->kind == AstNode_BlockStmt); diff --git a/src/check_expr.c b/src/check_expr.c index c6c9e738b..fcb3e948e 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n if (operand->mode == Addressing_Builtin) { // TODO(bill): is this a good enough error message? + // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use error_node(operand->expr, "Cannot assign builtin procedure `%s` in %.*s", expr_str, @@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, Entity **found = map_entity_get(&entity_map, key); if (found != NULL) { Entity *e = *found; - // TODO(bill): Scope checking already checks the declaration + // NOTE(bill): Scope checking already checks the declaration but in many cases, this can happen so why not? + // This may be a little janky but it's not really that much of a problem error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); error(e->token, "\tpreviously declared"); } else { @@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { { ast_node(fl, FieldList, f->list); - // TODO(bill): Just do a gb_memcopy here // NOTE(bill): Copy the contents for the common fields for now AstNodeArray list = {0}; array_init_count(&list, c->allocator, ut->fields.count+fl->list.count); - for (isize j = 0; j < ut->fields.count; j++) { - list.e[j] = ut->fields.e[j]; - } - for (isize j = 0; j < fl->list.count; j++) { - list.e[j+ut->fields.count] = fl->list.e[j]; - } + gb_memmove_array(list.e, ut->fields.e, ut->fields.count); + gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count); isize list_count = 0; for_array(j, list) { @@ -654,7 +651,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) { HashKey key = hash_string(name_token.string); if (map_entity_get(&entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration + // NOTE(bill): Scope checking already checks the declaration error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); } else { map_entity_set(&entity_map, key, e); @@ -1142,10 +1139,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type * } break; - case Entity_TypeName: { + case Entity_TypeName: + // NOTE(bill): Cyclical dependency checking is handled in the "type system" not here o->mode = Addressing_Type; - // TODO(bill): Fix cyclical dependancy checker - } break; + break; case Entity_Procedure: o->mode = Addressing_Value; @@ -1165,6 +1162,10 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type * error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name)); return e; + case Entity_Label: + o->mode = Addressing_NoValue; + break; + case Entity_Nil: o->mode = Addressing_Value; break; @@ -1691,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type if (s < 64) { umax = (1ull << s) - 1ull; } else { - // TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats + // IMPORTANT TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats s = 64; } i64 imax = (1ll << (s-1ll)); @@ -2884,7 +2885,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h operand->value = entity->Constant.value; break; case Entity_Variable: - // TODO(bill): This is the rule I need? + // TODO(bill): Is this the rule I need? if (operand->mode == Addressing_Immutable) { // Okay } else if (sel.indirect || operand->mode != Addressing_Value) { @@ -3073,9 +3074,11 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id case BuiltinProc_clear: { Type *type = operand->type; - if (!is_type_dynamic_array(type) && !is_type_map(type)) { + bool is_pointer = is_type_pointer(type); + type = base_type(type_deref(type)); + if (!is_type_dynamic_array(type) && !is_type_map(type) && !is_type_slice(type)) { gbString str = type_to_string(type); - error_node(operand->expr, "Expected a map or dynamic array, got `%s`", str); + error_node(operand->expr, "Invalid type for `clear`, got `%s`", str); gb_string_free(str); return false; } @@ -3107,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } Type *elem = NULL; - Type *slice_elem = NULL; if (is_type_dynamic_array(type)) { - // TODO(bill): Semi-memory leaks elem = type->DynamicArray.elem; } else { elem = type->Slice.elem; } - slice_elem = make_type_slice(c->allocator, elem); + Type *slice_elem = make_type_slice(c->allocator, elem); Type *proc_type_params = make_type_tuple(c->allocator); proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2); @@ -3501,113 +3502,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_Value; } break; -#if 0 - case BuiltinProc_ptr_offset: { - // ptr_offset :: proc(ptr: ^T, offset: int) -> ^T - // ^T cannot be rawptr - Type *ptr_type = base_type(operand->type); - if (!is_type_pointer(ptr_type)) { - gbString type_str = type_to_string(operand->type); - defer (gb_string_free(type_str)); - error_node(call, - "Expected a pointer to `ptr_offset`, got `%s`", - type_str); - return false; - } - - if (ptr_type == t_rawptr) { - error_node(call, - "`rawptr` cannot have pointer arithmetic"); - return false; - } - - AstNode *offset = ce->args.e[1]; - Operand op = {0}; - check_expr(c, &op, offset); - if (op.mode == Addressing_Invalid) - return false; - Type *offset_type = base_type(op.type); - if (!is_type_integer(offset_type)) { - error_node(op.expr, "Pointer offsets for `ptr_offset` must be an integer"); - return false; - } - - if (operand->mode == Addressing_Constant && - op.mode == Addressing_Constant) { - i64 ptr = operand->value.value_pointer; - i64 elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem); - ptr += elem_size * op.value.value_integer; - operand->value.value_pointer = ptr; - } else { - operand->mode = Addressing_Value; - } - - } break; - - case BuiltinProc_ptr_sub: { - // ptr_sub :: proc(a, b: ^T) -> int - // ^T cannot be rawptr - Type *ptr_type = base_type(operand->type); - if (!is_type_pointer(ptr_type)) { - gbString type_str = type_to_string(operand->type); - defer (gb_string_free(type_str)); - error_node(call, - "Expected a pointer to `ptr_add`, got `%s`", - type_str); - return false; - } - - if (ptr_type == t_rawptr) { - error_node(call, - "`rawptr` cannot have pointer arithmetic"); - return false; - } - AstNode *offset = ce->args[1]; - Operand op = {0}; - check_expr(c, &op, offset); - if (op.mode == Addressing_Invalid) - return false; - if (!is_type_pointer(op.type)) { - gbString type_str = type_to_string(operand->type); - defer (gb_string_free(type_str)); - error_node(call, - "Expected a pointer to `ptr_add`, got `%s`", - type_str); - return false; - } - - if (base_type(op.type) == t_rawptr) { - error_node(call, - "`rawptr` cannot have pointer arithmetic"); - return false; - } - - if (!are_types_identical(operand->type, op.type)) { - gbString a = type_to_string(operand->type); - gbString b = type_to_string(op.type); - defer (gb_string_free(a)); - defer (gb_string_free(b)); - error_node(op.expr, - "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b); - return false; - } - - operand->type = t_int; - - if (operand->mode == Addressing_Constant && - op.mode == Addressing_Constant) { - u8 *ptr_a = cast(u8 *)operand->value.value_pointer; - u8 *ptr_b = cast(u8 *)op.value.value_pointer; - isize elem_size = type_size_of(c->allocator, ptr_type->Pointer.elem); - operand->value = exact_value_integer((ptr_a - ptr_b) / elem_size); - } else { - operand->mode = Addressing_Value; - } - } break; -#endif - case BuiltinProc_slice_ptr: { // slice_ptr :: proc(a: ^T, len: int) -> []T + // slice_ptr :: proc(a: ^T, len, cap: int) -> []T // ^T cannot be rawptr Type *ptr_type = base_type(operand->type); if (!is_type_pointer(ptr_type)) { @@ -3625,21 +3522,28 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id return false; } - AstNode *len = ce->args.e[1]; + isize arg_count = ce->args.count; + if (arg_count < 2 || 3 < arg_count) { + error_node(ce->args.e[0], "`slice_ptr` expects 2 or 3 arguments, found %td", arg_count); + // NOTE(bill): Return the correct type to reduce errors + } else { + // If any are constant + i64 sizes[2] = {0}; + isize size_count = 0; + for (isize i = 1; i < arg_count; i++) { + i64 val = 0; + bool ok = check_index_value(c, ce->args.e[i], -1, &val); + if (ok && val >= 0) { + GB_ASSERT(size_count < gb_count_of(sizes)); + sizes[size_count++] = val; + } + } - Operand op = {0}; - check_expr(c, &op, len); - if (op.mode == Addressing_Invalid) - return false; - if (!is_type_integer(op.type)) { - gbString type_str = type_to_string(operand->type); - error_node(call, - "Length for `slice_ptr` must be an integer, got `%s`", - type_str); - gb_string_free(type_str); - return false; + if (size_count == 2 && sizes[0] > sizes[1]) { + error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped"); + // No need quit + } } - operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem); operand->mode = Addressing_Value; } break; @@ -4491,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_end; case_ast_node(te, TernaryExpr, node); - if (c->proc_stack.count == 0) { - error_node(node, "A ternary expression is only allowed within a procedure"); - goto error; - } - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, te->cond); - if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { + Operand cond = {Addressing_Invalid}; + check_expr(c, &cond, te->cond); + if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { error_node(te->cond, "Non-boolean condition in if expression"); } @@ -4539,6 +4439,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint o->type = x.type; o->mode = Addressing_Value; + + if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) && + x.mode == Addressing_Constant && + y.mode == Addressing_Constant) { + + o->mode = Addressing_Constant; + + if (cond.value.value_bool) { + o->value = x.value; + } else { + o->value = y.value; + } + } + case_end; case_ast_node(cl, CompoundLit, node); diff --git a/src/check_stmt.c b/src/check_stmt.c index 1837dd404..5e8766f11 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { return rhs->type; } -bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { - if (is_type_pointer(type)) { - *is_union_ptr = is_type_union(type_deref(type)); - return *is_union_ptr; +typedef enum MatchTypeKind { + MatchType_Invalid, + MatchType_Union, + MatchType_Any, +} MatchTypeKind; + +MatchTypeKind check_valid_type_match_type(Type *type) { + type = type_deref(type); + if (is_type_union(type)) { + return MatchType_Union; } if (is_type_any(type)) { - *is_any = true; - return *is_any; + return MatchType_Any; } - return false; + return MatchType_Invalid; } void check_stmt_internal(Checker *c, AstNode *node, u32 flags); @@ -385,6 +390,47 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) { } } +void check_label(Checker *c, AstNode *label) { + if (label == NULL) { + return; + } + ast_node(l, Label, label); + if (l->name->kind != AstNode_Ident) { + error_node(l->name, "A label's name must be an identifier"); + return; + } + String name = l->name->Ident.string; + if (str_eq(name, str_lit("_"))) { + error_node(l->name, "A label's name cannot be a blank identifier"); + return; + } + + + if (c->proc_stack.count == 0) { + error_node(l->name, "A label is only allowed within a procedure"); + return; + } + GB_ASSERT(c->context.decl != NULL); + + bool ok = true; + for_array(i, c->context.decl->labels) { + BlockLabel bl = c->context.decl->labels.e[i]; + if (str_eq(bl.name, name)) { + error_node(label, "Duplicate label with the name `%.*s`", LIT(name)); + ok = false; + break; + } + } + + Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label); + add_entity(c, c->context.scope, l->name, e); + + if (ok) { + BlockLabel bl = {name, label}; + array_add(&c->context.decl->labels, bl); + } +} + void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { u32 mod_flags = flags & (~Stmt_FallthroughAllowed); switch (node->kind) { @@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { return; } - // TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just - // leave it? - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be @@ -515,7 +558,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { error(op, "Unknown Assignment operation `%.*s`", LIT(op.string)); return; } - // TODO(bill): Check if valid assignment operator Operand operand = {Addressing_Invalid}; AstNode binary_expr = {AstNode_BinaryExpr}; ast_node(be, BinaryExpr, &binary_expr); @@ -580,7 +622,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { if (c->context.in_defer) { error(rs->token, "You cannot `return` within a defer statement"); - // TODO(bill): Should I break here? break; } @@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case_ast_node(fs, ForStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; + check_open_scope(c, node); + check_label(c, fs->label); // TODO(bill): What should the label's "scope" be? if (fs->init != NULL) { check_stmt(c, fs->init, 0); @@ -648,6 +691,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(c, node); + check_label(c, rs->label); + Type *val = NULL; Type *idx = NULL; Entity *entities[2] = {0}; @@ -718,7 +763,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case Token_Ellipsis: op = Token_Lt; break; default: error(ie->op, "Invalid range operator"); break; } - bool ok = compare_exact_values(Token_Lt, a, b); + bool ok = compare_exact_values(op, a, b); if (!ok) { // TODO(bill): Better error message error(ie->op, "Invalid interval range"); @@ -982,8 +1027,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { mod_flags |= Stmt_BreakAllowed; check_open_scope(c, node); - bool is_union_ptr = false; - bool is_any = false; + MatchTypeKind match_type_kind = MatchType_Invalid; if (ms->tag->kind != AstNode_AssignStmt) { error_node(ms->tag, "Expected an `in` assignment for this type match statement"); @@ -1005,7 +1049,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_expr(c, &x, rhs); check_assignment(c, &x, NULL, str_lit("type match expression")); - if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) { + match_type_kind = check_valid_type_match_type(x.type); + if (check_valid_type_match_type(x.type) == MatchType_Invalid) { gbString str = type_to_string(x.type); error_node(x.expr, "Invalid type for this type match expression, got `%s`", str); @@ -1062,14 +1107,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { // TODO(bill): Make robust Type *bt = base_type(type_deref(x.type)); - AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL; Type *case_type = NULL; if (type_expr != NULL) { // Otherwise it's a default expression Operand y = {0}; check_expr_or_type(c, &y, type_expr); - if (is_union_ptr) { + if (match_type_kind == MatchType_Union) { GB_ASSERT(is_type_union(bt)); bool tag_type_found = false; for (isize i = 0; i < bt->Record.variant_count; i++) { @@ -1086,7 +1130,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { continue; } case_type = y.type; - } else if (is_any) { + } else if (match_type_kind == MatchType_Any) { case_type = y.type; } else { GB_PANIC("Unknown type to type match statement"); @@ -1110,11 +1154,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_open_scope(c, stmt); if (case_type == NULL) { - if (is_union_ptr) { - case_type = type_deref(x.type); - } else { - case_type = x.type; - } + case_type = type_deref(x.type); } add_type_info_type(c, case_type); @@ -1122,7 +1162,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { { // NOTE(bill): Dummy type Type *tt = case_type; - if (is_union_ptr) { + if (is_type_pointer(x.type)) { tt = make_type_pointer(c->allocator, case_type); add_type_info_type(c, tt); } @@ -1173,20 +1213,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); break; } + + if (bs->label != NULL) { + if (bs->label->kind != AstNode_Ident) { + error_node(bs->label, "A branch statement's label name must be an identifier"); + return; + } + AstNode *ident = bs->label; + String name = ident->Ident.string; + Entity *e = scope_lookup_entity(c->context.scope, name); + if (e == NULL) { + error_node(ident, "Undeclared label name: %.*s", LIT(name)); + return; + } + add_entity_use(c, ident, e); + if (e->kind != Entity_Label) { + error_node(ident, "`%.*s` is not a label", LIT(name)); + return; + } + } + case_end; case_ast_node(us, UsingStmt, node); - switch (us->node->kind) { - default: - // TODO(bill): Better error message for invalid using statement - error(us->token, "Invalid `using` statement"); - break; - case_ast_node(es, ExprStmt, us->node); - // TODO(bill): Allow for just a LHS expression list rather than this silly code + if (us->list.count == 0) { + error(us->token, "Empty `using` list"); + return; + } + for_array(i, us->list) { + AstNode *expr = unparen_expr(us->list.e[0]); Entity *e = NULL; bool is_selector = false; - AstNode *expr = unparen_expr(es->expr); if (expr->kind == AstNode_Ident) { String name = expr->Ident.string; e = scope_lookup_entity(c->context.scope, name); @@ -1194,11 +1252,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { Operand o = {0}; e = check_selector(c, &o, expr, NULL); is_selector = true; + } else if (expr->kind == AstNode_Implicit) { + error(us->token, "`using` applied to an implicit value"); + continue; } if (e == NULL) { error(us->token, "`using` applied to an unknown entity"); - return; + continue; } switch (e->kind) { @@ -1296,6 +1357,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { error(us->token, "`using` cannot be applied to `nil`"); break; + case Entity_Label: + error(us->token, "`using` cannot be applied to a label"); + break; + case Entity_Invalid: error(us->token, "`using` cannot be applied to an invalid entity"); break; @@ -1303,13 +1368,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { default: GB_PANIC("TODO(bill): `using` other expressions?"); } - case_end; - } case_end; - case_ast_node(pa, PushAllocator, node); Operand op = {0}; check_expr(c, &op, pa->expr); diff --git a/src/checker.c b/src/checker.c index 881e86c8b..4cd8e7ae9 100644 --- a/src/checker.c +++ b/src/checker.c @@ -98,7 +98,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { // {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, // {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, - {STR_LIT("slice_ptr"), 2, false, Expr_Expr}, + {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, {STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt}, {STR_LIT("min"), 2, false, Expr_Expr}, @@ -163,6 +163,11 @@ bool is_operand_nil(Operand o) { } +typedef struct BlockLabel { + String name; + AstNode *label; // AstNode_Label +} BlockLabel; + // DeclInfo is used to store information of certain declarations to allow for "any order" usage typedef struct DeclInfo { Scope *scope; @@ -175,16 +180,19 @@ typedef struct DeclInfo { AstNode *proc_lit; // AstNode_ProcLit MapBool deps; // Key: Entity * + Array(BlockLabel) labels; } DeclInfo; // ProcedureInfo stores the information needed for checking a procedure + + typedef struct ProcedureInfo { - AstFile * file; - Token token; - DeclInfo *decl; - Type * type; // Type_Procedure - AstNode * body; // AstNode_BlockStmt - u32 tags; + AstFile * file; + Token token; + DeclInfo * decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStmt + u32 tags; } ProcedureInfo; // ExprInfo stores information used for "untyped" expressions @@ -320,6 +328,7 @@ typedef Array(DelayedEntity) DelayedEntities; void init_declaration_info(DeclInfo *d, Scope *scope) { d->scope = scope; map_bool_init(&d->deps, heap_allocator()); + array_init(&d->labels, heap_allocator()); } DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) { @@ -455,6 +464,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit if (found) { Entity *e = *found; if (gone_thru_proc) { + if (e->kind == Entity_Label) { + continue; + } if (e->kind == Entity_Variable && !e->scope->is_file && !e->scope->is_global) { diff --git a/src/entity.c b/src/entity.c index 446edfa54..aa1b8f52a 100644 --- a/src/entity.c +++ b/src/entity.c @@ -15,12 +15,13 @@ typedef struct Type Type; ENTITY_KIND(LibraryName) \ ENTITY_KIND(Alias) \ ENTITY_KIND(Nil) \ - ENTITY_KIND(Count) + ENTITY_KIND(Label) typedef enum EntityKind { #define ENTITY_KIND(k) GB_JOIN2(Entity_, k), ENTITY_KINDS #undef ENTITY_KIND + Entity_Count, } EntityKind; String const entity_strings[] = { @@ -100,6 +101,10 @@ struct Entity { Entity *original; } Alias; i32 Nil; + struct { + String name; + AstNode *node; + } Label; }; }; @@ -235,6 +240,15 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) { return entity; } +Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type, + AstNode *node) { + Entity *entity = alloc_entity(a, Entity_Label, scope, token, type); + entity->Label.node = node; + return entity; +} + + + Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) { token.string = str_lit("_"); return make_entity_variable(a, scope, token, NULL, false); diff --git a/src/exact_value.c b/src/exact_value.c index 88e3352f3..7e8e69f50 100644 --- a/src/exact_value.c +++ b/src/exact_value.c @@ -82,6 +82,7 @@ ExactValue exact_value_integer_from_string(String string) { case 'b': base = 2; has_prefix = true; break; case 'o': base = 8; has_prefix = true; break; case 'd': base = 10; has_prefix = true; break; + case 'z': base = 12; has_prefix = true; break; case 'x': base = 16; has_prefix = true; break; } } @@ -100,14 +101,10 @@ ExactValue exact_value_integer_from_string(String string) { continue; } i64 v = 0; - if (gb_char_is_digit(r)) { - v = r - '0'; - } else if (gb_char_is_hex_digit(r)) { - v = gb_hex_digit_to_int(r); - } else { + v = digit_value(r); + if (v >= base) { break; } - result *= base; result += v; } @@ -137,10 +134,10 @@ ExactValue exact_value_float_from_string(String string) { if (r == '_') { continue; } - if (!gb_char_is_digit(r)) { + i64 v = digit_value(r); + if (v >= 10) { break; } - i64 v = r - '0'; value *= 10.0; value += v; } @@ -153,29 +150,38 @@ ExactValue exact_value_float_from_string(String string) { if (r == '_') { continue; } - if (!gb_char_is_digit(r)) { + i64 v = digit_value(r); + if (v >= 10) { break; } - value += (r-'0')/pow10; + value += v/pow10; pow10 *= 10.0; } } - f64 frac = 0; + bool frac = false; f64 scale = 1.0; if ((str[i] == 'e') || (str[i] == 'E')) { i++; if (str[i] == '-') { - frac = 1; + frac = true; i++; } else if (str[i] == '+') { i++; } - u32 exp; - for (exp = 0; gb_char_is_digit(str[i]); i++) { - exp = exp * 10 + (str[i]-'0'); + u32 exp = 0; + for (; i < len; i++) { + Rune r = cast(Rune)str[i]; + if (r == '_') { + continue; + } + u32 d = cast(u32)digit_value(r); + if (d >= 10) { + break; + } + exp = exp * 10 + d; } if (exp > 308) exp = 308; diff --git a/src/gb/gb.h b/src/gb/gb.h index 4b048e60d..aa85e5c1d 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -811,6 +811,10 @@ GB_DEF void const *gb_memrchr (void const *data, u8 byte_value, isize size); #define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count)) #endif +#ifndef gb_memmove_array +#define gb_memmove_array(dst, src, count) gb_memmove((dst), (src), gb_size_of(*(dst))*(count)) +#endif + // NOTE(bill): Very similar to doing `*cast(T *)(&u)` #ifndef GB_BIT_CAST #define GB_BIT_CAST(dest, source) do { \ diff --git a/src/ir.c b/src/ir.c index 9d44d0188..d0e650ad4 100644 --- a/src/ir.c +++ b/src/ir.c @@ -91,7 +91,7 @@ typedef enum irDeferKind { typedef struct irDefer { irDeferKind kind; - isize scope_index; + isize scope_index; irBlock * block; union { AstNode *stmt; @@ -100,32 +100,41 @@ typedef struct irDefer { }; } irDefer; + +typedef struct irBranchBlocks { + AstNode *label; + irBlock *break_; + irBlock *continue_; +} irBranchBlocks; + + struct irProcedure { - irProcedure * parent; - Array(irProcedure *) children; + irProcedure * parent; + Array(irProcedure *) children; - Entity * entity; - irModule * module; - String name; - Type * type; - AstNode * type_expr; - AstNode * body; - u64 tags; + Entity * entity; + irModule * module; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; - irValueArray params; - Array(irDefer) defer_stmts; - Array(irBlock *) blocks; - i32 scope_index; - irBlock * decl_block; - irBlock * entry_block; - irBlock * curr_block; - irTargetList * target_list; - irValueArray referrers; + irValueArray params; + Array(irDefer) defer_stmts; + Array(irBlock *) blocks; + i32 scope_index; + irBlock * decl_block; + irBlock * entry_block; + irBlock * curr_block; + irTargetList * target_list; + irValueArray referrers; + Array(irBranchBlocks) branch_blocks; - i32 local_count; - i32 instr_count; - i32 block_count; + i32 local_count; + i32 instr_count; + i32 block_count; }; #define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" @@ -1328,10 +1337,10 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) { #if 1 // NOTE(bill): Sanity check - Type *a = core_type(type_deref(ir_type(address))); - Type *b = core_type(ir_type(value)); + Type *a = type_deref(ir_type(address)); + Type *b = ir_type(value); if (!is_type_untyped(b)) { - GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b)); + GB_ASSERT_MSG(are_types_identical(core_type(a), core_type(b)), "%s %s", type_to_string(a), type_to_string(b)); } #endif return ir_emit(p, ir_instr_store(p, address, value)); @@ -1801,8 +1810,8 @@ irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index) { irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) { Type *t = ir_type(u); - GB_ASSERT(is_type_pointer(t) && - is_type_union(type_deref(t))); + GB_ASSERT_MSG(is_type_pointer(t) && + is_type_union(type_deref(t)), "%s", type_to_string(t)); irValue *tag_ptr = ir_emit(proc, ir_instr_union_tag_ptr(proc, u)); Type *tpt = ir_type(tag_ptr); GB_ASSERT(is_type_pointer(tpt)); @@ -1948,8 +1957,9 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { } -irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selection sel) { +irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { GB_ASSERT(sel.index.count > 0); + Type *type = type_deref(ir_type(e)); for_array(i, sel.index) { i32 index = cast(i32)sel.index.e[i]; @@ -1958,7 +1968,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec e = ir_emit_load(proc, e); e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? } - type = base_type(type); + type = core_type(type); if (is_type_raw_union(type)) { @@ -2005,7 +2015,7 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec case 2: e = ir_emit_struct_ep(proc, e, 3); break; // allocator } } else { - GB_PANIC("un-gep-able type"); + GB_PANIC("un-gep-able type %s", type_to_string(type)); } } @@ -2013,8 +2023,9 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *e, Selec } -irValue *ir_emit_deep_field_ev(irProcedure *proc, Type *type, irValue *e, Selection sel) { +irValue *ir_emit_deep_field_ev(irProcedure *proc, irValue *e, Selection sel) { GB_ASSERT(sel.index.count > 0); + Type *type = ir_type(e); for_array(i, sel.index) { i32 index = cast(i32)sel.index.e[i]; @@ -2359,7 +2370,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { if (src_is_ptr) { value = ir_emit_load(proc, value); } - return ir_emit_deep_field_ev(proc, sb, value, sel); + return ir_emit_deep_field_ev(proc, value, sel); } } } @@ -3368,8 +3379,13 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { case BuiltinProc_clear: { ir_emit_comment(proc, str_lit("clear")); - irValue *ptr = ir_build_addr(proc, ce->args.e[0]).addr; - Type *t = base_type(type_deref(ir_type(ptr))); + Type *original_type = type_of_expr(proc->module->info, ce->args.e[0]); + irAddr addr = ir_build_addr(proc, ce->args.e[0]); + irValue *ptr = addr.addr; + if (is_type_pointer(original_type)) { + ptr = ir_addr_load(proc, addr); + } + Type *t = base_type(type_deref(original_type)); if (is_type_dynamic_array(t)) { irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1); ir_emit_store(proc, count_ptr, v_zero); @@ -3378,6 +3394,9 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { irValue *ea = ir_emit_struct_ep(proc, ptr, 1); ir_emit_store(proc, ir_emit_struct_ep(proc, ha, 1), v_zero); ir_emit_store(proc, ir_emit_struct_ep(proc, ea, 1), v_zero); + } else if (is_type_slice(t)) { + irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1); + ir_emit_store(proc, count_ptr, v_zero); } else { GB_PANIC("TODO(bill): ir clear for `%s`", type_to_string(t)); } @@ -3627,10 +3646,15 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { irValue *ptr = ir_build_expr(proc, ce->args.e[0]); irValue *count = ir_build_expr(proc, ce->args.e[1]); count = ir_emit_conv(proc, count, t_int); + irValue *capacity = count; + if (ce->args.count > 2) { + capacity = ir_build_expr(proc, ce->args.e[2]); + capacity = ir_emit_conv(proc, capacity, t_int); + } Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr))); irValue *slice = ir_add_local_generated(proc, slice_type); - ir_fill_slice(proc, slice, ptr, count, count); + ir_fill_slice(proc, slice, ptr, count, capacity); return ir_emit_load(proc, slice); } break; @@ -3809,7 +3833,8 @@ irValue *ir_get_using_variable(irProcedure *proc, Entity *e) { v = ir_build_addr(proc, e->using_expr).addr; } GB_ASSERT(v != NULL); - return ir_emit_deep_field_gep(proc, parent->type, v, sel); + GB_ASSERT(parent->type == type_deref(ir_type(v))); + return ir_emit_deep_field_gep(proc, v, sel); } // irValue *ir_add_using_variable(irProcedure *proc, Entity *e) { @@ -3925,7 +3950,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { GB_ASSERT(sel.entity != NULL); irValue *a = ir_build_addr(proc, se->expr).addr; - a = ir_emit_deep_field_gep(proc, type, a, sel); + a = ir_emit_deep_field_gep(proc, a, sel); return ir_addr(a); } else { Type *type = base_type(type_of_expr(proc->module->info, se->expr)); @@ -3937,7 +3962,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { GB_ASSERT(sel.entity != NULL); irValue *a = ir_build_addr(proc, se->expr).addr; - a = ir_emit_deep_field_gep(proc, type, a, sel); + a = ir_emit_deep_field_gep(proc, a, sel); return ir_addr(a); } case_end; @@ -4032,7 +4057,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { if (using_field != NULL) { Selection sel = lookup_field(a, t, using_field->token.string, false); irValue *e = ir_build_addr(proc, ie->expr).addr; - using_addr = ir_emit_deep_field_gep(proc, t, e, sel); + using_addr = ir_emit_deep_field_gep(proc, e, sel); t = using_field->type; } @@ -4817,6 +4842,44 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type if (done_) *done_ = done; } +void ir_set_label_blocks(irProcedure *proc, AstNode *label, irBlock *break_, irBlock *continue_) { + if (label == NULL) { + return; + } + GB_ASSERT(label->kind == AstNode_Label); + + + for_array(i, proc->branch_blocks) { + irBranchBlocks *b = &proc->branch_blocks.e[i]; + GB_ASSERT(b->label != NULL && label != NULL); + GB_ASSERT(b->label->kind == AstNode_Label); + if (b->label == label) { + b->break_ = break_; + b->continue_ = continue_; + return; + } + } + + GB_PANIC("ir_set_label_blocks: Unreachable"); +} + +irBranchBlocks ir_lookup_branch_blocks(irProcedure *proc, AstNode *ident) { + GB_ASSERT(ident->kind == AstNode_Ident); + Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(ident)); + GB_ASSERT(found != NULL); + Entity *e = *found; + GB_ASSERT(e->kind == Entity_Label); + for_array(i, proc->branch_blocks) { + irBranchBlocks *b = &proc->branch_blocks.e[i]; + if (b->label == e->Label.node) { + return *b; + } + } + + GB_PANIC("Unreachable"); + return (irBranchBlocks){0}; +} + void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { switch (node->kind) { @@ -4824,9 +4887,11 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { case_end; case_ast_node(us, UsingStmt, node); - AstNode *decl = unparen_expr(us->node); - if (decl->kind == AstNode_ValueDecl) { - ir_build_stmt(proc, decl); + for_array(i, us->list) { + AstNode *decl = unparen_expr(us->list.e[i]); + if (decl->kind == AstNode_ValueDecl) { + ir_build_stmt(proc, decl); + } } case_end; @@ -4944,7 +5009,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { irValue *value = ir_value_procedure(proc->module->allocator, - proc->module, e, e->type, pd->type, pd->body, name); + proc->module, e, e->type, pd->type, pd->body, name); value->Proc.tags = pd->tags; value->Proc.parent = proc; @@ -5172,6 +5237,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { case_ast_node(fs, ForStmt, node); ir_emit_comment(proc, str_lit("ForStmt")); + if (fs->init != NULL) { irBlock *init = ir_new_block(proc, node, "for.init"); ir_emit_jump(proc, init); @@ -5188,6 +5254,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { if (fs->post != NULL) { post = ir_new_block(proc, node, "for.post"); } + + ir_emit_jump(proc, loop); ir_start_block(proc, loop); @@ -5196,6 +5264,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_start_block(proc, body); } + ir_set_label_blocks(proc, fs->label, done, post); ir_push_target_list(proc, done, post, NULL); ir_open_scope(proc); @@ -5330,6 +5399,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_addr_store(proc, idx_addr, index); } + ir_set_label_blocks(proc, rs->label, done, loop); ir_push_target_list(proc, done, loop, NULL); ir_open_scope(proc); @@ -5442,16 +5512,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { AstNode *rhs = as->rhs.e[0]; irValue *parent = ir_build_expr(proc, rhs); - bool is_union_ptr = false; - bool is_any = false; - GB_ASSERT(check_valid_type_match_type(ir_type(parent), &is_union_ptr, &is_any)); + bool is_parent_ptr = is_type_pointer(ir_type(parent)); + MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent)); + GB_ASSERT(match_type_kind != MatchType_Invalid); irValue *tag_index = NULL; irValue *union_data = NULL; - if (is_union_ptr) { + if (match_type_kind == MatchType_Union) { + if (!is_parent_ptr) { + parent = ir_address_from_load_or_generate_local(proc, parent); + } ir_emit_comment(proc, str_lit("get union's tag")); tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent)); union_data = ir_emit_conv(proc, parent, t_rawptr); + } else if (match_type_kind == MatchType_Any) { + if (!is_parent_ptr) { + parent = ir_address_from_load_or_generate_local(proc, parent); + } } irBlock *start_block = ir_new_block(proc, node, "type-match.case.first"); @@ -5478,7 +5555,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { Type *tag_var_type = NULL; if (str_eq(tag_var_name, str_lit("_"))) { Type *t = type_of_expr(proc->module->info, cc->list.e[0]); - if (is_union_ptr) { + if (match_type_kind == MatchType_Union) { t = make_type_pointer(proc->module->allocator, t); } tag_var_type = t; @@ -5505,7 +5582,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { } else { tag_var = ir_add_local_generated(proc, tag_var_type); } - ir_emit_store(proc, tag_var, parent); + + if (!is_parent_ptr) { + ir_emit_store(proc, tag_var, ir_emit_load(proc, parent)); + } else { + ir_emit_store(proc, tag_var, parent); + } continue; } GB_ASSERT(cc->list.count == 1); @@ -5513,7 +5595,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { irBlock *body = ir_new_block(proc, clause, "type-match.case.body"); - if (is_union_ptr) { + if (match_type_kind == MatchType_Union) { Type *bt = type_deref(tag_var_type); irValue *index = NULL; Type *ut = base_type(type_deref(ir_type(parent))); @@ -5534,20 +5616,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { tag_var = ir_add_local_generated(proc, tag_var_type); } - - irValue *data_ptr = ir_emit_conv(proc, union_data, tag_var_type); + Type *bt_ptr = make_type_pointer(proc->module->allocator, bt); + irValue *data_ptr = ir_emit_conv(proc, union_data, bt_ptr); + if (!is_type_pointer(type_deref(ir_type(tag_var)))) { + data_ptr = ir_emit_load(proc, data_ptr); + } ir_emit_store(proc, tag_var, data_ptr); cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index); - } else if (is_any) { + } else if (match_type_kind == MatchType_Any) { Type *type = tag_var_type; - irValue *any_data = ir_emit_struct_ev(proc, parent, 1); + irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 1)); irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); if (tag_var_entity != NULL) { ir_module_add_value(proc->module, tag_var_entity, data); } - irValue *any_ti = ir_emit_struct_ev(proc, parent, 0); + irValue *any_ti = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 0)); irValue *case_ti = ir_type_info(proc, type); cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); } else { @@ -5588,22 +5673,34 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { case_ast_node(bs, BranchStmt, node); irBlock *block = NULL; - switch (bs->token.kind) { - case Token_break: - for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->break_; + + if (bs->label != NULL) { + irBranchBlocks bb = ir_lookup_branch_blocks(proc, bs->label); + switch (bs->token.kind) { + case Token_break: block = bb.break_; break; + case Token_continue: block = bb.continue_; break; + case Token_fallthrough: + GB_PANIC("fallthrough cannot have a label"); + break; } - break; - case Token_continue: - for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->continue_; + } else { + switch (bs->token.kind) { + case Token_break: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->break_; + } + break; + case Token_continue: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->continue_; + } + break; + case Token_fallthrough: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->fallthrough_; + } + break; } - break; - case Token_fallthrough: - for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->fallthrough_; - } - break; } if (block != NULL) { ir_emit_defer_stmts(proc, irDeferExit_Branch, block); @@ -5692,9 +5789,20 @@ void ir_number_proc_registers(irProcedure *proc) { void ir_begin_procedure_body(irProcedure *proc) { array_add(&proc->module->procs, proc); - array_init(&proc->blocks, heap_allocator()); - array_init(&proc->defer_stmts, heap_allocator()); - array_init(&proc->children, heap_allocator()); + array_init(&proc->blocks, heap_allocator()); + array_init(&proc->defer_stmts, heap_allocator()); + array_init(&proc->children, heap_allocator()); + array_init(&proc->branch_blocks, heap_allocator()); + + DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity)); + if (found != NULL) { + DeclInfo *decl = *found; + for_array(i, decl->labels) { + BlockLabel bl = decl->labels.e[i]; + irBranchBlocks bb = {bl.label, NULL, NULL}; + array_add(&proc->branch_blocks, bb); + } + } proc->decl_block = ir_new_block(proc, proc->type_expr, "decls"); ir_start_block(proc, proc->decl_block); diff --git a/src/parser.c b/src/parser.c index d80e2380d..83948e504 100644 --- a/src/parser.c +++ b/src/parser.c @@ -219,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ }) \ AST_NODE_KIND(ForStmt, "for statement", struct { \ Token token; \ + AstNode *label; \ AstNode *init; \ AstNode *cond; \ AstNode *post; \ @@ -226,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ }) \ AST_NODE_KIND(RangeStmt, "range statement", struct { \ Token token; \ + AstNode *label; \ AstNode *value; \ AstNode *index; \ Token in_token; \ @@ -249,10 +251,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ AstNode *body; \ }) \ AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ - AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ + AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \ AST_NODE_KIND(UsingStmt, "using statement", struct { \ Token token; \ - AstNode *node; \ + AstNodeArray list; \ }) \ AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ Token string; \ @@ -305,6 +307,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ AstNode *cond; \ bool is_system; \ }) \ + AST_NODE_KIND(Label, "label", struct { \ + Token token; \ + AstNode *name; \ + }) \ AST_NODE_KIND(_DeclEnd, "", i32) \ AST_NODE_KIND(Field, "field", struct { \ AstNodeArray names; \ @@ -499,6 +505,7 @@ Token ast_node_token(AstNode *node) { case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]); case AstNode_ImportDecl: return node->ImportDecl.token; case AstNode_ForeignLibrary: return node->ForeignLibrary.token; + case AstNode_Label: return node->Label.token; case AstNode_Field: @@ -869,9 +876,10 @@ AstNode *ast_return_stmt(AstFile *f, Token token, AstNodeArray results) { } -AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { +AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *label, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { AstNode *result = make_ast_node(f, AstNode_ForStmt); result->ForStmt.token = token; + result->ForStmt.label = label; result->ForStmt.init = init; result->ForStmt.cond = cond; result->ForStmt.post = post; @@ -879,8 +887,9 @@ AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast return result; } -AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) { +AstNode *ast_range_stmt(AstFile *f, Token token, AstNode *label, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) { AstNode *result = make_ast_node(f, AstNode_RangeStmt); + result->RangeStmt.label = label; result->RangeStmt.token = token; result->RangeStmt.value = value; result->RangeStmt.index = index; @@ -924,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) { return result; } -AstNode *ast_branch_stmt(AstFile *f, Token token) { +AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) { AstNode *result = make_ast_node(f, AstNode_BranchStmt); result->BranchStmt.token = token; + result->BranchStmt.label = label; return result; } -AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) { +AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) { AstNode *result = make_ast_node(f, AstNode_UsingStmt); result->UsingStmt.token = token; - result->UsingStmt.node = node; + result->UsingStmt.list = list; return result; } + AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) { AstNode *result = make_ast_node(f, AstNode_AsmOperand); result->AsmOperand.string = string; @@ -1138,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr return result; } +AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) { + AstNode *result = make_ast_node(f, AstNode_Label); + result->Label.token = token; + result->Label.name = name; + return result; +} + bool next_token(AstFile *f) { Token prev = f->curr_token; @@ -2335,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) { return parse_body(f); } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow); +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow); AstNode *parse_results(AstFile *f) { @@ -2354,7 +2372,7 @@ AstNode *parse_results(AstFile *f) { AstNode *list = NULL; expect_token(f, Token_OpenParen); - list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen); + list = parse_field_list(f, NULL, 0, Token_CloseParen); expect_token_after(f, Token_CloseParen, "parameter list"); return list; } @@ -2365,7 +2383,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign Token proc_token = expect_token(f, Token_proc); expect_token(f, Token_OpenParen); - params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen); + params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen); expect_token_after(f, Token_CloseParen, "parameter list"); results = parse_results(f); @@ -2384,17 +2402,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign return ast_proc_type(f, proc_token, params, results, tags, cc); } -bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) { - if (separator == Token_Semicolon) { - expect_semicolon(f, param); - } else { - if (!allow_token(f, separator)) { - return true; - } - } - return false; -} - AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) { if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) { Token tok = f->curr_token; @@ -2505,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i return idents; } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) { + +bool parse_expect_field_separator(AstFile *f, AstNode *param) { + Token token = f->curr_token; + if (allow_token(f, Token_Comma)) { + return true; + } + if (token.kind == Token_Semicolon) { + next_token(f); + error(f->curr_token, "Expected a comma, got a semicolon"); + return true; + } + return false; +} + +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) { + TokenKind separator = Token_Comma; Token start_token = f->curr_token; AstNodeArray params = make_ast_node_array(f); @@ -2543,7 +2565,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *param = ast_field(f, names, type, set_flags); array_add(¶ms, param); - parse_expect_separator(f, separator, type); + parse_expect_field_separator(f, type); while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF) { @@ -2561,7 +2583,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *param = ast_field(f, names, type, set_flags); array_add(¶ms, param); - if (parse_expect_separator(f, separator, param)) { + if (!parse_expect_field_separator(f, param)) { break; } } @@ -2590,7 +2612,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) { - return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace); + return parse_field_list(f, field_count_, flags, Token_CloseBrace); } AstNode *parse_type_or_ident(AstFile *f) { @@ -2987,7 +3009,7 @@ AstNode *parse_return_stmt(AstFile *f) { // return ast_expr_stmt(f, ge); // } -AstNode *parse_for_stmt(AstFile *f) { +AstNode *parse_for_stmt(AstFile *f, AstNode *label) { if (f->curr_proc == NULL) { syntax_error(f->curr_token, "You cannot use a for statement in the file scope"); return ast_bad_stmt(f, f->curr_token, f->curr_token); @@ -3051,11 +3073,11 @@ AstNode *parse_for_stmt(AstFile *f) { if (cond->AssignStmt.rhs.count > 0) { rhs = cond->AssignStmt.rhs.e[0]; } - return ast_range_stmt(f, token, value, index, in_token, rhs, body); + return ast_range_stmt(f, token, label, value, index, in_token, rhs, body); } cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); - return ast_for_stmt(f, token, init, cond, post, body); + return ast_for_stmt(f, token, label, init, cond, post, body); #if 0 Token token = expect_token(f, Token_for); @@ -3262,7 +3284,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_if: return parse_if_stmt(f); case Token_when: return parse_when_stmt(f); - case Token_for: return parse_for_stmt(f); + case Token_for: return parse_for_stmt(f, NULL); case Token_match: return parse_match_stmt(f); case Token_defer: return parse_defer_stmt(f); case Token_asm: return parse_asm_stmt(f); @@ -3271,43 +3293,47 @@ AstNode *parse_stmt(AstFile *f) { case Token_break: case Token_continue: - case Token_fallthrough: + case Token_fallthrough: { + AstNode *label = NULL; next_token(f); - s = ast_branch_stmt(f, token); + if (token.kind != Token_fallthrough && + f->curr_token.kind == Token_Ident) { + label = parse_ident(f); + } + s = ast_branch_stmt(f, token, label); expect_semicolon(f, s); return s; + } case Token_using: { // TODO(bill): Make using statements better Token token = expect_token(f, Token_using); - AstNode *node = parse_stmt(f); + AstNodeArray list = parse_lhs_expr_list(f); + if (list.count == 0) { + syntax_error(token, "Illegal use of `using` statement"); + expect_semicolon(f, NULL); + return ast_bad_stmt(f, token, f->curr_token); + } - switch (node->kind) { - case AstNode_ValueDecl: - if (!node->ValueDecl.is_var) { + if (f->curr_token.kind != Token_Colon) { + expect_semicolon(f, list.e[list.count-1]); + return ast_using_stmt(f, token, list); + } + + AstNode *decl = parse_simple_stmt(f, false); + expect_semicolon(f, decl); + + if (decl->kind == AstNode_ValueDecl) { + if (!decl->ValueDecl.is_var) { syntax_error(token, "`using` may not be applied to constant declarations"); + return decl; + } + if (f->curr_proc == NULL) { + syntax_error(token, "`using` is not allowed at the file scope"); } else { - if (f->curr_proc == NULL) { - syntax_error(token, "`using` is not allowed at the file scope"); - } else { - node->ValueDecl.flags |= VarDeclFlag_using; - } + decl->ValueDecl.flags |= VarDeclFlag_using; } - return node; - case AstNode_ExprStmt: { - AstNode *e = unparen_expr(node->ExprStmt.expr); - while (e->kind == AstNode_SelectorExpr) { - e = unparen_expr(e->SelectorExpr.selector); - } - if (e->kind == AstNode_Ident) { - return ast_using_stmt(f, token, node); - } else if (e->kind == AstNode_Implicit) { - syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string)); - return ast_bad_stmt(f, token, f->curr_token); - } - } break; - - + return decl; } syntax_error(token, "Illegal use of `using` statement"); @@ -3518,6 +3544,20 @@ AstNode *parse_stmt(AstFile *f) { syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); } return s; + } else if (str_eq(tag, str_lit("label"))) { + AstNode *name = parse_ident(f); + AstNode *label = ast_label_decl(f, token, name); + + Token tok = f->curr_token; + switch (tok.kind) { + case Token_for: + return parse_for_stmt(f, label); + default: + syntax_error(token, "#label may only be applied to a loop"); + fix_advance_to_next_stmt(f); + s = ast_bad_stmt(f, token, f->curr_token); + return s; + } } diff --git a/src/ssa.c b/src/ssa.c index 67c16a1e4..2ed9d59f9 100644 --- a/src/ssa.c +++ b/src/ssa.c @@ -1,4 +1,3 @@ -typedef enum ssaOp ssaOp; typedef struct ssaModule ssaModule; typedef struct ssaValue ssaValue; typedef struct ssaValueArgs ssaValueArgs; @@ -19,282 +18,7 @@ String ssa_mangle_name(ssaModule *m, String path, Entity *e); typedef Array(ssaValue *) ssaValueArray; - -#define SSA_OPS \ - SSA_OP(Invalid)\ -\ - SSA_OP(Unknown)\ -\ - SSA_OP(Comment) /* Does nothing */\ -\ - SSA_OP(SP) /* Stack Pointer */\ - SSA_OP(SB) /* Stack Base */\ - SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\ -\ - SSA_OP(Local)\ - SSA_OP(Global)\ - SSA_OP(Proc)\ -\ - SSA_OP(Load)\ - SSA_OP(Store)\ - SSA_OP(Move)\ - SSA_OP(Zero) /* Zero initialize */\ -\ - SSA_OP(ArrayIndex) /* Index for a fixed array */\ - SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\ - SSA_OP(OffsetPtr)\ - SSA_OP(ValueIndex) /* Extract for a value from a register */\ -\ - SSA_OP(Phi)\ - SSA_OP(Copy)\ -\ - /* TODO(bill): calling conventions */\ - SSA_OP(CallOdin)\ - SSA_OP(CallC)\ - SSA_OP(CallStd)\ - SSA_OP(CallFast)\ -\ - SSA_OP(BoundsCheck)\ - SSA_OP(SliceBoundsCheck)\ -\ - /* Built in operations/procedures */\ - SSA_OP(Bswap16)\ - SSA_OP(Bswap32)\ - SSA_OP(Bswap64)\ -\ - SSA_OP(Assume)\ - SSA_OP(DebugTrap)\ - SSA_OP(Trap)\ - SSA_OP(ReadCycleCounter)\ -\ -\ - SSA_OP(ConstBool)\ - SSA_OP(ConstString)\ - SSA_OP(ConstSlice)\ - SSA_OP(ConstNil)\ - SSA_OP(Const8)\ - SSA_OP(Const16)\ - SSA_OP(Const32)\ - SSA_OP(Const64)\ - SSA_OP(Const32F)\ - SSA_OP(Const64F)\ -\ - /* These should be all the operations I could possibly need for the mean time */\ - SSA_OP(Add8)\ - SSA_OP(Add16)\ - SSA_OP(Add32)\ - SSA_OP(Add64)\ - SSA_OP(AddPtr)\ - SSA_OP(Add32F)\ - SSA_OP(Add64F)\ - SSA_OP(Sub8)\ - SSA_OP(Sub16)\ - SSA_OP(Sub32)\ - SSA_OP(Sub64)\ - SSA_OP(SubPtr)\ - SSA_OP(Sub32F)\ - SSA_OP(Sub64F)\ - SSA_OP(Mul8)\ - SSA_OP(Mul16)\ - SSA_OP(Mul32)\ - SSA_OP(Mul64)\ - SSA_OP(Mul32F)\ - SSA_OP(Mul64F)\ - SSA_OP(Div8)\ - SSA_OP(Div8U)\ - SSA_OP(Div16)\ - SSA_OP(Div16U)\ - SSA_OP(Div32)\ - SSA_OP(Div32U)\ - SSA_OP(Div64)\ - SSA_OP(Div64U)\ - SSA_OP(Div32F)\ - SSA_OP(Div64F)\ - SSA_OP(Mod8)\ - SSA_OP(Mod8U)\ - SSA_OP(Mod16)\ - SSA_OP(Mod16U)\ - SSA_OP(Mod32)\ - SSA_OP(Mod32U)\ - SSA_OP(Mod64)\ - SSA_OP(Mod64U)\ -\ - SSA_OP(And8)\ - SSA_OP(And16)\ - SSA_OP(And32)\ - SSA_OP(And64)\ - SSA_OP(Or8)\ - SSA_OP(Or16)\ - SSA_OP(Or32)\ - SSA_OP(Or64)\ - SSA_OP(Xor8)\ - SSA_OP(Xor16)\ - SSA_OP(Xor32)\ - SSA_OP(Xor64)\ - SSA_OP(AndNot8)\ - SSA_OP(AndNot16)\ - SSA_OP(AndNot32)\ - SSA_OP(AndNot64)\ -\ - SSA_OP(Lsh8x8)\ - SSA_OP(Lsh8x16)\ - SSA_OP(Lsh8x32)\ - SSA_OP(Lsh8x64)\ - SSA_OP(Lsh16x8)\ - SSA_OP(Lsh16x16)\ - SSA_OP(Lsh16x32)\ - SSA_OP(Lsh16x64)\ - SSA_OP(Lsh32x8)\ - SSA_OP(Lsh32x16)\ - SSA_OP(Lsh32x32)\ - SSA_OP(Lsh32x64)\ - SSA_OP(Lsh64x8)\ - SSA_OP(Lsh64x16)\ - SSA_OP(Lsh64x32)\ - SSA_OP(Lsh64x64)\ - SSA_OP(Rsh8x8)\ - SSA_OP(Rsh8x16)\ - SSA_OP(Rsh8x32)\ - SSA_OP(Rsh8x64)\ - SSA_OP(Rsh16x8)\ - SSA_OP(Rsh16x16)\ - SSA_OP(Rsh16x32)\ - SSA_OP(Rsh16x64)\ - SSA_OP(Rsh32x8)\ - SSA_OP(Rsh32x16)\ - SSA_OP(Rsh32x32)\ - SSA_OP(Rsh32x64)\ - SSA_OP(Rsh64x8)\ - SSA_OP(Rsh64x16)\ - SSA_OP(Rsh64x32)\ - SSA_OP(Rsh64x64)\ - SSA_OP(Rsh8Ux8)\ - SSA_OP(Rsh8Ux16)\ - SSA_OP(Rsh8Ux32)\ - SSA_OP(Rsh8Ux64)\ - SSA_OP(Rsh16Ux8)\ - SSA_OP(Rsh16Ux16)\ - SSA_OP(Rsh16Ux32)\ - SSA_OP(Rsh16Ux64)\ - SSA_OP(Rsh32Ux8)\ - SSA_OP(Rsh32Ux16)\ - SSA_OP(Rsh32Ux32)\ - SSA_OP(Rsh32Ux64)\ - SSA_OP(Rsh64Ux8)\ - SSA_OP(Rsh64Ux16)\ - SSA_OP(Rsh64Ux32)\ - SSA_OP(Rsh64Ux64)\ -\ - SSA_OP(Eq8)\ - SSA_OP(Eq16)\ - SSA_OP(Eq32)\ - SSA_OP(Eq64)\ - SSA_OP(EqPtr)\ - SSA_OP(Eq32F)\ - SSA_OP(Eq64F)\ - SSA_OP(Ne8)\ - SSA_OP(Ne16)\ - SSA_OP(Ne32)\ - SSA_OP(Ne64)\ - SSA_OP(NePtr)\ - SSA_OP(Ne32F)\ - SSA_OP(Ne64F)\ - SSA_OP(Lt8)\ - SSA_OP(Lt16)\ - SSA_OP(Lt32)\ - SSA_OP(Lt64)\ - SSA_OP(LtPtr)\ - SSA_OP(Lt32F)\ - SSA_OP(Lt64F)\ - SSA_OP(Gt8)\ - SSA_OP(Gt16)\ - SSA_OP(Gt32)\ - SSA_OP(Gt64)\ - SSA_OP(GtPtr)\ - SSA_OP(Gt32F)\ - SSA_OP(Gt64F)\ - SSA_OP(Le8)\ - SSA_OP(Le16)\ - SSA_OP(Le32)\ - SSA_OP(Le64)\ - SSA_OP(LePtr)\ - SSA_OP(Le32F)\ - SSA_OP(Le64F)\ - SSA_OP(Ge8)\ - SSA_OP(Ge16)\ - SSA_OP(Ge32)\ - SSA_OP(Ge64)\ - SSA_OP(GePtr)\ - SSA_OP(Ge32F)\ - SSA_OP(Ge64F)\ -\ - SSA_OP(NotB)\ - SSA_OP(EqB)\ - SSA_OP(NeB)\ -\ - SSA_OP(Neg8)\ - SSA_OP(Neg16)\ - SSA_OP(Neg32)\ - SSA_OP(Neg64)\ - SSA_OP(Neg32F)\ - SSA_OP(Neg64F)\ -\ - SSA_OP(Not8)\ - SSA_OP(Not16)\ - SSA_OP(Not32)\ - SSA_OP(Not64)\ -\ - SSA_OP(SignExt8to16)\ - SSA_OP(SignExt8to32)\ - SSA_OP(SignExt8to64)\ - SSA_OP(SignExt16to32)\ - SSA_OP(SignExt16to64)\ - SSA_OP(SignExt32to64)\ - SSA_OP(ZeroExt8to16)\ - SSA_OP(ZeroExt8to32)\ - SSA_OP(ZeroExt8to64)\ - SSA_OP(ZeroExt16to32)\ - SSA_OP(ZeroExt16to64)\ - SSA_OP(ZeroExt32to64)\ - SSA_OP(Trunc16to8)\ - SSA_OP(Trunc32to8)\ - SSA_OP(Trunc32to16)\ - SSA_OP(Trunc64to8)\ - SSA_OP(Trunc64to16)\ - SSA_OP(Trunc64to32)\ -\ - SSA_OP(Cvt32to32F)\ - SSA_OP(Cvt32to64F)\ - SSA_OP(Cvt64to32F)\ - SSA_OP(Cvt64to64F)\ - SSA_OP(Cvt32Fto32)\ - SSA_OP(Cvt32Fto64)\ - SSA_OP(Cvt64Fto32)\ - SSA_OP(Cvt64Fto64)\ - SSA_OP(Cvt32Fto64F)\ - SSA_OP(Cvt64Fto32F)\ - SSA_OP(Cvt32Uto32F)\ - SSA_OP(Cvt32Uto64F)\ - SSA_OP(Cvt32Fto32U)\ - SSA_OP(Cvt64Fto32U)\ - SSA_OP(Cvt64Uto32F)\ - SSA_OP(Cvt64Uto64F)\ - SSA_OP(Cvt32Fto64U)\ - SSA_OP(Cvt64Fto64U)\ - - -enum ssaOp { -#define SSA_OP(k) GB_JOIN2(ssaOp_, k), - SSA_OPS -#undef SSA_OP -}; - -String const ssa_op_strings[] = { -#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1}, - SSA_OPS -#undef SSA_OP -}; - +#include "ssa_op.c" #define SSA_DEFAULT_VALUE_ARG_CAPACITY 8 struct ssaValueArgs { @@ -379,6 +103,7 @@ struct ssaTargetList { struct ssaProc { ssaModule * module; // Parent module + gbAllocator allocator; // Same allocator as the parent module String name; // Mangled name Entity * entity; DeclInfo * decl_info; @@ -424,7 +149,7 @@ struct ssaModule { void ssa_push_target_list(ssaProc *p, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { - ssaTargetList *tl = gb_alloc_item(p->module->allocator, ssaTargetList); + ssaTargetList *tl = gb_alloc_item(p->allocator, ssaTargetList); tl->prev = p->target_list; tl->break_ = break_; tl->continue_ = continue_; @@ -438,7 +163,7 @@ void ssa_pop_target_list(ssaProc *p) { ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind, char *name) { - ssaBlock *b = gb_alloc_item(p->module->allocator, ssaBlock); + ssaBlock *b = gb_alloc_item(p->allocator, ssaBlock); b->id = p->block_id++; b->kind = kind; b->proc = p; @@ -531,64 +256,68 @@ void ssa_add_arg(ssaValueArgs *va, ssaValue *arg) { ssaValue *ssa_new_value(ssaProc *p, ssaOp op, Type *t, ssaBlock *b) { - ssaValue *v = gb_alloc_item(p->module->allocator, ssaValue); + GB_ASSERT(b != NULL); + ssaValue *v = gb_alloc_item(p->allocator, ssaValue); v->id = p->value_id++; v->op = op; v->type = t; v->block = b; - ssa_init_value_args(&v->args, p->module->allocator); + ssa_init_value_args(&v->args, p->allocator); array_add(&b->values, v); return v; } -ssaValue *ssa_new_value0(ssaBlock *b, ssaOp op, Type *t) { - ssaValue *v = ssa_new_value(b->proc, op, t, b); +ssaValue *ssa_new_value0(ssaProc *p, ssaOp op, Type *t) { + ssaValue *v = ssa_new_value(p, op, t, p->curr_block); return v; } -ssaValue *ssa_new_value0v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value) { - ssaValue *v = ssa_new_value0(b, op, t); +ssaValue *ssa_new_value0v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) { + ssaValue *v = ssa_new_value0(p, op, t); v->exact_value = exact_value; return v; } -ssaValue *ssa_new_value1(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg) { - ssaValue *v = ssa_new_value(b->proc, op, t, b); +ssaValue *ssa_new_value1(ssaProc *p, ssaOp op, Type *t, ssaValue *arg) { + ssaValue *v = ssa_new_value(p, op, t, p->curr_block); ssa_add_arg(&v->args, arg); return v; } -ssaValue *ssa_new_value1v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) { - ssaValue *v = ssa_new_value1(b, op, t, arg); +ssaValue *ssa_new_value1v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) { + ssaValue *v = ssa_new_value1(p, op, t, arg); v->exact_value = exact_value; return v; } +ssaValue *ssa_new_value1i(ssaProc *p, ssaOp op, Type *t, i64 i, ssaValue *arg) { + return ssa_new_value1v(p, op, t, exact_value_integer(i), arg); +} -ssaValue *ssa_new_value2(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) { - ssaValue *v = ssa_new_value(b->proc, op, t, b); +ssaValue *ssa_new_value2(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) { + ssaValue *v = ssa_new_value(p, op, t, p->curr_block); ssa_add_arg(&v->args, arg0); ssa_add_arg(&v->args, arg1); return v; } -ssaValue *ssa_new_value2v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) { - ssaValue *v = ssa_new_value2(b, op, t, arg0, arg1); +ssaValue *ssa_new_value2v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) { + ssaValue *v = ssa_new_value2(p, op, t, arg0, arg1); v->exact_value = exact_value; return v; } -ssaValue *ssa_new_value3(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { - ssaValue *v = ssa_new_value(b->proc, op, t, b); +ssaValue *ssa_new_value3(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { + ssaValue *v = ssa_new_value(p, op, t, p->curr_block); ssa_add_arg(&v->args, arg0); ssa_add_arg(&v->args, arg1); ssa_add_arg(&v->args, arg2); return v; } -ssaValue *ssa_new_value3v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { - ssaValue *v = ssa_new_value3(b, op, t, arg0, arg1, arg2); +ssaValue *ssa_new_value3v(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { + ssaValue *v = ssa_new_value3(p, op, t, arg0, arg1, arg2); v->exact_value = exact_value; return v; } -ssaValue *ssa_new_value4(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) { - ssaValue *v = ssa_new_value(b->proc, op, t, b); +ssaValue *ssa_new_value4(ssaProc *p, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) { + ssaValue *v = ssa_new_value(p, op, t, p->curr_block); ssa_add_arg(&v->args, arg0); ssa_add_arg(&v->args, arg1); ssa_add_arg(&v->args, arg2); @@ -597,7 +326,7 @@ ssaValue *ssa_new_value4(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValu } ssaValue *ssa_const_val(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) { - return ssa_new_value0v(p->curr_block, op, t, exact_value); + return ssa_new_value0v(p, op, t, exact_value); } ssaValue *ssa_const_bool (ssaProc *p, Type *t, bool c) { return ssa_const_val(p, ssaOp_ConstBool, t, exact_value_bool(c)); } @@ -613,7 +342,7 @@ ssaValue *ssa_const_slice (ssaProc *p, Type *t, ExactValue v) { return ssa ssaValue *ssa_const_nil (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstNil, t, (ExactValue){0}); } ssaValue *ssa_const_int(ssaProc *p, Type *t, i64 c) { - switch (8*type_size_of(p->module->allocator, t)) { + switch (8*type_size_of(p->allocator, t)) { case 8: return ssa_const_i8 (p, t, cast(i8)c); case 16: return ssa_const_i16(p, t, cast(i16)c); case 32: return ssa_const_i32(p, t, cast(i32)c); @@ -630,6 +359,47 @@ void ssa_reset_value_args(ssaValue *v) { v->args.count = 0; } +void ssa_reset(ssaValue *v, ssaOp op) { + v->op = op; + v->exact_value = (ExactValue){0}; + ssa_reset_value_args(v); +} + +ssaValue *ssa_emit_load(ssaProc *p, ssaValue *v) { + GB_ASSERT(is_type_pointer(v->type)); + return ssa_new_value1(p, ssaOp_Load, type_deref(v->type), v); +} + +ssaValue *ssa_emit_store(ssaProc *p, ssaValue *dst, ssaValue *v) { + GB_ASSERT(is_type_pointer(dst->type)); +#if 1 + // NOTE(bill): Sanity check + Type *a = core_type(type_deref(dst->type)); + Type *b = core_type(v->type); + if (!is_type_untyped(b)) { + GB_ASSERT_MSG(are_types_identical(a, b), "%s %s", type_to_string(a), type_to_string(b)); + } +#endif + return ssa_new_value2(p, ssaOp_Store, dst->type, dst, v); +} + +bool ssa_is_op_const(ssaOp op) { + switch (op) { + case ssaOp_ConstBool: + case ssaOp_ConstString: + case ssaOp_ConstSlice: + case ssaOp_ConstNil: + case ssaOp_Const8: + case ssaOp_Const16: + case ssaOp_Const32: + case ssaOp_Const64: + case ssaOp_Const32F: + case ssaOp_Const64F: + return true; + } + return false; +} + bool ssa_is_blank_ident(AstNode *node) { @@ -680,6 +450,7 @@ Type *ssa_addr_type(ssaAddr addr) { ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_info) { ssaProc *p = gb_alloc_item(m->allocator, ssaProc); p->module = m; + p->allocator = m->allocator; p->name = name; p->entity = entity; p->decl_info = decl_info; @@ -691,14 +462,19 @@ ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_ } ssaAddr ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) { - Type *t = make_type_pointer(p->module->allocator, e->type); - ssaValue *local = ssa_new_value0(p->entry, ssaOp_Local, t); + Type *t = make_type_pointer(p->allocator, e->type); + + ssaBlock *cb = p->curr_block; + p->curr_block = p->entry; + ssaValue *local = ssa_new_value0(p, ssaOp_Local, t); + p->curr_block = cb; + map_ssa_value_set(&p->values, hash_pointer(e), local); map_ssa_value_set(&p->module->values, hash_pointer(e), local); local->comment_string = e->token.string; - ssaValue *addr = ssa_new_value1(p->curr_block, ssaOp_Addr, local->type, local); - ssa_new_value1(p->curr_block, ssaOp_Zero, t, addr); + ssaValue *addr = ssa_new_value1(p, ssaOp_Addr, local->type, local); + ssa_new_value1(p, ssaOp_Zero, t, addr); return ssa_addr(addr); } ssaAddr ssa_add_local_for_ident(ssaProc *p, AstNode *name) { @@ -718,12 +494,12 @@ ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) { if (p->curr_block) { // scope = p->curr_block->scope; } - Entity *e = make_entity_variable(p->module->allocator, scope, empty_token, t, false); + Entity *e = make_entity_variable(p->allocator, scope, empty_token, t, false); return ssa_add_local(p, e, NULL); } void ssa_emit_comment(ssaProc *p, String s) { - // ssa_new_value0v(p->curr_block, ssaOp_Comment, NULL, exact_value_string(s)); + // ssa_new_value0v(p, ssaOp_Comment, NULL, exact_value_string(s)); } #define SSA_MAX_STRUCT_FIELD_COUNT 4 @@ -773,10 +549,11 @@ bool can_ssa_type(Type *t) { return true; } - - -void ssa_build_stmt(ssaProc *p, AstNode *node); -void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes); +ssaAddr ssa_build_addr (ssaProc *p, AstNode *expr); +ssaValue *ssa_build_expr (ssaProc *p, AstNode *expr); +void ssa_build_stmt (ssaProc *p, AstNode *node); +void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes); +ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel); void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) { if (addr.addr == NULL) { @@ -787,7 +564,7 @@ void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) { return; } - ssa_new_value2(p->curr_block, ssaOp_Store, addr.addr->type, addr.addr, value); + ssa_emit_store(p, addr.addr, value); } ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) { @@ -806,26 +583,25 @@ ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) { return addr.addr; } - return ssa_new_value1(p->curr_block, ssaOp_Load, type_deref(t), addr.addr); + return ssa_emit_load(p, addr.addr); } ssaValue *ssa_get_using_variable(ssaProc *p, Entity *e) { - GB_PANIC("TODO(bill): ssa_get_using_variable"); - return NULL; - // GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); - // String name = e->token.string; - // Entity *parent = e->using_parent; - // Selection sel = lookup_field(proc->module->allocator, parent->type, name, false); - // GB_ASSERT(sel.entity != NULL); - // irValue **pv = map_ir_value_get(&proc->module->values, hash_pointer(parent)); - // irValue *v = NULL; - // if (pv != NULL) { - // v = *pv; - // } else { - // v = ir_build_addr(proc, e->using_expr).addr; - // } - // GB_ASSERT(v != NULL); - // return ir_emit_deep_field_gep(proc, parent->type, v, sel); + GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); + String name = e->token.string; + Entity *parent = e->using_parent; + Selection sel = lookup_field(p->allocator, parent->type, name, false); + GB_ASSERT(sel.entity != NULL); + ssaValue **pv = map_ssa_value_get(&p->module->values, hash_pointer(parent)); + ssaValue *v = NULL; + if (pv != NULL) { + v = *pv; + } else { + v = ssa_build_addr(p, e->using_expr).addr; + } + GB_ASSERT(v != NULL); + GB_ASSERT(type_deref(v->type) == parent->type); + return ssa_emit_deep_field_ptr_index(p, v, sel); } ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) { @@ -847,6 +623,311 @@ ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) { return ssa_addr(v); } + +ssaValue *ssa_emit_conv(ssaProc *p, ssaValue *v, Type *t) { + Type *src_type = v->type; + if (are_types_identical(t, src_type)) { + return v; + } + + Type *src = core_type(src_type); + Type *dst = core_type(t); + + if (is_type_untyped_nil(src)) { + return ssa_const_nil(p, t); + } + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ssa_new_value1(p, ssaOp_Copy, dst, v); + } + // proc <-> proc + if (is_type_proc(src) && is_type_proc(dst)) { + return ssa_new_value1(p, ssaOp_Copy, dst, v); + } + // pointer -> proc + if (is_type_pointer(src) && is_type_proc(dst)) { + return ssa_new_value1(p, ssaOp_Copy, dst, v); + } + // proc -> pointer + if (is_type_proc(src) && is_type_pointer(dst)) { + return ssa_new_value1(p, ssaOp_Copy, dst, v); + } + + + gb_printf_err("ssa_emit_conv: src -> dst\n"); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); + + + GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + + +// NOTE(bill): Returns NULL if not possible +ssaValue *ssa_address_from_load_or_generate_local(ssaProc *p, ssaValue *v) { + if (v->op == ssaOp_Load) { + return v->args.e[0]; + } + ssaAddr addr = ssa_add_local_generated(p, v->type); + ssa_new_value2(p, ssaOp_Store, addr.addr->type, addr.addr, v); + return addr.addr; +} + + +ssaValue *ssa_emit_array_index(ssaProc *p, ssaValue *v, ssaValue *index) { + GB_ASSERT(v != NULL); + GB_ASSERT(is_type_pointer(v->type)); + Type *t = base_type(type_deref(v->type)); + GB_ASSERT_MSG(is_type_array(t) || is_type_vector(t), "%s", type_to_string(t)); + Type *elem_ptr = NULL; + if (is_type_array(t)) { + elem_ptr = make_type_pointer(p->allocator, t->Array.elem); + } else if (is_type_vector(t)) { + elem_ptr = make_type_pointer(p->allocator, t->Vector.elem); + } + + return ssa_new_value2(p, ssaOp_ArrayIndex, elem_ptr, v, index); +} + +ssaValue *ssa_emit_ptr_index(ssaProc *p, ssaValue *s, i64 index) { + gbAllocator a = p->allocator; + Type *t = base_type(type_deref(s->type)); + Type *result_type = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + } else if (is_type_union(t)) { + type_set_offsets(a, t); + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + i64 offset = t->Record.offsets[index]; + ssaValue *ptr = ssa_emit_conv(p, s, t_u8_ptr); + ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset)); + return ssa_emit_conv(p, ptr, result_type); + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = make_type_pointer(a, t->Tuple.variables[index]->type); + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; + case 1: result_type = make_type_pointer(a, t_int); break; + case 2: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_u8_ptr); break; + case 1: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_type_info_ptr); break; + case 1: result_type = make_type_pointer(a, t_rawptr); break; + } + } else if (is_type_dynamic_array(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->DynamicArray.elem)); break; + case 1: result_type = t_int_ptr; break; + case 2: result_type = t_int_ptr; break; + case 3: result_type = t_allocator_ptr; break; + } + } else if (is_type_dynamic_map(t)) { + Type *gst = t->Map.generated_struct_type; + switch (index) { + case 0: result_type = make_type_pointer(a, gst->Record.fields[0]->type); break; + case 1: result_type = make_type_pointer(a, gst->Record.fields[1]->type); break; + } + }else { + GB_PANIC("TODO(bill): ssa_emit_ptr_index type: %s, %d", type_to_string(s->type), index); + } + + GB_ASSERT(result_type != NULL); + + return ssa_new_value1i(p, ssaOp_PtrIndex, result_type, index, s); +} +ssaValue *ssa_emit_value_index(ssaProc *p, ssaValue *s, i64 index) { + if (s->op == ssaOp_Load) { + if (!can_ssa_type(s->type)) { + ssaValue *e = ssa_emit_ptr_index(p, s->args.e[0], index); + return ssa_emit_load(p, e); + } + } + GB_ASSERT(can_ssa_type(s->type)); + + gbAllocator a = p->allocator; + Type *t = base_type(s->type); + Type *result_type = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = t->Record.fields[index]->type; + } else if (is_type_union(t)) { + type_set_offsets(a, t); + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + Type *ptr_type = make_type_pointer(a, t->Record.fields[index]->type); + i64 offset = t->Record.offsets[index]; + ssaValue *ptr = ssa_address_from_load_or_generate_local(p, s); + ptr = ssa_emit_conv(p, s, t_u8_ptr); + ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset)); + ptr = ssa_emit_conv(p, ptr, ptr_type); + return ssa_emit_load(p, ptr); + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = t->Tuple.variables[index]->type; + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Slice.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = t_u8_ptr; break; + case 1: result_type = t_int; break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = t_type_info_ptr; break; + case 1: result_type = t_rawptr; break; + } + } else if (is_type_dynamic_array(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + case 3: result_type = t_allocator; break; + } + } else if (is_type_dynamic_map(t)) { + Type *gst = t->Map.generated_struct_type; + switch (index) { + case 0: result_type = gst->Record.fields[0]->type; break; + case 1: result_type = gst->Record.fields[1]->type; break; + } + } else { + GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s->type), index); + } + + GB_ASSERT(result_type != NULL); + + return ssa_new_value1i(p, ssaOp_ValueIndex, result_type, index, s); +} + + +ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + Type *type = type_deref(e->type); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(p, e); + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + type = type->Record.fields[index]->type; + e = ssa_emit_conv(p, e, make_type_pointer(p->allocator, type)); + } else if (type->kind == Type_Record) { + type = type->Record.fields[index]->type; + e = ssa_emit_ptr_index(p, e, index); + } else if (type->kind == Type_Tuple) { + type = type->Tuple.variables[index]->type; + e = ssa_emit_ptr_index(p, e, index); + }else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_ptr_index(p, e, index); + } break; + + case Basic_string: + e = ssa_emit_ptr_index(p, e, index); + break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else if (type->kind == Type_Slice) { + e = ssa_emit_ptr_index(p, e, index); + } else if (type->kind == Type_DynamicArray) { + e = ssa_emit_ptr_index(p, e, index); + } else if (type->kind == Type_Vector) { + e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index)); + } else if (type->kind == Type_Array) { + e = ssa_emit_array_index(p, e, ssa_const_int(p, t_int, index)); + } else if (type->kind == Type_Map) { + e = ssa_emit_ptr_index(p, e, 1); + switch (index) { + case 0: e = ssa_emit_ptr_index(p, e, 1); break; // count + case 1: e = ssa_emit_ptr_index(p, e, 2); break; // capacity + case 2: e = ssa_emit_ptr_index(p, e, 3); break; // allocator + } + } else { + GB_PANIC("un-gep-able type"); + } + } + + return e; +} + +ssaValue *ssa_emit_deep_field_value_index(ssaProc *p, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + Type *type = e->type; + if (e->op == ssaOp_Load) { + if (!can_ssa_type(e->type)) { + ssaValue *ptr = ssa_emit_deep_field_ptr_index(p, e->args.e[0], sel); + return ssa_emit_load(p, ptr); + } + } + GB_ASSERT(can_ssa_type(e->type)); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + e = ssa_emit_load(p, e); + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?"); + type = type->Record.fields[index]->type; + e = ssa_emit_conv(p, e, type); + } else if (type->kind == Type_Map) { + e = ssa_emit_value_index(p, e, 1); + switch (index) { + case 0: e = ssa_emit_value_index(p, e, 1); break; // count + case 1: e = ssa_emit_value_index(p, e, 2); break; // capacity + case 2: e = ssa_emit_value_index(p, e, 3); break; // allocator + } + } else { + e = ssa_emit_value_index(p, e, index); + } + } + + return e; +} + + + + + ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) { switch (expr->kind) { case_ast_node(i, Ident, expr); @@ -861,6 +942,71 @@ ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) { case_ast_node(pe, ParenExpr, expr); return ssa_build_addr(p, unparen_expr(expr)); case_end; + + case_ast_node(se, SelectorExpr, expr); + ssa_emit_comment(p, str_lit("SelectorExpr")); + AstNode *sel = unparen_expr(se->selector); + if (sel->kind == AstNode_Ident) { + String selector = sel->Ident.string; + TypeAndValue *tav = type_and_value_of_expression(p->module->info, se->expr); + + if (tav == NULL) { + // NOTE(bill): Imports + Entity *imp = entity_of_ident(p->module->info, se->expr); + if (imp != NULL) { + GB_ASSERT(imp->kind == Entity_ImportName); + } + return ssa_build_addr(p, se->selector); + } + + + Type *type = base_type(tav->type); + if (tav->mode == Addressing_Type) { // Addressing_Type + GB_PANIC("TODO: SelectorExpr Addressing_Type"); + // Selection sel = lookup_field(p->allocator, type, selector, true); + // Entity *e = sel.entity; + // GB_ASSERT(e->kind == Entity_Variable); + // GB_ASSERT(e->flags & EntityFlag_TypeField); + // String name = e->token.string; + // if (str_eq(name, str_lit("names"))) { + // ssaValue *ti_ptr = ir_type_info(p, type); + + // ssaValue *names_ptr = NULL; + + // if (is_type_enum(type)) { + // ssaValue *enum_info = ssa_emit_conv(p, ti_ptr, t_type_info_enum_ptr); + // names_ptr = ssa_emit_ptr_index(p, enum_info, 1); + // } else if (type->kind == Type_Record) { + // ssaValue *record_info = ssa_emit_conv(p, ti_ptr, t_type_info_record_ptr); + // names_ptr = ssa_emit_ptr_index(p, record_info, 1); + // } + // return ssa_addr(names_ptr); + // } else { + // GB_PANIC("Unhandled TypeField %.*s", LIT(name)); + // } + GB_PANIC("Unreachable"); + } + + Selection sel = lookup_field(p->allocator, type, selector, false); + GB_ASSERT(sel.entity != NULL); + + ssaValue *a = ssa_build_addr(p, se->expr).addr; + a = ssa_emit_deep_field_ptr_index(p, a, sel); + return ssa_addr(a); + } else { + Type *type = base_type(type_of_expr(p->module->info, se->expr)); + GB_ASSERT(is_type_integer(type)); + ExactValue val = type_and_value_of_expression(p->module->info, sel)->value; + i64 index = val.value_integer; + + Selection sel = lookup_field_from_index(p->allocator, type, index); + GB_ASSERT(sel.entity != NULL); + + ssaValue *a = ssa_build_addr(p, se->expr).addr; + a = ssa_emit_deep_field_ptr_index(p, a, sel); + return ssa_addr(a); + } + case_end; } GB_PANIC("Cannot get entity's address"); @@ -1088,6 +1234,131 @@ ssaOp ssa_determine_op(TokenKind op, Type *t) { return ssaOp_Invalid; } +ssaValue *ssa_emit_comp(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y) { + GB_ASSERT(x != NULL && y != NULL); + Type *a = core_type(x->type); + Type *b = core_type(y->type); + if (are_types_identical(a, b)) { + // NOTE(bill): No need for a conversion + } else if (ssa_is_op_const(x->op)) { + x = ssa_emit_conv(p, x, y->type); + } else if (ssa_is_op_const(y->op)) { + y = ssa_emit_conv(p, y, x->type); + } + + Type *result = t_bool; + if (is_type_vector(a)) { + result = make_type_vector(p->allocator, t_bool, a->Vector.count); + } + + if (is_type_vector(a)) { + ssa_emit_comment(p, str_lit("vector.comp.begin")); + Type *tl = base_type(a); + ssaValue *lhs = ssa_address_from_load_or_generate_local(p, x); + ssaValue *rhs = ssa_address_from_load_or_generate_local(p, y); + + GB_ASSERT(is_type_vector(result)); + Type *elem_type = base_type(result)->Vector.elem; + + ssaAddr addr = ssa_add_local_generated(p, result); + for (i32 i = 0; i < tl->Vector.count; i++) { + ssaValue *index = ssa_const_int(p, t_int, i); + ssaValue *x = ssa_emit_load(p, ssa_emit_array_index(p, lhs, index)); + ssaValue *y = ssa_emit_load(p, ssa_emit_array_index(p, rhs, index)); + ssaValue *z = ssa_emit_comp(p, op, x, y); + ssa_emit_store(p, ssa_emit_array_index(p, addr.addr, index), z); + } + + ssa_emit_comment(p, str_lit("vector.comp.end")); + return ssa_addr_load(p, addr); + } + + return ssa_new_value2(p, ssa_determine_op(op, x->type), x->type, x, y); +} + + + +ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + return ssa_build_cond(p, pe->expr, yes, no); + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + return ssa_build_cond(p, ue->expr, no, yes); + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and"); + ssa_build_cond(p, be->left, block, no); + ssa_start_block(p, block); + return ssa_build_cond(p, be->right, yes, no); + } else if (be->op.kind == Token_CmpOr) { + ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or"); + ssa_build_cond(p, be->left, yes, block); + ssa_start_block(p, block); + return ssa_build_cond(p, be->right, yes, no); + } + case_end; + } + + ssaValue *c = ssa_build_expr(p, cond); + ssaBlock *b = ssa_end_block(p); + b->kind = ssaBlock_If; + ssa_set_control(b, c); + ssa_add_edge_to(b, yes); + ssa_add_edge_to(b, no); + return c; +} + +ssaValue *ssa_emit_logical_binary_expr(ssaProc *p, AstNode *expr) { + ast_node(be, BinaryExpr, expr); + + ssaBlock *rhs = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.rhs"); + ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "logical.cmp.done"); + + GB_ASSERT(p->curr_block != NULL); + + Type *type = default_type(type_of_expr(p->module->info, expr)); + + bool short_circuit_value = false; + if (be->op.kind == Token_CmpAnd) { + ssa_build_cond(p, be->left, rhs, done); + short_circuit_value = false; + } else if (be->op.kind == Token_CmpOr) { + ssa_build_cond(p, be->left, done, rhs); + short_circuit_value = true; + } + if (rhs->preds.count == 0) { + ssa_start_block(p, done); + return ssa_const_bool(p, type, short_circuit_value); + } + + if (done->preds.count == 0) { + ssa_start_block(p, rhs); + return ssa_build_expr(p, be->right); + } + + ssa_start_block(p, rhs); + ssaValue *short_circuit = ssa_const_bool(p, type, short_circuit_value); + ssaValueArgs edges = {0}; + ssa_init_value_args(&edges, p->allocator); + for_array(i, done->preds) { + ssa_add_arg(&edges, short_circuit); + } + + ssa_add_arg(&edges, ssa_build_expr(p, be->right)); + ssa_emit_jump(p, done); + ssa_start_block(p, done); + + ssaValue *phi = ssa_new_value0(p, ssaOp_Phi, type); + phi->args = edges; + return phi; +} + ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { expr = unparen_expr(expr); @@ -1106,7 +1377,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { } else if (is_type_integer(t)) { GB_ASSERT(tv->value.kind == ExactValue_Integer); - i64 s = 8*type_size_of(p->module->allocator, t); + i64 s = 8*type_size_of(p->allocator, t); switch (s) { case 8: return ssa_const_i8 (p, tv->type, tv->value.value_integer); case 16: return ssa_const_i16(p, tv->type, tv->value.value_integer); @@ -1116,7 +1387,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { } } else if (is_type_float(t)) { GB_ASSERT(tv->value.kind == ExactValue_Float); - i64 s = 8*type_size_of(p->module->allocator, t); + i64 s = 8*type_size_of(p->allocator, t); switch (s) { case 32: return ssa_const_f32(p, tv->type, tv->value.value_float); case 64: return ssa_const_f64(p, tv->type, tv->value.value_float); @@ -1170,41 +1441,40 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { case_ast_node(ue, UnaryExpr, expr); switch (ue->op.kind) { case Token_Pointer: { - ssaValue *ptr = ssa_build_addr(p, ue->expr).addr; - return ssa_new_value1(p->curr_block, ssaOp_Copy, tv->type, ptr); + return ssa_build_addr(p, ue->expr).addr; } break; case Token_Add: return ssa_build_expr(p, ue->expr); case Token_Not: // Boolean not - return ssa_new_value1(p->curr_block, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr)); + return ssa_new_value1(p, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr)); case Token_Xor: { // Bitwise not ssaValue *x = ssa_build_expr(p, ue->expr); - isize bits = 8*type_size_of(p->module->allocator, x->type); + isize bits = 8*type_size_of(p->allocator, x->type); switch (bits) { - case 8: return ssa_new_value1(p->curr_block, ssaOp_Not8, tv->type, x); - case 16: return ssa_new_value1(p->curr_block, ssaOp_Not16, tv->type, x); - case 32: return ssa_new_value1(p->curr_block, ssaOp_Not32, tv->type, x); - case 64: return ssa_new_value1(p->curr_block, ssaOp_Not64, tv->type, x); + case 8: return ssa_new_value1(p, ssaOp_Not8, tv->type, x); + case 16: return ssa_new_value1(p, ssaOp_Not16, tv->type, x); + case 32: return ssa_new_value1(p, ssaOp_Not32, tv->type, x); + case 64: return ssa_new_value1(p, ssaOp_Not64, tv->type, x); } GB_PANIC("unknown integer size"); } break; case Token_Sub: { // 0-x ssaValue *x = ssa_build_expr(p, ue->expr); - isize bits = 8*type_size_of(p->module->allocator, x->type); + isize bits = 8*type_size_of(p->allocator, x->type); if (is_type_integer(x->type)) { switch (bits) { - case 8: return ssa_new_value1(p->curr_block, ssaOp_Neg8, tv->type, x); - case 16: return ssa_new_value1(p->curr_block, ssaOp_Neg16, tv->type, x); - case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32, tv->type, x); - case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64, tv->type, x); + case 8: return ssa_new_value1(p, ssaOp_Neg8, tv->type, x); + case 16: return ssa_new_value1(p, ssaOp_Neg16, tv->type, x); + case 32: return ssa_new_value1(p, ssaOp_Neg32, tv->type, x); + case 64: return ssa_new_value1(p, ssaOp_Neg64, tv->type, x); } } else if (is_type_float(x->type)) { switch (bits) { - case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32F, tv->type, x); - case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64F, tv->type, x); + case 32: return ssa_new_value1(p, ssaOp_Neg32F, tv->type, x); + case 64: return ssa_new_value1(p, ssaOp_Neg64F, tv->type, x); } } GB_PANIC("unknown type for -x"); @@ -1228,7 +1498,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { ssaValue *x = ssa_build_expr(p, be->left); ssaValue *y = ssa_build_expr(p, be->right); GB_ASSERT(x != NULL && y != NULL); - return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y); + return ssa_new_value2(p, ssa_determine_op(be->op.kind, x->type), tv->type, x, y); } case Token_Shl: @@ -1245,15 +1515,12 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { case Token_GtEq: { ssaValue *x = ssa_build_expr(p, be->left); ssaValue *y = ssa_build_expr(p, be->right); - GB_ASSERT(x != NULL && y != NULL); - return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y); + return ssa_emit_comp(p, be->op.kind, x, y); } break; case Token_CmpAnd: case Token_CmpOr: - GB_PANIC("TODO: inline && and ||"); - return NULL; - // return ir_emit_logical_binary_expr(proc, expr); + return ssa_emit_logical_binary_expr(p, expr); default: GB_PANIC("Invalid binary expression"); @@ -1275,48 +1542,6 @@ void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes) { } -ssaValue *ssa_emit_struct_ep(ssaProc *p, ssaValue *ptr, i32 index) { - GB_ASSERT(ptr->type != NULL); - GB_ASSERT(is_type_pointer(ptr->type)); - return NULL; -} - - -ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) { - switch (cond->kind) { - case_ast_node(pe, ParenExpr, cond); - return ssa_build_cond(p, pe->expr, yes, no); - case_end; - - case_ast_node(ue, UnaryExpr, cond); - if (ue->op.kind == Token_Not) { - return ssa_build_cond(p, ue->expr, no, yes); - } - case_end; - - case_ast_node(be, BinaryExpr, cond); - if (be->op.kind == Token_CmpAnd) { - ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and"); - ssa_build_cond(p, be->left, block, no); - ssa_start_block(p, block); - return ssa_build_cond(p, be->right, yes, no); - } else if (be->op.kind == Token_CmpOr) { - ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or"); - ssa_build_cond(p, be->left, yes, block); - ssa_start_block(p, block); - return ssa_build_cond(p, be->right, yes, no); - } - case_end; - } - - ssaValue *c = ssa_build_expr(p, cond); - ssaBlock *b = ssa_end_block(p); - b->kind = ssaBlock_If; - ssa_set_control(b, c); - ssa_add_edge_to(b, yes); - ssa_add_edge_to(b, no); - return c; -} void ssa_build_when_stmt(ssaProc *p, AstNodeWhenStmt *ws) { ssaValue *cond = ssa_build_expr(p, ws->cond); @@ -1370,9 +1595,11 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) { case_end; case_ast_node(us, UsingStmt, node); - AstNode *decl = unparen_expr(us->node); - if (decl->kind == AstNode_ValueDecl) { - ssa_build_stmt(p, decl); + for_array(i, us->list) { + AstNode *decl = unparen_expr(us->list.e[i]); + if (decl->kind == AstNode_ValueDecl) { + ssa_build_stmt(p, decl); + } } case_end; @@ -1426,7 +1653,7 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) { if (init->op == ssaOp_Addr && t->kind == Type_Tuple) { for (isize i = 0; i < t->Tuple.variable_count; i++) { Entity *e = t->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ep(p, init, i); + ssaValue *v = ssa_emit_ptr_index(p, init, i); array_add(&inits, v); } } else { @@ -1492,7 +1719,7 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) { if (init->op == ssaOp_Addr && t->kind == Type_Tuple) { for (isize i = 0; i < t->Tuple.variable_count; i++) { Entity *e = t->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ep(p, init, i); + ssaValue *v = ssa_emit_ptr_index(p, init, i); array_add(&inits, v); } } else { @@ -1839,6 +2066,9 @@ void ssa_print_proc(gbFile *f, ssaProc *p) { } +void ssa_opt_proc(ssaProc *p) { +} + void ssa_build_proc(ssaModule *m, ssaProc *p) { p->module = m; m->proc = p; @@ -1860,6 +2090,8 @@ void ssa_build_proc(ssaModule *m, ssaProc *p) { p->exit = ssa_new_block(p, ssaBlock_Exit, "exit"); ssa_emit_jump(p, p->exit); + ssa_opt_proc(p); + ssa_print_proc(gb_file_get_standard(gbFileStandard_Error), p); } diff --git a/src/ssa_op.c b/src/ssa_op.c new file mode 100644 index 000000000..22bde663d --- /dev/null +++ b/src/ssa_op.c @@ -0,0 +1,277 @@ +#define SSA_OPS \ + SSA_OP(Invalid)\ +\ + SSA_OP(Unknown)\ +\ + SSA_OP(Comment) /* Does nothing */\ +\ + SSA_OP(SP) /* Stack Pointer */\ + SSA_OP(SB) /* Stack Base */\ + SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\ +\ + SSA_OP(Local)\ + SSA_OP(Global)\ + SSA_OP(Proc)\ +\ + SSA_OP(Load)\ + SSA_OP(Store)\ + SSA_OP(Move)\ + SSA_OP(LoadReg)\ + SSA_OP(StoreReg)\ + SSA_OP(Zero) /* Zero initialize */\ +\ + SSA_OP(ArrayIndex) /* Index for a fixed array */\ + SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\ + SSA_OP(PtrOffset)\ + SSA_OP(ValueIndex) /* Extract for a value from a register */\ +\ + SSA_OP(Phi)\ + SSA_OP(Copy)\ +\ + /* TODO(bill): calling conventions */\ + SSA_OP(CallOdin)\ + SSA_OP(CallC)\ + SSA_OP(CallStd)\ + SSA_OP(CallFast)\ +\ + SSA_OP(BoundsCheck)\ + SSA_OP(SliceBoundsCheck)\ +\ + /* Built in operations/procedures */\ + SSA_OP(Bswap16)\ + SSA_OP(Bswap32)\ + SSA_OP(Bswap64)\ +\ + SSA_OP(Assume)\ + SSA_OP(DebugTrap)\ + SSA_OP(Trap)\ + SSA_OP(ReadCycleCounter)\ +\ +\ + SSA_OP(ConstBool)\ + SSA_OP(ConstString)\ + SSA_OP(ConstSlice)\ + SSA_OP(ConstNil)\ + SSA_OP(Const8)\ + SSA_OP(Const16)\ + SSA_OP(Const32)\ + SSA_OP(Const64)\ + SSA_OP(Const32F)\ + SSA_OP(Const64F)\ +\ + /* These should be all the operations I could possibly need for the mean time */\ + SSA_OP(Add8)\ + SSA_OP(Add16)\ + SSA_OP(Add32)\ + SSA_OP(Add64)\ + SSA_OP(AddPtr)\ + SSA_OP(Add32F)\ + SSA_OP(Add64F)\ + SSA_OP(Sub8)\ + SSA_OP(Sub16)\ + SSA_OP(Sub32)\ + SSA_OP(Sub64)\ + SSA_OP(SubPtr)\ + SSA_OP(Sub32F)\ + SSA_OP(Sub64F)\ + SSA_OP(Mul8)\ + SSA_OP(Mul16)\ + SSA_OP(Mul32)\ + SSA_OP(Mul64)\ + SSA_OP(Mul32F)\ + SSA_OP(Mul64F)\ + SSA_OP(Div8)\ + SSA_OP(Div8U)\ + SSA_OP(Div16)\ + SSA_OP(Div16U)\ + SSA_OP(Div32)\ + SSA_OP(Div32U)\ + SSA_OP(Div64)\ + SSA_OP(Div64U)\ + SSA_OP(Div32F)\ + SSA_OP(Div64F)\ + SSA_OP(Mod8)\ + SSA_OP(Mod8U)\ + SSA_OP(Mod16)\ + SSA_OP(Mod16U)\ + SSA_OP(Mod32)\ + SSA_OP(Mod32U)\ + SSA_OP(Mod64)\ + SSA_OP(Mod64U)\ +\ + SSA_OP(And8)\ + SSA_OP(And16)\ + SSA_OP(And32)\ + SSA_OP(And64)\ + SSA_OP(Or8)\ + SSA_OP(Or16)\ + SSA_OP(Or32)\ + SSA_OP(Or64)\ + SSA_OP(Xor8)\ + SSA_OP(Xor16)\ + SSA_OP(Xor32)\ + SSA_OP(Xor64)\ + SSA_OP(AndNot8)\ + SSA_OP(AndNot16)\ + SSA_OP(AndNot32)\ + SSA_OP(AndNot64)\ +\ + SSA_OP(Lsh8x8)\ + SSA_OP(Lsh8x16)\ + SSA_OP(Lsh8x32)\ + SSA_OP(Lsh8x64)\ + SSA_OP(Lsh16x8)\ + SSA_OP(Lsh16x16)\ + SSA_OP(Lsh16x32)\ + SSA_OP(Lsh16x64)\ + SSA_OP(Lsh32x8)\ + SSA_OP(Lsh32x16)\ + SSA_OP(Lsh32x32)\ + SSA_OP(Lsh32x64)\ + SSA_OP(Lsh64x8)\ + SSA_OP(Lsh64x16)\ + SSA_OP(Lsh64x32)\ + SSA_OP(Lsh64x64)\ + SSA_OP(Rsh8x8)\ + SSA_OP(Rsh8x16)\ + SSA_OP(Rsh8x32)\ + SSA_OP(Rsh8x64)\ + SSA_OP(Rsh16x8)\ + SSA_OP(Rsh16x16)\ + SSA_OP(Rsh16x32)\ + SSA_OP(Rsh16x64)\ + SSA_OP(Rsh32x8)\ + SSA_OP(Rsh32x16)\ + SSA_OP(Rsh32x32)\ + SSA_OP(Rsh32x64)\ + SSA_OP(Rsh64x8)\ + SSA_OP(Rsh64x16)\ + SSA_OP(Rsh64x32)\ + SSA_OP(Rsh64x64)\ + SSA_OP(Rsh8Ux8)\ + SSA_OP(Rsh8Ux16)\ + SSA_OP(Rsh8Ux32)\ + SSA_OP(Rsh8Ux64)\ + SSA_OP(Rsh16Ux8)\ + SSA_OP(Rsh16Ux16)\ + SSA_OP(Rsh16Ux32)\ + SSA_OP(Rsh16Ux64)\ + SSA_OP(Rsh32Ux8)\ + SSA_OP(Rsh32Ux16)\ + SSA_OP(Rsh32Ux32)\ + SSA_OP(Rsh32Ux64)\ + SSA_OP(Rsh64Ux8)\ + SSA_OP(Rsh64Ux16)\ + SSA_OP(Rsh64Ux32)\ + SSA_OP(Rsh64Ux64)\ +\ + SSA_OP(Eq8)\ + SSA_OP(Eq16)\ + SSA_OP(Eq32)\ + SSA_OP(Eq64)\ + SSA_OP(EqPtr)\ + SSA_OP(Eq32F)\ + SSA_OP(Eq64F)\ + SSA_OP(Ne8)\ + SSA_OP(Ne16)\ + SSA_OP(Ne32)\ + SSA_OP(Ne64)\ + SSA_OP(NePtr)\ + SSA_OP(Ne32F)\ + SSA_OP(Ne64F)\ + SSA_OP(Lt8)\ + SSA_OP(Lt16)\ + SSA_OP(Lt32)\ + SSA_OP(Lt64)\ + SSA_OP(LtPtr)\ + SSA_OP(Lt32F)\ + SSA_OP(Lt64F)\ + SSA_OP(Gt8)\ + SSA_OP(Gt16)\ + SSA_OP(Gt32)\ + SSA_OP(Gt64)\ + SSA_OP(GtPtr)\ + SSA_OP(Gt32F)\ + SSA_OP(Gt64F)\ + SSA_OP(Le8)\ + SSA_OP(Le16)\ + SSA_OP(Le32)\ + SSA_OP(Le64)\ + SSA_OP(LePtr)\ + SSA_OP(Le32F)\ + SSA_OP(Le64F)\ + SSA_OP(Ge8)\ + SSA_OP(Ge16)\ + SSA_OP(Ge32)\ + SSA_OP(Ge64)\ + SSA_OP(GePtr)\ + SSA_OP(Ge32F)\ + SSA_OP(Ge64F)\ +\ + SSA_OP(NotB)\ + SSA_OP(EqB)\ + SSA_OP(NeB)\ +\ + SSA_OP(Neg8)\ + SSA_OP(Neg16)\ + SSA_OP(Neg32)\ + SSA_OP(Neg64)\ + SSA_OP(Neg32F)\ + SSA_OP(Neg64F)\ +\ + SSA_OP(Not8)\ + SSA_OP(Not16)\ + SSA_OP(Not32)\ + SSA_OP(Not64)\ +\ + SSA_OP(SignExt8to16)\ + SSA_OP(SignExt8to32)\ + SSA_OP(SignExt8to64)\ + SSA_OP(SignExt16to32)\ + SSA_OP(SignExt16to64)\ + SSA_OP(SignExt32to64)\ + SSA_OP(ZeroExt8to16)\ + SSA_OP(ZeroExt8to32)\ + SSA_OP(ZeroExt8to64)\ + SSA_OP(ZeroExt16to32)\ + SSA_OP(ZeroExt16to64)\ + SSA_OP(ZeroExt32to64)\ + SSA_OP(Trunc16to8)\ + SSA_OP(Trunc32to8)\ + SSA_OP(Trunc32to16)\ + SSA_OP(Trunc64to8)\ + SSA_OP(Trunc64to16)\ + SSA_OP(Trunc64to32)\ +\ + SSA_OP(Cvt32to32F)\ + SSA_OP(Cvt32to64F)\ + SSA_OP(Cvt64to32F)\ + SSA_OP(Cvt64to64F)\ + SSA_OP(Cvt32Fto32)\ + SSA_OP(Cvt32Fto64)\ + SSA_OP(Cvt64Fto32)\ + SSA_OP(Cvt64Fto64)\ + SSA_OP(Cvt32Fto64F)\ + SSA_OP(Cvt64Fto32F)\ + SSA_OP(Cvt32Uto32F)\ + SSA_OP(Cvt32Uto64F)\ + SSA_OP(Cvt32Fto32U)\ + SSA_OP(Cvt64Fto32U)\ + SSA_OP(Cvt64Uto32F)\ + SSA_OP(Cvt64Uto64F)\ + SSA_OP(Cvt32Fto64U)\ + SSA_OP(Cvt64Fto64U)\ + + +enum ssaOp { +#define SSA_OP(k) GB_JOIN2(ssaOp_, k), + SSA_OPS +#undef SSA_OP +}; +typedef enum ssaOp ssaOp; + +String const ssa_op_strings[] = { +#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1}, + SSA_OPS +#undef SSA_OP +}; diff --git a/src/tokenizer.c b/src/tokenizer.c index cfc1423f0..6c7f76c02 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -484,15 +484,9 @@ gb_inline i32 digit_value(Rune r) { return 16; // NOTE(bill): Larger than highest possible } -gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) { - if (allow_underscore) { - while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { - advance_to_next_rune(t); - } - } else { - while (digit_value(t->curr_rune) < base) { - advance_to_next_rune(t); - } +gb_inline void scan_mantissa(Tokenizer *t, i32 base) { + while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { + advance_to_next_rune(t); } } @@ -506,7 +500,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { if (seen_decimal_point) { token.kind = Token_Float; - scan_mantissa(t, 10, true); + scan_mantissa(t, 10); goto exponent; } @@ -515,31 +509,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { advance_to_next_rune(t); if (t->curr_rune == 'b') { // Binary advance_to_next_rune(t); - scan_mantissa(t, 2, true); + scan_mantissa(t, 2); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'o') { // Octal advance_to_next_rune(t); - scan_mantissa(t, 8, true); + scan_mantissa(t, 8); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'd') { // Decimal advance_to_next_rune(t); - scan_mantissa(t, 10, true); + scan_mantissa(t, 10); + if (t->curr - prev <= 2) { + token.kind = Token_Invalid; + } + } else if (t->curr_rune == 'z') { // Dozenal + advance_to_next_rune(t); + scan_mantissa(t, 12); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'x') { // Hexadecimal advance_to_next_rune(t); - scan_mantissa(t, 16, true); + scan_mantissa(t, 16); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else { seen_decimal_point = false; - scan_mantissa(t, 10, true); + scan_mantissa(t, 10); if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') { seen_decimal_point = true; @@ -551,7 +551,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { return token; } - scan_mantissa(t, 10, true); + scan_mantissa(t, 10); fraction: if (t->curr_rune == '.') { @@ -564,7 +564,7 @@ fraction: goto end; } token.kind = Token_Float; - scan_mantissa(t, 10, true); + scan_mantissa(t, 10); } exponent: @@ -574,7 +574,7 @@ exponent: if (t->curr_rune == '-' || t->curr_rune == '+') { advance_to_next_rune(t); } - scan_mantissa(t, 10, false); + scan_mantissa(t, 10); } end: