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

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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];
}

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 {
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);

View File

@@ -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;
}

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);
// 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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;

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))
#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 { \

252
src/ir.c
View File

@@ -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);

View File

@@ -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(&params, 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(&params, 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;
}
}

1030
src/ssa.c

File diff suppressed because it is too large Load Diff

277
src/ssa_op.c Normal file
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
};

View File

@@ -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: