Add branch labels for loops; using list

This commit is contained in:
Ginger Bill
2017-03-19 16:59:11 +00:00
parent 32150e401e
commit 5562364a98
17 changed files with 1637 additions and 846 deletions
+3 -4
View File
@@ -44,10 +44,9 @@ del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.c" ^ cl %compiler_settings% "src\main.c" ^
/link %linker_settings% -OUT:%exe_name% ^ /link %linker_settings% -OUT:%exe_name% ^
&& odin build code/metagen.odin ^ && odin run code/demo.odin
&& call "code\metagen.exe" "src\ast_nodes.metagen" rem && odin build code/metagen.odin ^
rem && odin build code/markdown.odin ^ rem && call "code\metagen.exe" "src\ast_nodes.metagen"
rem && call "code\markdown.exe" "misc\example.md"
rem && odin run code/Jaze/src/main.odin rem && odin run code/Jaze/src/main.odin
del *.obj > NUL 2> NUL del *.obj > NUL 2> NUL
+210 -74
View File
@@ -10,40 +10,18 @@
#import win32 "sys/windows.odin"; #import win32 "sys/windows.odin";
main :: proc() { main :: proc() {
fmt.println("Here");
when false {
/* /*
Version 0.1.1
Added: Added:
* Dynamic Arrays `[dynamic]Type` * Unexported entities and fields using an underscore prefix
* Dynamic Maps `map[Key]Value` - See `sync.odin` and explain
* 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
Removed: Removed:
* Maybe/option types * Maybe/option types
* Remove `type` keyword and other "reserved" keywords * 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: Changed:
* `compile_assert` and `assert`return the value of the condition for semantic reasons
* thread_local -> #thread_local * thread_local -> #thread_local
* #include -> #load * #include -> #load
* Files only get checked if they are actually used * 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: * Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch - major.minor.patch
* Core library additions to Windows specific stuff * 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 { Fruit :: enum {
@@ -75,6 +40,147 @@ when false {
fmt.println(Fruit.names); 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; m: map[f32]int;
reserve(m, 16); reserve(m, 16);
@@ -109,49 +215,79 @@ when false {
c := m["c"]; c := m["c"];
_, ok := m["c"]; _, ok := m["c"];
assert(ok && c == 7654); assert(ok && c == 7654);
fmt.println(m);
delete(m, "c"); // deletes entry with key "c"
_, found := m["c"];
assert(!found);
fmt.println(m); 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; Vector3 :: struct{x, y, z: f32};
reserve(x, 16); Quaternion :: struct{x, y, z, w: f32};
defer free(x);
append(x, 2_000_000.500_000, 3, 5, 7);
for p, i in x { Entity :: union {
if i > 0 { fmt.print(", "); } // Common Fields
fmt.print(p); id: u64,
} name: string,
fmt.println(); using position: Vector3,
} orientation: Quaternion,
flags: u32,
{ // Variants
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; Frog{
defer free(x); ribbit_volume: f32,
fmt.println(x); jump_height: f32,
} },
Door{
openness: f32,
{ },
Vec3 :: [vector 3]f32; Map{
width, height: f32,
x := Vec3{1, 2, 3}; place_positions: []Vector3,
y := Vec3{4, 5, 6}; place_names: []string,
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)); entity: Entity;
compile_assert(size_of([vector 7]i32) == size_of([7]i32)); // implicit conversion from variant to base type
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case 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
} }
} }
}
+3 -3
View File
@@ -295,15 +295,15 @@ bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
sprint :: proc(buf: []byte, args: ..any) -> string { sprint :: proc(buf: []byte, args: ..any) -> string {
count := bprint(^buf, ..args); count := bprint(^buf, ..args);
return cast(string)buf; return cast(string)buf[..count];
} }
sprintln :: proc(buf: []byte, args: ..any) -> string { sprintln :: proc(buf: []byte, args: ..any) -> string {
count := bprintln(^buf, ..args); count := bprintln(^buf, ..args);
return cast(string)buf; return cast(string)buf[..count];
} }
sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
count := bprintf(^buf, fmt, ..args); count := bprintf(^buf, fmt, ..args);
return cast(string)buf; return cast(string)buf[..count];
} }
+1 -3
View File
@@ -30,9 +30,7 @@ append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
append_int :: proc(buf: []byte, i: i64, 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); return append_bits(buf, cast(u64)i, base, true, 8*size_of(int), digits, 0);
} }
itoa :: proc(buf: []byte, i: int) -> string { itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, cast(i64)i, 10); }
return append_int(buf, cast(i64)i, 10);
}
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { 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); return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);
+2 -8
View File
@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
x->count = capacity; x->count = capacity;
} }
{ x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
// TODO(bill): Resize rather than copy and delete x->capacity = capacity;
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;
}
} }
+1 -6
View File
@@ -12,6 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
gbString expr_str = expr_to_string(operand->expr); gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message? // 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, error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s", "Cannot assign builtin procedure `%s` in %.*s",
expr_str, 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"); 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; d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt); GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
+58 -144
View File
@@ -272,6 +272,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
if (operand->mode == Addressing_Builtin) { if (operand->mode == Addressing_Builtin) {
// TODO(bill): is this a good enough error message? // 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, error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s", "Cannot assign builtin procedure `%s` in %.*s",
expr_str, expr_str,
@@ -379,7 +380,8 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls,
Entity **found = map_entity_get(&entity_map, key); Entity **found = map_entity_get(&entity_map, key);
if (found != NULL) { if (found != NULL) {
Entity *e = *found; 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(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
error(e->token, "\tpreviously declared"); error(e->token, "\tpreviously declared");
} else { } else {
@@ -604,16 +606,11 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
{ {
ast_node(fl, FieldList, f->list); 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 // NOTE(bill): Copy the contents for the common fields for now
AstNodeArray list = {0}; AstNodeArray list = {0};
array_init_count(&list, c->allocator, ut->fields.count+fl->list.count); array_init_count(&list, c->allocator, ut->fields.count+fl->list.count);
for (isize j = 0; j < ut->fields.count; j++) { gb_memmove_array(list.e, ut->fields.e, ut->fields.count);
list.e[j] = ut->fields.e[j]; gb_memmove_array(list.e+ut->fields.count, fl->list.e, fl->list.count);
}
for (isize j = 0; j < fl->list.count; j++) {
list.e[j+ut->fields.count] = fl->list.e[j];
}
isize list_count = 0; isize list_count = 0;
for_array(j, list) { 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); HashKey key = hash_string(name_token.string);
if (map_entity_get(&entity_map, key) != NULL) { 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)); error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
} else { } else {
map_entity_set(&entity_map, key, e); 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; break;
case Entity_TypeName: { case Entity_TypeName:
// NOTE(bill): Cyclical dependency checking is handled in the "type system" not here
o->mode = Addressing_Type; o->mode = Addressing_Type;
// TODO(bill): Fix cyclical dependancy checker break;
} break;
case Entity_Procedure: case Entity_Procedure:
o->mode = Addressing_Value; 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)); error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
return e; return e;
case Entity_Label:
o->mode = Addressing_NoValue;
break;
case Entity_Nil: case Entity_Nil:
o->mode = Addressing_Value; o->mode = Addressing_Value;
break; break;
@@ -1691,7 +1692,7 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type
if (s < 64) { if (s < 64) {
umax = (1ull << s) - 1ull; umax = (1ull << s) - 1ull;
} else { } 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; s = 64;
} }
i64 imax = (1ll << (s-1ll)); 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; operand->value = entity->Constant.value;
break; break;
case Entity_Variable: case Entity_Variable:
// TODO(bill): This is the rule I need? // TODO(bill): Is this the rule I need?
if (operand->mode == Addressing_Immutable) { if (operand->mode == Addressing_Immutable) {
// Okay // Okay
} else if (sel.indirect || operand->mode != Addressing_Value) { } 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: { case BuiltinProc_clear: {
Type *type = operand->type; 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); 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); gb_string_free(str);
return false; return false;
} }
@@ -3107,14 +3110,12 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
} }
Type *elem = NULL; Type *elem = NULL;
Type *slice_elem = NULL;
if (is_type_dynamic_array(type)) { if (is_type_dynamic_array(type)) {
// TODO(bill): Semi-memory leaks
elem = type->DynamicArray.elem; elem = type->DynamicArray.elem;
} else { } else {
elem = type->Slice.elem; 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); Type *proc_type_params = make_type_tuple(c->allocator);
proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2); 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; operand->mode = Addressing_Value;
} break; } 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: { case BuiltinProc_slice_ptr: {
// slice_ptr :: proc(a: ^T, len: int) -> []T // slice_ptr :: proc(a: ^T, len: int) -> []T
// slice_ptr :: proc(a: ^T, len, cap: int) -> []T
// ^T cannot be rawptr // ^T cannot be rawptr
Type *ptr_type = base_type(operand->type); Type *ptr_type = base_type(operand->type);
if (!is_type_pointer(ptr_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; 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}; if (size_count == 2 && sizes[0] > sizes[1]) {
check_expr(c, &op, len); error_node(ce->args.e[1], "`slice_ptr` count and capacity are swapped");
if (op.mode == Addressing_Invalid) // No need quit
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;
} }
operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem); operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
operand->mode = Addressing_Value; operand->mode = Addressing_Value;
} break; } break;
@@ -4491,13 +4395,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case_end; case_end;
case_ast_node(te, TernaryExpr, node); case_ast_node(te, TernaryExpr, node);
if (c->proc_stack.count == 0) { Operand cond = {Addressing_Invalid};
error_node(node, "A ternary expression is only allowed within a procedure"); check_expr(c, &cond, te->cond);
goto error; if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
}
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, te->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error_node(te->cond, "Non-boolean condition in if expression"); 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->type = x.type;
o->mode = Addressing_Value; 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_end;
case_ast_node(cl, CompoundLit, node); case_ast_node(cl, CompoundLit, node);
+99 -37
View File
@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
return rhs->type; return rhs->type;
} }
bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { typedef enum MatchTypeKind {
if (is_type_pointer(type)) { MatchType_Invalid,
*is_union_ptr = is_type_union(type_deref(type)); MatchType_Union,
return *is_union_ptr; 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)) { if (is_type_any(type)) {
*is_any = true; return MatchType_Any;
return *is_any;
} }
return false; return MatchType_Invalid;
} }
void check_stmt_internal(Checker *c, AstNode *node, u32 flags); 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) { void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed); u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) { switch (node->kind) {
@@ -475,9 +521,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return; 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); 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 // 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)); error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
return; return;
} }
// TODO(bill): Check if valid assignment operator
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
AstNode binary_expr = {AstNode_BinaryExpr}; AstNode binary_expr = {AstNode_BinaryExpr};
ast_node(be, BinaryExpr, &binary_expr); 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) { if (c->context.in_defer) {
error(rs->token, "You cannot `return` within a defer statement"); error(rs->token, "You cannot `return` within a defer statement");
// TODO(bill): Should I break here?
break; break;
} }
@@ -619,7 +660,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(fs, ForStmt, node); case_ast_node(fs, ForStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
if (fs->init != NULL) { if (fs->init != NULL) {
check_stmt(c, fs->init, 0); 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; u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, rs->label);
Type *val = NULL; Type *val = NULL;
Type *idx = NULL; Type *idx = NULL;
Entity *entities[2] = {0}; 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; case Token_Ellipsis: op = Token_Lt; break;
default: error(ie->op, "Invalid range operator"); 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) { if (!ok) {
// TODO(bill): Better error message // TODO(bill): Better error message
error(ie->op, "Invalid interval range"); error(ie->op, "Invalid interval range");
@@ -982,8 +1027,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
mod_flags |= Stmt_BreakAllowed; mod_flags |= Stmt_BreakAllowed;
check_open_scope(c, node); check_open_scope(c, node);
bool is_union_ptr = false; MatchTypeKind match_type_kind = MatchType_Invalid;
bool is_any = false;
if (ms->tag->kind != AstNode_AssignStmt) { if (ms->tag->kind != AstNode_AssignStmt) {
error_node(ms->tag, "Expected an `in` assignment for this type match statement"); 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_expr(c, &x, rhs);
check_assignment(c, &x, NULL, str_lit("type match expression")); 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); gbString str = type_to_string(x.type);
error_node(x.expr, error_node(x.expr,
"Invalid type for this type match expression, got `%s`", str); "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 // TODO(bill): Make robust
Type *bt = base_type(type_deref(x.type)); Type *bt = base_type(type_deref(x.type));
AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL; AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
Type *case_type = NULL; Type *case_type = NULL;
if (type_expr != NULL) { // Otherwise it's a default expression if (type_expr != NULL) { // Otherwise it's a default expression
Operand y = {0}; Operand y = {0};
check_expr_or_type(c, &y, type_expr); check_expr_or_type(c, &y, type_expr);
if (is_union_ptr) { if (match_type_kind == MatchType_Union) {
GB_ASSERT(is_type_union(bt)); GB_ASSERT(is_type_union(bt));
bool tag_type_found = false; bool tag_type_found = false;
for (isize i = 0; i < bt->Record.variant_count; i++) { 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; continue;
} }
case_type = y.type; case_type = y.type;
} else if (is_any) { } else if (match_type_kind == MatchType_Any) {
case_type = y.type; case_type = y.type;
} else { } else {
GB_PANIC("Unknown type to type match statement"); 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); check_open_scope(c, stmt);
if (case_type == NULL) { if (case_type == NULL) {
if (is_union_ptr) { case_type = type_deref(x.type);
case_type = type_deref(x.type);
} else {
case_type = x.type;
}
} }
add_type_info_type(c, case_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 // NOTE(bill): Dummy type
Type *tt = case_type; Type *tt = case_type;
if (is_union_ptr) { if (is_type_pointer(x.type)) {
tt = make_type_pointer(c->allocator, case_type); tt = make_type_pointer(c->allocator, case_type);
add_type_info_type(c, tt); 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)); error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
break; 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_end;
case_ast_node(us, UsingStmt, node); case_ast_node(us, UsingStmt, node);
switch (us->node->kind) { if (us->list.count == 0) {
default: error(us->token, "Empty `using` list");
// TODO(bill): Better error message for invalid using statement return;
error(us->token, "Invalid `using` statement"); }
break; for_array(i, us->list) {
case_ast_node(es, ExprStmt, us->node); AstNode *expr = unparen_expr(us->list.e[0]);
// TODO(bill): Allow for just a LHS expression list rather than this silly code
Entity *e = NULL; Entity *e = NULL;
bool is_selector = false; bool is_selector = false;
AstNode *expr = unparen_expr(es->expr);
if (expr->kind == AstNode_Ident) { if (expr->kind == AstNode_Ident) {
String name = expr->Ident.string; String name = expr->Ident.string;
e = scope_lookup_entity(c->context.scope, name); 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}; Operand o = {0};
e = check_selector(c, &o, expr, NULL); e = check_selector(c, &o, expr, NULL);
is_selector = true; is_selector = true;
} else if (expr->kind == AstNode_Implicit) {
error(us->token, "`using` applied to an implicit value");
continue;
} }
if (e == NULL) { if (e == NULL) {
error(us->token, "`using` applied to an unknown entity"); error(us->token, "`using` applied to an unknown entity");
return; continue;
} }
switch (e->kind) { 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`"); error(us->token, "`using` cannot be applied to `nil`");
break; break;
case Entity_Label:
error(us->token, "`using` cannot be applied to a label");
break;
case Entity_Invalid: case Entity_Invalid:
error(us->token, "`using` cannot be applied to an invalid entity"); error(us->token, "`using` cannot be applied to an invalid entity");
break; break;
@@ -1303,13 +1368,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
default: default:
GB_PANIC("TODO(bill): `using` other expressions?"); GB_PANIC("TODO(bill): `using` other expressions?");
} }
case_end;
} }
case_end; case_end;
case_ast_node(pa, PushAllocator, node); case_ast_node(pa, PushAllocator, node);
Operand op = {0}; Operand op = {0};
check_expr(c, &op, pa->expr); check_expr(c, &op, pa->expr);
+19 -7
View File
@@ -98,7 +98,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
// {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, // {STR_LIT("ptr_offset"), 2, false, Expr_Expr},
// {STR_LIT("ptr_sub"), 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("slice_to_bytes"), 1, false, Expr_Stmt},
{STR_LIT("min"), 2, false, Expr_Expr}, {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 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
typedef struct DeclInfo { typedef struct DeclInfo {
Scope *scope; Scope *scope;
@@ -175,16 +180,19 @@ typedef struct DeclInfo {
AstNode *proc_lit; // AstNode_ProcLit AstNode *proc_lit; // AstNode_ProcLit
MapBool deps; // Key: Entity * MapBool deps; // Key: Entity *
Array(BlockLabel) labels;
} DeclInfo; } DeclInfo;
// ProcedureInfo stores the information needed for checking a procedure // ProcedureInfo stores the information needed for checking a procedure
typedef struct ProcedureInfo { typedef struct ProcedureInfo {
AstFile * file; AstFile * file;
Token token; Token token;
DeclInfo *decl; DeclInfo * decl;
Type * type; // Type_Procedure Type * type; // Type_Procedure
AstNode * body; // AstNode_BlockStmt AstNode * body; // AstNode_BlockStmt
u32 tags; u32 tags;
} ProcedureInfo; } ProcedureInfo;
// ExprInfo stores information used for "untyped" expressions // ExprInfo stores information used for "untyped" expressions
@@ -320,6 +328,7 @@ typedef Array(DelayedEntity) DelayedEntities;
void init_declaration_info(DeclInfo *d, Scope *scope) { void init_declaration_info(DeclInfo *d, Scope *scope) {
d->scope = scope; d->scope = scope;
map_bool_init(&d->deps, heap_allocator()); map_bool_init(&d->deps, heap_allocator());
array_init(&d->labels, heap_allocator());
} }
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) { 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) { if (found) {
Entity *e = *found; Entity *e = *found;
if (gone_thru_proc) { if (gone_thru_proc) {
if (e->kind == Entity_Label) {
continue;
}
if (e->kind == Entity_Variable && if (e->kind == Entity_Variable &&
!e->scope->is_file && !e->scope->is_file &&
!e->scope->is_global) { !e->scope->is_global) {
+15 -1
View File
@@ -15,12 +15,13 @@ typedef struct Type Type;
ENTITY_KIND(LibraryName) \ ENTITY_KIND(LibraryName) \
ENTITY_KIND(Alias) \ ENTITY_KIND(Alias) \
ENTITY_KIND(Nil) \ ENTITY_KIND(Nil) \
ENTITY_KIND(Count) ENTITY_KIND(Label)
typedef enum EntityKind { typedef enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k), #define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
ENTITY_KINDS ENTITY_KINDS
#undef ENTITY_KIND #undef ENTITY_KIND
Entity_Count,
} EntityKind; } EntityKind;
String const entity_strings[] = { String const entity_strings[] = {
@@ -100,6 +101,10 @@ struct Entity {
Entity *original; Entity *original;
} Alias; } Alias;
i32 Nil; i32 Nil;
struct {
String name;
AstNode *node;
} Label;
}; };
}; };
@@ -235,6 +240,15 @@ Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
return entity; 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) { Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_"); token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL, false); return make_entity_variable(a, scope, token, NULL, false);
+21 -15
View File
@@ -82,6 +82,7 @@ ExactValue exact_value_integer_from_string(String string) {
case 'b': base = 2; has_prefix = true; break; case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break; case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; 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; case 'x': base = 16; has_prefix = true; break;
} }
} }
@@ -100,14 +101,10 @@ ExactValue exact_value_integer_from_string(String string) {
continue; continue;
} }
i64 v = 0; i64 v = 0;
if (gb_char_is_digit(r)) { v = digit_value(r);
v = r - '0'; if (v >= base) {
} else if (gb_char_is_hex_digit(r)) {
v = gb_hex_digit_to_int(r);
} else {
break; break;
} }
result *= base; result *= base;
result += v; result += v;
} }
@@ -137,10 +134,10 @@ ExactValue exact_value_float_from_string(String string) {
if (r == '_') { if (r == '_') {
continue; continue;
} }
if (!gb_char_is_digit(r)) { i64 v = digit_value(r);
if (v >= 10) {
break; break;
} }
i64 v = r - '0';
value *= 10.0; value *= 10.0;
value += v; value += v;
} }
@@ -153,29 +150,38 @@ ExactValue exact_value_float_from_string(String string) {
if (r == '_') { if (r == '_') {
continue; continue;
} }
if (!gb_char_is_digit(r)) { i64 v = digit_value(r);
if (v >= 10) {
break; break;
} }
value += (r-'0')/pow10; value += v/pow10;
pow10 *= 10.0; pow10 *= 10.0;
} }
} }
f64 frac = 0; bool frac = false;
f64 scale = 1.0; f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) { if ((str[i] == 'e') || (str[i] == 'E')) {
i++; i++;
if (str[i] == '-') { if (str[i] == '-') {
frac = 1; frac = true;
i++; i++;
} else if (str[i] == '+') { } else if (str[i] == '+') {
i++; i++;
} }
u32 exp; u32 exp = 0;
for (exp = 0; gb_char_is_digit(str[i]); i++) { for (; i < len; i++) {
exp = exp * 10 + (str[i]-'0'); 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; if (exp > 308) exp = 308;
+4
View File
@@ -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)) #define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count))
#endif #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)` // NOTE(bill): Very similar to doing `*cast(T *)(&u)`
#ifndef GB_BIT_CAST #ifndef GB_BIT_CAST
#define GB_BIT_CAST(dest, source) do { \ #define GB_BIT_CAST(dest, source) do { \
+180 -72
View File
@@ -91,7 +91,7 @@ typedef enum irDeferKind {
typedef struct irDefer { typedef struct irDefer {
irDeferKind kind; irDeferKind kind;
isize scope_index; isize scope_index;
irBlock * block; irBlock * block;
union { union {
AstNode *stmt; AstNode *stmt;
@@ -100,32 +100,41 @@ typedef struct irDefer {
}; };
} irDefer; } irDefer;
typedef struct irBranchBlocks {
AstNode *label;
irBlock *break_;
irBlock *continue_;
} irBranchBlocks;
struct irProcedure { struct irProcedure {
irProcedure * parent; irProcedure * parent;
Array(irProcedure *) children; Array(irProcedure *) children;
Entity * entity; Entity * entity;
irModule * module; irModule * module;
String name; String name;
Type * type; Type * type;
AstNode * type_expr; AstNode * type_expr;
AstNode * body; AstNode * body;
u64 tags; u64 tags;
irValueArray params; irValueArray params;
Array(irDefer) defer_stmts; Array(irDefer) defer_stmts;
Array(irBlock *) blocks; Array(irBlock *) blocks;
i32 scope_index; i32 scope_index;
irBlock * decl_block; irBlock * decl_block;
irBlock * entry_block; irBlock * entry_block;
irBlock * curr_block; irBlock * curr_block;
irTargetList * target_list; irTargetList * target_list;
irValueArray referrers; irValueArray referrers;
Array(irBranchBlocks) branch_blocks;
i32 local_count; i32 local_count;
i32 instr_count; i32 instr_count;
i32 block_count; i32 block_count;
}; };
#define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #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) { irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
#if 1 #if 1
// NOTE(bill): Sanity check // NOTE(bill): Sanity check
Type *a = core_type(type_deref(ir_type(address))); Type *a = type_deref(ir_type(address));
Type *b = core_type(ir_type(value)); Type *b = ir_type(value);
if (!is_type_untyped(b)) { 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 #endif
return ir_emit(p, ir_instr_store(p, address, value)); 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) { irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) {
Type *t = ir_type(u); Type *t = ir_type(u);
GB_ASSERT(is_type_pointer(t) && GB_ASSERT_MSG(is_type_pointer(t) &&
is_type_union(type_deref(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)); irValue *tag_ptr = ir_emit(proc, ir_instr_union_tag_ptr(proc, u));
Type *tpt = ir_type(tag_ptr); Type *tpt = ir_type(tag_ptr);
GB_ASSERT(is_type_pointer(tpt)); 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); GB_ASSERT(sel.index.count > 0);
Type *type = type_deref(ir_type(e));
for_array(i, sel.index) { for_array(i, sel.index) {
i32 index = cast(i32)sel.index.e[i]; 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_load(proc, e);
e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? 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)) { 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 case 2: e = ir_emit_struct_ep(proc, e, 3); break; // allocator
} }
} else { } 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); GB_ASSERT(sel.index.count > 0);
Type *type = ir_type(e);
for_array(i, sel.index) { for_array(i, sel.index) {
i32 index = cast(i32)sel.index.e[i]; 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) { if (src_is_ptr) {
value = ir_emit_load(proc, value); 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: { case BuiltinProc_clear: {
ir_emit_comment(proc, str_lit("clear")); ir_emit_comment(proc, str_lit("clear"));
irValue *ptr = ir_build_addr(proc, ce->args.e[0]).addr; Type *original_type = type_of_expr(proc->module->info, ce->args.e[0]);
Type *t = base_type(type_deref(ir_type(ptr))); 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)) { if (is_type_dynamic_array(t)) {
irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1); irValue *count_ptr = ir_emit_struct_ep(proc, ptr, 1);
ir_emit_store(proc, count_ptr, v_zero); 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); 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, ha, 1), v_zero);
ir_emit_store(proc, ir_emit_struct_ep(proc, ea, 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 { } else {
GB_PANIC("TODO(bill): ir clear for `%s`", type_to_string(t)); 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 *ptr = ir_build_expr(proc, ce->args.e[0]);
irValue *count = ir_build_expr(proc, ce->args.e[1]); irValue *count = ir_build_expr(proc, ce->args.e[1]);
count = ir_emit_conv(proc, count, t_int); 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))); Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr)));
irValue *slice = ir_add_local_generated(proc, slice_type); 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); return ir_emit_load(proc, slice);
} break; } break;
@@ -3809,7 +3833,8 @@ irValue *ir_get_using_variable(irProcedure *proc, Entity *e) {
v = ir_build_addr(proc, e->using_expr).addr; v = ir_build_addr(proc, e->using_expr).addr;
} }
GB_ASSERT(v != NULL); 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) { // 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); GB_ASSERT(sel.entity != NULL);
irValue *a = ir_build_addr(proc, se->expr).addr; 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); return ir_addr(a);
} else { } else {
Type *type = base_type(type_of_expr(proc->module->info, se->expr)); 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); GB_ASSERT(sel.entity != NULL);
irValue *a = ir_build_addr(proc, se->expr).addr; 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); return ir_addr(a);
} }
case_end; case_end;
@@ -4032,7 +4057,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
if (using_field != NULL) { if (using_field != NULL) {
Selection sel = lookup_field(a, t, using_field->token.string, false); Selection sel = lookup_field(a, t, using_field->token.string, false);
irValue *e = ir_build_addr(proc, ie->expr).addr; 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; t = using_field->type;
} }
@@ -4817,6 +4842,44 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type
if (done_) *done_ = done; 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) { void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
switch (node->kind) { switch (node->kind) {
@@ -4824,9 +4887,11 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
case_end; case_end;
case_ast_node(us, UsingStmt, node); case_ast_node(us, UsingStmt, node);
AstNode *decl = unparen_expr(us->node); for_array(i, us->list) {
if (decl->kind == AstNode_ValueDecl) { AstNode *decl = unparen_expr(us->list.e[i]);
ir_build_stmt(proc, decl); if (decl->kind == AstNode_ValueDecl) {
ir_build_stmt(proc, decl);
}
} }
case_end; case_end;
@@ -4944,7 +5009,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
irValue *value = ir_value_procedure(proc->module->allocator, 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.tags = pd->tags;
value->Proc.parent = proc; value->Proc.parent = proc;
@@ -5172,6 +5237,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
case_ast_node(fs, ForStmt, node); case_ast_node(fs, ForStmt, node);
ir_emit_comment(proc, str_lit("ForStmt")); ir_emit_comment(proc, str_lit("ForStmt"));
if (fs->init != NULL) { if (fs->init != NULL) {
irBlock *init = ir_new_block(proc, node, "for.init"); irBlock *init = ir_new_block(proc, node, "for.init");
ir_emit_jump(proc, init); ir_emit_jump(proc, init);
@@ -5188,6 +5254,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
if (fs->post != NULL) { if (fs->post != NULL) {
post = ir_new_block(proc, node, "for.post"); post = ir_new_block(proc, node, "for.post");
} }
ir_emit_jump(proc, loop); ir_emit_jump(proc, loop);
ir_start_block(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_start_block(proc, body);
} }
ir_set_label_blocks(proc, fs->label, done, post);
ir_push_target_list(proc, done, post, NULL); ir_push_target_list(proc, done, post, NULL);
ir_open_scope(proc); 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_addr_store(proc, idx_addr, index);
} }
ir_set_label_blocks(proc, rs->label, done, loop);
ir_push_target_list(proc, done, loop, NULL); ir_push_target_list(proc, done, loop, NULL);
ir_open_scope(proc); ir_open_scope(proc);
@@ -5442,16 +5512,23 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
AstNode *rhs = as->rhs.e[0]; AstNode *rhs = as->rhs.e[0];
irValue *parent = ir_build_expr(proc, rhs); irValue *parent = ir_build_expr(proc, rhs);
bool is_union_ptr = false; bool is_parent_ptr = is_type_pointer(ir_type(parent));
bool is_any = false; MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent));
GB_ASSERT(check_valid_type_match_type(ir_type(parent), &is_union_ptr, &is_any)); GB_ASSERT(match_type_kind != MatchType_Invalid);
irValue *tag_index = NULL; irValue *tag_index = NULL;
irValue *union_data = 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")); ir_emit_comment(proc, str_lit("get union's tag"));
tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent)); tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent));
union_data = ir_emit_conv(proc, parent, t_rawptr); 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"); 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; Type *tag_var_type = NULL;
if (str_eq(tag_var_name, str_lit("_"))) { if (str_eq(tag_var_name, str_lit("_"))) {
Type *t = type_of_expr(proc->module->info, cc->list.e[0]); 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); t = make_type_pointer(proc->module->allocator, t);
} }
tag_var_type = t; tag_var_type = t;
@@ -5505,7 +5582,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
} else { } else {
tag_var = ir_add_local_generated(proc, tag_var_type); 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; continue;
} }
GB_ASSERT(cc->list.count == 1); 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"); 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); Type *bt = type_deref(tag_var_type);
irValue *index = NULL; irValue *index = NULL;
Type *ut = base_type(type_deref(ir_type(parent))); 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); tag_var = ir_add_local_generated(proc, tag_var_type);
} }
Type *bt_ptr = make_type_pointer(proc->module->allocator, bt);
irValue *data_ptr = ir_emit_conv(proc, union_data, tag_var_type); 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); ir_emit_store(proc, tag_var, data_ptr);
cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index); 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; 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)); irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type));
if (tag_var_entity != NULL) { if (tag_var_entity != NULL) {
ir_module_add_value(proc->module, tag_var_entity, data); 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); irValue *case_ti = ir_type_info(proc, type);
cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
} else { } else {
@@ -5588,22 +5673,34 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
case_ast_node(bs, BranchStmt, node); case_ast_node(bs, BranchStmt, node);
irBlock *block = NULL; irBlock *block = NULL;
switch (bs->token.kind) {
case Token_break: if (bs->label != NULL) {
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { irBranchBlocks bb = ir_lookup_branch_blocks(proc, bs->label);
block = t->break_; 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; } else {
case Token_continue: switch (bs->token.kind) {
for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { case Token_break:
block = t->continue_; 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) { if (block != NULL) {
ir_emit_defer_stmts(proc, irDeferExit_Branch, block); 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) { void ir_begin_procedure_body(irProcedure *proc) {
array_add(&proc->module->procs, proc); array_add(&proc->module->procs, proc);
array_init(&proc->blocks, heap_allocator()); array_init(&proc->blocks, heap_allocator());
array_init(&proc->defer_stmts, heap_allocator()); array_init(&proc->defer_stmts, heap_allocator());
array_init(&proc->children, 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"); proc->decl_block = ir_new_block(proc, proc->type_expr, "decls");
ir_start_block(proc, proc->decl_block); ir_start_block(proc, proc->decl_block);
+95 -55
View File
@@ -219,6 +219,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \ }) \
AST_NODE_KIND(ForStmt, "for statement", struct { \ AST_NODE_KIND(ForStmt, "for statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *init; \ AstNode *init; \
AstNode *cond; \ AstNode *cond; \
AstNode *post; \ AstNode *post; \
@@ -226,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \ }) \
AST_NODE_KIND(RangeStmt, "range statement", struct { \ AST_NODE_KIND(RangeStmt, "range statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *value; \ AstNode *value; \
AstNode *index; \ AstNode *index; \
Token in_token; \ Token in_token; \
@@ -249,10 +251,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
AstNode *body; \ AstNode *body; \
}) \ }) \
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ 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 { \ AST_NODE_KIND(UsingStmt, "using statement", struct { \
Token token; \ Token token; \
AstNode *node; \ AstNodeArray list; \
}) \ }) \
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
Token string; \ Token string; \
@@ -305,6 +307,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
AstNode *cond; \ AstNode *cond; \
bool is_system; \ bool is_system; \
}) \ }) \
AST_NODE_KIND(Label, "label", struct { \
Token token; \
AstNode *name; \
}) \
AST_NODE_KIND(_DeclEnd, "", i32) \ AST_NODE_KIND(_DeclEnd, "", i32) \
AST_NODE_KIND(Field, "field", struct { \ AST_NODE_KIND(Field, "field", struct { \
AstNodeArray names; \ 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_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]);
case AstNode_ImportDecl: return node->ImportDecl.token; case AstNode_ImportDecl: return node->ImportDecl.token;
case AstNode_ForeignLibrary: return node->ForeignLibrary.token; case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
case AstNode_Label: return node->Label.token;
case AstNode_Field: 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); AstNode *result = make_ast_node(f, AstNode_ForStmt);
result->ForStmt.token = token; result->ForStmt.token = token;
result->ForStmt.label = label;
result->ForStmt.init = init; result->ForStmt.init = init;
result->ForStmt.cond = cond; result->ForStmt.cond = cond;
result->ForStmt.post = post; result->ForStmt.post = post;
@@ -879,8 +887,9 @@ AstNode *ast_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
return result; 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); AstNode *result = make_ast_node(f, AstNode_RangeStmt);
result->RangeStmt.label = label;
result->RangeStmt.token = token; result->RangeStmt.token = token;
result->RangeStmt.value = value; result->RangeStmt.value = value;
result->RangeStmt.index = index; result->RangeStmt.index = index;
@@ -924,19 +933,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
return result; 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); AstNode *result = make_ast_node(f, AstNode_BranchStmt);
result->BranchStmt.token = token; result->BranchStmt.token = token;
result->BranchStmt.label = label;
return result; 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); AstNode *result = make_ast_node(f, AstNode_UsingStmt);
result->UsingStmt.token = token; result->UsingStmt.token = token;
result->UsingStmt.node = node; result->UsingStmt.list = list;
return result; return result;
} }
AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) { AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
AstNode *result = make_ast_node(f, AstNode_AsmOperand); AstNode *result = make_ast_node(f, AstNode_AsmOperand);
result->AsmOperand.string = string; result->AsmOperand.string = string;
@@ -1138,6 +1149,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
return result; 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) { bool next_token(AstFile *f) {
Token prev = f->curr_token; Token prev = f->curr_token;
@@ -2335,7 +2353,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
return parse_body(f); 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) { AstNode *parse_results(AstFile *f) {
@@ -2354,7 +2372,7 @@ AstNode *parse_results(AstFile *f) {
AstNode *list = NULL; AstNode *list = NULL;
expect_token(f, Token_OpenParen); 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"); expect_token_after(f, Token_CloseParen, "parameter list");
return 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); Token proc_token = expect_token(f, Token_proc);
expect_token(f, Token_OpenParen); 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"); expect_token_after(f, Token_CloseParen, "parameter list");
results = parse_results(f); 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); 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) { AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) { if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
Token tok = f->curr_token; Token tok = f->curr_token;
@@ -2505,7 +2512,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
return idents; 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; Token start_token = f->curr_token;
AstNodeArray params = make_ast_node_array(f); 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); AstNode *param = ast_field(f, names, type, set_flags);
array_add(&params, param); array_add(&params, param);
parse_expect_separator(f, separator, type); parse_expect_field_separator(f, type);
while (f->curr_token.kind != follow && while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF) { 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); AstNode *param = ast_field(f, names, type, set_flags);
array_add(&params, param); array_add(&params, param);
if (parse_expect_separator(f, separator, param)) { if (!parse_expect_field_separator(f, param)) {
break; 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) { 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) { AstNode *parse_type_or_ident(AstFile *f) {
@@ -2987,7 +3009,7 @@ AstNode *parse_return_stmt(AstFile *f) {
// return ast_expr_stmt(f, ge); // 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) { if (f->curr_proc == NULL) {
syntax_error(f->curr_token, "You cannot use a for statement in the file scope"); 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); 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) { if (cond->AssignStmt.rhs.count > 0) {
rhs = cond->AssignStmt.rhs.e[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")); 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 #if 0
Token token = expect_token(f, Token_for); 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_if: return parse_if_stmt(f);
case Token_when: return parse_when_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_match: return parse_match_stmt(f);
case Token_defer: return parse_defer_stmt(f); case Token_defer: return parse_defer_stmt(f);
case Token_asm: return parse_asm_stmt(f); case Token_asm: return parse_asm_stmt(f);
@@ -3271,43 +3293,47 @@ AstNode *parse_stmt(AstFile *f) {
case Token_break: case Token_break:
case Token_continue: case Token_continue:
case Token_fallthrough: case Token_fallthrough: {
AstNode *label = NULL;
next_token(f); 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); expect_semicolon(f, s);
return s; return s;
}
case Token_using: { case Token_using: {
// TODO(bill): Make using statements better // TODO(bill): Make using statements better
Token token = expect_token(f, Token_using); 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) { if (f->curr_token.kind != Token_Colon) {
case AstNode_ValueDecl: expect_semicolon(f, list.e[list.count-1]);
if (!node->ValueDecl.is_var) { 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"); 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 { } else {
if (f->curr_proc == NULL) { decl->ValueDecl.flags |= VarDeclFlag_using;
syntax_error(token, "`using` is not allowed at the file scope");
} else {
node->ValueDecl.flags |= VarDeclFlag_using;
}
} }
return node; return decl;
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;
} }
syntax_error(token, "Illegal use of `using` statement"); 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"); syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
} }
return s; 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;
}
} }
+631 -399
View File
File diff suppressed because it is too large Load Diff
+277
View File
@@ -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
};
+18 -18
View File
@@ -484,15 +484,9 @@ gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible return 16; // NOTE(bill): Larger than highest possible
} }
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) { gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
if (allow_underscore) { while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { advance_to_next_rune(t);
advance_to_next_rune(t);
}
} else {
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
}
} }
} }
@@ -506,7 +500,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
if (seen_decimal_point) { if (seen_decimal_point) {
token.kind = Token_Float; token.kind = Token_Float;
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
goto exponent; goto exponent;
} }
@@ -515,31 +509,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
advance_to_next_rune(t); advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 2, true); scan_mantissa(t, 2);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'o') { // Octal } else if (t->curr_rune == 'o') { // Octal
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 8, true); scan_mantissa(t, 8);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'd') { // Decimal } else if (t->curr_rune == 'd') { // Decimal
advance_to_next_rune(t); 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) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'x') { // Hexadecimal } else if (t->curr_rune == 'x') { // Hexadecimal
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 16, true); scan_mantissa(t, 16);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else { } else {
seen_decimal_point = false; 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') { if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true; seen_decimal_point = true;
@@ -551,7 +551,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
return token; return token;
} }
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
fraction: fraction:
if (t->curr_rune == '.') { if (t->curr_rune == '.') {
@@ -564,7 +564,7 @@ fraction:
goto end; goto end;
} }
token.kind = Token_Float; token.kind = Token_Float;
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
} }
exponent: exponent:
@@ -574,7 +574,7 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') { if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t); advance_to_next_rune(t);
} }
scan_mantissa(t, 10, false); scan_mantissa(t, 10);
} }
end: end: